設(shè)計模式04-行為型模式

寫在前面

  • 軟件設(shè)計七大原則
    • 開閉原則
    • 里氏替換原則
    • 依賴倒置原則
    • 單一職責(zé)原則
    • 接口隔離原則
    • 迪米特法則
    • 合成復(fù)用原則
  • 創(chuàng)建型模式
    • 單例(Singleton)模式
    • 原型(Prototype)模式
    • 簡單工廠(Simple Factory)模式
    • 工廠方法(Factory Method)模式
    • 抽象工廠(Abstract Factory)模式
    • 建造者(Builder)模式
  • 結(jié)構(gòu)型模式
    • 代理(Proxy)模式
    • 適配器(Adapter)模式
    • 橋接(Bridge)模式
    • 裝飾(Decorator)模式
    • 外觀(Facade)模式
    • 享元(Flyweight)模式
    • 組合(Composite)模式
  • 行為型模式
    • 模板方法(Template Method)模式
    • 策略(Strategy)模式
    • 命令(Command)模式
    • 職責(zé)鏈(Chain of Responsibility)模式
    • 狀態(tài)(State)模式
    • 觀察者(Observer)模式
    • 中介者(Mediator)模式
    • 迭代器(Iterator)模式
    • 訪問者(Visitor)模式
    • 備忘錄(Memento)模式
    • 解釋器(Interpreter)模式

本系列共四篇筆記分別如下:

行為型模式

行為型模式用于描述程序在運(yùn)行時復(fù)雜的流程控制质帅,即描述多個類或?qū)ο笾g怎樣相互協(xié)作共同完成單個對象都無法單獨(dú)完成的任務(wù)久又,它涉及算法與對象間職責(zé)的分配。

行為型模式分為類行為模式和對象行為模式墅茉,前者采用繼承機(jī)制來在類間分派行為办桨,后者采用組合或聚合在對象間分配行為觅玻。由于組合關(guān)系或聚合關(guān)系比繼承關(guān)系耦合度低,滿足“合成復(fù)用原則”皱炉,所以對象行為模式比類行為模式具有更大的靈活性怀估。

行為型模式是 GoF 設(shè)計模式中最為龐大的一類,它包含以下 11 種模式合搅。

  1. 模板方法(Template Method)模式:定義一個操作中的算法骨架多搀,將算法的一些步驟延遲到子類中,使得子類在可以不改變該算法結(jié)構(gòu)的情況下重定義該算法的某些特定步驟灾部。
  2. 策略(Strategy)模式:定義了一系列算法康铭,并將每個算法封裝起來,使它們可以相互替換赌髓,且算法的改變不會影響使用算法的客戶从藤。
  3. 命令(Command)模式:將一個請求封裝為一個對象,使發(fā)出請求的責(zé)任和執(zhí)行請求的責(zé)任分割開锁蠕。
  4. 職責(zé)鏈(Chain of Responsibility)模式:把請求從鏈中的一個對象傳到下一個對象夷野,直到請求被響應(yīng)為止。通過這種方式去除對象之間的耦合荣倾。
  5. 狀態(tài)(State)模式:允許一個對象在其內(nèi)部狀態(tài)發(fā)生改變時改變其行為能力悯搔。
  6. 觀察者(Observer)模式:多個對象間存在一對多關(guān)系,當(dāng)一個對象發(fā)生改變時舌仍,把這種改變通知給其他多個對象妒貌,從而影響其他對象的行為。
  7. 中介者(Mediator)模式:定義一個中介對象來簡化原有對象之間的交互關(guān)系铸豁,降低系統(tǒng)中對象間的耦合度灌曙,使原有對象之間不必相互了解。
  8. 迭代器(Iterator)模式:提供一種方法來順序訪問聚合對象中的一系列數(shù)據(jù)推姻,而不暴露聚合對象的內(nèi)部表示平匈。
  9. 訪問者(Visitor)模式:在不改變集合元素的前提下,為一個集合中的每個元素提供多種訪問方式,即每個元素有多個訪問者對象訪問增炭。
  10. 備忘錄(Memento)模式:在不破壞封裝性的前提下忍燥,獲取并保存一個對象的內(nèi)部狀態(tài),以便以后恢復(fù)它隙姿。
  11. 解釋器(Interpreter)模式:提供如何定義語言的文法梅垄,以及對語言句子的解釋方法,即解釋器输玷。

模板方法(Template Method)模式

定義: 定義一個操作中的算法骨架队丝,而將算法的一些步驟延遲到子類中,使得子類可以不改變該算法結(jié)構(gòu)的情況下重定義該算法的某些特定步驟欲鹏。

優(yōu)點(diǎn):

  1. 它封裝了不變部分机久,擴(kuò)展可變部分。它把認(rèn)為是不變部分的算法封裝到父類中實現(xiàn)赔嚎,而把可變部分算法由子類繼承實現(xiàn)膘盖,便于子類繼續(xù)擴(kuò)展。
  2. 它在父類中提取了公共的部分代碼尤误,便于代碼復(fù)用侠畔。
  3. 部分方法是由子類實現(xiàn)的,因此子類可以通過擴(kuò)展方式增加相應(yīng)的功能损晤,符合開閉原則软棺。

缺點(diǎn):

  1. 對每個不同的實現(xiàn)都需要定義一個子類,這會導(dǎo)致類的個數(shù)增加尤勋,系統(tǒng)更加龐大喘落,設(shè)計也更加抽象,間接地增加了系統(tǒng)實現(xiàn)的復(fù)雜度最冰。
  2. 父類中的抽象方法由子類實現(xiàn)揖盘,子類執(zhí)行的結(jié)果會影響父類的結(jié)果,這導(dǎo)致一種反向的控制結(jié)構(gòu)锌奴,它提高了代碼閱讀的難度。
  3. 由于繼承關(guān)系自身的缺點(diǎn)憾股,如果父類添加新的抽象方法鹿蜀,則所有子類都要改一遍。

應(yīng)用場景:

  • 算法的整體步驟很固定服球,但其中個別部分易變時茴恰,這時候可以使用模板方法模式,將容易變的部分抽象出來斩熊,供子類實現(xiàn)往枣。
  • 當(dāng)多個子類存在公共的行為時,可以將其提取出來并集中到一個公共父類中以避免代碼重復(fù)。首先分冈,要識別現(xiàn)有代碼中的不同之處圾另,并且將不同之處分離為新的操作。最后雕沉,用一個調(diào)用這些新的操作的模板方法來替換這些不同的代碼集乔。
  • 當(dāng)需要控制子類的擴(kuò)展時,模板方法只在特定點(diǎn)調(diào)用鉤子操作坡椒,這樣就只允許在這些點(diǎn)進(jìn)行擴(kuò)展扰路。

實現(xiàn):

  • 抽象類/抽象模板(Abstract Class):抽象模板類,負(fù)責(zé)給出一個算法的輪廓和骨架倔叼。它由一個模板方法和若干個基本方法構(gòu)成汗唱。這些方法的定義如下。
    1. 模板方法:定義了算法的骨架丈攒,按某種順序調(diào)用其包含的基本方法哩罪。
    2. 基本方法:是整個算法中的一個步驟,包含以下幾種類型肥印。
      • 抽象方法:在抽象類中聲明识椰,由具體子類實現(xiàn)。
      • 具體方法:在抽象類中已經(jīng)實現(xiàn)深碱,在具體子類中可以繼承或重寫它腹鹉。
      • 鉤子方法:在抽象類中已經(jīng)實現(xiàn),包括用于判斷的邏輯方法和需要子類重寫的空方法兩種敷硅。
  • 具體子類/具體實現(xiàn)(Concrete Class):具體實現(xiàn)類功咒,實現(xiàn)抽象類中所定義的抽象方法和鉤子方法,它們是一個頂級邏輯的一個組成步驟绞蹦。
