JAVA設(shè)計(jì)模式相關(guān)

轉(zhuǎn)自JsonChao的GitHub
https://github.com/JsonChao/Awesome-Android-Notebook/edit/master/notes/Android%E5%BC%80%E5%8F%91%E8%80%85%E5%BF%85%E9%A1%BB%E6%8E%8C%E6%8F%A1%E7%9A%84%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F.md

一、設(shè)計(jì)模式六大原則

設(shè)計(jì)模式有六大原則,如下所示:

  • 單一職責(zé)原則
  • 開放封閉原則
  • 里氏替換原則
  • 依賴倒置
  • 迪米特原則
  • 接口隔離原則

單一職責(zé)原則

一個(gè)類應(yīng)該僅有一個(gè)引起它變化的原因,即不要讓一個(gè)類承擔(dān)過多的職責(zé),以此降低耦合性。

開放封閉原則

類、函數(shù)、模塊應(yīng)該是可以擴(kuò)展的,但是不可以修改,即對擴(kuò)展開放,修改封閉。

里氏替換原則

所有引用基類的地方都能透明地替換為子類對象,即可以在定義時(shí)盡量使用基類對象,等到運(yùn)行時(shí)再確定其子類類型,用子類對象來替換父類對象。

依賴倒置原則

高層、底層模塊、模塊間和細(xì)節(jié)都應(yīng)該依賴于抽象,即通過接口或抽象類產(chǎn)生依賴關(guān)系。

迪米特原則

一個(gè)軟件實(shí)體應(yīng)該盡可能少地與其它實(shí)體發(fā)生相互作用,即最少知識原則。

如果一個(gè)對象需要調(diào)用其它對象的某個(gè)方法,可以通過第三者來調(diào)用,這個(gè)第三者的作用就如Android中的事件總線EventBus一樣。

接口隔離原則

一個(gè)類對另一個(gè)類的依賴應(yīng)該建立在最小的接口上。

二、設(shè)計(jì)模式分類

GoF提出的設(shè)計(jì)模式有23種,按照目的準(zhǔn)則分類,有三大類:

  • 創(chuàng)建性設(shè)計(jì)模式5種:單例、工廠方法、抽象工廠、建造者、原型。
  • 結(jié)構(gòu)型設(shè)計(jì)模式7種:適配器、裝飾、代理、外觀、橋接、組合、享元。
  • 行為型設(shè)計(jì)模式11種:策略、模板方法、觀察者、迭代器、責(zé)任鏈、命令、備忘錄、狀態(tài)、訪問者、中介者、解釋器。

三、Android開發(fā)常用設(shè)計(jì)模式

1、創(chuàng)建型設(shè)計(jì)模式

單例模式

保證一個(gè)類僅有一個(gè)實(shí)例,提供一個(gè)訪問它的全局訪問點(diǎn)。

單例模式共有5種寫法:

1、餓漢模式
public class Singleton {
    private static Singleton instance = new Singleton;
    private Singleton () {
        
    }
    public static Singleton getInstance() {
        return instance;
    }
}
  • 在類加載的時(shí)候就完成實(shí)例化,如果從始至終未使用這個(gè)實(shí)例,則會造成內(nèi)存的浪費(fèi)。
2、懶漢模式(線程安全)
public class Singletion {
    private static Singleton instance;
    private Singleton () {
    }
    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}
  • 為了處理并發(fā),每次調(diào)用getInstance方法時(shí)都需要進(jìn)行同步,會有不必要的同步開銷。
3、雙重檢查模式(DCL)
public class Singleton {
    private static volatile Singleton instance;
    private Singleton {
    }
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}
  • 第一次判空,省去了不必要的同步。第二次是在Singleton等于空時(shí)才創(chuàng)建實(shí)例。
  • 使用volatile保證了實(shí)例的可見性。
  • DCL在一定程度上解決了資源的消耗和多余的同步、線程安全等問題,但是在某些情況下會失效。

假設(shè)線程A執(zhí)行到instance = new Singleton()語句,看起來只有一行代碼,但實(shí)際上它并不是原子操作,這句代碼最終會被編譯成多條匯編指令,它大致做了3件事:

1)給instance的實(shí)例分配內(nèi)存。

2)調(diào)用Singleton()構(gòu)造函數(shù),初始化成員字段。

