設計模式複習
1. 物件導向設計原則
1.1 可維護性較低的軟體設計
- 過於僵硬
- 過於脆弱
- 複用率低
- 黏度過高
1.2 一個好的系統設計
- 可擴充套件性
- 靈活性
- 可插入性
複用:一個軟體的組成部分可以在同一個專案的不同地方甚至在不同的專案重複使用。
物件導向設計複用的目標:實現支援可維護性的複用。(抽象、繼承、封裝、多型)
重構:在不改變軟體現有功能的基礎上,通過調整程式程式碼改善軟體的質量、效能、使其程式的設計模式和架構更趨合理,提高軟體的擴充套件性和維護性。
1.3 七大設計原則
- 單一職責原則(Single Responsibility Principle , SRP):一個物件應該只包含單一的職責,並且該職責被完整地封裝在一個類中。
- 單一職責用於控制類的粒度大小。
- 是實現高內聚低耦合的指導方針
- 開閉原則(Open-Closed Principle , OCP):一個軟體實體應當對擴充套件開放,對修改關閉。
- 抽象化是開閉原則的關鍵
- 里氏代換原則(Liskov Substitution Principle , LSP ):所有引用基類(父類)的地方必須能透明地使用其子類的物件。
- 里氏代換原則是實現開閉原則的重要方式之一
- 子類必須實現父類的所有方法
- 儘量把父類設計為抽象類或者介面
- 依賴倒轉原則(Dependence Inversion Principle . DIP ):高層模組不應該依賴低層模組,它們都應該依賴抽象。抽象不依賴於細節,細節應該依賴於抽象。
- 要針對介面程式設計,不要針對實現程式設計。
- 開閉原則是物件導向設計的目標,依賴倒轉原則就是物件導向設計的主要手段。
- 類之間的耦合
- 依賴注入:將一個類的物件傳入另一個類,注入時儘量注入父類物件,程式執行時通過子類物件覆蓋父類物件。
- 構造注入、Setter注入、介面注入
- 介面隔離原則(Interface Segregation Principle , ISP):客戶端不應該依賴那些它不需要的介面(方法)。
- 大介面要分割成一些更細小的介面。
- 使用多個專門的介面,而不使用單一的總介面。
- 介面僅僅提供客戶端需要的方法。
- 合成複用原則(Composite Reuse Principle , CRP ):又稱為組合/聚合複用原則,儘量使用物件組合,而不是繼承來達到複用的目的。
- 在一個新的物件裡通過關聯關係來使用一些已知物件,使之成為新物件的一部分。
- 迪米特法則(Law of Demeter , LoD ):又稱為最少知識原則,不要和“陌生人”說話只與你的直接朋友通訊。
2. 初識設計模式
2.1 設計模式定義
設計模式(Design Pattern)是一套被反覆使用、多數人知曉的、經過分類編目的、程式碼設計經驗的總結,使用設計模式是為了可重用程式碼,讓程式碼更容易被他人理解、保證程式碼的可靠性。
2.2 設計模式基本要素
- 模式名稱、問題、目的、解決方案、效果、例項程式碼和相關設計模式
- 關鍵元素包括以下四個方面
- 模式名稱:通過一兩個詞來描述模式的問題、解決方案和效果,多數模式是根據其功能或者模式結構來命名的。
- 問題:描述了應該在何時使用模式,包含了設計中存在的問題以及問題存在的原因。
- 解決方案:描述了設計模式的組成成分,以及這些組成成分之間的相互關係,各自的職責和協作方式。解決方案通過類圖和核心程式碼來進行說明。
- 效果:描述了模式應用的效果以及在使用模式時應該權衡的問題。包含了模式的優缺點分析
2.3 設計模式的分類
-
根據目的(模式是用來做什麼的)可以分為建立型(Creational)、結構型(Structural)和行為型(Behavioral)三種
- 建立型模式:用於描述“怎樣建立物件”,它的主要特點是“將物件的建立與使用分離”。GoF 中提供了單例、原型、工廠方法、抽象工廠、建造者等 5 種建立型模式。
- 結構型模式:用於描述如何將類或物件按某種佈局組成更大的結構,GoF 中提供了代理、介面卡、橋接、裝飾、外觀、享元、組合等 7 種結構型模式。
- 行為型模式:用於描述類或物件之間怎樣相互協作共同完成單個物件都無法單獨完成的任務,以及怎樣分配職責。GoF 中提供了模板方法、策略、命令、職責鏈、狀態、觀察者、中介者、迭代器、訪問者、備忘錄、直譯器等 11 種行為型模式。
-
根據範圍(模式主要是用於類之間的關係還是處理物件之間的關係)可以分為類模式和物件模式兩種
- 類模式處理類和子類關係,這些關係通過繼承確立,在編譯的時候就被確定了,是靜態的。
- 物件模式處理物件的關係,時刻變化,有動態性。
3.深入理解設計模式
3.1 建造者模式(Builder)
例子:根據計算機元件組裝不同的計算機。
用這個例子來理解一下建立者模式:首先這個模式做的事情是這樣的,現在有一堆計算機零件,比如說一塊硬碟它可以放在膝上型電腦上也可以放在桌上型電腦上,那麼建造者模式就是把不同電腦的構建和表示分離,提供一個計算機產品類,裡面包含了計算機的零件,之後提供一張“圖紙”這張圖紙就是一個抽象建造者介面,這個介面提供了建立的方法以及返回複雜物件的方法,具體的建造者會實現這個介面,用這張“圖紙”來創造不同型別的計算機。
具體角色:
- 具體產品角色:Computer
- 抽象建造者:ComputerBuilder
- 具體建造者:DeskTopComputer、LapTopComputer
- 指揮者:ComputerWaiter
注意點:
-
抽象建造者裡要去new一個具體產品。 protected Computer computer = new Computer(); 抽象建造者裡需要定義一個返回複雜產品的方法 public Computer getComputer(){ return computer; } 具體建造者繼承自抽象建造者,實現裡面的所有建造方法! public class DeskTopBuilder extends ComputerBuilder 指揮者類是真正幹活的類 public class ComputerWaiter { private ComputerBuilder cb; public void setCb(ComputerBuilder cb){ this.cb=cb; } public Computer construct(){ cb.buildCPU(); cb.buildHardDisk(); cb.buildMainFrame(); cb.buildMemory(); return cb.getComputer(); } } 這個類需要擁有一個抽象建造者的物件,利用這個物件呼叫其建造的方法來完整一個具體的產品並呼叫其方法把這個產品返回!
總結:
- 具體產品類提供了一個產品需要的零件。
- 抽象建造類相當於是總工程師畫的一張圖紙,這張圖紙總體上實現了這個產品的建造,裡面需要一個建立(new)一個具體產品的成員物件,並提供返回這個物件的方法。
- 具體建造類相當於是拿著總工程師的圖紙根據實際的需要進行了二次加工,這個類繼承自抽象建造類,需要實現總圖紙的所有方法,不過具體的建造細節可以自己決定。
- 指揮者類是真正幹活的工人,這個類拿著實際的圖紙來完成工作做出具體的產品,這個類需要聚合抽象建造類,並提供setter介面方法,讓外界傳入具體的“圖紙”引數,然後進行建造。
核心理解
建造者模式的核心在於抽象建造者類,這個類要做的事情是定義方法:首先這個類是用來建造一個例項物件的,所以一定要new一個新的產品物件作為其屬性成員,然後定義建造的介面方法,根據具體需要被建造的例項的setter方法提供不同的多個建造方法介面,最後需要一個方法返回最終建造完成的物件。
這個抽象建造方法就是一張建造的圖紙,後面實現這個介面的類是具體的建造圖紙,把具體的建造圖紙用set注入的方式給指揮者類(相當於工人)讓指揮者類幹活,最後根據具體圖紙完成一個例項產品!
3.2 原型模式(ProtoType)
具體角色:
- ProtoType抽象原型類
- ConcreteProtoType具體原型類
步驟:
-
實現一個介面:Cloneable
-
重寫一個方法:clone
pubilc Object clone() object = super.clone() ; return object;
- 淺克隆:複製物件的引用,物件的屬性仍然指向同一處。
- 深克隆:不止複製物件的引用,而且要把物件的所有屬性全部克隆一次,兩個物件的屬性將不會指向同一塊區域,從而實現兩個物件徹底分離。
核心理解
原型模式只做了一件事情,就是克隆一份一模一樣的自己並返回。
- 實現一個介面Cloneable
- 呼叫一個方法:object = super.clone() ;
- 返回這個object
3.3 單例模式(Singleton)
注意點:
- 靜態私有成員變數。
- 私有建構函式。
- 靜態公有工廠方法,返回唯一物件例項,方法中判斷物件是否為空,如果為空則new一個新物件返回,俄國不為空,則直接將私有成員變數物件返回。
package com.a007;
public class StuNo
{
//靜態私有成員變數
private static StuNo instance=null;
private String no;
//私有構造方法
private StuNo()
{
}
//靜態公有工廠方法,返回唯一例項
public static StuNo getInstance()
{
if(instance==null)
{
System.out.println("新學號");
instance=new StuNo();
instance.setStuNo("20194074");
}
else
{
System.out.println("學號重複,獲得舊學號");
}
return instance;
}
private void setStuNo(String no)
{
this.no=no;
}
public String getStuNo()
{
return this.no;
}
}
核心理解
單例模式做的事情是保證一個類有且只有一個例項物件!
- 首先要保證這個類的構造方法是私有的
- 其次要保證這個物件作為成員屬性是靜態私有的
- 最後提供一個公有的對外介面返回這個例項化的物件
3.4 介面卡模式(Adapter)
用途:將一個類的介面轉換成客戶希望的另一個類的介面。
例子:電腦網線USB轉接器
角色:
電腦(客戶端)、網線、轉接器、目標介面NetToUsb
- 目標介面或抽象類(目標抽象類或目標抽象介面):這裡例子中就是目標介面USB。
- 適配者類(需要適配的類 Adaptee):它定義了一個已經存在的介面,這個介面需要被適配。在這個例子中網線類就是那個已經存在的介面,但是網線不可以直接插到電腦的USB上。
- 介面卡類(Adapter):包裝網線,讓網線支援USB介面,把網線插到USB上並處理請求。
- 介面卡類需要同時和兩個類打交道,它要把網線和電腦的USB介面連線在一起。有兩種方式,
- 一種是繼承要被適配的類(網線類)同時實現目標介面。
- 另一種是使用組合模式,不去繼承適配者類,而是使用聚合的方式,讓網線類作為介面卡類的一個成員變數,然後再去實現目標抽象介面。
- 介面卡類需要同時和兩個類打交道,它要把網線和電腦的USB介面連線在一起。有兩種方式,
分類:
- 類介面卡:繼承模式,繼承需要被適配的類,實現目標抽象介面。
- 物件介面卡:組合模式,把需要適配的類作為成員屬性變數,同時實現目標抽象介面。
類圖:(雙向適配)
核心理解
介面卡模式做的事情是這樣的:
有兩個不相干的類,但是它們想組合到一起使用,那麼就通過一個介面卡把二者適配在一起使用。
比如說:電腦有一個USB介面,而網線的接頭不是USB的,可是電腦想上網,那麼就需要一個介面轉接的介面卡來完成這個工作,這時候會出現三個類。
- 網線類:這個類提供了具體要實現的業務方法,也就是它可以完成上網這件事,比如說有一個方法是net()
- USB介面類:這個介面是使用者想要的介面,使用者希望通過USB介面完成上網這件事,比如說有一個方法是execute()
- 轉接器類:這個類來完成二者的適配:首先實現USB介面,然後或者通過繼承網線類或者通過組合網線類,選擇二者的任意一個方式,重寫USB介面裡的方法execute(),在這個方法裡去呼叫網線類的真實業務方法net()來完成上網這件事
- 客戶端在呼叫時,只需要把例項化的網線類通過set注入交給介面卡,然後通過呼叫介面卡類的execute()方法就可以完成上網這件事情!
3.5 橋接模式(Bridge)
圖片來自bilibili遇見狂神說
核心理解
橋接模式做了這樣一件事情:
就像圖中所示:如果想要一個聯想的臺式電腦,那麼就需要兩層繼承來拿到這個物件(類),第一這是低效率的,第二這是一種靜態的定死的方式,擴充套件性很差。橋接模式的思想是把抽象化和實現化進行解耦分離,比如說無論有多少個品牌,抽象來看它們都只是品牌,無論有多少種電腦,它們都只是電腦。這樣的話可以抽象出兩個維度,一個是型別、另一個是品牌。具體的實現就是自由組合:XX品牌的XX種類電腦。
優化:本來如果要這九種電腦需要3*3=9個類,現在需要這些電腦只需要3+3=6個類,如果數量級更大,橋接模式的好處可想而知,可以大大減少子類的個數!
根據依賴倒轉原則,實現要依賴抽象,所以首先會有一個抽象的電腦類,這個抽象類的子類是各種型別的電腦,其次需要一個電腦的品牌介面,實現這個介面的類是各種品牌!
這個抽象電腦類和品牌介面類是組合的關係,抽象電腦類通過Setter方法注入一個具體的電腦品牌物件,然後用其方法結合自身的電腦種類獲得這個品牌的各種型別的電腦!
補充:【抽象類和介面的區別】
含有abstract修飾符的class即為抽象類,abstract 類不能建立例項物件。含有abstract方法的類必須定義為abstract class,abstract class類中的方法不必是抽象的。abstract class類中定義抽象方法必須在具體(Concrete)子類中實現,所以,不能有抽象構造方法或抽象靜態方法。如果子類沒有實現抽象父類中的所有抽象方法,那麼子類也必須定義為abstract型別。
介面(interface)可以說成是抽象類的一種特例,介面中的所有方法都必須是抽象的。介面中的方法定義預設為public abstract型別,介面中的成員變數型別預設為public static final。
下面比較一下兩者的語法區別:
-
抽象類可以有構造方法,介面中不能有構造方法。
-
抽象類中可以有普通成員變數,介面中沒有普通成員變數
-
抽象類中可以包含非抽象的普通方法,介面中的所有方法必須都是抽象的,不能有非抽象的普通方法。
-
抽象類中的抽象方法的訪問型別可以是public,protected和(預設型別,雖然
eclipse下不報錯,但應該也不行),但介面中的抽象方法只能是public型別的,並且預設即為public abstract型別。
- 抽象類中可以包含靜態方法,介面中不能包含靜態方法
- 抽象類和介面中都可以包含靜態成員變數,抽象類中的靜態成員變數的訪問型別可以任意,但介面中定義的變數只能是public static final型別,並且預設即為public static final型別。
- 一個類可以實現多個介面,但只能繼承一個抽象類。
3.6 裝飾模式(Decorator)
定義:動態地給一個物件增加一些額外的職責。
角色:
- 抽象構件:Component
- 具體構件:ConcreteComponent
- 抽象裝飾類:Decorator
- 具體裝飾類:ConcreteDecorator
模式分析:
具體構件類和抽象構件類都實現了抽象構件介面,模式的關鍵在於抽象裝飾類,這個類實現了抽象構件介面並且組合了抽象構件,在其建構函式中設定引數注入具體構件物件,在其裝飾方法中呼叫這個注入的構件類已有的方法,再通過具體裝飾類的繼承,新增其他方法和功能。
核心理解
裝飾模式做的事情是動態修改被裝飾者的一些屬性方法等等。
根據依賴倒轉原則,待裝飾的類和裝飾者類都要實現自同一個抽象構件介面,在裝飾者類的構造方法裡要注入一個待裝飾者物件,裝飾者類和抽象構件介面是組合關係和介面實現關係,在裝飾者類中提供一個可擴充套件的方法供子類重寫。
具體的裝飾者類繼承抽象裝飾者類,重寫其擴充套件方法完成對待裝飾物件的裝飾!
3.7 外觀模式(Facade)
定義:外部與一個子系統的通訊必須通過一個統一的外觀物件進行,為子系統的一組介面提供 了一個一致的介面。
例項:一個電源總開關可以控制四盞燈、一個風扇、一臺空調和一個電視機的啟動和關閉。
類圖:
核心理解
外觀模式做的事情是這樣的:
比如說你現在想把家裡的燈關了、把空調關了、把電視機也關了。正常的過程是你要一個個去把它們關閉,但是如果給你一個統一的關閉按鈕,只要你按這一個按鈕,這三種電器就會同時關閉,這樣的一個按鈕的實現,就是外觀模式的核心!
使用簡單的關聯關係,實現對多個物件的方法的同時呼叫,統一分配!
3.8 代理模式(Proxy)
定義:
給某個物件提供一個代理,並由代理物件控制對原物件的引用。
角色:
- 抽象主題角色:裡面包含了抽象的業務操作。
- 代理主題角色:實現抽象主題介面,關聯真實主題角色,對真實主題角色的一些業務進行一些預先處理和延後處理。
- 真實主題角色:裡面包含的真實的業務需求,客戶端呼叫的時只需要面向代理角色,根據不同的客戶,代理角色將給出不同的業務實現,代替真實主題角色進行業務的安排。
核心理解
代理模式的關鍵在於:
首先根據依賴倒轉原則:具體主題類和代理主題類都要實現自同一個抽象主題角色。
代理主題類關聯真實主題類,代替真實主題針對不同的客戶做出不同的處理!
3.9 職責鏈模式(Chain of Responsibility)
定義:避免請求的傳送者和接收者耦合在一起,讓多個物件都有可能接收請求,將這些物件連線成一條鏈,沿著這條鏈傳遞請求,直到有物件處理它為止。
角色:
- 抽象處理者:Handler
- 具體處理者:ConcreteHandler
- 客戶類:Client
模式分析:
關鍵在於抽象處理者類的設計:很多物件由每一個物件對其下家的引用而連線在一起。
抽象處理者典型程式碼:
//審批者類:抽象處理者
abstract class Approver {
protected Approver successor; //定義後繼物件
protected String name; //審批者姓名
public Approver(String name) {
this.name = name;
}
//設定後繼者
public void setSuccessor(Approver successor) {
this.successor = successor;
}
//抽象請求處理方法
public abstract void processRequest(PurchaseRequest request);
}
具體處理者典型程式碼:
//董事長類:具體處理者
class ViceManager extends Approver {
public ViceManager(String name) {
super(name);
}
//具體請求處理方法
public void processRequest(PurchaseRequest request) {
if (request.getAmount() < 100000) {
System.out.println("副總經理" + this.name + "審批採購單:" + request.getNumber() + ",金額:" + request.getAmount() + "元,採購目的:" + request.getPurpose() + "。"); //處理請求
}
else {
this.successor.processRequest(request); //轉發請求
}
}
}
客戶端呼叫典型程式碼:輪流設定下家
position1.setSuccessor(position2);
position2.setSuccessor(position3);
position3.setSuccessor(position4);
position4.setSuccessor(meeting);
package com.c015;
public class Client {
public static void main(String[] args) {
Approver position1,position2,position3,position4,meeting; // 多個處理者
position1 = new Director("甲");
position2 = new PartManager("乙");
position3 = new ViceManager("丙");
position4 = new Manager("丁");
meeting = new Congress("職工大會");
//建立職責鏈
position1.setSuccessor(position2);
position2.setSuccessor(position3);
position3.setSuccessor(position4);
position4.setSuccessor(meeting);
//建立採購單
PurchaseRequest pr1 = new PurchaseRequest(5000,10001,"XXX");
position1.processRequest(pr1);
PurchaseRequest pr2 = new PurchaseRequest(45000,10002,"XXX");
position1.processRequest(pr2);
PurchaseRequest pr3 = new PurchaseRequest(77000,10003,"XXX");
position1.processRequest(pr3);
PurchaseRequest pr4 = new PurchaseRequest(150000,10004,"XXX");
position1.processRequest(pr4);
PurchaseRequest pr5 = new PurchaseRequest(800000,10005,"XXX");
position1.processRequest(pr5);
}
}
核心理解
職責鏈模式關鍵在於設定職責的下家!
抽象處理者類要有一個自身的物件作為成員屬性變數,並通過一個set方法完成賦值,之後要提供一個具體處理的方法介面供子類重寫!
後續的子類重寫具體的處理辦法,如果處理不了,再次呼叫父類的處理方法直接把請求交給下家來完成!
3.10 命令模式(Command)
定義:
將一個請求封裝為一個物件,從而使我們可用不同的請求對客戶進行引數化。
本質上是對命令進行封裝,將發出命令的責任和執行命令的責任分隔開。
角色:
- 接收者類:實現了具體的業務操作,拿電視機來說,這個類實現了電視機的開啟和關閉的真實操作方法。
- 抽象命令類:定義了一個執行命令的方法介面,由其子類實現。
- 具體命令類(一個命令一個類):實現抽象命令介面,關聯接收者類,呼叫接受者類中具體的一個命令,比如這個具體命令類是要開啟電視機,那麼執行命令的方法就呼叫接受者物件中的開啟命令。
- 呼叫者類:相當於遙控器,把所有可能的操作集合在一起,客戶端只需要使用遙控器就可以完成所有命令的發起,構造方法(形參是是抽象命令隊物件,實參是具體命令物件)完成所有具體命令物件的注入,提供執行命令的方法,用具體命令物件呼叫具體命令的執行方法。
關鍵程式碼:
//接收者:真正執行命令的物件
public class Light {
public void open(){
System.out.println("開啟電燈!");
}
}
public interface Command {
public void execute();
}
// 這是一個命令,所以需要實現Command介面
public class LightOnCommand implements Command {
Light light;
// 構造器傳入某個電燈,以便讓這個命令控制,然後記錄在例項變數中
public LightOnCommand(Light light) {
this.light = light;
}
// 這個execute方法呼叫接收物件的on方法
public void execute() {
light.on();
}
}
public class SimpleRemoteControl {
// 有一個插槽持有命令,而這個命令控制著一個裝置
Command slot;
public SimpleRemoteControl() {}
// 這個方法用來設定插槽控制的命令
public void setCommand(Command command) {
slot = command;
}
// 當按下按鈕時,這個方法就會被呼叫,使得當前命令銜接插槽,並呼叫它的execute方法
public void buttonWasPressed() {
slot.execute();
}
}
客戶端使用
public class RemoteControlTest {
public static void main(String[] args) {
// 遙控器就是呼叫者,會傳入一個命令物件,可以用來發出請求
SimpleRemoteControl remote = new SimpleRemoteControl();
// 現在建立一個電燈物件,此物件也就是請求的接收者
Light light = new Light();
// 這裡建立一個命令,然後將接收者傳給它
LightOnCommand lightOn = new LightOnCommand(light);
// 把命令傳給呼叫者
remote.setCommand(lightOn);
// 模擬按下按鈕
remote.buttonWasPressed();
}
}
核心理解
命令模式主要完成的事情是把命令的具體實施和命令的發出解耦。
有一個具體幹活的類(命令接收者類),這個類裡有所有執行具體命令的方法。
有一個抽象的命令類,這個類定義了一個執行的方法介面,然後它的子類(這些子類的個數和具體命令的個數是一致的,比如說那個具體幹活的類需要做兩件事,一個是開啟電腦,一個是關閉電腦,那麼就會有兩個不同的子類來繼承這個抽象的命令類!)繼承這個類並重寫它的執行命令的方法,這裡有個點需要注意:這些子類需要關聯那個命令接收者類,用那個類的方法來重寫執行方法!
3.11 迭代器模式(Iterator)
定義:
定義了遍歷和訪問元素的介面,一般宣告如下方法:用於獲取第一個元素的first(),用於訪問下一個元素的next(),用於判斷是否還有下一個元素的hasNext(),用於獲取當前元素的currentItem()。
3.12 觀察者模式(Observer)
定義:
定義物件之間的一種一對多的依賴關係,使得每當一個物件的狀態發生變化時,其相關的依賴物件都可以得到通知並被自動更新。
模式主要用於多個不同的物件對一個物件的某個方法會做出不同的反應!
比如貓叫之後狗會叫老鼠會逃跑,這時候貓就是被觀察者,老鼠和狗都是觀察者。
角色:
- 抽象目標:這是被觀察的物件(抽象)
- 這是核心,裡面需要一個成員屬性變數儲存所有的觀察者,需要定義add和remove觀察者的方法,需要給出notify方法通知所有的觀察者物件。
- 具體目標(具體的被觀察者):貓繼承抽象目標類,實現裡面的方法,寫出貓的反應,並且迴圈輸出所有觀察者的反應。
- 抽象觀察者:介面,定義響應方法。
- 具體觀察者:實現抽象觀察者方法,重寫響應方法。
- 客戶端呼叫:先使用具體目標物件的add方法新增具體觀察者物件,然後呼叫其notify方法通知觀察者。
核心理解
觀察者模式做的事情是這樣的:
有這麼一個場景,比如說一個物件的某個變化會造成其他類的不同的反應,比如說股票的漲跌和股民的狀態就是一種動態的關聯變化,觀察者模式就是來描述這樣的一個場景的!
具體是這樣完成的:
根據依賴倒轉原則,首先需要一個抽象的被觀察的類,這個類擁有的成員屬性變數是和它有關係的那些觀察者物件,一般是有多個物件,如果這個屬性是一個集合,那麼需要定義兩個介面方法,一個增加一個刪除,最後還需要一個描述自身狀態的方法。
具體的被觀察者繼承自抽象的被觀察類, 這個類重寫它的狀態變化方法!注意這個方法需要遍歷所有觀察者物件的response方法
觀察者同樣也需要進行抽象,需要一個觀察者介面類,這個類只有一個方法就是response()
具體的觀察者實現這個介面,重寫response方法!
客戶端在呼叫時,需要把觀察者新增到被觀察者裡,然後呼叫被觀察者的狀態變化方法,就會看到它所有的觀察者對這個狀態做出的不同的反應!