img16.gif
public class TemplateMethodPattern {
    public static void main(String[] args) {
        AbstractClass tm = new ConcreteClass();
        tm.TemplateMethod();
    }
}
//抽象類
abstract class AbstractClass {
    //模板方法
    public void TemplateMethod() {
        SpecificMethod();
        abstractMethod1();
        abstractMethod2();
    }
    //具體方法
    public void SpecificMethod() {
        System.out.println("抽象類中的具體方法被調(diào)用...");
    }
    //抽象方法1
    public abstract void abstractMethod1();
    //抽象方法2
    public abstract void abstractMethod2();
}
//具體子類
class ConcreteClass extends AbstractClass {
    public void abstractMethod1() {
        System.out.println("抽象方法1的實現(xiàn)被調(diào)用...");
    }
    public void abstractMethod2() {
        System.out.println("抽象方法2的實現(xiàn)被調(diào)用...");
    }
}

策略(Strategy)模式

定義: 該模式定義了一系列算法力奋,并將每個算法封裝起來,使它們可以相互替換幽七,且算法的變化不會影響使用算法的客戶景殷。策略模式屬于對象行為模式,它通過對算法進(jìn)行封裝澡屡,把使用算法的責(zé)任和算法的實現(xiàn)分割開來猿挚,并委派給不同的對象對這些算法進(jìn)行管理。

優(yōu)點(diǎn):

  1. 多重條件語句不易維護(hù)驶鹉,而使用策略模式可以避免使用多重條件語句绩蜻,如 if...else 語句、switch...case 語句室埋。
  2. 策略模式提供了一系列的可供重用的算法族办绝,恰當(dāng)使用繼承可以把算法族的公共代碼轉(zhuǎn)移到父類里面伊约,從而避免重復(fù)的代碼。
  3. 策略模式可以提供相同行為的不同實現(xiàn)孕蝉,客戶可以根據(jù)不同時間或空間要求選擇不同的屡律。
  4. 策略模式提供了對開閉原則的完美支持,可以在不修改原代碼的情況下昔驱,靈活增加新算法疹尾。
  5. 策略模式把算法的使用放到環(huán)境類中,而算法的實現(xiàn)移到具體策略類中骤肛,實現(xiàn)了二者的分離纳本。

缺點(diǎn):

  1. 客戶端必須理解所有策略算法的區(qū)別,以便適時選擇恰當(dāng)?shù)乃惴悺?/li>
  2. 策略模式造成很多的策略類腋颠,增加維護(hù)難度繁成。

應(yīng)用場景:

  • 一個系統(tǒng)需要動態(tài)地在幾種算法中選擇一種時,可將每個算法封裝到策略類中淑玫。
  • 一個類定義了多種行為巾腕,并且這些行為在這個類的操作中以多個條件語句的形式出現(xiàn),可將每個條件分支移入它們各自的策略類中以代替這些條件語句。
  • 系統(tǒng)中各算法彼此完全獨(dú)立,且要求對客戶隱藏具體算法的實現(xiàn)細(xì)節(jié)時覆糟。
  • 系統(tǒng)要求使用算法的客戶不應(yīng)該知道其操作的數(shù)據(jù)時裸影,可使用策略模式來隱藏與算法相關(guān)的數(shù)據(jù)結(jié)構(gòu)验庙。
  • 多個類只區(qū)別在表現(xiàn)行為不同,可以使用策略模式,在運(yùn)行時動態(tài)選擇具體要執(zhí)行的行為。

實現(xiàn):

策略模式是準(zhǔn)備一組算法冀泻,并將這組算法封裝到一系列的策略類里面,作為一個抽象策略類的子類蜡饵。策略模式的重心不是如何實現(xiàn)算法弹渔,而是
如何組織這些算法,從而讓程序結(jié)構(gòu)更加靈活溯祸,具有更好的維護(hù)性和擴(kuò)展性肢专。

  • 抽象策略(Strategy)類:定義了一個公共接口,各種不同的算法以不同的方式實現(xiàn)這個接口焦辅,環(huán)境角色使用這個接口調(diào)用不同的算法鸟召,一般使用接口或抽象類實現(xiàn)。
  • 具體策略(Concrete Strategy)類:實現(xiàn)了抽象策略定義的接口氨鹏,提供具體的算法實現(xiàn)。
  • 環(huán)境(Context)類:持有一個策略類的引用压状,最終給客戶端調(diào)用仆抵。
img17.gif
public class StrategyPattern {
    public static void main(String[] args) {
        Context c = new Context();
        Strategy s = new ConcreteStrategyA();
        c.setStrategy(s);
        c.strategyMethod();
        System.out.println("-----------------");
        s = new ConcreteStrategyB();
        c.setStrategy(s);
        c.strategyMethod();
    }
}
//抽象策略類
interface Strategy {
    public void strategyMethod();    //策略方法
}
//具體策略類A
class ConcreteStrategyA implements Strategy {
    public void strategyMethod() {
        System.out.println("具體策略A的策略方法被訪問跟继!");
    }
}
//具體策略類B
class ConcreteStrategyB implements Strategy {
    public void strategyMethod() {
        System.out.println("具體策略B的策略方法被訪問!");
    }
}
//環(huán)境類
class Context {
    private Strategy strategy;
    public Strategy getStrategy() {
        return strategy;
    }
    public void setStrategy(Strategy strategy) {
        this.strategy = strategy;
    }
    public void strategyMethod() {
        strategy.strategyMethod();
    }
}

命令(Command)模式

定義: 將一個請求封裝為一個對象镣丑,使發(fā)出請求的責(zé)任和執(zhí)行請求的責(zé)任分割開舔糖。這樣兩者之間通過命令對象進(jìn)行溝通,這樣方便將命令對象進(jìn)行儲存莺匠、傳遞金吗、調(diào)用、增加與管理趣竣。

優(yōu)點(diǎn):

  1. 通過引入中間件(抽象接口)降低系統(tǒng)的耦合度摇庙。
  2. 擴(kuò)展性良好,增加或刪除命令非常方便遥缕。采用命令模式增加與刪除命令不會影響其他類卫袒,且滿足“開閉原則”。
  3. 可以實現(xiàn)宏命令单匣。命令模式可以與組合模式結(jié)合夕凝,將多個命令裝配成一個組合命令,即宏命令户秤。
  4. 方便實現(xiàn) Undo 和 Redo 操作码秉。命令模式可以與后面介紹的備忘錄模式結(jié)合,實現(xiàn)命令的撤銷與恢復(fù)鸡号。
  5. 可以在現(xiàn)有命令的基礎(chǔ)上转砖,增加額外功能。比如日志記錄膜蠢,結(jié)合裝飾器模式會更加靈活堪藐。

缺點(diǎn):

  1. 可能產(chǎn)生大量具體的命令類。因為每一個具體操作都需要設(shè)計一個具體命令類挑围,這會增加系統(tǒng)的復(fù)雜性礁竞。
  2. 命令模式的結(jié)果其實就是接收方的執(zhí)行結(jié)果,但是為了以命令的形式進(jìn)行架構(gòu)杉辙、解耦請求與實現(xiàn)模捂,引入了額外類型結(jié)構(gòu)(引入了請求方與抽象命令接口),增加了理解上的困難蜘矢。不過這也是設(shè)計模式的通病狂男,抽象必然會額外增加類的數(shù)量,代碼抽離肯定比代碼聚合更加難理解品腹。

應(yīng)用場景:

當(dāng)系統(tǒng)的某項操作具備命令語義岖食,且命令實現(xiàn)不穩(wěn)定(變化)時,可以通過命令模式解耦請求與實現(xiàn)舞吭。使用抽象命令接口使請求方的代碼架構(gòu)穩(wěn)定泡垃,封裝接收方具體命令的實現(xiàn)細(xì)節(jié)析珊。接收方與抽象命令呈現(xiàn)弱耦合(內(nèi)部方法無需一致),具備良好的擴(kuò)展性蔑穴。