3)將instance對象指向分配的內(nèi)存空間(此時(shí)instance就不是null了)。

但是,由于Java編譯器允許處理器亂序執(zhí)行,以及JDK1.5之前JMM中的Cache、寄存器到主內(nèi)存回寫順序的規(guī)定,上面的2和3的順序是無法保證的,也就是說,執(zhí)行順序可能是1-2-3也可能是1-3-2。如果是后者,并且在3執(zhí)行完畢、2未執(zhí)行之前,被切換到線程B上,這時(shí)候instance因?yàn)橐呀?jīng)在線程A內(nèi)執(zhí)行過了3,instance已經(jīng)是非空了,所以,線程B直接取走instance,再使用時(shí)就會出錯(cuò),這就是DCL失效問題,而且這種難以跟蹤難以重現(xiàn)的錯(cuò)誤可能會隱藏很久。

在JDK1.5之后,SUN官方已經(jīng)注意到這種問題,調(diào)整了JVM,具體化了volatile關(guān)鍵字,因此,如果JDK1.5或之后的版本,只需要將instance的定義改成private volatile static Singleton instance = null就可以保證instance對象每次都是從主內(nèi)存中讀取,就可以使用DCL的寫法來完成單例模式。當(dāng)然,volatile或多或少也會影響到性能,但考慮到程序的正確性,這點(diǎn)犧牲也是值得的。

DCL優(yōu)點(diǎn):資源利用率高,第一次執(zhí)行g(shù)etInstance時(shí)單例對象才會被實(shí)例化,效率高。

缺點(diǎn):第一次加載稍慢,也由于JMM的原因?qū)е屡紶枙?。在高并發(fā)環(huán)境下也有一定的缺陷,雖然發(fā)生概率很小。DCL模式是使用最多的單例實(shí)現(xiàn)方式,它能夠在需要時(shí)才實(shí)例化對象,并且能在絕大多數(shù)場景下保證對象的唯一性,除非你的代碼在并發(fā)場景比較復(fù)雜或低于JDK1.6版本下使用,否則,這種方式一般能夠滿足要求。

4、靜態(tài)內(nèi)部類單例模式
public class Singleton() {
    private Singleton() {
    }
    public static Singleton getInstance() {
        return SingletonHolder.sInstance;
    }
    private static class SingletonHolder {
        private static final Singleton sInstance = new Singleton();
    }
}
  • 第一次調(diào)用getInstance方法時(shí)虛擬機(jī)才加載SingletonHolder并初始化sInstance,這樣保證了線程安全和實(shí)例的唯一性。
5、枚舉單例
public enum Singleton {
    INSTANCE;
    public void doSomeThing() {
    }
}
  • 默認(rèn)枚舉實(shí)例的創(chuàng)建是線程安全的,并且在任何情況下都是單例。
  • 簡單、可讀性不高。

注意:上面的幾種單例模式創(chuàng)建的單例對象被反序列化時(shí)會重新創(chuàng)建實(shí)例,可以重寫readReslove方法返回當(dāng)前的單例對象。

簡單工廠模式(補(bǔ)充)

也稱為靜態(tài)工廠方法模式,由一個(gè)工廠對象決定創(chuàng)建出哪一種產(chǎn)品類的實(shí)例。

簡單工廠模式中有如下角色:

  • 工廠類:核心,負(fù)責(zé)創(chuàng)建所有實(shí)例的內(nèi)部邏輯,由外界直接調(diào)用。
  • 抽象產(chǎn)品類:要?jiǎng)?chuàng)建所有對象的抽象父類,負(fù)責(zé)描述所有實(shí)例所共有的公共接口。
  • 具體產(chǎn)品類:要?jiǎng)?chuàng)建的產(chǎn)品。
簡單示例

1、抽象產(chǎn)品類

public abstract class Computer {
    public abstarct void start();
}

2、具體產(chǎn)品類

public class LenovaComputer extends Computer {
    @Override
    public void start() {
        ...
    }
}

public class HpComputer extends Computer {
    @Override
    public void start() {
        ...
    }
}

public class AsusComputer extends Computer {
    @Override
    public void start() {
        ...
    }
}

3、工廠類

