目錄
一、單例模式
二、工廠模式
三、?抽象工廠模式
四、適配器模式
五、策略模式
六、裝飾器模式
?編輯
考點:會挖空super(coffeOpertion);
七、代理模式
為什么必須要使用代理對象?
和裝飾器模式的區別
八、備忘錄模式
一、單例模式
這個模式能確保一個類只有一個實例,并提供全局訪問點,非常適合配置管理、日志對象等場景。
我們不使用單例模式的時候會導致
內存浪費(重復加載配置)
配置不一致(不同實例可能讀取不同狀態)
以下是不使用單例模式的例子
public class Printer {// 構造函數,用于初始化打印機對象時打印提示信息public Printer() {System.out.println("這是一臺普通的打印機");}public static void main(String[] args) {// 創建第一個打印機對象Printer printer1 = new Printer();// 創建第二個打印機對象Printer printer2 = new Printer();// 判斷兩個打印機對象是否為同一實例,并輸出結果System.out.println("printer1和printer2是同一臺嗎?" + (printer1 == printer2));} }
運行結果
這是使用單例模式的結果:
知識點掃盲:
// ? ?互斥訪問 使用關鍵字synchronized 意思是打印完第一個,才能打印第二個,保證有序
public class Printer1 {private static Printer1 instance;
private Printer1(){System.out.println("全公司唯一一臺打印機已經啟動");
}
private static synchronized Printer1 getInstance(){if (instance==null){instance = new Printer1();}return instance;
}public void PrintDocument(String employee,String document){System.out.println(employee+"正在打印"+document);
}
public static void main(String[] args) {
// 員工A申請使用打印機Printer1 printerA = Printer1.getInstance();
// 員工B申請使用打印機Printer1 printerB= Printer1.getInstance();
// 檢查是否是同一臺打印機System.out.println("printerA和printerB是同一臺嗎?"+(printerA==printerB));}
}
true
二、工廠模式
假設你開了一家奶茶店,顧客可以點不同口味的奶茶(原味、芒果、草莓)。
問題:如果直接在代碼里用new
創建每種奶茶,會導致:
-
代碼臃腫(每次新增口味都要改多處邏輯)
-
難以維護(制作流程分散在各處)
解決方案:用工廠模式統一管理奶茶的創建過程!
首先我們先掃盲一下接下來代碼的知識點
package org.factory;
public class Case {public static void main(String[] args) {String str1="Hello";String str2="hello";String str3="World";boolean result1 =str1.equals(str2);boolean result2 =str1.equalsIgnoreCase(str2);System.out.println(result1); // 輸出falseSystem.out.println(result2); // 輸出true}}
equalsIgnoreCase()
?是?Java 中?String
?類的一個方法,用于比較兩個字符串的內容是否相同,忽略大小寫差異。
首先,先來梳理一下代碼思路。首先定義一個接口,將制作奶茶的步驟在這個接口里。
之后創建不同的類實現先創建的接口,表示某種奶茶的具體制作過程。
之后創建工廠類,在工廠類里,客人要點什么奶茶就做什么奶茶。
最后,客人點單,具體喝什么奶茶就使用什么方法的調用。也算是做到了一個客戶與程序之間的交互。
可能有人會疑問,為什么要寫接口,為什么要寫不同的奶茶類,試想,如果我們把所有實現奶茶的方法放在一個類里,讓客戶去從一個類里調用,判斷條件會變多,代碼也會變得很長,我們在尋找這些條件的時候,運行速度可能會變慢。所以當我們想要添加新口味時,前者需要修改同一個類,而后者只需擴展,不會影響已有代碼。
這里也運用了多態的思想,客戶點奶茶時,無論奶茶是原味、芒果還是其他類型,客戶端都通過同一個接口方法(如prepare()
)操作。本質就是,不同奶茶類(Original、Mango)對這些方法有不同的實現。
經常面試題里會出現多態這樣的提問,我以前會說,就像水,可以是冰,可以是氣。現在我明白了,就像奶茶有不同的口味,就像王者榮耀的英雄同樣是攻擊卻有不同的傷害技能,可以是物理攻擊可以是法術攻擊。
接下來我們來具體實現,觀察工廠模式的作用。
至少需要5個文件來實現。
1.定義同一種類的行為模式
package org.factory;public interface MilkTea {void prepare();//準備材料void make();//制作奶茶void pack();//打包
//這里介紹了奶茶的制作過程
}
2.具體類里是做什么
我們實現接口,注意:接口是實現,類是繼承
這是經典珍珠奶茶
package org.factory;public class OriginalMilkTea implements MilkTea{
// 代碼實現流程:/*** 1.創建接口* 2.實現接口* 3.創建工廠類,在工廠中實現制造奶茶* 4.賣給客戶不同的奶茶* 我們可以創建很多類似的類去表示不同奶茶的制作方式*/@Overridepublic void prepare() {
// 如果我要做珍珠奶茶 珍珠、紅茶、牛奶System.out.println("正在準備材料");System.out.println("準備好材料:珍珠、紅茶、牛奶");}@Overridepublic void make() {System.out.println("正在制作中");}@Overridepublic void pack() {System.out.println("用經典杯打包原味奶茶!");}
}
這是紅糖姜茶
package org.factory;public class RedSugarTea implements MilkTea{/*此類我表示的是紅糖姜茶*/@Overridepublic void prepare() {System.out.println("準備材料!");System.out.println("紅糖、生姜、紅茶");}@Overridepublic void make() {System.out.println("混合熬煮中!");}@Overridepublic void pack() {System.out.println("使用夏季限定包裝杯包裝!");}/**/
}
3.工廠模式來進行判斷生產
package org.factory;public class MilkFactory {
// 工廠里面制作奶茶
// 客人需要什么奶茶我就制作什么奶茶,所以需要我們定義一個函數public static MilkTea createTea (String type){if ("Orginal".equalsIgnoreCase(type)){return new OriginalMilkTea();}else if ("RedSugar".equalsIgnoreCase(type)){return new RedSugarTea();}throw new IllegalArgumentException("沒有這種口味的奶茶");}
}
4.客戶需要去定義 這里也相當于一個測試類? 假如需要喝紅糖姜茶
package org.factory;public class Customer {public static void main(String[] args) {MilkTea RedSugar =MilkFactory.createTea("RedSugar");RedSugar.prepare();RedSugar.make();RedSugar.pack();}
}
接口就是一個數據類型,后面是一個對象,用對象去調用接口里面的方法,再識別出需要去調用哪個方法。
三、?抽象工廠模式
生活實例:蘋果 vs 小米全家桶
假設你要購買一套電子產品,包含?手機?和?耳機。不同品牌(如蘋果、小米)的產品風格不同,且手機和耳機需要配套使用(比如蘋果手機和AirPods配對更佳)。
抽象工廠模式?就是用來生產?同一品牌系列產品?的解決方案,確保你獲得的手機和耳機是同一品牌風格,避免混搭不兼容。
上一個我們講的是制作奶茶的過程,但是只是同一款產品,那就是奶茶。
但是在此設計模式中,可以同時制造耳機和手機,只需要多設計一些接口和實現類。生產?多個相關聯的產品(比如同時生產手機+耳機+手表,且保證同一品牌)。
四、適配器模式
顧名思義,適配器模式主要用于解決接口不兼容的問題,讓原本無法一起工作的類可以協同工作。生活中常見的例子是電源適配器,比如不同國家的插頭標準不同,適配器可以讓不同插頭在同一個插座上使用。
其實我覺得這個雖然好理解,但是我感覺代碼是不好理解的。
本質是功能不同的兩個類,轉為功能相同的一個類。
實際例子:現在有英語導游給中國人導游 為了讓中國人聽得懂外語 ,? ?發明了耳機適配器.
代碼實現:先明確輸出的是中文,英文是英語導游說的,適配器是將英語導游的話轉換為中文。
1.定義一個接口
package org.adapter;public interface ChineseSpeaker {
// 我們是將英文翻譯成中文 這里定義的是說中文的能力String ChineseAbility();
}
2.英語導游說英語
package org.adapter;public class EnglishGuide {public String SpeakEnglish(){return "Hello !This is a GreatWall";}
}
3.,首先讓值傳入翻譯器中,翻譯之后,傳出來
package org.adapter;public class Translator implements ChineseSpeaker{private EnglishGuide guide;public Translator(EnglishGuide guide){this.guide=guide;}
// 翻譯功能,要先傳入英文,再讓其轉換為中文@Overridepublic String ChineseAbility() {String message = guide.SpeakEnglish();
// replace原來的值 想要替換的值return message.replace("Hello !This is a","你好!這里是");
// System.out.println(replace(message,"Hi,這里是"));}
}
4.給翻譯器傳入值,調用接口實現的函數
package org.adapter;public class Test {public static void main(String[] args) {EnglishGuide englishGuide = new EnglishGuide();Translator translator = new Translator(englishGuide);System.out.println(translator.ChineseAbility());}
}
五、策略模式
這個模式很好理解,面對同一個類有不同的策略,假如你想買車,不考慮預算,你想買什么車就買什么車,可以買白色寶馬,灰色寶馬,黑色寶馬,黑色奔馳,灰色奔馳,白色小米,黃色小米,等等。任意搭配。我們把所有能夠組成的策略的類都生成出來。代碼文件很多。
需要定義的東西很多,我們需要把不同屬性作為接口抽出來,比如顏色,品牌,油型。
假如我們想要買白色特斯拉用98的油型。就是這樣的代碼。
package org.strategy;public abstract class Car {
// Car 顏色、油、品牌protected Color color;protected Soil soil;protected Brand brand;protected void info() {String info = color.color() + brand.Brand() + ": " + soil.soil();System.out.println(info);}}
package org.strategy;public class tebaiba extends Car{public tebaiba() {color = new WhiteImp();brand = new Tesila();soil = new Ninety_eight();}
}
package org.strategy;public class Test {public static void main(String[] args) {Car tebaiba = new tebaiba();tebaiba.info();}
}
White特斯拉: 98
在考試的時候最有可能挖空就是挖?
第一個空一般都是接口或者抽象類。
六、裝飾器模式
我使用了DeepSeek預測,今年最可能考的就是裝飾器模式和代理模式。
不修改原有對象,只是將原有對象進行加工。
這里我們使用(咖啡加料系統)來進行學習。
假如普通咖啡10元,不加糖。假如我們這里有兩種操作。
A類咖啡:我們需要加椰果來增加口感,價格就變成了12元。
B類咖啡:我們需要加牛奶來增加醇香,價格就變成了15元。
1.? ? ? ?接口類
package org.Decorator;public interface CoffeOpertion {
// 1.對咖啡進行加料String addSugar();
// 2.對咖啡進行加價double price(double money);
}
2.? ? ? ?實現普通咖啡類
package org.Decorator;public class SimpleCoffe implements CoffeOpertion{
// 我們首先要知道普通咖啡是什么規格,再進行裝飾@Overridepublic String addSugar() {return "無糖";}@Overridepublic double price(double money) {return 10.0;}
}
3.? ? ? ?抽象裝飾類
package org.Decorator;
//抽象裝飾類
public abstract class CoffeeDecorator implements CoffeOpertion {protected CoffeOpertion coffeOpertion;
// 這個是裝飾類,你要把裝飾的對象傳進來public CoffeeDecorator(CoffeOpertion coffeOpertion){this.coffeOpertion=coffeOpertion;}}
4.? ? ? ?A操作
package org.Decorator;public class CoffeDecoratorA extends CoffeeDecorator{public CoffeDecoratorA(CoffeOpertion coffeOpertion) {super(coffeOpertion);}@Overridepublic String addSugar() {return "A類咖啡飲品,額外加椰果";}@Overridepublic double price(double money) {money = money+2;return money;}
}
5.B操作
package org.Decorator;public class CoffeDecoratorB extends CoffeeDecorator{public CoffeDecoratorB(CoffeOpertion coffeOpertion) {super(coffeOpertion);}@Overridepublic String addSugar() {return "拿鐵咖啡,額外加牛奶";}@Overridepublic double price(double money) {money =money+5;return money;}
}
6.? ? ? ?實現類
package org.Decorator;public class Coffeshop extends SimpleCoffe{public static void main(String[] args) {SimpleCoffe simple = new SimpleCoffe();String sugar=simple.addSugar();double money = simple.price(10);System.out.println("這是普通咖啡的規格:"+sugar + ",價格" + money);CoffeOpertion coffeOpertionA= new CoffeDecoratorA(new SimpleCoffe());CoffeDecoratorB coffeDecoratorB = new CoffeDecoratorB(new SimpleCoffe());System.out.println(coffeOpertionA.addSugar()+"價格為"+coffeOpertionA.price(10));System.out.println(coffeDecoratorB.addSugar()+"價格為"+coffeDecoratorB.price(10));}
}
這是運行結果
考點:會挖空super(coffeOpertion);
在子類的構造函數里一定要強調父類。
public CoffeDecoratorA(CoffeOpertion coffeOpertion) {super(coffeOpertion);
// this.coffeOpertion=coffeOpertion;}
七、代理模式
為什么必須要使用代理對象?
1.隱藏復雜性 :代理對象可以控制何時、如何創建真實對象(例如延遲加載)
2.功能擴展:代理可以在不修改真實對象的前提下,添加額外邏輯(緩存、權限驗證)
3.接口透明:客戶端無需知道背后是代理還是真實對象,只需調用統一接口
和裝飾器模式的區別
-
代理:控制訪問,側重隱藏真實對象(如權限控制)。
-
裝飾器:動態擴展功能,側重增強(如咖啡加糖)。
代碼實現:一個真實的類,一個代理真實的類,一個抽象的接口,代理與真實的類擁有相同的功能。在特定條件下被實現,比如,老師今天感冒不能上課就由課代表代表上課。
2
1.
package org.Proxy;public interface Lesson {String CanLesson();
}
2.
package org.Proxy;public class RealTeacher implements Lesson{@Overridepublic String CanLesson() {return "老師正在朗讀荷塘月色";}
}
3.
package org.Proxy;public class StudentProxy implements Lesson{
// 關鍵:持有真實對象的引用protected RealTeacher teacher;
// 構造函數public StudentProxy(){this.teacher = new RealTeacher();}
// 添加檢查權限,沒有老師的時候學生上課public boolean checkPermission(){return false;}@Overridepublic String CanLesson() {// 添加代理邏輯(示例:權限校驗)if (checkPermission()) {return "老師在,老師上課:\n" +teacher.CanLesson();} else {return "老師不在學生代替老師上課,語文課代表正在朗讀荷塘月色\n";}}
}
4.
package org.Proxy;public class Test {public static void main(String[] args) {StudentProxy proxy = new StudentProxy();String message= proxy.CanLesson();System.out.println(message);}
}
對我來說寫多了,好像調用來調用去沒什么區別哈哈。。。