命令模式通常適用于以下場景忠寻。

  • 請求調(diào)用者需要與請求接收者解耦時,命令模式可以使調(diào)用者和接收者不直接交互存和。
  • 系統(tǒng)隨機(jī)請求命令或經(jīng)常增加奕剃、刪除命令時,命令模式可以方便地實現(xiàn)這些功能捐腿。
  • 當(dāng)系統(tǒng)需要執(zhí)行一組操作時纵朋,命令模式可以定義宏命令來實現(xiàn)該功能。
  • 當(dāng)系統(tǒng)需要支持命令的撤銷(Undo)操作和恢復(fù)(Redo)操作時叙量,可以將命令對象存儲起來倡蝙,采用備忘錄模式來實現(xiàn)。

實現(xiàn):

  • 抽象命令類(Command)角色:聲明執(zhí)行命令的接口绞佩,擁有執(zhí)行命令的抽象方法 execute()寺鸥。
  • 具體命令類(Concrete Command)角色:是抽象命令類的具體實現(xiàn)類,它擁有接收者對象品山,并通過調(diào)用接收者的功能來完成命令要執(zhí)行的操作胆建。
  • 實現(xiàn)者/接收者(Receiver)角色:執(zhí)行命令功能的相關(guān)操作,是具體命令對象業(yè)務(wù)的真正實現(xiàn)者肘交。
  • 調(diào)用者/請求者(Invoker)角色:是請求的發(fā)送者笆载,它通常擁有很多的命令對象,并通過訪問命令對象來執(zhí)行相關(guān)請求涯呻,它不直接訪問接收者凉驻。
img18.gif
package command;
public class CommandPattern {
    public static void main(String[] args) {
        Command cmd = new ConcreteCommand();
        Invoker ir = new Invoker(cmd);
        System.out.println("客戶訪問調(diào)用者的call()方法...");
        ir.call();
    }
}
//調(diào)用者
class Invoker {
    private Command command;
    public Invoker(Command command) {
        this.command = command;
    }
    public void setCommand(Command command) {
        this.command = command;
    }
    public void call() {
        System.out.println("調(diào)用者執(zhí)行命令command...");
        command.execute();
    }
}
//抽象命令
interface Command {
    public abstract void execute();
}
//具體命令
class ConcreteCommand implements Command {
    private Receiver receiver;
    ConcreteCommand() {
        receiver = new Receiver();
    }
    public void execute() {
        receiver.action();
    }
}
//接收者
class Receiver {
    public void action() {
        System.out.println("接收者的action()方法被調(diào)用...");
    }
}

職責(zé)鏈(Chain of Responsibility)模式

定義: 為了避免請求發(fā)送者與多個請求處理者耦合在一起,于是將所有請求的處理者通過前一對象記住其下一個對象的引用而連成一條鏈复罐;當(dāng)有請求發(fā)生時涝登,可將請求沿著這條鏈傳遞,直到有對象處理它為止效诅。

在責(zé)任鏈模式中胀滚,客戶只需要將請求發(fā)送到責(zé)任鏈上即可,無須關(guān)心請求的處理細(xì)節(jié)和請求的傳遞過程乱投,請求會自動進(jìn)行傳遞咽笼。所以責(zé)任鏈將請求的發(fā)送者和請求的處理者解耦了。

優(yōu)點(diǎn):

  1. 降低了對象之間的耦合度戚炫。該模式使得一個對象無須知道到底是哪一個對象處理其請求以及鏈的結(jié)構(gòu)剑刑,發(fā)送者和接收者也無須擁有對方的明確信息。
  2. 增強(qiáng)了系統(tǒng)的可擴(kuò)展性双肤∈┨停可以根據(jù)需要增加新的請求處理類层宫,滿足開閉原則。
  3. 增強(qiáng)了給對象指派職責(zé)的靈活性其监。當(dāng)工作流程發(fā)生變化,可以動態(tài)地改變鏈內(nèi)的成員或者調(diào)動它們的次序限匣,也可動態(tài)地新增或者刪除責(zé)任抖苦。
  4. 責(zé)任鏈簡化了對象之間的連接。每個對象只需保持一個指向其后繼者的引用米死,不需保持其他所有處理者的引用锌历,這避免了使用眾多的 if 或者 if···else 語句。
  5. 責(zé)任分擔(dān)峦筒。每個類只需要處理自己該處理的工作究西,不該處理的傳遞給下一個對象完成,明確各類的責(zé)任范圍物喷,符合類的單一職責(zé)原則卤材。

缺點(diǎn):

  1. 不能保證每個請求一定被處理。由于一個請求沒有明確的接收者峦失,所以不能保證它一定會被處理扇丛,該請求可能一直傳到鏈的末端都得不到處理。
  2. 對比較長的職責(zé)鏈尉辑,請求的處理可能涉及多個處理對象帆精,系統(tǒng)性能將受到一定影響。
  3. 職責(zé)鏈建立的合理性要靠客戶端來保證隧魄,增加了客戶端的復(fù)雜性卓练,可能會由于職責(zé)鏈的錯誤設(shè)置而導(dǎo)致系統(tǒng)出錯,如可能會造成循環(huán)調(diào)用购啄。

應(yīng)用場景:

  • 多個對象可以處理一個請求襟企,但具體由哪個對象處理該請求在運(yùn)行時自動確定。
  • 可動態(tài)指定一組對象處理請求闸溃,或添加新的處理者整吆。
  • 需要在不明確指定請求處理者的情況下,向多個處理者中的一個提交請求辉川。

模式的擴(kuò)展表蝙,職責(zé)鏈模式存在以下兩種情況。

  • 純的職責(zé)鏈模式:一個請求必須被某一個處理者對象所接收乓旗,且一個具體處理者對某個請求的處理只能采用以下兩種行為之一:自己處理(承擔(dān)責(zé)任)府蛇;把責(zé)任推給下家處理。
  • 不純的職責(zé)鏈模式:允許出現(xiàn)某一個具體處理者對象在承擔(dān)了請求的一部分責(zé)任后又將剩余的責(zé)任傳給下家的情況屿愚,且一個請求可以最終不被任何接收端對象所接收汇跨。

實現(xiàn):

通常情況下务荆,可以通過數(shù)據(jù)鏈表來實現(xiàn)職責(zé)鏈模式的數(shù)據(jù)結(jié)構(gòu)。

  • 抽象處理者(Handler)角色:定義一個處理請求的接口穷遂,包含抽象處理方法和一個后繼連接函匕。
  • 具體處理者(Concrete Handler)角色:實現(xiàn)抽象處理者的處理方法,判斷能否處理本次請求蚪黑,如果可以處理請求則處理盅惜,否則將該請求轉(zhuǎn)給它的后繼者。
  • 客戶類(Client)角色:創(chuàng)建處理鏈忌穿,并向鏈頭的具體處理者對象提交請求抒寂,它不關(guān)心處理細(xì)節(jié)和請求的傳遞過程。
img19.gif
img20.gif
package chainOfResponsibility;
public class ChainOfResponsibilityPattern {
    public static void main(String[] args) {
        //組裝責(zé)任鏈
        Handler handler1 = new ConcreteHandler1();
        Handler handler2 = new ConcreteHandler2();
        handler1.setNext(handler2);
        //提交請求
        handler1.handleRequest("two");
    }
}
//抽象處理者角色
abstract class Handler {
    private Handler next;
    public void setNext(Handler next) {
        this.next = next;
    }
    public Handler getNext() {
        return next;
    }
    //處理請求的方法
    public abstract void handleRequest(String request);
}
//具體處理者角色1
class ConcreteHandler1 extends Handler {
    public void handleRequest(String request) {
        if (request.equals("one")) {
            System.out.println("具體處理者1負(fù)責(zé)處理該請求掠剑!");
        } else {
            if (getNext() != null) {
                getNext().handleRequest(request);
            } else {
                System.out.println("沒有人處理該請求屈芜!");
            }
        }
    }
}
//具體處理者角色2
class ConcreteHandler2 extends Handler {
    public void handleRequest(String request) {
        if (request.equals("two")) {
            System.out.println("具體處理者2負(fù)責(zé)處理該請求!");
        } else {
            if (getNext() != null) {
                getNext().handleRequest(request);
            } else {
                System.out.println("沒有人處理該請求朴译!");
            }
        }
    }
}