public class ComputerFactory {
    public static Computer createComputer(String type) {
        Computer mComputer = null;
        switch (type) {
            case "lenovo":
                mComputer = new LenovoComputer();
                break;
            case "hp":
                mComputer = new HpComputer();
                break;
            case "asus":
                mComputer = new AsusComputer();
                break;
        }
        return mComputer;
    }
}
  • 它需要知道所有工廠類型,因此只適合工廠類負(fù)責(zé)創(chuàng)建的對象比較少的情況。
  • 避免直接實(shí)例化類,降低耦合性。
  • 增加新產(chǎn)品需要修改工廠,違背開放封閉原則。

工廠方法模式

定義一個(gè)用于創(chuàng)建對象的接口,使類的實(shí)例化延遲到子類。

工廠方法有以下角色:

  • 抽象產(chǎn)品類。
  • 具體產(chǎn)品類。
  • 抽象工廠類:返回一個(gè)泛型的產(chǎn)品對象。
  • 具體工廠類:返回具體的產(chǎn)品對象。
簡單示例

抽象產(chǎn)品類和具體產(chǎn)品類同簡單工廠一樣。

3、抽象工廠類

public abstract class ComputerFactory {
    public abstract <T extends Computer> T createComputer(Class<T> clz);
}

4、具體工廠類

public class GDComputerFactory extends ComputerFactory {
    @Override
    public <T extends Computer> T createComputer(Class<T> clz) {
        Computer computer = null;
        String classname = clz.getName();
        try {
            computer = (Computer) Class.forName(classname).newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return (T) computer;
    }
}
  • 相比簡單工廠,如果我們需要新增產(chǎn)品類,無需修改工廠類,直接創(chuàng)建產(chǎn)品即可。

建造者模式

將一個(gè)復(fù)雜對象的構(gòu)建和它的表示分離,使得同樣的構(gòu)建過程可以創(chuàng)建不同的表示。

建造者有以下角色:

  • 導(dǎo)演類:負(fù)責(zé)安排已有模塊的安裝順序,最后通知建造者開始建造。
  • 建造者:抽象Builder類,用于規(guī)范產(chǎn)品的組建。
  • 具體建造者:實(shí)現(xiàn)抽象Builder類的所有方法,并返回建造好的對象。
  • 產(chǎn)品類。
簡單示例

1、產(chǎn)品類

public class Computer {
    private String mCpu;
    private Stiring mMainboard;
    private String mRam;
    public void setmCpu(String mCpu) {
        this.mCpu = mCpu;
    }
    public void setmMainboard(String mMainboard) {
        this.mMainboard = mMainboard;
    }
    public void setmRam(String mRam) {
        this.mRam = mRam;
    }
}

2、抽象建造者

public abstract class Builder {
    public abstract void buildCpu(String cpu);
    public abstract void buildMainboard(String mainboard);
    public abstract void buildRam(String ram);
    public abstract Computer create();
}

3、具體建造者

public class MoonComputerBuilder extends Builder {
    private Computer mComputer = new Computer();
    
    @Override
    public void buildCpu(String cpu) {
        mComputer.setmCpu(cpu);
    }
    
    @Override
    public void buildMainboard(String mainboard) {
        mComputer.setmMainboard(mainboard);
    }
    
    @Override
    public void buildRam(String ram) {
        mComputer.setmRam(ram);
    }
    
    @Override
    public Computer create() {
        return mComputer;
    }
}

4、導(dǎo)演類

public class Director {
    Builder mBuilder = null;
    public Director (Builder builder) {
        this.mBuilder = builder;
    }
    
    public Computer createComputer(String cpu, String mainboard, String ram) {
        this.mBuilder.buildCpu(cpu);
        this.mBuilder.buildMainboard(mainboard);
        this.mBuilder.buildRam(ram);
        return mBuilder.create();
    }
}
  • 屏蔽產(chǎn)品內(nèi)部組成細(xì)節(jié)。
  • 具體建造者類之間相互獨(dú)立,容易擴(kuò)展。
  • 會產(chǎn)生多余的建造者對象和導(dǎo)演類。

2、結(jié)構(gòu)型設(shè)計(jì)模式

1、代理模式

為其它對象提供一種代理以控制這個(gè)對象的訪問。

代理模式中有以下角色:

  • 抽象主題類:聲明真實(shí)主題和代理的共同接口方法。
  • 真實(shí)主題類。
  • 代理類:持有對真實(shí)主題類的引用。
  • 客戶端類。
靜態(tài)代理示例代碼

1、抽象主題類

public interface IShop {
    void buy();
}

2、真實(shí)主題類

public class JsonChao implements IShop {
    @Override 
    public void buy() {
        ...
    }
}

3、代理類

public class Purchasing implements IShop {
    private IShop mShop;
    public Purchasing(IShop shop) {
        this.mShop = shop;
    }
    
    @Override 
    public void buy() {
        mShop.buy();
    }
}

4、客戶端類

public class Clent {
    
    public static void main(String[] args) {
        IShop jsonChao = new JsonChao();
        IShop purchasing = new Purchasing(jsonChao);
        purchasing.buy();
    }
}
動(dòng)態(tài)代理

在代碼運(yùn)行時(shí)通過反射來動(dòng)態(tài)地生成代理類的對象,并確定到底來代理誰。

動(dòng)態(tài)代理示例代碼

改寫靜態(tài)代理的代理類和客戶端類,如下所示:

1、動(dòng)態(tài)代理類

public class DynamicPurchasing implements InvocationHandler {
    private Object obj;
    public DynamicPurchasing(Object obj) {
        this.obj = obj;
    }
    
    @Overrdie
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return method.invoke(obj, args);
    }
}

2、客戶端類

public class Clent {
    
    public static void main(String[] args) {
        IShop jsonChao = new JsonChao();
        DynamicPurchasing mDynamicPurchasing = new DynamicPurchasing(jsonChao);
        ClassLoader cl = jsonChao.getClass.getClassLoader();
        IShop purchasing = Proxy.newProxyInstance(cl, new Class[]{IShop.class}, mDynamicPurchasing);
        purchasing.buy();
    }
}
  • 真實(shí)主題類發(fā)生變化時(shí),由于它實(shí)現(xiàn)了公用的接口,因此代理類不需要修改。

裝飾模式

動(dòng)態(tài)地給一個(gè)對象添加一些額外的職責(zé)。

裝飾模式有以下角色:

  • 抽象組件:接口/抽象類,被裝飾的最原始的對象。
  • 具體組件:被裝飾的具體對象。
  • 抽象裝飾者:擴(kuò)展抽象組件的功能。
  • 具體裝飾者:裝飾者具體實(shí)現(xiàn)類。
示例代碼

1、抽象組件

public abstract class Swordsman {
    public abstract void attackMagic();
}

2、具體組件

public class YangGuo extends Swordsman {
    @Override
    public void attackMagic() {
        ...
    }
}

3、抽象裝飾者

抽象裝飾者必須持有抽象組件的引用,以便擴(kuò)展功能。

public abstract class Master extends Swordsman {
    private Swordsman swordsman;
    public Master(Swordsman swordsman) {
        this.swordman = swordman;
    }
    
    @Override
    public void attackMagic() {
        swordsman.attackMagic();
    }
}

4、具體裝飾者

public class HongQiGong extends Master {
    public HongQiGong(Swordsman swordsman) {
        this.swordsman = swordsman;
    }
    
    public void teachAttackMagic() {
        ...
    }
    
    @Override
    public void attackMagic() {
        super.attackMagic();
        teackAttackMagic();
    }
}

5、使用

YangGuo mYangGuo = new YangGuo();
HongQiGong mHongQiGong = new HongQiGong(mYangGuo);
mHongQiGong.attackMagic(); 
  • 使用組合,動(dòng)態(tài)地?cái)U(kuò)展對象的功能,在運(yùn)行時(shí)能夠使用不同的裝飾器實(shí)現(xiàn)不同的行為。
  • 比繼承更易出錯(cuò),旨在必要時(shí)使用。

外觀模式(門面模式)

一個(gè)子系統(tǒng)的內(nèi)部和外部通信必須通過一個(gè)統(tǒng)一的對象進(jìn)行。即提供一個(gè)高層的接口,方便子系統(tǒng)更易于使用。

外觀模式有以下角色:

  • 外觀類:將客戶端的請求代理給適當(dāng)?shù)淖酉到y(tǒng)對象。
  • 子系統(tǒng)類:可以有一個(gè)或多個(gè)子系統(tǒng),用于處理外觀類指派的任務(wù)。注意子系統(tǒng)不含外觀類的引用。
