設計模式(Design pattern)是一套被反複使用、多數人知曉的、經過分類編目的、代碼設計經驗的總結。
設計模式分為三種類型,共23種:
- 建立型模式:單例模式、抽象工廠模式、建造者模式、工廠模式、原型模式。
- 結構型模式:擴充卡模式、橋接模式、裝飾模式、組合模式、外觀模式、享元模式、代理模式。
- 行為型模式:模版方法模式、指令模式、疊代器模式、觀察者模式、中介者模式、備忘錄模式、解釋器模式、狀态模式、政策模式、責任鍊模式、通路者模式。
GitHub:youlookwhat/DesignPattern
參照Hongyang、菜鳥教程等處文章所寫。如有錯誤歡迎指正,如有侵權,請聯系我删除。
Blog Catalogue:
- 1. 設計模式 觀察者模式(Observer Pattern) 以微信公衆服務為例
- 2. 設計模式 工廠模式(Factory Pattern) 從賣肉夾馍說起
- 3. 設計模式 單例設計模式(Singleton Pattern) 完全解析
- 4. 設計模式 政策模式(Strategy Pattern) 以角色遊戲為背景
- 5. 設計模式 擴充卡模式(Adapter Pattern) 以手機充電器為例
- 6. 設計模式 指令模式(Command Pattern) 管理智能家電
- 7. 設計模式 裝飾者模式(Decorator Pattern) 帶你重回傳奇世界
- 8. 設計模式 外觀模式(Facade Pattern) 一鍵電影模式
- 9. 設計模式 模版方法模式(Template Method Pattern) 展現程式員的一天
- 10. 設計模式 狀态模式(State Pattern) 以自動售貨機為例
- 11. 設計模式 建造者模式(Builder Pattern) 以造汽車買汽車為例
- 12. 設計模式 原型模式(Prototype Pattern) 以擷取多種形狀為例
- 13. 設計模式 享元模式(Flyweight Pattern) 以随機擷取多種形狀為例
- 14. 設計模式 代理模式(Proxy Pattern) 以擷取磁盤中的圖檔為例
- 15. 設計模式 橋接模式(Bridge Pattern) 以畫不同顔色的圓為例
- 16. 設計模式 組合模式(Composite Pattern) 以建立和列印員工的層次結構為例
- 17. 設計模式 疊代器模式(Iterator Pattern) 以使用疊代器列印名字為例
- 18. 設計模式 中介者模式(Mediator Pattern) 以公共聊天室為例
- 19. 設計模式 備忘錄模式(Memento Pattern) 以使用備忘錄為例
- 20. 設計模式 解釋器模式(Interpreter Pattern) 以解釋一句話為例
- 21. 設計模式 責任鍊模式(Chain of Responsibility Pattern) 以Android Studio中列印日志為例
- 22. 設計模式 通路者模式(Visitor Pattern) 以顯示計算機的組成部分為例
Source Code
- Observer
- Factory
- Singleton
- Strategy
- Adapter
- Command
- Decorator
- Facade
- Template Method
- State
- Builder
- Prototype
- Flyweight
- Proxy
- Bridge
- Composite
- Iterator
- Mediator
- Memento
- Chain of Responsibility
- Visitor
Project Picture