狀態(tài)(State)模式

定義: 對有狀態(tài)的對象井佑,把復(fù)雜的“判斷邏輯”提取到不同的狀態(tài)對象中,允許狀態(tài)對象在其內(nèi)部狀態(tài)發(fā)生改變時改變其行為动分。

優(yōu)點(diǎn):

  1. 結(jié)構(gòu)清晰毅糟,狀態(tài)模式將與特定狀態(tài)相關(guān)的行為局部化到一個狀態(tài)中,并且將不同狀態(tài)的行為分割開來澜公,滿足“單一職責(zé)原則”姆另。
  2. 將狀態(tài)轉(zhuǎn)換顯示化,減少對象間的相互依賴坟乾。將不同的狀態(tài)引入獨(dú)立的對象中會使得狀態(tài)轉(zhuǎn)換變得更加明確迹辐,且減少對象間的相互依賴。
  3. 狀態(tài)類職責(zé)明確甚侣,有利于程序的擴(kuò)展明吩。通過定義新的子類很容易地增加新的狀態(tài)和轉(zhuǎn)換。

缺點(diǎn):

  1. 狀態(tài)模式的使用必然會增加系統(tǒng)的類與對象的個數(shù)殷费。
  2. 狀態(tài)模式的結(jié)構(gòu)與實現(xiàn)都較為復(fù)雜印荔,如果使用不當(dāng)會導(dǎo)致程序結(jié)構(gòu)和代碼的混亂。
  3. 狀態(tài)模式對開閉原則的支持并不太好详羡,對于可以切換狀態(tài)的狀態(tài)模式仍律,增加新的狀態(tài)類需要修改那些負(fù)責(zé)狀態(tài)轉(zhuǎn)換的源碼,否則無法切換到新增狀態(tài)实柠,而且修改某個狀態(tài)類的行為也需要修改對應(yīng)類的源碼水泉。

應(yīng)用場景:

  • 當(dāng)一個對象的行為取決于它的狀態(tài),并且它必須在運(yùn)行時根據(jù)狀態(tài)改變它的行為時,就可以考慮使用狀態(tài)模式草则。
  • 一個操作中含有龐大的分支結(jié)構(gòu)钢拧,并且這些分支決定于對象的狀態(tài)時。

實現(xiàn):

狀態(tài)模式把受環(huán)境改變的對象行為包裝在不同的狀態(tài)對象里炕横,其意圖是讓一個對象在其內(nèi)部狀態(tài)改變的時候源内,其行為也隨之改變。現(xiàn)在我們來分析其基本結(jié)構(gòu)和實現(xiàn)方法份殿。

  • 環(huán)境類(Context)角色:也稱為上下文姿锭,它定義了客戶端需要的接口,內(nèi)部維護(hù)一個當(dāng)前狀態(tài)伯铣,并負(fù)責(zé)具體狀態(tài)的切換。
  • 抽象狀態(tài)(State)角色:定義一個接口轮纫,用以封裝環(huán)境對象中的特定狀態(tài)所對應(yīng)的行為腔寡,可以有一個或多個行為。
  • 具體狀態(tài)(Concrete State)角色:實現(xiàn)抽象狀態(tài)所對應(yīng)的行為掌唾,并且在需要的情況下進(jìn)行狀態(tài)切換放前。
img21.gif
public class StatePatternClient {
    public static void main(String[] args) {
        Context context = new Context();    //創(chuàng)建環(huán)境      
        context.Handle();    //處理請求
        context.Handle();
        context.Handle();
        context.Handle();
    }
}
//環(huán)境類
class Context {
    private State state;
    //定義環(huán)境類的初始狀態(tài)
    public Context() {
        this.state = new ConcreteStateA();
    }
    //設(shè)置新狀態(tài)
    public void setState(State state) {
        this.state = state;
    }
    //讀取狀態(tài)
    public State getState() {
        return (state);
    }
    //對請求做處理
    public void Handle() {
        state.Handle(this);
    }
}
//抽象狀態(tài)類
abstract class State {
    public abstract void Handle(Context context);
}
//具體狀態(tài)A類
class ConcreteStateA extends State {
    public void Handle(Context context) {
        System.out.println("當(dāng)前狀態(tài)是 A.");
        context.setState(new ConcreteStateB());
    }
}
//具體狀態(tài)B類
class ConcreteStateB extends State {
    public void Handle(Context context) {
        System.out.println("當(dāng)前狀態(tài)是 B.");
        context.setState(new ConcreteStateA());
    }
}

觀察者(Observer)模式

定義: 指多個對象間存在一對多的依賴關(guān)系,當(dāng)一個對象的狀態(tài)發(fā)生改變時糯彬,所有依賴于它的對象都得到通知并被自動更新凭语。這種模式有時又稱作發(fā)布-訂閱模式、模型-視圖模式撩扒,它是對象行為型模式似扔。

優(yōu)點(diǎn):

  1. 降低了目標(biāo)與觀察者之間的耦合關(guān)系,兩者之間是抽象耦合關(guān)系搓谆。符合依賴倒置原則炒辉。
  2. 目標(biāo)與觀察者之間建立了一套觸發(fā)機(jī)制。

缺點(diǎn):

  1. 目標(biāo)與觀察者之間的依賴關(guān)系并沒有完全解除泉手,而且有可能出現(xiàn)循環(huán)引用黔寇。
  2. 當(dāng)觀察者對象很多時,通知的發(fā)布會花費(fèi)很多時間媳瞪,影響程序的效率刑峡。

應(yīng)用場景:

在軟件系統(tǒng)中房蝉,當(dāng)系統(tǒng)一方行為依賴另一方行為的變動時,可使用觀察者模式松耦合聯(lián)動雙方憋飞,使得一方的變動可以通知到感興趣的另一方對象,從而讓另一方對象對此做出響應(yīng)袭艺。

  • 對象間存在一對多關(guān)系搀崭,一個對象的狀態(tài)發(fā)生改變會影響其他對象。
  • 當(dāng)一個抽象模型有兩個方面,其中一個方面依賴于另一方面時瘤睹,可將這二者封裝在獨(dú)立的對象中以使它們可以各自獨(dú)立地改變和復(fù)用升敲。
  • 實現(xiàn)類似廣播機(jī)制的功能,不需要知道具體收聽者轰传,只需分發(fā)廣播驴党,系統(tǒng)中感興趣的對象會自動接收該廣播。
  • 多層級嵌套使用获茬,形成一種鏈?zhǔn)接|發(fā)機(jī)制港庄,使得事件具備跨域(跨越兩種觀察者類型)通知。

實現(xiàn):

實現(xiàn)觀察者模式時要注意具體目標(biāo)對象和具體觀察者對象之間不能直接調(diào)用恕曲,否則將使兩者之間緊密耦合起來鹏氧,這違反了面向?qū)ο蟮脑O(shè)計原則。

  • 抽象主題(Subject)角色:也叫抽象目標(biāo)類佩谣,它提供了一個用于保存觀察者對象的聚集類和增加把还、刪除觀察者對象的方法,以及通知所有觀察者的抽象方法茸俭。
  • 具體主題(Concrete Subject)角色:也叫具體目標(biāo)類吊履,它實現(xiàn)抽象目標(biāo)中的通知方法,當(dāng)具體主題的內(nèi)部狀態(tài)發(fā)生改變時调鬓,通知所有注冊過的觀察者對象艇炎。
  • 抽象觀察者(Observer)角色:它是一個抽象類或接口,它包含了一個更新自己的抽象方法腾窝,當(dāng)接到具體主題的更改通知時被調(diào)用缀踪。
  • 具體觀察者(Concrete Observer)角色:實現(xiàn)抽象觀察者中定義的抽象方法,以便在得到目標(biāo)的更改通知時更新自身的狀態(tài)虹脯。
