天天看點

Java 23種設計模式全歸納 | 完結版

設計模式(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

Java 23種設計模式全歸納 | 完結版

image

Java 23種設計模式全歸納 | 完結版

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
    public RoujiaMo creatRoujiaMo(String type) { RoujiaMo roujiaMo = null; switch (type) { case "Suan": roujiaMo = new ZSuanRoujiaMo(); break; case "La": roujiaMo = new ZLaRoujiaMo(); break; case "Tian": roujiaMo = new ZTianRoujiaMo(); break; default:// 預設為酸肉夾馍 roujiaMo = new ZSuanRoujiaMo(); break; } return roujiaMo; }
  • 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
    /** * 準備工作 */ public void prepare(RoujiaMoYLFactory roujiaMoYLFactory) { Meet meet = roujiaMoYLFactory.creatMeet(); YuanLiao yuanLiao = roujiaMoYLFactory.creatYuanLiao(); Log.e("---RoujiaMo:", "使用官方的原料 ---" + name + ": 揉面-剁肉-完成準備工作 yuanLiao:"+meet+"yuanLiao:"+yuanLiao); }

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