簡單示例

1、子系統(tǒng)類(這個(gè)有三個(gè)子系統(tǒng))

public class ZhaoShi {
    public void TaiJiQuan() {
        ...
    }
    
    public void QiShanQuan() {
        ...
    }
    
    public void ShengHuo() {
        ...
    }
}

public class NeiGong {
    public void JiuYang() {
        ...
    }
    
    public void QianKun() {
        ...
    }
}

public class JingMai {
    public void JingMai() {
        ...
    }
}

2、外觀類

public class ZhangWuJi {
    private ZhaoShi zhaoShi;
    private JingMai jingMai;
    pirvate Neigong neiGong;
    
    public ZhangWuJi() {
        zhaoShi = new ZhaoShi();
        jingMai = new JingMai();
        neiGong = new NeiGong();
    }
    
    public void qianKun() {
        jingMai.JingMai();
        neiGong.QianKun();
    }
    
    public void qiShang() {
        jingMai.JingMai();
        neiGong.JiuYang();
        zhaoShi.QiShangQuan();
    }
}

3、使用

ZhangWuJi zhangWuJi = new ZhangWuJi();
zhangWuJi.QianKun();
zhangWuJi.QiShang();
  • 將對子系統(tǒng)的依賴轉(zhuǎn)換為對外觀類的依賴。
  • 對外部隱藏子系統(tǒng)的具體實(shí)現(xiàn)。
  • 這種外觀特性增強(qiáng)了安全性。

享元模式

使用共享對象有效支持大量細(xì)粒度(性質(zhì)相似)的對象。

額外的兩個(gè)概念:

  • 1、內(nèi)部狀態(tài):共享信息,不可改變。
  • 2、外部狀態(tài):依賴標(biāo)記,可以改變。

享元模式有以下角色:

  • 抽象享元角色:定義對象內(nèi)部和外部狀態(tài)的接口。
  • 具體享元角色:實(shí)現(xiàn)抽象享元角色的任務(wù)。
  • 享元工廠:管理對象池及創(chuàng)建享元對象。
簡單示例

1、抽象享元角色

public interface IGoods {
    public void showGoodsPrice(String name);
}

2、具體享元角色

public class Goods implements IGoods {
    private String name;
    private String price;
    
    Goods (String name) {
        this.name = name;
    }
    
    @Override
    public void showGoodsPrice(String name) {
        ...
    }
}

3、享元工廠

public class GoodsFactory {
    private static Map<String, Goods> pool = new HashMap<String, Goods>();
    public static Goods getGoods(String name) {
        if (pool.containsKey(name)) {
            return pool.get(name);
        } else {
            Goods goods = new Goods(name);
            pool.put(name, goods);
            return goods;
        }
    }
}

4、使用

Goods goods1 = GoodsFactory.getGoods("Android進(jìn)階之光");
goods1.showGoodsPrice("普通版");
Goods goods2 = GoodsFactory.getGoods("Android進(jìn)階之光");
goods1.showGoodsPrice("普通版");
Goods goods3 = GoodsFactory.getGoods("Android進(jìn)階之光");
goods1.showGoodsPrice("簽名版");

goods1為新創(chuàng)建的對象,后面的都是從對象池中取出的緩存對象。

適配器模式

將一個(gè)接口轉(zhuǎn)換為另一個(gè)需要的接口。

適配器有以下角色:

  • 要轉(zhuǎn)換的接口。
  • 要轉(zhuǎn)換的接口的實(shí)現(xiàn)類。
  • 轉(zhuǎn)換后的接口。
  • 轉(zhuǎn)換后的接口的實(shí)現(xiàn)類。
  • 適配器類。
簡單示例

1、要轉(zhuǎn)換的接口(火雞)

public interface Turkey {
    public void gobble();
    public void fly();
}

2、要轉(zhuǎn)換的接口的實(shí)現(xiàn)類

public class WildTurkey implements Turkey {
    @Override
    public void gobble() {
        ...
    }
    
    @Override
    public void fly() {
        ...
    }
}

3、轉(zhuǎn)換后的接口(鴨子)

public interface Duck {
    public void quack();
    public void fly();
}

4、轉(zhuǎn)換后的接口的實(shí)現(xiàn)類。

public class MallardDuck implements Duck {
    @Override
    public void quack() {
        ...
    }
    