img22.gif
package net.biancheng.c.observer;
import java.util.*;
public class ObserverPattern {
    public static void main(String[] args) {
        Subject subject = new ConcreteSubject();
        Observer obs1 = new ConcreteObserver1();
        Observer obs2 = new ConcreteObserver2();
        subject.add(obs1);
        subject.add(obs2);
        subject.notifyObserver();
    }
}
//抽象目標(biāo)
abstract class Subject {
    protected List<Observer> observers = new ArrayList<Observer>();
    //增加觀察者方法
    public void add(Observer observer) {
        observers.add(observer);
    }
    //刪除觀察者方法
    public void remove(Observer observer) {
        observers.remove(observer);
    }
    public abstract void notifyObserver(); //通知觀察者方法
}
//具體目標(biāo)
class ConcreteSubject extends Subject {
    public void notifyObserver() {
        System.out.println("具體目標(biāo)發(fā)生改變...");
        System.out.println("--------------");
        for (Object obs : observers) {
            ((Observer) obs).response();
        }
    }
}
//抽象觀察者
interface Observer {
    void response(); //反應(yīng)
}
//具體觀察者1
class ConcreteObserver1 implements Observer {
    public void response() {
        System.out.println("具體觀察者1作出反應(yīng)辜贵!");
    }
}
//具體觀察者1
class ConcreteObserver2 implements Observer {
    public void response() {
        System.out.println("具體觀察者2作出反應(yīng)!");
    }
}

中介者(Mediator)模式

定義: 定義一個中介對象來封裝一系列對象之間的交互归形,使原有對象之間的耦合松散托慨,且可以獨(dú)立地改變它們之間的交互。中介者模式又叫調(diào)停模式暇榴,它是迪米特法則的典型應(yīng)用厚棵。

優(yōu)點(diǎn):

  1. 類之間各司其職,符合迪米特法則蔼紧。
  2. 降低了對象之間的耦合性婆硬,使得對象易于獨(dú)立地被復(fù)用。
  3. 將對象間的一對多關(guān)聯(lián)轉(zhuǎn)變?yōu)橐粚σ坏年P(guān)聯(lián)奸例,提高系統(tǒng)的靈活性彬犯,使得系統(tǒng)易于維護(hù)和擴(kuò)展向楼。

缺點(diǎn):

  1. 中介者模式將原本多個對象直接的相互依賴變成了中介者和多個同事類的依賴關(guān)系。當(dāng)同事類越多時谐区,中介者就會越臃腫湖蜕,變得復(fù)雜且難以維護(hù)。

應(yīng)用場景:

  • 當(dāng)對象之間存在復(fù)雜的網(wǎng)狀結(jié)構(gòu)關(guān)系而導(dǎo)致依賴關(guān)系混亂且難以復(fù)用時宋列。
  • 當(dāng)想創(chuàng)建一個運(yùn)行于多個類之間的對象昭抒,又不想生成新的子類時。

模式的擴(kuò)展炼杖,在實際開發(fā)中灭返,通常采用以下兩種方法來簡化中介者模式,使開發(fā)變得更簡單坤邪。

  • 不定義中介者接口熙含,把具體中介者對象實現(xiàn)成為單例。
  • 同事對象不持有中介者艇纺,而是在需要的時候直接獲取中介者對象并調(diào)用婆芦。

實現(xiàn):

中介者模式實現(xiàn)的關(guān)鍵是找出“中介者”。

  • 抽象中介者(Mediator)角色:它是中介者的接口喂饥,提供了同事對象注冊與轉(zhuǎn)發(fā)同事對象信息的抽象方法。
  • 具體中介者(Concrete Mediator)角色:實現(xiàn)中介者接口肠鲫,定義一個 List 來管理同事對象员帮,協(xié)調(diào)各個同事角色之間的交互關(guān)系,因此它依賴于同事角色导饲。
  • 抽象同事類(Colleague)角色:定義同事類的接口捞高,保存中介者對象,提供同事對象交互的抽象方法渣锦,實現(xiàn)所有相互影響的同事類的公共功能硝岗。
  • 具體同事類(Concrete Colleague)角色:是抽象同事類的實現(xiàn)者,當(dāng)需要與其他同事對象交互時袋毙,由中介者對象負(fù)責(zé)后續(xù)的交互型檀。
img23.gif
package net.biancheng.c.mediator;
import java.util.*;
public class MediatorPattern {
    public static void main(String[] args) {
        Mediator md = new ConcreteMediator();
        Colleague c1, c2;
        c1 = new ConcreteColleague1();
        c2 = new ConcreteColleague2();
        md.register(c1);
        md.register(c2);
        c1.send();
        System.out.println("-------------");
        c2.send();
    }
}
//抽象中介者
abstract class Mediator {
    public abstract void register(Colleague colleague);
    public abstract void relay(Colleague cl); //轉(zhuǎn)發(fā)
}
//具體中介者
class ConcreteMediator extends Mediator {
    private List<Colleague> colleagues = new ArrayList<Colleague>();
    public void register(Colleague colleague) {
        if (!colleagues.contains(colleague)) {
            colleagues.add(colleague);
            colleague.setMedium(this);
        }
    }
    public void relay(Colleague cl) {
        for (Colleague ob : colleagues) {
            if (!ob.equals(cl)) {
                ((Colleague) ob).receive();
            }
        }
    }
}
//抽象同事類
abstract class Colleague {
    protected Mediator mediator;
    public void setMedium(Mediator mediator) {
        this.mediator = mediator;
    }
    public abstract void receive();
    public abstract void send();
}
//具體同事類
class ConcreteColleague1 extends Colleague {
    public void receive() {
        System.out.println("具體同事類1收到請求。");
    }
    public void send() {
        System.out.println("具體同事類1發(fā)出請求听盖。");
        mediator.relay(this); //請中介者轉(zhuǎn)發(fā)
    }
}
//具體同事類
class ConcreteColleague2 extends Colleague {
    public void receive() {
        System.out.println("具體同事類2收到請求胀溺。");
    }
    public void send() {
        System.out.println("具體同事類2發(fā)出請求。");
        mediator.relay(this); //請中介者轉(zhuǎn)發(fā)
    }
}

迭代器(Iterator)模式

定義: 提供一個對象來順序訪問聚合對象中的一系列數(shù)據(jù)皆看,而不暴露聚合對象的內(nèi)部表示仓坞。

優(yōu)點(diǎn):

  1. 訪問一個聚合對象的內(nèi)容而無須暴露它的內(nèi)部表示。
  2. 遍歷任務(wù)交由迭代器完成腰吟,這簡化了聚合類无埃。
  3. 它支持以不同方式遍歷一個聚合,甚至可以自定義迭代器的子類以支持新的遍歷。
  4. 增加新的聚合類和迭代器類都很方便嫉称,無須修改原有代碼侦镇。
  5. 封裝性良好,為遍歷不同的聚合結(jié)構(gòu)提供一個統(tǒng)一的接口澎埠。

缺點(diǎn):

  1. 增加了類的個數(shù)虽缕,這在一定程度上增加了系統(tǒng)的復(fù)雜性。

應(yīng)用場景:

  • 當(dāng)需要為聚合對象提供多種遍歷方式時蒲稳。
  • 當(dāng)需要為遍歷不同的聚合結(jié)構(gòu)提供一個統(tǒng)一的接口時氮趋。
  • 當(dāng)訪問一個聚合對象的內(nèi)容而無須暴露其內(nèi)部細(xì)節(jié)的表示時。

由于聚合與迭代器的關(guān)系非常密切江耀,所以大多數(shù)語言在實現(xiàn)聚合類時都提供了迭代器類剩胁,因此大數(shù)情況下使用語言中已有的聚合類的迭代器就已經(jīng)夠了。

實現(xiàn):

