JAVA常用設(shè)計(jì)原則及模式

設(shè)計(jì)模式是一套面向?qū)ο蟮拇a設(shè)計(jì)經(jīng)驗(yàn)總結(jié),是在編程領(lǐng)域被反復(fù)使用、多人知曉的通過了分類整理的代碼設(shè)計(jì)方法。

使用設(shè)計(jì)模式為了可重用代碼,使代碼更加容易理解、保證代碼的可靠性。合理的設(shè)計(jì)模式對(duì)開發(fā)人員和需要開發(fā)的系統(tǒng)都是有利的,設(shè)計(jì)模式可以讓代碼實(shí)現(xiàn)工程化。

設(shè)計(jì)原則

1. 單一職責(zé)原則

定義:對(duì)于一個(gè)類,應(yīng)該只有一個(gè)引起它變化的原因。
好處: 降低類的復(fù)雜度、提高類的可讀性、提高系統(tǒng)的可維護(hù)性。

2. 里氏代換原則

定義:子類型必須能夠替換掉父類型

優(yōu)點(diǎn):代碼共享、提高了代碼重用性、擴(kuò)展性、產(chǎn)品和項(xiàng)目的開放性。

只有滿足里氏代換原則,子類可以代替父類出現(xiàn)并且不影響層序的功能父類才能得到真正的復(fù)用,并且子類可以在弗雷德基礎(chǔ)上添加新的行為。面向?qū)ο笏枷胫?,父類和子類的繼承關(guān)系是抽象化的具體實(shí)現(xiàn)。

里氏代換原則是對(duì)實(shí)現(xiàn)抽象化的具體步驟的規(guī)范。

3. 開放封閉原則

定義:程序的實(shí)體對(duì)象(模塊、類、函數(shù)等)應(yīng)該可以進(jìn)行擴(kuò)展,但不應(yīng)該修改。

優(yōu)點(diǎn):可擴(kuò)展性好、耦合度低。

特征

  • 對(duì)于擴(kuò)展是開放的
  • 對(duì)于修改是封閉的
// 未實(shí)現(xiàn)開放封閉原則
public class BankWorker {
    public void saving() {
        System.out.println("存款");
    }

    public void drawing() {
        System.out.println("取款");
    }
}
// 實(shí)現(xiàn)開放封閉原則
public interface BankWorker {
    public void operation();
}

public class SavingBankWorker extends BankWorker {

    @Override
    public void operation() {
        System.out.println("存款");
    }
}
public class DrawingBankWorker extends BankWorker {

    @Override
    public void operation() {
        System.out.println("取款");
    }
}

開放封閉原則可以通過擴(kuò)展已有的軟件系統(tǒng)提供新的行為,以滿足軟件的新需求,使變化的軟件有一定的靈活性和適應(yīng)性。對(duì)于已有的軟件模塊,特別是最重要的抽象模塊不能再修改,這就是軟件系統(tǒng)有了一定的穩(wěn)定性和延續(xù)性。

4. 依賴倒轉(zhuǎn)原則

定義:程序高層模塊不應(yīng)該依賴于低層模塊,但兩者都應(yīng)該依賴與抽象,抽象不應(yīng)該依賴于具體細(xì)節(jié),而細(xì)節(jié)應(yīng)該依賴與抽象。

優(yōu)點(diǎn):可擴(kuò)展性好、耦合度低。

interface IWorker {
    public void work();
}

class Work implements IWorker {

    @Override
    public void work() {
        //......working
    }
}

class SeniorWorker implements IWorker {
    @Override
    public void work() {
        //......working much more
    }
}

class Manager {
    IWorker mWorker;

    public void setWorker(IWorker w) {
        mWorker = w;
    }

    public void manage() {
        mWorker.work();
    }
}

5. 合成/聚合復(fù)用原則

定義:盡量不實(shí)用類繼承、盡量使用合成/聚合。

優(yōu)點(diǎn):降低耦合度、提升代碼可讀性、隱藏實(shí)現(xiàn)細(xì)節(jié)。
缺點(diǎn):系統(tǒng)中有較多對(duì)象需要管理。

  • 將軍對(duì)自己的統(tǒng)領(lǐng)將士保持有引用,就是聚合。
  • 皇帝擁有天下,但將軍并非擁有全部士兵,所以對(duì)將軍“擁有”的關(guān)系,相當(dāng)于合成,大將軍是這個(gè)合成中的一個(gè)對(duì)象。