    @Overrdie
    public void fly() {
        ...
    }
}

5、適配器類

public class TurkeyAdapter implements Duck {
    Turkey turkey;
    
    public TurkeyAdapter(Turkey turkey) {
        this.turkey = turkey;
    }
    
    @Override
    public void quack() {
        turkey.gobble();
    }
    
    @Override
    public void fly() {
        // 火雞沒有鴨子飛的遠(yuǎn),因此多飛幾次,達(dá)到適配鴨子fly的作用
        for(int i;i < 5;i++) {
            turkey.fly();
        }
    }
}

6、使用

WildTurkey wildTurkey = new WildTurkey();
TurkeyAdapter turkeyAdapter = new TurkeyAdapter(wildTurkey);
turkeyAdapter.quack();
turkeyAdapter.fly();
  • 注重適度使用即可。

3、行為型設(shè)計(jì)模式

1、策略模式

定義一系列的算法,將每一個(gè)算法都封裝起來,并且可相互替換。這使得算法可以獨(dú)立于調(diào)用者而單獨(dú)變化。

策略模式有以下角色:

  • 上下文角色:用來操作策略使用的上下文環(huán)境。屏蔽了高層模塊對策略和算法的直接訪問。
  • 抽象策略角色。
  • 具體策略角色。
簡單示例

1、抽象策略角色

public interface FightingStrategy {
    public void fighting();
}

2、具體策略角色

public class WeakRivalStrategy implements FightingStrategy {
    
    @Override
    public void fighting() {
        ...
    }
}

public class CommonRivalStrategy implements FightingStrategy {
    
    @Override
    public void fighting() {
        ...
    }
}

public class StrongRivalStrategy implements FightingStrategy {
    
    @Override
    public void fighting() {
        ...
    }
}

3、上下文角色

public class Context {
    private FightingStrategy mFightingStrategy;
    
    public void Context(FightingStrategy fightingStrategy) {
        this.mFightingStrategy = fightingStrategy;
    }
    
    public void fighting() {
        mFightingStrategy.fighting();
    }
}

4、使用

Context context;
context = new Context(new WeakRivalStrategy());
context.fighting();
context = new Context(new CommonRivalStategy());
context.fighting();
context = new Context(new StrongRivalStategy());
context.fighting();
  • 隱藏具體策略中算法的實(shí)現(xiàn)細(xì)節(jié)。
  • 避免使用多重條件語句。
  • 易于擴(kuò)展
  • 每一個(gè)策略都是一個(gè)類,復(fù)用性小。
  • 上層模塊必須知道有哪些策略類,與迪米特原則相違背。

2、模板方法模式

定義了一套算法框架,將某些步驟交給子類去實(shí)現(xiàn)。使得子類不需改變框架結(jié)構(gòu)即可重寫算法中的某些步驟。

模板方法模式有以下角色:

  • 抽象類:定義了一套算法框架。
  • 具體實(shí)現(xiàn)類。
簡單示例

1、抽象類

public abstract class AbstractSwordsman {

    public final void fighting() {
        neigong();
        
        // 這個(gè)是具體方法
        jingmai();
        
        if (hasWeapons()) {
            weapons();
        }
        
        moves();
        
        hook();
    }
    
    protected void hook() { };
    protected void abstract neigong();
    protected void abstract weapons();
    protected void abstract moves();
    public void jingmai() {
        ...
    }
    
    protected boolean hasWeapons() {
        return ture;
    }
}

2、具體實(shí)現(xiàn)類

public class ZhangWuJi extends AbstractSwordsman {
    
    @Override
    public void neigong() {
        ...
    }
    
    @Override 
    public void weapons() {
        // 沒有武器,不做處理
    }
    
    @Override 
    public void moves() {
        ...
    }
    
    @Override
    public boolean hasWeapons() {
        return false;
    }
}

punlc class ZhangSanFeng extends AbstractSwordsman {
    
    @Override
    public void neigong() {
        ...
    }
    
    @Override 
    public void weapons() {
        ...
    }
    
    @Override 
    public void moves() {
        ...
    }
    
    @Override
    public void hook() {
        // 額外處理
        ...
    }
}

3、使用