迭代器模式是通過將聚合對象的遍歷行為分離出來祥国,抽象成迭代器類來實現(xiàn)的昵观,其目的是在不暴露聚合對象的內(nèi)部結(jié)構(gòu)的情況下,讓外部代碼透明地訪問聚合的內(nèi)部數(shù)據(jù)舌稀。

  • 抽象聚合(Aggregate)角色:定義存儲啊犬、添加、刪除聚合對象以及創(chuàng)建迭代器對象的接口壁查。
  • 具體聚合(ConcreteAggregate)角色:實現(xiàn)抽象聚合類觉至,返回一個具體迭代器的實例。
  • 抽象迭代器(Iterator)角色:定義訪問和遍歷聚合元素的接口睡腿,通常包含 hasNext()语御、first()、next() 等方法席怪。
  • 具體迭代器(Concretelterator)角色:實現(xiàn)抽象迭代器接口中所定義的方法应闯,完成對聚合對象的遍歷,記錄遍歷的當(dāng)前位置挂捻。
img24.gif
package net.biancheng.c.iterator;
import java.util.*;
public class IteratorPattern {
    public static void main(String[] args) {
        Aggregate ag = new ConcreteAggregate();
        ag.add("中山大學(xué)");
        ag.add("華南理工");
        ag.add("韶關(guān)學(xué)院");
        System.out.print("聚合的內(nèi)容有:");
        Iterator it = ag.getIterator();
        while (it.hasNext()) {
            Object ob = it.next();
            System.out.print(ob.toString() + "\t");
        }
        Object ob = it.first();
        System.out.println("\nFirst:" + ob.toString());
    }
}
//抽象聚合
interface Aggregate {
    public void add(Object obj);
    public void remove(Object obj);
    public Iterator getIterator();
}
//具體聚合
class ConcreteAggregate implements Aggregate {
    private List<Object> list = new ArrayList<Object>();
    public void add(Object obj) {
        list.add(obj);
    }
    public void remove(Object obj) {
        list.remove(obj);
    }
    public Iterator getIterator() {
        return (new ConcreteIterator(list));
    }
}
//抽象迭代器
interface Iterator {
    Object first();
    Object next();
    boolean hasNext();
}
//具體迭代器
class ConcreteIterator implements Iterator {
    private List<Object> list = null;
    private int index = -1;
    public ConcreteIterator(List<Object> list) {
        this.list = list;
    }
    public boolean hasNext() {
        if (index < list.size() - 1) {
            return true;
        } else {
            return false;
        }
    }
    public Object first() {
        index = 0;
        Object obj = list.get(index);
        ;
        return obj;
    }
    public Object next() {
        Object obj = null;
        if (this.hasNext()) {
            obj = list.get(++index);
        }
        return obj;
    }
}

訪問者(Visitor)模式

定義: 將作用于某種數(shù)據(jù)結(jié)構(gòu)中的各元素的操作分離出來封裝成獨(dú)立的類碉纺,使其在不改變數(shù)據(jù)結(jié)構(gòu)的前提下可以添加作用于這些元素的新的操作,為數(shù)據(jù)結(jié)構(gòu)中的每個元素提供多種訪問方式刻撒。

優(yōu)點(diǎn):

  1. 擴(kuò)展性好惜辑。能夠在不修改對象結(jié)構(gòu)中的元素的情況下,為對象結(jié)構(gòu)中的元素添加新的功能疫赎。
  2. 復(fù)用性好盛撑。可以通過訪問者來定義整個對象結(jié)構(gòu)通用的功能捧搞,從而提高系統(tǒng)的復(fù)用程度抵卫。
  3. 靈活性好狮荔。訪問者模式將數(shù)據(jù)結(jié)構(gòu)與作用于結(jié)構(gòu)上的操作解耦,使得操作集合可相對自由地演化而不影響系統(tǒng)的數(shù)據(jù)結(jié)構(gòu)介粘。
  4. 符合單一職責(zé)原則殖氏。訪問者模式把相關(guān)的行為封裝在一起,構(gòu)成一個訪問者姻采,使每一個訪問者的功能都比較單一雅采。

缺點(diǎn):

  1. 增加新的元素類很困難。在訪問者模式中慨亲,每增加一個新的元素類婚瓜,都要在每一個具體訪問者類中增加相應(yīng)的具體操作,這違背了“開閉原則”刑棵。
  2. 破壞封裝巴刻。訪問者模式中具體元素對訪問者公布細(xì)節(jié),這破壞了對象的封裝性蛉签。
  3. 違反了依賴倒置原則胡陪。訪問者模式依賴了具體類,而沒有依賴抽象類碍舍。

應(yīng)用場景:

當(dāng)系統(tǒng)中存在類型數(shù)量穩(wěn)定(固定)的一類數(shù)據(jù)結(jié)構(gòu)時柠座,可以使用訪問者模式方便地實現(xiàn)對該類型所有數(shù)據(jù)結(jié)構(gòu)的不同操作,而又不會對數(shù)據(jù)產(chǎn)生任何副作用(臟數(shù)據(jù))片橡。

簡而言之妈经,就是當(dāng)對集合中的不同類型數(shù)據(jù)(類型數(shù)量穩(wěn)定)進(jìn)行多種操作時,使用訪問者模式锻全。

  • 對象結(jié)構(gòu)相對穩(wěn)定,但其操作算法經(jīng)常變化的程序录煤。
  • 對象結(jié)構(gòu)中的對象需要提供多種不同且不相關(guān)的操作鳄厌,而且要避免讓這些操作的變化影響對象的結(jié)構(gòu)。
  • 對象結(jié)構(gòu)包含很多類型的對象妈踊,希望對這些對象實施一些依賴于其具體類型的操作了嚎。

實現(xiàn):

實現(xiàn)的關(guān)鍵是如何將作用于元素的操作分離出來封裝成獨(dú)立的類。

  • 抽象訪問者(Visitor)角色:定義一個訪問具體元素的接口廊营,為每個具體元素類對應(yīng)一個訪問操作 visit() 歪泳,該操作中的參數(shù)類型標(biāo)識了被訪問的具體元素。
  • 具體訪問者(ConcreteVisitor)角色:實現(xiàn)抽象訪問者角色中聲明的各個訪問操作露筒,確定訪問者訪問一個元素時該做什么呐伞。
  • 抽象元素(Element)角色:聲明一個包含接受操作 accept() 的接口,被接受的訪問者對象作為 accept() 方法的參數(shù)慎式。
  • 具體元素(ConcreteElement)角色:實現(xiàn)抽象元素角色提供的 accept() 操作伶氢,其方法體通常都是 visitor.visit(this) 趟径,另外具體元素中可能還包含本身業(yè)務(wù)邏輯的相關(guān)操作。
  • 對象結(jié)構(gòu)(Object Structure)角色:是一個包含元素角色的容器癣防,提供讓訪問者對象遍歷容器中的所有元素的方法蜗巧,通常由 List、Set蕾盯、Map 等聚合類實現(xiàn)幕屹。
img25.gif
package net.biancheng.c.visitor;
import java.util.*;
public class VisitorPattern {
    public static void main(String[] args) {
        ObjectStructure os = new ObjectStructure();
        os.add(new ConcreteElementA());
        os.add(new ConcreteElementB());
        Visitor visitor = new ConcreteVisitorA();
        os.accept(visitor);
        System.out.println("------------------------");
        visitor = new ConcreteVisitorB();
        os.accept(visitor);
    }
}
//抽象訪問者
interface Visitor {
    void visit(ConcreteElementA element);
    void visit(ConcreteElementB element);
}
//具體訪問者A類
class ConcreteVisitorA implements Visitor {
    public void visit(ConcreteElementA element) {
        System.out.println("具體訪問者A訪問-->" + element.operationA());
    }
    public void visit(ConcreteElementB element) {
        System.out.println("具體訪問者A訪問-->" + element.operationB());
    }
}
//具體訪問者B類
class ConcreteVisitorB implements Visitor {
    public void visit(ConcreteElementA element) {
        System.out.println("具體訪問者B訪問-->" + element.operationA());
    }
    public void visit(ConcreteElementB element) {
        System.out.println("具體訪問者B訪問-->" + element.operationB());
    }
}
//抽象元素類
interface Element {
    void accept(Visitor visitor);
}
//具體元素A類
class ConcreteElementA implements Element {
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
    public String operationA() {
        return "具體元素A的操作。";
    }
}
//具體元素B類
class ConcreteElementB implements Element {
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
    public String operationB() {
        return "具體元素B的操作级遭。";
    }
}
//對象結(jié)構(gòu)角色
class ObjectStructure {
    private List<Element> list = new ArrayList<Element>();
    public void accept(Visitor visitor) {
        Iterator<Element> i = list.iterator();
        while (i.hasNext()) {
            ((Element) i.next()).accept(visitor);
        }
    }
    public void add(Element element) {
        list.add(element);
    }
    public void remove(Element element) {
        list.remove(element);
    }
}