6. 迪米特法則

定義:如果兩個(gè)類之間不必進(jìn)行直接通信,則這兩個(gè)類不應(yīng)該發(fā)生直接的相互作用。如果一個(gè)類需要調(diào)用另一個(gè)類的某個(gè)方法,則可以通過第三方來(lái)轉(zhuǎn)發(fā)這個(gè)調(diào)用。

// 皇帝找一個(gè)侍衛(wèi),皇帝這么傲嬌,一定不會(huì)親自動(dòng)手啊,所以要通過太監(jiān)
public abstract class Stranger {
    public abstract void operation();
}

public class ShiWei extends Stranger {

    @Override
    public void operation() {
        System.out.println("參見皇上,我是侍衛(wèi)啊哈哈哈!");
    }
}

public class TaiJian extends Stranger {

    @Override
    public void operation() {

    }

    // 太監(jiān)找人啊哈哈
    public void findStranger() {
        Stranger s = new ShiWei();
        s.operation();
    }
}

public class HuangDi {
    private TaiJian t;

    
    public TaiJian getTaiJian() {
        return t;
    }

    public void setTaiJian(TaiJian t) {
        this.t = t;
    }

    public void operation() {
    }
}

public class Test {
    public static void main(String[] args){
        HuangDi HD = new HuangDi();
        
        // 找個(gè)太監(jiān),小桌子小凳子啊哈哈
        HD.setTaiJian(new TaiJian());
        
        // 來(lái),小桌子,去給我把他找來(lái)(這個(gè)過程中皇帝并沒有和侍衛(wèi)直接接觸)
        HD.getTaiJian().findStranger();
    }
}

Java設(shè)計(jì)模式

Java中一般有23種設(shè)計(jì)模式,這里只介紹一些常用的~

總體來(lái)說設(shè)計(jì)模式分為三大類:

  • 創(chuàng)建型模式,共5種
    工廠方法模式、抽象工廠模式、單例模式、建造者模式、原型模式。

  • 結(jié)構(gòu)型模式,共7種
    適配器模式、裝飾器模式、代理模式、外觀模式、橋接模式、組合模式、享元模式。

  • 行為型模式,共11種
    策略模式、模板方法模式、觀察者模式、迭代子模式、責(zé)任鏈模式、命令模式、備忘錄模式、狀態(tài)模式、訪問者模式、中介者模式、解釋器模式。


1. 單例設(shè)計(jì)模式

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

  • 當(dāng)一個(gè)類只能有一個(gè)實(shí)例而卻第三方可以從一個(gè)公共的訪問點(diǎn)訪問它時(shí)。
  • 當(dāng)一個(gè)唯一的實(shí)力可以通過子類來(lái)擴(kuò)展,而且第三方需要在不更改代碼的情況下就能使用一個(gè)擴(kuò)展的實(shí)例時(shí)。
  1. 餓漢式
public class Singleton {
    // 直接創(chuàng)建對(duì)象
    public static Singleton instance = new Singleton();

    // 私有化構(gòu)造函數(shù)
    private Singleton() {
    }
    
    // 返回對(duì)象實(shí)例
    public static Singleton getInstance() {
        return instance;
    }
}

這種方式比較常用,但容易產(chǎn)生垃圾對(duì)象。

優(yōu)點(diǎn):沒有加鎖,執(zhí)行效率會(huì)提高。

缺點(diǎn):類加載時(shí)就初始化,浪費(fèi)內(nèi)存。

它基于 Classloader機(jī)制避免了多線程的同步問題,不過,instance 在類裝載時(shí)就實(shí)例化,雖然導(dǎo)致類裝載的原因有很多種,在單例模式中大多數(shù)都是調(diào)用getInstance方法, 但是也不能確定有其他的方式(或者其他的靜態(tài)方法,比如反射)導(dǎo)致類裝載,這時(shí)候初始化 instance 顯然沒有達(dá)到 lazy loading 的效果。

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)行同步,會(huì)有不必要的同步開銷。
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在一定程度上解決了資源的消耗和多余的同步、線程安全等問題,但是在某些情況下會(huì)失效。

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

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

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

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