ZhangWuJi zhangWuJi = new ZhangWuJi();
zhangWuJi.fighting();
ZhangSanFeng zhangSanFeng = new ZhangSanFeng();
zhangSanFeng.fighting();
  • 可以使用hook方法實(shí)現(xiàn)子類對父類的反向控制。
  • 可以把核心或固定的邏輯搬移到基類,其它細(xì)節(jié)交給子類實(shí)現(xiàn)。
  • 每個(gè)不同的實(shí)現(xiàn)都需要定義一個(gè)子類,復(fù)用性小。

3、觀察者模式(發(fā)布 - 訂閱模式)

定義對象間的一種1對多的依賴關(guān)系,每當(dāng)這個(gè)對象的狀態(tài)改變時(shí),其它的對象都會接收到通知并被自動(dòng)更新。

觀察者模式有以下角色:

  • 抽象被觀察者:將所有已注冊的觀察者對象保存在一個(gè)集合中。
  • 具體被觀察者:當(dāng)內(nèi)部狀態(tài)發(fā)生變化時(shí),將會通知所有已注冊的觀察者。
  • 抽象觀察者:定義了一個(gè)更新接口,當(dāng)被觀察者狀態(tài)改變時(shí)更新自己。
  • 具體被觀察者:實(shí)現(xiàn)抽象觀察者的更新接口。
簡單示例

1、抽象觀察者

public interface observer {
    
    public void update(String message);
}

2、具體觀察者

public class WeXinUser implements observer {
    private String name;
    
    public WeXinUser(String name) {
        this.name = name;
    }
    
    @Override
    public void update(String message) {
        ...
    }
}

3、抽象被觀察者

public interface observable {
    
    public void addWeXinUser(WeXinUser weXinUser);
    
    public void removeWeXinUser(WeXinUser weXinUser);
    
    public void notify(String message);
}

4、具體被觀察者

public class Subscription implements observable {
    private List<WeXinUser> mUserList = new ArrayList();
    
    @Override
    public void addWeXinUser(WeXinUser weXinUser) {
        mUserList.add(weXinUser);
    }
    
    @Override
    public void removeWeXinUser(WeXinUser weXinUser) {
        mUserList.remove(weXinUser);
    }
    
    @Override
    public void notify(String message) {
        for(WeXinUser weXinUser : mUserList) {
            weXinUser.update(message);
        }
    }
}

5、使用

Subscription subscription = new Subscription();

WeXinUser hongYang = new WeXinUser("HongYang");
WeXinUser rengYuGang = new WeXinUser("RengYuGang");
WeXinUser liuWangShu = new WeXinUser("LiuWangShu");

subscription.addWeiXinUser(hongYang);
subscription.addWeiXinUser(rengYuGang);
subscription.addWeiXinUser(liuWangShu);
subscription.notify("New article coming");
  • 實(shí)現(xiàn)了觀察者和被觀察者之間的抽象耦合,容易擴(kuò)展。
  • 有利于建立一套觸發(fā)機(jī)制。
  • 一個(gè)被觀察者卡頓,會影響整體的執(zhí)行效率。采用異步機(jī)制可解決此類問題。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 這里是對《設(shè)計(jì)模式Java版》[https://gof.quanke.name]的提煉匯總,在真正深入理解之前,方...
    LeonXtp閱讀 1,158評論 0 0
  • 創(chuàng)建型模式 工廠模式 工廠模式(Factory Pattern)是 Java 中最常用的設(shè)計(jì)模式之一。這種類型的設(shè)...
    隔墻送來秋千影閱讀 2,812評論 0 11
  • 創(chuàng)建型模式 工廠模式 工廠模式(Factory Pattern)是 Java 中最常用的設(shè)計(jì)模式之一。這種類型的設(shè)...
    liuyang7519閱讀 390評論 0 2
  • 0.提前說明 模式選擇的方法1)模式的功能——看是否能解決問題2)模式的本質(zhì)——看模式是否主要用來解決這類問題3)...
    王偵閱讀 1,247評論 0 1
  • 日子就在吵吵鬧鬧磕磕絆絆中過著,賀小妹大學(xué)畢業(yè)參加工作,已經(jīng)要結(jié)婚了。這可是大事,老賀夫妻倆都嚴(yán)陣以待! 首先就是...
    油油的悠悠閱讀 700評論 0 0

友情鏈接更多精彩內(nèi)容