備忘錄(Memento)模式

定義: 在不破壞封裝性的前提下望拖,捕獲一個對象的內(nèi)部狀態(tài),并在該對象之外保存這個狀態(tài)装畅,以便以后當(dāng)需要時能將該對象恢復(fù)到原先保存的狀態(tài)靠娱。該模式又叫快照模式。

優(yōu)點(diǎn):

  1. 提供了一種可以恢復(fù)狀態(tài)的機(jī)制掠兄。當(dāng)用戶需要時能夠比較方便地將數(shù)據(jù)恢復(fù)到某個歷史的狀態(tài)像云。
  2. 實現(xiàn)了內(nèi)部狀態(tài)的封裝。除了創(chuàng)建它的發(fā)起人之外蚂夕,其他對象都不能夠訪問這些狀態(tài)信息迅诬。
  3. 簡化了發(fā)起人類。發(fā)起人不需要管理和保存其內(nèi)部狀態(tài)的各個備份婿牍,所有狀態(tài)信息都保存在備忘錄中侈贷,并由管理者進(jìn)行管理,這符合單一職責(zé)原則等脂。

缺點(diǎn):

  1. 資源消耗大俏蛮。如果要保存的內(nèi)部狀態(tài)信息過多或者特別頻繁,將會占用比較大的內(nèi)存資源上遥。

應(yīng)用場景:

  • 需要保存與恢復(fù)數(shù)據(jù)的場景搏屑,如玩游戲時的中間結(jié)果的存檔功能。
  • 需要提供一個可回滾操作的場景粉楚,如 Word辣恋、記事本、Photoshop模软,Eclipse 等軟件在編輯時按 Ctrl+Z 組合鍵伟骨,還有數(shù)據(jù)庫中事務(wù)操作。

實現(xiàn):

備忘錄模式的核心是設(shè)計備忘錄類以及用于管理備忘錄的管理者類燃异。

  • 發(fā)起人(Originator)角色:記錄當(dāng)前時刻的內(nèi)部狀態(tài)信息携狭,提供創(chuàng)建備忘錄和恢復(fù)備忘錄數(shù)據(jù)的功能,實現(xiàn)其他業(yè)務(wù)功能回俐,它可以訪問備忘錄里的所有信息暑中。
  • 備忘錄(Memento)角色:負(fù)責(zé)存儲發(fā)起人的內(nèi)部狀態(tài)壹瘟,在需要的時候提供這些內(nèi)部狀態(tài)給發(fā)起人。
  • 管理者(Caretaker)角色:對備忘錄進(jìn)行管理鳄逾,提供保存與獲取備忘錄的功能稻轨,但其不能對備忘錄的內(nèi)容進(jìn)行訪問與修改。
img26.gif
package net.biancheng.c.memento;
public class MementoPattern {
    public static void main(String[] args) {
        Originator or = new Originator();
        Caretaker cr = new Caretaker();
        or.setState("S0");
        System.out.println("初始狀態(tài):" + or.getState());
        cr.setMemento(or.createMemento()); //保存狀態(tài)
        or.setState("S1");
        System.out.println("新的狀態(tài):" + or.getState());
        or.restoreMemento(cr.getMemento()); //恢復(fù)狀態(tài)
        System.out.println("恢復(fù)狀態(tài):" + or.getState());
    }
}
//備忘錄
class Memento {
    private String state;
    public Memento(String state) {
        this.state = state;
    }
    public void setState(String state) {
        this.state = state;
    }
    public String getState() {
        return state;
    }
}
//發(fā)起人
class Originator {
    private String state;
    public void setState(String state) {
        this.state = state;
    }
    public String getState() {
        return state;
    }
    public Memento createMemento() {
        return new Memento(state);
    }
    public void restoreMemento(Memento m) {
        this.setState(m.getState());
    }
}
//管理者
class Caretaker {
    private Memento memento;
    public void setMemento(Memento m) {
        memento = m;
    }
    public Memento getMemento() {
        return memento;
    }
}

解釋器(Interpreter)模式

定義: 給分析對象定義一個語言雕凹,并定義該語言的文法表示殴俱,再設(shè)計一個解析器來解釋語言中的句子。也就是說枚抵,用編譯語言的方式來分析應(yīng)用中的實例线欲。這種模式實現(xiàn)了文法表達(dá)式處理的接口,該接口解釋一個特定的上下文汽摹。

這里提到的文法和句子的概念同編譯原理中的描述相同李丰,“文法”指語言的語法規(guī)則,而“句子”是語言集中的元素逼泣。例如趴泌,漢語中的句子有很多,“我是中國人”是其中的一個句子拉庶,可以用一棵語法樹來直觀地描述語言中的句子嗜憔。

優(yōu)點(diǎn):

  1. 擴(kuò)展性好。由于在解釋器模式中使用類來表示語言的文法規(guī)則氏仗,因此可以通過繼承等機(jī)制來改變或擴(kuò)展文法吉捶。
  2. 容易實現(xiàn)。在語法樹中的每個表達(dá)式節(jié)點(diǎn)類都是相似的皆尔,所以實現(xiàn)其文法較為容易呐舔。

缺點(diǎn):

  1. 執(zhí)行效率較低。解釋器模式中通常使用大量的循環(huán)和遞歸調(diào)用慷蠕,當(dāng)要解釋的句子較復(fù)雜時珊拼,其運(yùn)行速度很慢,且代碼的調(diào)試過程也比較麻煩砌们。
  2. 會引起類膨脹杆麸。解釋器模式中的每條規(guī)則至少需要定義一個類涂召,當(dāng)包含的文法規(guī)則很多時株灸,類的個數(shù)將急劇增加少梁,導(dǎo)致系統(tǒng)難以管理與維護(hù)。
  3. 可應(yīng)用的場景比較少影兽。在軟件開發(fā)中,需要定義語言文法的應(yīng)用實例非常少莱革,所以這種模式很少被使用到峻堰。

應(yīng)用場景:

  • 當(dāng)語言的文法較為簡單讹开,且執(zhí)行效率不是關(guān)鍵問題時。
  • 當(dāng)問題重復(fù)出現(xiàn)捐名,且可以用一種簡單的語言來進(jìn)行表達(dá)時旦万。
  • 當(dāng)一個語言需要解釋執(zhí)行,并且語言中的句子可以表示為一個抽象語法樹的時候镶蹋,如 XML 文檔解釋成艘。

實現(xiàn):

解釋器模式常用于對簡單語言的編譯或分析實例中,為了掌握好它的結(jié)構(gòu)與實現(xiàn)贺归,必須先了解編譯原理中的“文法淆两、句子、語法樹”等相關(guān)概念拂酣。

文法: 文法是用于描述語言的語法結(jié)構(gòu)的形式規(guī)則秋冰。不管它是機(jī)器語言還是自然語言,都有它自己的文法規(guī)則婶熬。例如剑勾,中文中的“句子”的文法如下。

〈句子〉::=〈主語〉〈謂語〉〈賓語〉
〈主語〉::=〈代詞〉|〈名詞〉
〈謂語〉::=〈動詞〉
〈賓語〉::=〈代詞〉|〈名詞〉
〈代詞〉你|我|他
〈名詞〉7大學(xué)生I筱霞I英語
〈動詞〉::=是|學(xué)習(xí)