原子操作:不需要synchronized,指不會(huì)被線程調(diào)度機(jī)制打斷的操作;這種操作一旦開始,就一直運(yùn)行到結(jié)束,不會(huì)切換到另一個(gè)線程。

eg:A-B銀行轉(zhuǎn)賬1000

但是,由于Java編譯器允許處理器亂序執(zhí)行,以及JDK1.5之前JMM中的Cache、寄存器到主內(nèi)存回寫順序的規(guī)定,上面的2和3的順序是無(wú)法保證的,也就是說,執(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í)就會(huì)出錯(cuò),這就是DCL失效問題,而且這種難以跟蹤難以重現(xiàn)的錯(cuò)誤可能會(huì)隱藏很久。

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

volatile:當(dāng)一個(gè)共享變量被volatile修飾時(shí),它會(huì)保證修改的值會(huì)立即被更新到主存,當(dāng)有其他線程需要讀取時(shí),它會(huì)去內(nèi)存中讀取新值。來(lái)保證可見性

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

缺點(diǎn):第一次加載稍慢,也由于JMM的原因?qū)е屡紶枙?huì)失敗。在高并發(fā)環(huán)境下也有一定的缺陷,雖然發(fā)生概率很小。DCL模式是使用最多的單例實(shí)現(xiàn)方式,它能夠在需要時(shí)才實(shí)例化對(duì)象,并且能在絕大多數(shù)場(chǎng)景下保證對(duì)象的唯一性,除非你的代碼在并發(fā)場(chǎng)景比較復(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í)例的唯一性。
  • (《JAVA并發(fā)編程實(shí)踐》贊成使用這種方法)
5、枚舉單例
public enum Singleton {
    INSTANCE;
    public void doSomeThing() {
    }
}
  • 默認(rèn)枚舉實(shí)例的創(chuàng)建是線程安全的,并且在任何情況下都是單例。
  • 簡(jiǎn)單、可讀性不高。

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

核心原理:將構(gòu)造函數(shù)私有化,并切通過靜態(tài)方法獲取一個(gè)唯一的實(shí)例,獲取過程中必須保證線程安全、方式反序列化導(dǎo)致重新生成實(shí)例對(duì)象。


  1. DCL升級(jí)版本?
public class Singleton {
    // 聲明變量
    private static volatile Singleton singleton = null;

    // 私有構(gòu)造函數(shù)
    private Singleton() {
    }

    // 提供對(duì)外方法
    public static Singleton getInstance() {
        Singleton mSingleton = singleton; //定義一個(gè)臨時(shí)變量
        if (singleton == null) {
            synchronized (Singleton.class) {
                if (singleton == null) {
                    mSingleton = new Singleton();
                    singleton = mSingleton;
                }
            }
        }
        return mSingleton ; // 返回臨時(shí)變量
    }
}

使用volatile
如果沒有 volatile 關(guān)鍵字,問題可能會(huì)出在singleton = new Singleton() 這句,用偽代碼表示:

// 分配內(nèi)存
inst = allocat();
// 賦值
singleton = inst;
// 真正執(zhí)行構(gòu)造函數(shù)
constructor(inst);

可能會(huì)由于虛擬機(jī)的優(yōu)化等導(dǎo)致賦值操作先執(zhí)行,而構(gòu)造函數(shù)還沒完成,導(dǎo)致其他線程訪問得到 singleton 變量不為null,但初始化還未完成,導(dǎo)致程序崩潰。

**定義一個(gè)臨時(shí)變量 mSingleton **
訪問 volatile 變量,需要保證一些執(zhí)行順序,所以的開銷比較大。這里定義一個(gè)臨時(shí)變量,在 sInst 不為空的時(shí)候(這是絕大部分的情況),只要在開始訪問一次 volatile 變量,返回的是臨時(shí)變量。如果沒有此臨時(shí)變量,則需要訪問兩次,而降低了效率。


2. 工廠設(shè)計(jì)模式

工廠模式分為工廠方法模式和抽象工廠模式。

  • 工廠方法模式