image
image
Pattern Analysis
1. 觀察者模式
定義了對象之間的一對多的依賴,這樣一來,當一個對象改變時,它的所有的依賴者都會收到通知并自動更新。
- 對于JDK或者Andorid中都有很多地方實作了觀察者模式,比如XXXView.addXXXListenter , 當然了 XXXView.setOnXXXListener不一定是觀察者模式,因為觀察者模式是一種一對多的關系,對于setXXXListener是1對1的關系,應該叫回調。
-
專題接口:Subject.java ;
/** * 注冊一個觀察者 */ public void registerObserver(Observer observer); /** * 移除一個觀察者 */ public void removeObserver(Observer observer); /** * 通知所有觀察者 */ public void notifyObservers();
-
3D服務号的實作類:ObjectFor3D.java
@Override public void registerObserver(Observer observer) { observers.add(observer); } @Override public void removeObserver(Observer observer) { int index = observers.indexOf(observer); if (index >= 0) { observers.remove(index); } } @Override public void notifyObservers() { for (Observer observer : observers) { observer.update(msg); } } /** * 主題更新資訊 */ public void setMsg(String msg) { this.msg = msg; notifyObservers(); }
-
所有觀察者需要實作此接口:Observer.java
public ObserverUser1(Subject subject) { subject.registerObserver(this); } @Override public void update(String msg) { Log.e("-----ObserverUser1 ", "得到 3D 号碼:" + msg + ", 我要記下來。"); }
-
最後測試:ObserverActivity.java
// 建立服務号 objectFor3D = new ObjectFor3D(); // 建立兩個訂閱者 observerUser1 = new ObserverUser1(objectFor3D); observerUser2 = new ObserverUser2(objectFor3D); // 兩個觀察者,發送兩條資訊 objectFor3D.setMsg("201610121 的3D号為:127"); objectFor3D.setMsg("20161022 的3D号為:000");
2. 工廠模式
簡單列一下這個模式的家族:
- 1、靜态工廠模式
- 這個最常見了,項目中的輔助類,TextUtil.isEmpty等,類+靜态方法。
- 2、簡單工廠模式(店裡買肉夾馍)
- 定義:通過專門定義一個類來負責建立其他類的執行個體,被建立的執行個體通常都具有共同的父類。
- 根據類型直接建立肉夾馍:SimpleRoujiaMoFactory.java
- 3、工廠方法模式(開分店)
- 定義:定義一個建立對象的接口,但由子類決定要執行個體化的類是哪一個。工廠方法模式把類執行個體化的過程推遲到子類。
- 對比定義:
- 1、定義了建立對象的一個接口:public abstract RouJiaMo sellRoujiaMo(String type);
- 2、由子類決定執行個體化的類,可以看到我們的馍是子類生成的。
-
提供建立肉夾馍店抽象方法:RoujiaMoStore.java
public abstract RoujiaMo sellRoujiaMo(String type);
- 具體實作抽象方法:XianRoujiaMoStore.java
- 分店依舊使用簡單工廠模式:XianSimpleRoujiaMoFactory.java
- 4、抽象工廠模式(使用官方提供的原料)
- 定義:提供一個接口,用于建立相關的或依賴對象的家族,而不需要明确指定具體類。
- 對比定義:
- 1、提供一個接口:public interface RouJiaMoYLFactroy
- 2、用于建立相關的或依賴對象的家族 public Meat createMeat();public YuanLiao createYuanliao();我們接口用于建立一系列的原材料。
- 建立用于提供原料的接口工廠:RoujiaMoYLFactory.java
- 各自分店實作接口,完成原料提供:XianRoujiaMoYLFoctory.java
- 準備時,使用官方的原料:RoujiaMo.java
3. 單例設計模式
單例模式主要是為了避免因為建立了多個執行個體造成資源的浪費,且多個執行個體由于多次調用容易導緻結果出現錯誤,而使用單例模式能夠保證整個應用中有且隻有一個執行個體。
- 定義:隻需要三步就可以保證對象的唯一性
- (1) 不允許其他程式用new對象
- (2) 在該類中建立對象
- (3) 對外提供一個可以讓其他程式擷取該對象的方法
- 對比定義:
- (1) 私有化該類的構造函數
- (2) 通過new在本類中建立一個本類對象
- (3) 定義一個公有的方法,将在該類中所建立的對象傳回
- 餓漢式[可用]:SingletonEHan.java
- 含懶漢式[雙重校驗鎖 推薦用]:SingletonLanHan.java
private SingletonLanHan() {}
private static SingletonLanHan singletonLanHanFour;
public static SingletonLanHan getSingletonLanHanFour() {
if (singletonLanHanFour == null) {
synchronized (SingletonLanHan.class) {
if (singletonLanHanFour == null) {
singletonLanHanFour = new SingletonLanHan();
}
}
}
return singletonLanHanFour;
}
複制
- 内部類[推薦用]:SingletonIn.java
- 枚舉[推薦用]:SingletonEnum.java
4. 政策模式
政策模式:定義了算法族,分别封裝起來,讓它們之間可互相替換,此模式讓算法的變化獨立于使用算法的客戶。
- 以建立遊戲角色為例子:
- 最初的遊戲角色的父類:Role.java
- 發現有重複代碼後,重構後的父類:Role.java
- 總結:
- 1、封裝變化(把可能變化的代碼封裝起來)
- 2、多用組合,少用繼承(我們使用組合的方式,為客戶設定了算法)
- 3、針對接口程式設計,不針對實作(對于Role類的設計完全的針對角色,和技能的實作沒有關系)
- 最後測試:建立角色:
RoleA roleA = new RoleA("---A");
roleA.setiDisplayBehavior(new DisplayYZ())
.setiAttackBehavior(new AttackXL())
.setiDefendBehavior(new DefendTMS())
.setiRunBehavior(new RunJCTQ());
roleA.display();// 樣子
roleA.attack();// 攻擊
roleA.run();// 逃跑
roleA.defend();// 防禦
複制
5. 擴充卡模式
定義:将一個類的接口轉換成客戶期望的另一個接口,擴充卡讓原本接口不相容的類可以互相合作。這個定義還好,說擴充卡的功能就是把一個接口轉成另一個接口。
- 以充電器為執行個體: 手機充電器一般都是5V左右吧,咱天朝的家用交流電壓220V,是以手機充電需要一個擴充卡(降壓器)
- 一部手機: Mobile.java
- 手機依賴一個提供5V電壓的接口: V5Power.java
- 我們擁有的是220V家用交流電: V220Power.java
- 擴充卡,完成220V轉5V的作用:V5PowerAdapter.java
-
最後測試:給手機沖個電:
Mobile mobile = new Mobile(); V5Power v5Power = new V5PowerAdapter(new V200Power()); mobile.inputPower(v5Power);
6. 指令模式
定義:将“請求”封裝成對象,以便使用不同的請求、隊列或者日志來參數化其他對象。指令模式也支援可撤銷的操作。(簡化: 将請求封裝成對象,将動作請求者和動作執行者解耦。)
- 需求:最近智能家電很火熱,假設現在有電視、電腦、電燈等家電,現在需要你做個遙控器控制所有家電的開關,要求做到每個按鈕對應的功能供使用者個性化,對于新買入家電要有非常強的擴充性。
- 1、家電的API:Door.java
- 2、把指令封裝成類:
- 統一的指令接口:Command.java
- 家電實作該接口:DoorOpenCommand.java
- 3、遙控器:ControlPanel.java
- 4、定義一個指令,可以幹一系列的事情:QuickCommand.java
QuickCommand quickCloseCommand = new QuickCommand(new Command[]{new LightOffCommand(light), new ComputerOffCommand(computer), new DoorCloseCommand(door)});
controlPanel.setCommands(6, quickOpenCommand);
controlPanel.keyPressed(6);
複制
- 5、遙控器面闆執行:CommandActivity.java
controlPanel.setCommands(0, new DoorOpenCommand(door));// 開門
controlPanel.keyPressed(0);
複制
7. 裝飾者模式
裝飾者模式:若要擴充功能,裝飾者提供了比內建更有彈性的替代方案,動态地将責任附加到對象上。
- 先簡單描述下裝飾者模式發揮作用的地方,當我們設計好了一個類,我們需要給這個類添加一些輔助的功能,并且不希望改變這個類的代碼,這時候就是裝飾者模式大展雄威的時候了。這裡還展現了一個原則:類應該對擴充開放,對修改關閉。
- 需求:設計遊戲的裝備系統,基本要求,要可以計算出每種裝備在鑲嵌了各種寶石後的攻擊力和描述:
- 1、裝備的超類:IEquip.java
- 2、各個裝備的實作類:
- eg:武器的實作類: ArmEquip.java
- 3、裝飾品的超類(裝飾品也屬于裝備):IEquipDecorator.java
- 4、裝飾品的實作類:
- eg:藍寶石的實作類(可累加): BlueGemDecorator.java
-
5、最後測試:計算攻擊力和檢視描述:
Log.e("---", "一個鑲嵌2顆紅寶石,1顆藍寶石的靴子: "); IEquip iEquip = new RedGemDecotator(new RedGemDecotator(new BlueGemDecotator(new ShoeEquip()))); Log.e("---", "攻擊力:" + iEquip.caculateAttack()); Log.e("---", "描述語:" + iEquip.description());
8. 外觀模式
定義:提供一個統一的接口,用來通路子系統中的一群接口,外觀定義了一個高層的接口,讓子系統更容易使用。其實就是為了友善客戶的使用,把一群操作,封裝成一個方法。
- 需求:我比較喜歡看電影,于是買了投影儀、電腦、音響、設計了房間的燈光、買了爆米花機,然後我想看電影的時候,我需要一鍵觀影和一鍵關閉。
- 每個裝置類的開關等操作:
- eg: 爆米花機:PopcornPopper.java
-
電影院類:HomeTheaterFacade.java
/** * 一鍵觀影 */ public void watchMovie() { computer.on(); light.down(); popcornPopper.on(); popcornPopper.makePopcorn(); projector.on(); projector.open(); player.on(); player.make3DListener(); }
-
最後測試:一鍵觀影:
new HomeTheaterFacade(computer, light, player, popcornPopper, projector).watchMovie();
9. 模闆方法模式
定義:定義了一個算法的骨架,而将一些步驟延遲到子類中,模版方法使得子類可以在不改變算法結構的情況下,重新定義算法的步驟。
- 需求:簡單描述一下:本公司有程式猿、測試、HR、項目經理等人,下面使用模版方法模式,記錄下所有人員的上班情況
- 模闆方法模式中的三類角色
- 1、具體方法(Concrete Method)
- 2、抽象方法(Abstract Method)
- 3、鈎子方法(Hook Method)
-
勞工的超類:Worker.java
// 具體方法 public final void workOneDay() { Log.e("workOneDay", "-----------------work start----------------"); enterCompany(); work(); exitCompany(); Log.e("workOneDay", "-----------------work end----------------"); } // 工作 抽象方法 public abstract void work(); // 鈎子方法 public boolean isNeedPrintDate() { return false; } private void exitCompany() { if (isNeedPrintDate()) { Log.e("exitCompany", "---" + new Date().toLocaleString() + "--->"); } Log.e("exitCompany", name + "---離開公司"); }
-
程式員實作類(可得知時間):ITWorker.java
/** * 重寫父類的此方法,使可以檢視離開公司時間 */ @Override public boolean isNeedPrintDate() { return true; }
- 最後測試:
-
檢視所有人員的工作情況:
QAWorker qaWorker = new QAWorker("測試人員"); qaWorker(); HRWorker hrWorker = new HRWorker("莉莉姐"); hrWorker.workOneDay(); ...
-
檢視程式猿離開公司的時間:
ITWorker itWorker = new ITWorker("jingbin"); itWorker.workOneDay();
-
10. 狀态模式
定義:允許對象在内部狀态改變時改變它的行為,對象看起來好像修改了它的類。
- 定義又開始模糊了,理一下,當對象的内部狀态改變時,它的行為跟随狀态的改變而改變了,看起來好像重新初始化了一個類似的。
- 需求:已自動售貨機為例(有已投币、未投币等狀态和投币、投币等方法)
- 最初實作待改進的售貨機:VendingMachine.java
-
改進後的售貨機(更具有延展性):VendingMachineBetter.java
// 放錢 public void insertMoney() { currentState.insertMoney(); } // 退錢 public void backMoney() { currentState.backMoney(); } // 轉動曲柄 public void turnCrank() { currentState.turnCrank(); if (currentState == soldState || currentState == winnerState) { currentState.dispense();//兩種情況會出貨 } } // 出商品 public void dispense() { Log.e("VendingMachineBetter", "---發出一件商品"); if (count > 0) { count--; } } // 設定對應狀态 public void setState(State state) { this.currentState = state; }
- 狀态的接口:State.java
- 對應狀态的接口實作類:
- eg: 中獎狀态:WinnerState.java
- eg: 售賣狀态:SoldState.java
-
改進後的售貨機測試:
// 初始化售貨機,且裡面有3個商品 VendingMachineBetter machineBetter = new VendingMachineBetter(3); machineBetter.insertMoney(); machineBetter.turnCrank();
11. 建造者模式
建造模式是對象的建立模式。建造模式可以将一個産品的内部表象(internal representation)與産品的生産過程分割開來,進而可以使一個建造過程生成具有不同的内部表象的産品對象。
- 需求:使用者去汽車店購買汽車。
- 分析:汽車店根據每個使用者的需求提取對應汽車
-
建造者超類:Builder
public abstract class Builder { public abstract void setPart(String name, String type); public abstract Product getProduct(); }
-
建造者對應實作類:ConcreteBuilder
public class ConcreteBuilder extends Builder { private Product product = new Product(); @Override public void setPart(String name, String type) { product.setName(name); product.setType(type); } @Override public Product getProduct() { return product; } }
-
店長Director取汽車:
// 店長 Director director = new Director(); // 得到寶馬汽車,内部實作提取寶馬汽車的詳情操作 Product product = director.getBProduct(); // 展示汽車資訊 product.showProduct();
12. 原型模式
原型模式是用于建立重複的對象,同時又能保證性能。這種類型的設計模式屬于建立型模式,它提供了一種建立對象的最佳方式。
這種模式是實作了一個原型接口,該接口用于建立目前對象的克隆。當直接建立對象的代價比較大時,則采用這種模式。例如,一個對象需要在一個高代價的資料庫操作之後被建立。我們可以緩存該對象,在下一個請求時傳回它的克隆,在需要的時候更新資料庫,以此來減少資料庫調用。
以擷取多種形狀為例,共分四步:
-
1、建立一個實作了 Cloneable 接口的抽象類。Shape(implements Cloneable)
public abstract class Shape implements Cloneable { private String id; protected String type; public abstract void draw(); public String getId() { return id; } public void setId(String id) { this.id = id; } @Override public Object clone() { Object object = null; try { object = super.clone(); } catch (CloneNotSupportedException e) { Log.e("--", e.getMessage()); } return object; } }
-
2、建立擴充了上面抽象類的實體類。Circle、Rectangle、Square
public class Circle extends Shape { public Circle() { type = "Circle"; } @Override public void draw() { Log.e("---", "Inside Circle::draw() method."); } }
-
3、建立一個類,從資料庫擷取實體類,并把它們存儲在一個 Hashtable 中。ShapeCache
public class ShapeCache { private static Hashtable<String, Shape> shapeMap = new Hashtable<String, Shape>(); public static Shape getShape(String shapeId) { Shape shapeCache = shapeMap.get(shapeId); return (Shape) shapeCache.clone(); } // 對每種形狀都運作資料庫查詢,并建立該形狀 // shapeMap.put(shapeKey, shape); // 例如,我們要添加三種形狀 public static void loadCache() { Circle circle = new Circle(); circle.setId("1"); shapeMap.put(circle.getId(), circle); Rectangle rectangle = new Rectangle(); rectangle.setId("2"); shapeMap.put(rectangle.getId(), rectangle); Square square = new Square(); square.setId("3"); shapeMap.put(square.getId(), square); } }
-
4、使用 ShapeCache 類來擷取存儲在 Hashtable 中的形狀的克隆。
// 使用 ShapeCache 類來擷取存儲在 Hashtable 中的形狀的克隆。 ShapeCache.loadCache(); Shape shapeCache1 = ShapeCache.getShape("1"); Shape shapeCache2 = ShapeCache.getShape("2"); Shape shapeCache3 = ShapeCache.getShape("3");
13. 享元模式
主要用于減少建立對象的數量,以減少記憶體占用和提高性能。這種類型的設計模式屬于結構型模式,它提供了減少對象數量進而改善應用所需的對象結構的方式。
享元模式嘗試重用現有的同類對象,如果未找到比對的對象,則建立新對象。我們将通過建立 5 個對象來畫出 20 個分布于不同位置的圓來示範這種模式。由于隻有 5 種可用的顔色,是以 color 屬性被用來檢查現有的 Circle 對象。
- 主要解決:在有大量對象時,有可能會造成記憶體溢出,我們把其中共同的部分抽象出來,如果有相同的業務請求,直接傳回在記憶體中已有的對象,避免重新建立。
以随機擷取多種形狀為例,共分四步:
-
1、建立一個接口。
public interface Shape { void draw(); }
-
2、建立實作接口的實體類。
public class Circle implements Shape { private String color; private int x; private int y; private int radius; public Circle(String color) { this.color = color; } public void setX(int x) { this.x = x; } public void setY(int y) { this.y = y; } public void setRadius(int radius) { this.radius = radius; } @Override public void draw() { Log.e("---", "Circle: Draw() [Color : " + color + ", x : " + x + ", y :" + y + ", radius :" + radius); } }
-
3、建立一個工廠,生成基于給定資訊的實體類的對象。
public class ShapeFactory { private static final HashMap<String, Shape> circleMap = new HashMap<String, Shape>(); public static Shape getShape(String color) { Shape shape = circleMap.get(color); if (shape == null) { shape = new Circle(color); circleMap.put(color, shape); Log.e("getShape", "Creating circle of color : " + color); } return shape; } }
-
4、使用該工廠,通過傳遞顔色資訊來擷取實體類的對象。
for (int i = 0; i < 20; i++) { Circle circle = (Circle) ShapeFactory.getShape(getRandomColor()); circle.setX(getRandomX()); circle.setY(getRandomY()); circle.setRadius(100); circle.draw(); }
14. 代理模式
一個類代表另一個類的功能。在代理模式中,我們建立具有現有對象的對象,以便向外界提供功能接口。可以了解為記憶體中沒有這個對象就建立,有就直接傳回這個對象。
- 主要解決:在直接通路對象時帶來的問題,比如說:要通路的對象在遠端的機器上。在面向對象系統中,有些對象由于某些原因(比如對象建立開銷很大,或者某些操作需要安全控制,或者需要程序外的通路),直接通路會給使用者或者系統結構帶來很多麻煩,我們可以在通路此對象時加上一個對此對象的通路層。
以擷取磁盤中的圖檔為例,總共分三步:
-
1、建立一個接口。
public interface Image { void display(); }
-
2、建立實作接口的實體類 RealImage。對應代理類:ProxyImage。
public class RealImage implements Image { private String fileName; public RealImage(String fileName) { this.fileName = fileName; loadFromDisk(fileName); } private void loadFromDisk(String fileName) { Log.e("RealImage", "loading " + fileName); } @Override public void display() { Log.e("RealImage", "Displaying " + fileName); } }
public class ProxyImage implements Image { private String fileName; private RealImage realImage; public ProxyImage(String fileName) { this.fileName = fileName; } @Override public void display() { if (realImage == null) { realImage = new RealImage(fileName); } realImage.display(); } }
-
3、當被請求時,使用 ProxyImage 來擷取 RealImage 類的對象。
Image image = new ProxyImage("test_10mb.png"); // 第一次是new的,圖像從磁盤加載 image.display(); // 第二次取緩存,圖像不需要從磁盤加載 image.display();
15. 橋接模式
橋接(Bridge)是用于把抽象化與實作化解耦,使得二者可以獨立變化。這種類型的設計模式屬于結構型模式,它通過提供抽象化和實作化之間的橋接結構,來實作二者的解耦。
- 主要解決:在有多種可能會變化的情況下,用繼承會造成類爆炸問題,擴充起來不靈活。
以畫不同顔色的圓為例,實作共分五步:
-
1、建立橋接實作接口。
public interface DrawAPI { void drawCircle(int radius, int x, int y); }
-
2、建立實作了 DrawAPI 接口的實體橋接實作類。RedCircle、GreenCircle
public class RedCircle implements DrawAPI { @Override public void drawCircle(int radius, int x, int y) { Log.e("---", "Drawing Circle[ color: red, radius: " + radius + ", x: " + x + ", " + y + "]"); } }
-
3、使用 DrawAPI 接口建立抽象類 Shape。
public abstract class Shape { protected DrawAPI drawAPI; protected Shape(DrawAPI drawAPI) { this.drawAPI = drawAPI; } public abstract void draw(); }
-
4、建立實作了 Shape 接口的實體類。
public class Circle extends Shape { private int x, y, radius; protected Circle(int x, int y, int radius, DrawAPI drawAPI) { super(drawAPI); this.x = x; this.y = y; this.radius = radius; } @Override public void draw() { drawAPI.drawCircle(radius, x, y); } }
-
5、使用 Shape 和 DrawAPI 類畫出不同顔色的圓。
// 畫紅圓 Circle circle = new Circle(10, 10, 100, new RedCircle());s circle.draw(); // 畫綠圓 Circle circle2 = new Circle(20, 20, 100, new GreenCircle()); circle2.draw();
16. 組合模式
又叫部分整體模式,是用于把一組相似的對象當作一個單一的對象。組合模式依據樹形結構來組合對象,用來表示部分以及整體層次。這種類型的設計模式屬于結構型模式,它建立了對象組的樹形結構。
- 主要解決:它在我們樹型結構的問題中,模糊了簡單元素和複雜元素的概念,客戶程式可以像處理簡單元素一樣來處理複雜元素,進而使得客戶程式與複雜元素的内部結構解耦。
以建立和列印員工的層次結構為例,最小單元示例:
-
1、建立 Employee 類,該類帶有 Employee 對象的清單。
public class Employee { private String name; // 部門 private String dept; // 工資 private int salary; // 員工 list private List<Employee> subordinates; public Employee(String name, String dept, int salary) { this.name = name; this.dept = dept; this.salary = salary; this.subordinates = new ArrayList<Employee>(); } public void add(Employee e) { subordinates.add(e); } public void remove(Employee e) { subordinates.remove(e); } public List<Employee> getSubordinates() { return subordinates; } @Override public String toString() { return "Employee{" + "name='" + name + '\'' + ", dept='" + dept + '\'' + ", salary=" + salary + ", subordinates=" + subordinates + '}'; } }
-
2.使用 Employee 類來建立和列印員工的層次結構。
final Employee ceo = new Employee("John", "CEO", 30000); Employee headSales = new Employee("Robert", "Head sales", 20000); Employee headMarketing = new Employee("Michel", "Head Marketing", 20000); Employee clerk1 = new Employee("Laura", "Marketing", 10000); Employee clerk2 = new Employee("Bob", "Marketing", 10000); Employee salesExecutive1 = new Employee("Richard", "Sales", 10000); Employee salesExecutive2 = new Employee("Rob", "Sales", 10000); ceo.add(headSales); ceo.add(headMarketing); headSales.add(salesExecutive1); headSales.add(salesExecutive2); headMarketing.add(clerk1); headMarketing.add(clerk2); Log.e("---", ceo.toString()); // 列印 /* * Employee{name='John', dept='CEO', salary=30000, * subordinates=[Employee{name='Robert', dept='Head sales', salary=20000, * subordinates=[ * Employee{name='Richard', dept='Sales', salary=10000, subordinates=[]}, * Employee{name='Rob', dept='Sales', salary=10000, subordinates=[]}]}, * Employee{name='Michel', dept='Head Marketing', salary=20000, * subordinates=[Employee{name='Laura', dept='Marketing', salary=10000, subordinates=[]}, * Employee{name='Bob', dept='Marketing', salary=10000, subordinates=[]}]}]} */
17. 疊代器模式
Java 和 .Net 程式設計環境中非常常用的設計模式。這種模式用于順序通路集合對象的元素,不需要知道集合對象的底層表示。疊代器模式屬于行為型模式。
- 主要解決:不同的方式來周遊整個整合對象。
以使用疊代器列印名字為例,總共分三步:
-
1、建立接口:
public interface Iterator { public boolean hasNext(); public Object next(); }
public interface Container { public Iterator getIterator(); }
-
2、建立實作了 Container 接口的實體類。該類有實作了 Iterator 接口的内部類 NameIterator。
public class NameRepository implements Container { private String names[] = {"John", "jingbin", "youlookwhat", "lookthis"}; @Override public Iterator getIterator() { return new NameIterator(); } private class NameIterator implements Iterator { int index; @Override public boolean hasNext() { if (index < names.length) { return true; } return false; } @Override public Object next() { if (hasNext()) { return names[index++]; } return null; } } }
-
3、使用 NameRepository 來擷取疊代器,并列印名字。
NameRepository nameRepository = new NameRepository(); for (Iterator iterator = nameRepository.getIterator(); iterator.hasNext(); ) { String name = (String) iterator.next(); Log.e("---", name); /* * /---: John * /---: jingbin * /---: youlookwhat * /---: lookthis */ }
18. 中介者模式
用來降低多個對象和類之間的通信複雜性。這種模式提供了一個中介類,該類通常處理不同類之間的通信,并支援松耦合,使代碼易于維護。中介者模式屬于行為型模式。
- 主要解決:對象與對象之間存在大量的關聯關系,這樣勢必會導緻系統的結構變得很複雜,同時若一個對象發生改變,我們也需要跟蹤與之相關聯的對象,同時做出相應的處理。
以公共聊天室為例,最小單元示例步驟:
-
1、建立中介類。
public class CharRoom { public static void showMessage(User user, String message) { Log.e("---", new Date().toString() + " [" + user.getName() + "] : " + message); } }
-
2、建立 user 類。
public class User { private String name; public User(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } public void sendMessage(String message) { // 使用中介類 CharRoom.showMessage(this, message); } }
-
3、使用 User 對象來顯示他們之間的通信。
User jingbin = new User("jingbin"); jingbin.sendMessage("Hi~ youlookwhat!"); //---: Sun Feb 02 08:11:47 GMT+00:00 2020 [jingbin] : Hi~ youlookwhat! User jingbin = new User("youlookwhat"); jingbin.sendMessage("Hi~ jingbin!"); //---: Sun Feb 02 08:11:49 GMT+00:00 2020 [youlookwhat] : Hi~ jingbin!
19. 備忘錄模式
儲存一個對象的某個狀态,以便在适當的時候恢複對象。備忘錄模式屬于行為型模式。
- 主要解決:所謂備忘錄模式就是在不破壞封裝的前提下,捕獲一個對象的内部狀态,并在該對象之外儲存這個狀态,這樣可以在以後将對象恢複到原先儲存的狀态。
以使用備忘錄為例,最小單元步驟:
-
1、建立 備忘錄 Memento 類。
public class Memento { private String state; public Memento(String state) { this.state = state; } public String getState() { return state; } public void setState(String state) { this.state = state; } }
-
2、建立 Originator 類。
public class Originator { private String state; public String getState() { return state; } public void setState(String state) { this.state = state; } public Memento setSateToMemento() { return new Memento(state); } public String getStateFromMemento(Memento memento) { return memento.getState(); } }
-
3、建立 CareTaker 類。
public class CareTaker { private List<Memento> mementoList = new ArrayList<Memento>(); public void add(Memento memento) { mementoList.add(memento); } public Memento get(int index) { return mementoList.get(index); } }
-
4、使用 CareTaker 和 Originator 對象。
// 管理者 CareTaker careTaker = new CareTaker(); Originator originator = new Originator(); originator.setState("State #1"); originator.setState("State #2"); // 儲存狀态 careTaker.add(originator.setSateToMemento()); originator.setState("State #3"); // 儲存狀态 careTaker.add(originator.setSateToMemento()); originator.setState("State #4"); Log.e("---", "Current State: " + originator.getState()); // 得到儲存的狀态 String fromMemento1 = originator.getStateFromMemento(careTaker.get(0)); Log.e("---", "First Saved State: " + fromMemento1); String fromMemento2 = originator.getStateFromMemento(careTaker.get(1)); Log.e("---", "Second Saved State: " + fromMemento2); /* * /---: Current State: State #4 * /---: First Saved State: State #2 * /---: Second Saved State: State #3 */
20. 解釋器模式
提供了評估語言的文法或表達式的方式,它屬于行為型模式。這種模式實作了一個表達式接口,該接口解釋一個特定的上下文。這種模式被用在 SQL 解析、符号處理引擎等。
- 主要解決:對于一些固定文法建構一個解釋句子的解釋器。
以解釋一句話為例,最小單元步驟:
-
1、建立一個表達式接口 Expression。
public interface Expression { public boolean interpreter(String content); }
-
2、建立實作了上述接口的實體類。TerminalExpression、OrExpression、AndExpression。
public class TerminalExpression implements Expression { private String data; public TerminalExpression(String data) { this.data = data; } @Override public boolean interpreter(String content) { // 是包含判斷 return content.contains(data); } }
public class OrExpression implements Expression { private Expression expression1; private Expression expression2; public OrExpression(Expression expression1, Expression expression2) { this.expression1 = expression1; this.expression2 = expression2; } @Override public boolean interpreter(String content) { return expression1.interpreter(content) || expression2.interpreter(content); } }
public class AndExpression implements Expression { private Expression expression1; private Expression expression2; public AndExpression(Expression expression1, Expression expression2) { this.expression1 = expression1; this.expression2 = expression2; } @Override public boolean interpreter(String content) { return expression1.interpreter(content) && expression2.interpreter(content); } }
-
3、使用 Expression 類來建立規則,并解析它們。
/** * 規則:jingbin 和 youlookwhat 是男性 */ public static Expression getMaleExpression() { TerminalExpression jingbin = new TerminalExpression("jingbin"); TerminalExpression youlookwhat = new TerminalExpression("youlookwhat"); return new OrExpression(jingbin, youlookwhat); } /** * 規則:Julie 是一個已婚的女性 */ public static Expression getMarriedWomanExpression() { TerminalExpression julie = new TerminalExpression("Julie"); TerminalExpression married = new TerminalExpression("Married"); return new AndExpression(julie, married); } Expression maleExpression = getMaleExpression(); // jingbin is male: true Log.e("---", "jingbin is male: " + maleExpression.interpreter("jingbin")); Expression womanExpression = getMarriedWomanExpression(); // Julie is married woman: true Log.e("---", "Julie is married woman: " + womanExpression.interpreter("Married Julie"));
21. 責任鍊模式
責任鍊模式(Chain of Responsibility Pattern)為請求建立了一個接收者對象的鍊。這種模式給予請求的類型,對請求的發送者和接收者進行解耦。這種類型的設計模式屬于行為型模式。在這種模式中,通常每個接收者都包含對另一個接收者的引用。如果一個對象不能處理該請求,那麼它會把相同的請求傳給下一個接收者,依此類推。
- 主要解決:職責鍊上的處理者負責處理請求,客戶隻需要将請求發送到職責鍊上即可,無須關心請求的處理細節和請求的傳遞,是以職責鍊将請求的發送者和請求的處理者解耦了。
以Android Studio中列印日志為例,最小單元步驟:
-
1、建立抽象的記錄器類 AbstractLogger。
public abstract class AbstractLogger { public static int INFO = 1; public static int DEBUG = 2; public static int ERROR = 3; protected int level; // 責任鍊中的下一個元素 protected AbstractLogger nextLogger; public void setNextLogger(AbstractLogger nextLogger) { this.nextLogger = nextLogger; } public void logMessage(int level, String message) { if (this.level <= level) { write(message); } // 遞歸效果,不斷調用下一級 logMessage if (nextLogger != null) { nextLogger.logMessage(level, message); } } protected abstract void write(String message); }
-
2、建立擴充了該記錄器類的實體類。
public class ConsoleLogger extends AbstractLogger { public ConsoleLogger(int level) { this.level = level; } @Override protected void write(String message) { Log.e("---", "Standard Console::Logger " + message); } }
public class FileLogger extends AbstractLogger { public FileLogger(int level) { this.level = level; } @Override protected void write(String message) { Log.e("---", "File::Logger " + message); } }
public class ErrorLogger extends AbstractLogger { public ErrorLogger(int level) { this.level = level; } @Override protected void write(String message) { Log.e("---", "Error Console::Logger " + message); } }
-
3、建立不同類型的記錄器。賦予它們不同的錯誤級别,并在每個記錄器中設定下一個記錄器。每個記錄器中的下一個記錄器代表的是鍊的一部分。
public static AbstractLogger getChainOfLoggers() { ErrorLogger errorLogger = new ErrorLogger(AbstractLogger.ERROR); FileLogger fileLogger = new FileLogger(AbstractLogger.DEBUG); ConsoleLogger consoleLogger = new ConsoleLogger(AbstractLogger.INFO); errorLogger.setNextLogger(fileLogger); fileLogger.setNextLogger(consoleLogger); return errorLogger; } AbstractLogger logger = getChainOfLoggers(); // ---: Standard Console::Logger this is an information. logger.logMessage(AbstractLogger.INFO, "this is an information."); // ---: File::Logger this is a debug level information. // ---: Standard Console::Logger this is a debug level information. logger.logMessage(AbstractLogger.DEBUG, "this is a debug level information."); // ---: Error Console::Logger this is a error level information. // ---: File::Logger this is a error level information. // ---: Standard Console::Logger this is a error level information. logger.logMessage(AbstractLogger.ERROR, "this is a error level information.");
22. 通路者模式
在通路者模式中,我們使用了一個通路者類,它改變了元素類的執行算法。通過這種方式,元素的執行算法可以随着通路者改變而改變。這種類型的設計模式屬于行為型模式。根據模式,元素對象已接受通路者對象,這樣通路者對象就可以處理元素對象上的操作。
- 主要解決:穩定的資料結構和易變的操作耦合問題。
以顯示計算機的組成部分為例,主要分五步實作:
-
1、定義一個表示元素的接口。
public interface ComputerPart { public void accept(ComputerPartVisitor computerPartVisitor); }
-
2、建立擴充了上述類的實體類。Keyboard、Monitor、Mouse、Computer
public class Computer implements ComputerPart { private ComputerPart[] parts; public Computer() { this.parts = new ComputerPart[]{new Mouse(), new Keyboard(), new Monitor()}; } @Override public void accept(ComputerPartVisitor computerPartVisitor) { for (ComputerPart part : parts) { part.accept(computerPartVisitor); } computerPartVisitor.visit(this); } }
public class Mouse implements ComputerPart { @Override public void accept(ComputerPartVisitor computerPartVisitor) { computerPartVisitor.visit(this); } }
-
3、定義一個表示通路者的接口。
public interface ComputerPartVisitor { public void visit(Computer computer); public void visit(Mouse mouse); public void visit(Keyboard keyboard); public void visit(Monitor monitor); }
-
4、建立實作了上述類的實體通路者。
public class ComputerPartDisplayVisitor implements ComputerPartVisitor { @Override public void visit(Computer computer) { Log.e("---", "Displaying Computer."); } @Override public void visit(Mouse mouse) { Log.e("---", "Displaying Mouse."); } @Override public void visit(Keyboard keyboard) { Log.e("---", "Displaying Keyboard."); } @Override public void visit(Monitor monitor) { Log.e("---", "Displaying Monitor."); } }
-
5、使用 ComputerPartDisplayVisitor 來顯示 Computer 的組成部分。
ComputerPart computer = new Computer(); computer.accept(new ComputerPartDisplayVisitor()); /* *列印: *---: Displaying Mouse. *---: Displaying Keyboard. *---: Displaying Monitor. *---: Displaying Computer. */
Download
- DesignPattern.apk