句子: 句子是語言的基本單位尸诽,是語言集中的一個元素甥材,它由終結(jié)符構(gòu)成,能由“文法”推導(dǎo)出性含。例如洲赵,上述文法可以推出“我是大學(xué)生”,所以它是句子商蕴。

語法樹: 語法樹是句子結(jié)構(gòu)的一種樹型表示叠萍,它代表了句子的推導(dǎo)結(jié)果,它有利于理解句子語法結(jié)構(gòu)的層次绪商。例如:“我是大學(xué)生”的語法樹苛谷。

img27.gif

有了以上基礎(chǔ)知識,現(xiàn)在來介紹解釋器模式的結(jié)構(gòu)就簡單了格郁。解釋器模式的結(jié)構(gòu)與組合模式相似腹殿,不過其包含的組成元素比組合模式多,而且組合模式是對象結(jié)構(gòu)型模式例书,而解釋器模式是類行為型模式锣尉。

  • 抽象表達(dá)式(Abstract Expression)角色:定義解釋器的接口,約定解釋器的解釋操作决采,主要包含解釋方法 interpret()自沧。
  • 終結(jié)符表達(dá)式(Terminal Expression)角色:是抽象表達(dá)式的子類,用來實現(xiàn)文法中與終結(jié)符相關(guān)的操作树瞭,文法中的每一個終結(jié)符都有一個具體終結(jié)表達(dá)式與之相對應(yīng)拇厢。
  • 非終結(jié)符表達(dá)式(Nonterminal - Expression)角色:也是抽象表達(dá)式的子類爱谁,用來實現(xiàn)文法中與非終結(jié)符相關(guān)的操作,文法中的每條規(guī)則都對應(yīng)于一個非終結(jié)符表達(dá)式孝偎。
  • 環(huán)境(Context)角色:通常包含各個解釋器需要的數(shù)據(jù)或是公共的功能访敌,一般用來傳遞被所有解釋器共享的數(shù)據(jù),后面的解釋器可以從這里獲取這些值衣盾。
  • 客戶端(Client):主要任務(wù)是將需要分析的句子或表達(dá)式轉(zhuǎn)換成使用解釋器對象描述的抽象語法樹捐顷,然后調(diào)用解釋器的解釋方法,當(dāng)然也可以通過環(huán)境角色間接訪問解釋器的解釋方法雨效。
img28.gif
package net.biancheng.c.interpreter;
//抽象表達(dá)式類
interface AbstractExpression {
    public void interpret(String info);    //解釋方法
}
//終結(jié)符表達(dá)式類
class TerminalExpression implements AbstractExpression {
    public void interpret(String info) {
        //對終結(jié)符表達(dá)式的處理
    }
}
//非終結(jié)符表達(dá)式類
class NonterminalExpression implements AbstractExpression {
    private AbstractExpression exp1;
    private AbstractExpression exp2;
    public void interpret(String info) {
        //非對終結(jié)符表達(dá)式的處理
    }
}
//環(huán)境類
class Context {
    private AbstractExpression exp;
    public Context() {
        //數(shù)據(jù)初始化
    }
    public void operation(String info) {
        //調(diào)用相關(guān)表達(dá)式類的解釋方法
    }
}

總結(jié)

設(shè)計模式 簡述 目的
模板模式 定義一套流程模板迅涮,根據(jù)需要實現(xiàn)模板中的操作 邏輯復(fù)用
策略模式 封裝不同的算法,算法之間能互相替換 把選擇權(quán)交給用戶
責(zé)任鏈模式 攔截的類都實現(xiàn)統(tǒng)一接口徽龟,每個接收者都包含對下一個接收者的引用叮姑。將這些對象連接成一條鏈,并且沿著這條鏈傳遞請求据悔,直到有對象處理它為止传透。 解耦處理邏輯
迭代器模式 提供一種方法順序訪問一個聚合對象中的各個元素 統(tǒng)一對集合的訪問方式
命令模式 將請求封裝成命令,并記錄下來极颓,能夠撤銷與重做 解耦請求和處理
狀態(tài)模式 根據(jù)不同的狀態(tài)做出不同的行為 綁定狀態(tài)和行為
備忘錄模式 保存對象的狀態(tài)朱盐,在需要時進(jìn)行恢復(fù) 備份、恢復(fù)機(jī)制
中介者模式 將對象之間的通信關(guān)聯(lián)關(guān)系封裝到一個中介類中單獨(dú)處理菠隆,從而使其耦合松散 統(tǒng)一管理網(wǎng)狀資源
觀察者模式 狀態(tài)發(fā)生改變時通知觀察者兵琳,一對多的關(guān)系 解耦觀察者與被觀察者
訪問者模式 穩(wěn)定數(shù)據(jù)結(jié)構(gòu),定義新的操作行為理 解耦數(shù)據(jù)結(jié)構(gòu)和數(shù)據(jù)操作
解釋器模式 給定一個語言骇径,定義它的語法表示躯肌,并定義一個解釋器,這個解釋器使用該標(biāo)識來解釋語言中的句子 實現(xiàn)特定語法解析

參考資料

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末破衔,一起剝皮案震驚了整個濱河市清女,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌晰筛,老刑警劉巖嫡丙,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異读第,居然都是意外死亡曙博,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進(jìn)店門卦方,熙熙樓的掌柜王于貴愁眉苦臉地迎上來羊瘩,“玉大人泰佳,你說我怎么就攤上這事盼砍〕韭穑” “怎么了?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵浇坐,是天一觀的道長睬捶。 經(jīng)常有香客問我,道長近刘,這世上最難降的妖魔是什么擒贸? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮觉渴,結(jié)果婚禮上介劫,老公的妹妹穿的比我還像新娘。我一直安慰自己案淋,他們只是感情好座韵,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著踢京,像睡著了一般誉碴。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上瓣距,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天黔帕,我揣著相機(jī)與錄音,去河邊找鬼蹈丸。 笑死成黄,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的逻杖。 我是一名探鬼主播慨默,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼弧腥!你這毒婦竟也來了厦取?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤管搪,失蹤者是張志新(化名)和其女友劉穎虾攻,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體更鲁,經(jīng)...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡霎箍,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了澡为。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片漂坏。...
    茶點(diǎn)故事閱讀 39,711評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出顶别,到底是詐尸還是另有隱情谷徙,我是刑警寧澤,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布驯绎,位于F島的核電站完慧,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏剩失。R本人自食惡果不足惜屈尼,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望拴孤。 院中可真熱鬧脾歧,春花似錦、人聲如沸演熟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽绽媒。三九已至蚕冬,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間是辕,已是汗流浹背囤热。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留获三,地道東北人旁蔼。 一個月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像疙教,于是被迫代替她去往敵國和親棺聊。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評論 2 353

推薦閱讀更多精彩內(nèi)容

  • 1. 模板方法模式* 模板方法模式:定義一個操作中的算法的骨架贞谓,而將一些步驟延遲到子類中限佩,使得子類可以不改變一個算...
    皇天閱讀 131評論 0 0
  • 下面總結(jié)設(shè)計模式中的行為型模式: 1.責(zé)任鏈模式 顧名思義,責(zé)任鏈模式(Chain of Responsibili...
    Steven1997閱讀 3,460評論 0 1
  • 設(shè)計模式-面相對象5個原則設(shè)計模式-創(chuàng)造性模式 單例 原型 工廠 建造者設(shè)計模式-設(shè)計模式 - 結(jié)構(gòu)性模式 代理 ...
    月影路西法閱讀 406評論 0 0
  • 1.責(zé)任鏈模式(Chain of Responsibility)2.命令模式(Command)3.解釋器模式(In...
    hadoop_a9bb閱讀 1,059評論 0 5
  • 策略模式【strategy】(接口主要) 【學(xué)習(xí)難度:★☆☆☆☆裸弦,使用頻率:★★★★☆】 定義:策略模式定義了一系...
    xuexiangjys閱讀 438評論 1 1