工廠方法模式分為三種:
a. 普通工廠模式:建立一個(gè)工廠類,對(duì)實(shí)現(xiàn)了同一接口的一些類進(jìn)行實(shí)例的創(chuàng)建。
b. 多個(gè)工廠方法模式:對(duì)普通工廠方法模式的改進(jìn),在普通工廠方法模式中,如果傳遞的字符串出錯(cuò),則不能正確創(chuàng)建對(duì)象,而多個(gè)工廠方法模式是提供多個(gè)工廠方法,分別創(chuàng)建對(duì)象。
c. 靜態(tài)工廠方法模式:將上面的多個(gè)工廠方法模式里的方法置為靜態(tài)的,不需要?jiǎng)?chuàng)建實(shí)例,直接調(diào)用即可。

普通工廠模式
建立一個(gè)工廠類,對(duì)實(shí)現(xiàn)了同一接口的一些類進(jìn)行實(shí)例的創(chuàng)建。

// 普通工廠模式
public interface Sender {
    public void Send();
}

public class MailSender implements Sender {
    @Override
    public void Send() {
        System.out.println("this is mail sender!");
    }
}

public class SmsSender implements Sender {
    @Override
    public void Send() {
        System.out.println("this is sms sender!");
    }
}

public class SendFactory {
    public Sender produce(String type) {
        if ("mail".equals(type)) {
            return new MailSender();
        } else if ("sms".equals(type)) {
            return new SmsSender();
        } else {
            System.out.println("請(qǐng)輸入正確的類型!");
            return null;
        }
    }
}

多個(gè)工廠方法模式
對(duì)普通工廠方法模式的改進(jìn),在普通工廠方法模式中,如果傳遞的字符串出錯(cuò),則不能正確創(chuàng)建對(duì)象,而多個(gè)工廠方法模式是提供多個(gè)工廠方法,分別創(chuàng)建對(duì)象。

// 多個(gè)工廠方法模式

public class SendFactory {
    public Sender produceMail() {
        return new MailSender();
    }

    public Sender produceSms() {
        return new SmsSender();
    }
}

public class FactoryTest {
    public static void main(String[] args) {
        SendFactory factory = new SendFactory();
        Sender sender = factory.produceMail();
        sender.send();
    }
}

靜態(tài)工廠方法模式
將上面的多個(gè)工廠方法模式里的方法置為靜態(tài)的,不需要?jiǎng)?chuàng)建實(shí)例,直接調(diào)用即可。

// 靜態(tài)工廠方法模式
public class SendFactory {
    public static Sender produceMail() {
        return new MailSender();
    }

    public static Sender produceSms() {
        return new SmsSender();
    }
}

public class FactoryTest {
    public static void main(String[] args) {
        Sender sender = SendFactory.produceMail();
        sender.send();
    }
}

抽象工廠模式
工廠方法模式有一個(gè)問題就是,類的創(chuàng)建依賴工廠類,也就是說,如果想要拓展程序,必須對(duì)工廠類進(jìn)行修改,這違背了閉包原則,所以,從設(shè)計(jì)角度考慮,有一定的問題,如何解決?就用到抽象工廠模式,創(chuàng)建多個(gè)工廠類,這樣一旦需要增加新的功能,直接增加新的工廠類就可以了,不需要修改之前的代碼。

// 抽象工廠模式

public interface Provider {
    public Sender produce();
}
---------------------------------------------------

public interface Sender {
    public void send();
}
---------------------------------------------------

public class MailSender implements Sender {
    @Override
    public void send() {
        System.out.println("this is mail sender!");
    }
}

public class SmsSender implements Sender {
    @Override
    public void send() {
        System.out.println("this is sms sender!");
    }
}
---------------------------------------------------

public class SendSmsFactory implements Provider {
    @Override
    public Sender produce() {
        return new SmsSender();
    }
}

public class SendMailFactory implements Provider {
    @Override
    public Sender produce() {
        return new MailSender();
    }
}
---------------------------------------------------

public class Test {
    public static void main(String[] args) {
        Provider provider = new SendMailFactory();
        Sender sender = provider.produce();
        sender.send();
    }
}

3. 建造者模式

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

建造者有以下角色:

  • 導(dǎo)演類:負(fù)責(zé)安排已有模塊的安裝順序,最后通知建造者開始建造。
  • 建造者:抽象Builder類,用于規(guī)范產(chǎn)品的組建。
  • 具體建造者:實(shí)現(xiàn)抽象Builder類的所有方法,并返回建造好的對(duì)象。
  • 產(chǎn)品類。
簡(jiǎ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ò)展。
  • 會(huì)產(chǎn)生多余的建造者對(duì)象和導(dǎo)演類。

4.適配器模式

適配器模式將某個(gè)類的接口轉(zhuǎn)換成客戶端期望的另一個(gè)接口表示,目的是消除由于接口不匹配所造成的類的兼容性問題。主要分為三類:類的適配器模式、對(duì)象的適配器模式、接口的適配器模式。

類的適配器模式

package Pattern;


interface FiveVolt {
    public int getVolt5();
}

class Volt220 implements FiveVolt {

    @Override
    public int getVolt5() {
        return 220;
    }
}
/**
 * 類適配器
 */
class VoltAdapter extends Volt220 implements FiveVolt {
    @Override
    public int getVolt5() {
        return 5;
    }
}
public class AdapterTest {

    public static void main(String[] args) {
        VoltAdapter adapter = new VoltAdapter();
        System.out.println("輸出電壓:" + adapter.getVolt5());
    }
}

對(duì)象的適配器模式
基本思路和類的適配器模式相同,只是將 Adapter 類作修改,這次不繼承 Source 類,而是持有 Source 類的實(shí)例,以達(dá)到解決兼容性的問題。


/**
 * 對(duì)象適配器
 */
class VoltAdapter implements FiveVolt {
    Volt220 volt220;
    public  VoltAdapter(Volt220 volt220) {
        this.volt220 = volt220;
    }

    public Volt220 getVolt220() {
        return volt220;
    }

    @Override
    public int getVolt5() {
        return 5;
    }
}


public class AdapterTest {

    public static void main(String[] args) {
        VoltAdapter adapter = new VoltAdapter(new Volt220());
        System.out.println("輸出電壓:" + adapter.getVolt5());
    }
}

接口的適配器模式
接口的適配器是這樣的:有時(shí)我們寫的一個(gè)接口中有多個(gè)抽象方法,當(dāng)我們寫該接口的實(shí)現(xiàn)類時(shí),必須實(shí)現(xiàn)該接口的所有方法,這明顯有時(shí)比較浪費(fèi),因?yàn)椴⒉皇撬械姆椒ǘ际俏覀冃枰?,有時(shí)只需要某一些,此處為了解決這個(gè)問題,我們引入了接口的適配器模式,借助于一個(gè)抽象類,該抽象類實(shí)現(xiàn)了該接口,實(shí)現(xiàn)了所有的方法,而我們不和原始的接口打交道,只和該抽象類取得聯(lián)系,所以我們寫一個(gè)類,繼承該抽象類,重寫我們需要的方法就行。

5. 裝飾模式(Decorator)

裝飾模式就是給一個(gè)對(duì)象增加一些新的功能,而且是動(dòng)態(tài)的,要求裝飾對(duì)象和被裝飾對(duì)象實(shí)現(xiàn)同一個(gè)接口,裝飾對(duì)象持有被裝飾對(duì)象的實(shí)例。


public interface Sourceable {
    public void method();
}
----------------------------------------------------

public class Source implements Sourceable {
    @Override
    public void method() {
        System.out.println("the original method!");
    }
}
----------------------------------------------------

public class Decorator implements Sourceable {
    private Sourceable source;

    public Decorator(Sourceable source) {
        super();
        this.source = source;
    }

    @Override
    public void method() {
        System.out.println("before decorator!");
        source.method();
        System.out.println("after decorator!");
    }
}
----------------------------------------------------

public class DecoratorTest {
    public static void main(String[] args) {
        Sourceable source = new Source();
        Sourceable obj = new Decorator(source);
        obj.method();
    }
}
  • 使用組合,動(dòng)態(tài)地?cái)U(kuò)展對(duì)象的功能,在運(yùn)行時(shí)能夠使用不同的裝飾器實(shí)現(xiàn)不同的行為。
  • 比繼承更易出錯(cuò),旨在必要時(shí)使用。

6. 策略模式(strategy)

策略模式定義了一系列算法,并將每個(gè)算法封裝起來(lái),使他們可以相互替換,且算法的變化不會(huì)影響到使用算法的客戶。需要設(shè)計(jì)一個(gè)接口,為一系列實(shí)現(xiàn)類提供統(tǒng)一的方法,多個(gè)實(shí)現(xiàn)類實(shí)現(xiàn)該接口,設(shè)計(jì)一個(gè)抽象類(可有可無(wú),屬于輔助類),提供輔助函數(shù)。策略模式的決定權(quán)在用戶,系統(tǒng)本身提供不同算法的實(shí)現(xiàn),新增或者刪除算法,對(duì)各種算法做封裝。因此,策略模式多用在算法決策系統(tǒng)中,外部用戶只需要決定用哪個(gè)算法即可。

public interface ICalculator {
    public int calculate(String exp);
}
---------------------------------------------------------
public class Minus extends AbstractCalculator implements ICalculator {
    @Override
    public int calculate(String exp) {
        int arrayInt[] = split(exp, "-");
        return arrayInt[0] - arrayInt[1];
    }
}
---------------------------------------------------------
public class Plus extends AbstractCalculator implements ICalculator {
    @Override
    public int calculate(String exp) {
        int arrayInt[] = split(exp, "\\+");
        return arrayInt[0] + arrayInt[1];
    }
}
--------------------------------------------------------
public class AbstractCalculator {
    public int[] split(String exp, String opt) {
        String array[] = exp.split(opt);
        int arrayInt[] = new int[2];
        arrayInt[0] = Integer.parseInt(array[0]);
        arrayInt[1] = Integer.parseInt(array[1]);
        return arrayInt;
    }
}
public class StrategyTest {
    public static void main(String[] args) {
        String exp = "2+8";
        ICalculator cal = new Plus();
        int result = cal.calculate(exp);
        System.out.println(result);
    }
}

7. 觀察者模式(Observer)

類似于郵件訂閱和 RSS 訂閱,當(dāng)我們?yōu)g覽一些博客或 wiki時(shí),經(jīng)常會(huì)看到 RSS 圖標(biāo),就這的意思是,當(dāng)你訂閱了該文章,如果后續(xù)有更新,會(huì)及時(shí)通知你。其實(shí),簡(jiǎn)單來(lái)講就一句話:當(dāng)一個(gè)對(duì)象變化時(shí),其它依賴該對(duì)象的對(duì)象都會(huì)收到通知,并且隨著變化!對(duì)象之間是一種一對(duì)多的關(guān)系。


public interface Observer {
    public void update();
}

public class Observer1 implements Observer {
    @Override
    public void update() {
        System.out.println("observer1 has received!");
    }
}

public class Observer2 implements Observer {
    @Override
    public void update() {
        System.out.println("observer2 has received!");
    }
}

public interface Subject {
    /*增加觀察者*/
    public void add(Observer observer);

    /*刪除觀察者*/
    public void del(Observer observer);

    /*通知所有的觀察者*/
    public void notifyObservers();

    /*自身的操作*/
    public void operation();
}

public abstract class AbstractSubject implements Subject {
    private Vector<Observer> vector = new Vector<Observer>();

    @Override
    public void add(Observer observer) {
        vector.add(observer);
    }

    @Override
    public void del(Observer observer) {
        vector.remove(observer);
    }

    @Override
    public void notifyObservers() {
        Enumeration<Observer> enumo = vector.elements();
        while (enumo.hasMoreElements()) {
            enumo.nextElement().update();
        }
    }
}

public class MySubject extends AbstractSubject {
    @Override
    public void operation() {
        System.out.println("update self!");
        notifyObservers();
    }
}

public class ObserverTest {
    public static void main(String[] args) {
        Subject sub = new MySubject();
        sub.add(new Observer1());
        sub.add(new Observer2());
        sub.operation();
    }
}

8. 代理模式

- 靜態(tài)代理

// 訴訟接口
public interface ILawsiut {
    // 提交申請(qǐng)
    void submit();
    // 進(jìn)行舉證
    void burden();
    // 開始辯護(hù)
    void defend();
    // 訴訟完成
    void finish();
}

// 訴訟人
public class XiaoMin implements ILawsiut{
    @Override
    public void submit() {
        System.out.println("申請(qǐng)仲裁");
    }

    @Override
    public void burden() {
        System.out.println("提交證據(jù)");
    }

    @Override
    public void defend() {
        System.out.println("進(jìn)行辯護(hù)");
    }

    @Override
    public void finish() {
        System.out.println("訴訟成功");
    }
}

// 律師
package proxy;

public class Lawyer implements ILawsiut {
    private ILawsiut mLawsuit;

    public Lawyer(ILawsiut lawsiut) {
        this.mLawsuit = lawsiut;
    }

    @Override
    public void submit() {
        mLawsuit.submit();
    }

    @Override
    public void burden() {
        mLawsuit.burden();
    }

    @Override
    public void defend() {
        mLawsuit.defend();
    }

    @Override
    public void finish() {
        mLawsuit.finish();
    }
}


public class Client {
    public static void main(String[] args){
        ILawsiut xiaoMin = new XiaoMin();
        ILawsiut lawyer = new Lawyer(xiaoMin);
        lawyer.submit();
        lawyer.burden();
        lawyer.defend();
        lawyer.finish();
    }
}


- 動(dòng)態(tài)代理

java 提供了一個(gè)動(dòng)態(tài)代理接口InvocationHandler,實(shí)現(xiàn)該接口需要重寫其代理方法invoke。

// 動(dòng)態(tài)代理類
public class DynamicProxy implements InvocationHandler {
    private Object obj; // 被代理的類引用

    public DynamicProxy(Object obj) {
        this.obj = obj;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
          // 調(diào)用被代理類對(duì)象的方法
        Object result = method.invoke(obj,args);
        return result;
    }
}

//////////////////////////


// 修改后的客戶類
import java.lang.reflect.Proxy;

public class Client {

    public static void main(String[] args) {
        ILawsiut xiaoMin = new XiaoMin();
        
         // 夠早一個(gè)動(dòng)態(tài)代理
        DynamicProxy proxy = new DynamicProxy(xiaoMin);

        // 獲取被代理類小民的ClassLoader
        ClassLoader loader = xiaoMin.getClass().getClassLoader();

        // 動(dòng)態(tài)構(gòu)造一個(gè)代理者律師
        ILawsiut lawyer = (ILawsiut) Proxy.newProxyInstance(loader, new Class[]{ILawsiut.class}, proxy);

        lawyer.submit();
        lawyer.burden();
        lawyer.defend();
        lawyer.finish();
    }
}

靜態(tài)代理與動(dòng)態(tài)代理的區(qū)別在于代理類生成的時(shí)間不同,即根據(jù)程序運(yùn)行前代理類是否已經(jīng)存在,可以將代理分為靜態(tài)代理和動(dòng)態(tài)代理。如果需要對(duì)多個(gè)類進(jìn)行代理,并且代理的功能都是一樣的,用靜態(tài)代理重復(fù)編寫代理類就非常的麻煩,可以用動(dòng)態(tài)代理動(dòng)態(tài)的生成代理類。


設(shè)計(jì)模式雖多,但不要貪杯啊哈哈哈。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • 設(shè)計(jì)模式匯總 一、基礎(chǔ)知識(shí) 1. 設(shè)計(jì)模式概述 定義:設(shè)計(jì)模式(Design Pattern)是一套被反復(fù)使用、多...
    MinoyJet閱讀 4,093評(píng)論 1 15
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法,類相關(guān)的語(yǔ)法,內(nèi)部類的語(yǔ)法,繼承相關(guān)的語(yǔ)法,異常的語(yǔ)法,線程的語(yǔ)...
    子非魚_t_閱讀 34,687評(píng)論 18 399
  • 原文鏈接:http://blog.csdn.net/zhangerqing http://www.cnblogs....
    孤獨(dú)雜貨鋪閱讀 1,638評(píng)論 0 3
  • 一、設(shè)計(jì)模式的分類 總體來(lái)說設(shè)計(jì)模式分為三大類: 創(chuàng)建型模式,共五種:工廠方法模式、抽象工廠模式、單例模式、建造者...
    RamboLI閱讀 830評(píng)論 0 1
  • 一、設(shè)計(jì)模式的分類 總體來(lái)說設(shè)計(jì)模式分為三大類: 創(chuàng)建型模式,共五種:工廠方法模式、抽象工廠模式、單例模式、建造者...
    lichengjin閱讀 999評(píng)論 0 8

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