Java中的常用設(shè)計(jì)模式

一、概念:

設(shè)計(jì)模式(Design pattern)是一套被反復(fù)使用蹈集、多數(shù)人知曉的、經(jīng)過分類編目的代碼設(shè)計(jì)經(jīng)驗(yàn)的總結(jié)减响,是一種用于固定場合的固定套路

基本分類:

  • 創(chuàng)建型模式:單例設(shè)計(jì)模式,工廠方法模式支示,抽象工廠模式
  • 結(jié)構(gòu)性模式:裝飾器模式,代理模式
  • 行為型模式:模板設(shè)計(jì)模式

二促绵、設(shè)計(jì)模式詳解:

  • 單例設(shè)計(jì)模式:懶漢式嘴纺,餓漢式。懶漢式需要對多線程進(jìn)行同步處理
  • 餓漢式:(立即加載)使用類的時候就創(chuàng)建實(shí)例
public class Singleton {

    // 將自身實(shí)例化對象設(shè)置為一個屬性栽渴,并用static、final修飾
    private static final Singleton instance = new Singleton();

    // 構(gòu)造方法私有化
    private Singleton() {}

    // 靜態(tài)方法返回該實(shí)例
    public static Singleton getInstance() {
        return instance;
    }
}

優(yōu)點(diǎn):實(shí)現(xiàn)起來簡單糖驴,沒有多線程同步問題佛致。
缺點(diǎn):當(dāng)類SingletonTest被加載的時候,會初始化static的instance俺榆,靜態(tài)變量被創(chuàng)建并分配內(nèi)存空間,從這以后定嗓,這個static的instance對象便一直占著這段內(nèi)存(即便你還沒有用到這個實(shí)例)萍桌,當(dāng)類被卸載時宵溅,靜態(tài)變量被摧毀上炎,并釋放所占有的內(nèi)存,因此在某些特定條件下會耗費(fèi)內(nèi)存寇损。

  • 懶漢式:(延遲加載)當(dāng)調(diào)用get方法時裳食,再創(chuàng)建實(shí)例
public class Singleton {

    // 將自身實(shí)例化對象設(shè)置為一個屬性,并用static修飾
    private static Singleton instance;
    
    // 構(gòu)造方法私有化
    private Singleton() {}
    
    // 靜態(tài)方法返回該實(shí)例
    public static Singleton getInstance() {
        if(instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

優(yōu)點(diǎn):實(shí)現(xiàn)起來比較簡單诲祸,當(dāng)類SingletonTest被加載的時候憨愉,靜態(tài)變量static的instance未被創(chuàng)建并分配內(nèi)存空間卿捎,當(dāng)getInstance方法第一次被調(diào)用時,初始化instance變量躺孝,并分配內(nèi)存底桂,因此在某些特定條件下會節(jié)約了內(nèi)存。
缺點(diǎn):存在線程安全問題

  • 線程安全的懶漢式:
public class Singleton {

    // 將自身實(shí)例化對象設(shè)置為一個屬性籽懦,并用static修飾
    private static Singleton instance;
    
    // 構(gòu)造方法私有化
    private Singleton() {}
    
    // 靜態(tài)方法返回該實(shí)例,加synchronized關(guān)鍵字實(shí)現(xiàn)同步
    public static synchronized Singleton getInstance() {
        if(instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

優(yōu)點(diǎn):在多線程的情況下厅篓,使用synchronized方法保證線程安全
缺點(diǎn):效率低下

  • 最優(yōu)懶漢式:
public class Singleton {

    // 將自身實(shí)例化對象設(shè)置為一個屬性捶码,并用static修飾
    private static Singleton instance;
    
    // 構(gòu)造方法私有化
    private Singleton() {}
    
    // 靜態(tài)方法返回該實(shí)例
    public static Singleton getInstance() {
        // 第一次檢查instance是否被實(shí)例化出來,如果沒有進(jìn)入if塊
        if(instance == null) {
            synchronized (Singleton.class) {
                // 某個線程取得了類鎖档押,實(shí)例化對象前第二次檢查instance是否已經(jīng)被實(shí)例化出來祈纯,如果沒有令宿,才最終實(shí)例出對象
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}
  • 普通工廠模式:建立一個工廠類粒没,對實(shí)現(xiàn)了同一接口的不同實(shí)現(xiàn)類進(jìn)行實(shí)例的創(chuàng)建


    普通工廠模式類圖
/**
 * 發(fā)送郵件類
 */
public class MailSender implements Sender{
    @Override
    public void send() {
        System.out.println("正在發(fā)送郵件...");
    }
}
/**
 * 發(fā)送短信類
 */
public class SmsSender implements Sender{
    @Override
    public void send() {
        System.out.println("正在發(fā)送短信...");
    }
}
/**
 * 工廠類
 */
public class SendFactory {

    // 自定義成員方法實(shí)現(xiàn)對象的創(chuàng)建
    public Sender produce(String type) {
        if ("mail".equals(type)) {
            return new MailSender();
        }
        if ("sms".equals(type)) {
            return new SmsSender();
        }
        return null;
    }
}
/**
 * 測試類
 */
public class SendFactoryTest {

    public static void main(String[] args) {

        // 1. 聲明工廠類型的引用指向工廠類型的對象
        SendFactory sf = new SendFactory();
        // 2. 調(diào)用生產(chǎn)方法來實(shí)現(xiàn)對象的創(chuàng)建
        Sender sender = sf.produce("mail");
        // 3. 使用對象調(diào)用方法模擬發(fā)生的行為
        sender.send();
    }
}

主要缺點(diǎn):在普通工廠方法模式中油昂,如果傳遞的字符串出錯,則不能正確創(chuàng)建對象,并可能出現(xiàn)空指針異常
優(yōu)點(diǎn):在創(chuàng)建大量對象的前提下匆浙,擴(kuò)展性和可維護(hù)性強(qiáng)

  • 多個工廠模式:


    多個工廠模式類圖
/**
 * 工廠類
 */
public class SendFactory {

    // 自定義成員方法實(shí)現(xiàn)對象的創(chuàng)建
    public Sender produce(String type) {
        if ("mail".equals(type)) {
            return new MailSender();
        }
        if ("sms".equals(type)) {
            return new SmsSender();
        }
        return null;
    }
    // 自定義對應(yīng)的成員方法創(chuàng)建對應(yīng)的對象
    public Sender produceMail() {
        return new MailSender();
    }

    public Sender produceSms() {
        return new SmsSender();
    }
}
/**
 * 測試類
 */
public class SendFactoryTest {

    public static void main(String[] args) {

        // 1. 聲明工廠類型的引用指向工廠類型的對象
        SendFactory sf = new SendFactory();
        // 2. 調(diào)用生產(chǎn)方法來實(shí)現(xiàn)對象的創(chuàng)建
        //Sender sender = sf.produce("mail");
        Sender sender = sf.produceMail();
        // 3. 使用對象調(diào)用方法模擬發(fā)生的行為
        sender.send();
    }
}

主要缺點(diǎn):在多個工廠方法模式中首尼,為了能夠正確創(chuàng)建對象言秸,先需要創(chuàng)建工廠類的對象才能調(diào)用工廠類中的生產(chǎn)方法

  • 靜態(tài)工廠模式:


    靜態(tài)工廠模式類圖
/**
 * 工廠類
 */
public class SendFactory {

    // 自定義成員方法實(shí)現(xiàn)對象的創(chuàng)建
    public Sender produce(String type) {
        if ("mail".equals(type)) {
            return new MailSender();
        }
        if ("sms".equals(type)) {
            return new SmsSender();
        }
        return null;
    }

    // 自定義對應(yīng)的靜態(tài)成員方法創(chuàng)建對應(yīng)的對象
    public static Sender produceMail() {
        return new MailSender();
    }

    public static Sender produceSms() {
        return new SmsSender();
    }
}
/**
 * 測試類
 */
public class SendFactoryTest {

    public static void main(String[] args) {

        // 1. 聲明工廠類型的引用指向工廠類型的對象
        //SendFactory sf = new SendFactory();
        // 2. 調(diào)用生產(chǎn)方法來實(shí)現(xiàn)對象的創(chuàng)建
        //Sender sender = sf.produce("mail");
        //Sender sender = sf.produceMail();
        Sender sender = SendFactory.produceMail();
        // 3. 使用對象調(diào)用方法模擬發(fā)生的行為
        sender.send();
    }
}

實(shí)際意義:適合于出現(xiàn)了大量的產(chǎn)品需要創(chuàng)建且它們具有共同的接口時举畸,可以通過工廠方法模式進(jìn)行創(chuàng)建
主要缺點(diǎn):工廠方法模式有一個問題就是凳枝,類的創(chuàng)建依賴工廠類抄沮,如果想要拓展程序生產(chǎn)新的產(chǎn)品岖瑰,就必須對工廠的代碼進(jìn)行修改,就違背了開閉原則

  • 抽象工廠模式:


    抽象工廠模式類圖
/**
 * 工廠接口類
 */
public interface Provider {

    // 自定義抽象方法描述產(chǎn)品的生產(chǎn)行為
    Sender produce();
}
/**
 * 發(fā)送郵件的工廠類
 */
public class MailSendFactory implements Provider{
    @Override
    public Sender produce() {
        return new MailSender();
    }
}
/**
 * 發(fā)送短信的工廠類
 */
public class SmsSendFactory implements Provider{
    @Override
    public Sender produce() {
        return new SmsSender();
    }
}
/**
 * 測試類
 */
public class SendFactoryTest {

    public static void main(String[] args) {

        Provider provider = new MailSendFactory();
        Sender sender = provider.produce();
        sender.send();
    }
}

優(yōu)點(diǎn):當(dāng)需要生產(chǎn)新的產(chǎn)品時率挣,只需要創(chuàng)建對應(yīng)的工廠類即可

  • 裝飾器模式:給一個對象動態(tài)地增加一些新功能露戒,要求裝飾對象和被裝飾對象實(shí)現(xiàn)同一個接口,裝飾對象持有被裝飾對象的實(shí)例


    裝飾器模式類圖
/**
 * 定義接口
 */
public interface Sourceable {

    // 自定義抽象方法
    void method();
}
/**
 * 定義原本的類
 */
public class Source implements Sourceable{
    @Override
    public void method() {
        System.out.println("原本的類蛾茉!");
    }
}
/**
 * 裝飾類
 */
public class Decorator implements Sourceable{

    private Sourceable source;

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

    @Override
    public void method() {
        // 保證原有功能
        source.method();

        // 添加新的功能
        System.out.println("我是裝飾器撩鹿!");
    }
}
/**
 * 測試類
 */
public class SourceableTest {

    public static void main(String[] args) {

        // 使用原有類實(shí)現(xiàn)功能
        Sourceable sourceable = new Source();
        sourceable.method();

        System.out.println("------------------------------");

        // 使用裝飾類實(shí)現(xiàn)功能
        Sourceable sourceable1 = new Decorator(sourceable);
        sourceable1.method();
    }
}
輸出結(jié)果

實(shí)際意義:

  • 可以實(shí)現(xiàn)一個類功能的擴(kuò)展
  • 可以動態(tài)地增加功能,而且還能動態(tài)撤銷

缺點(diǎn):產(chǎn)生過多相似的對象键思,不易排錯

  • 代理模式:找一個代理類甫贯,替原對象進(jìn)行一些操作


    代理模式類圖
/**
 * 代理類
 */
public class Proxy implements Sourceable{

    private Source source;

    public Proxy() {
        source = new Source();
    }
    @Override
    public void method() {
        source.method();
        System.out.println("我是代理模式!");
    }
}
/**
 * 測試類
 */
public class SourceableTest {

    public static void main(String[] args) {

        // 使用原有類實(shí)現(xiàn)功能
        Sourceable sourceable = new Source();
        sourceable.method();

        System.out.println("------------------------------");
        
        // 使用代理類實(shí)現(xiàn)功能
        Sourceable sourceable1 = new Proxy();
        sourceable1.method();
    }
}

實(shí)際意義:

  • 如果在使用的時候需要對原有的方法進(jìn)行改進(jìn)赔桌,可以采用一個代理類調(diào)用原有方法渴逻,并且對產(chǎn)生的結(jié)果進(jìn)行控制
  • 使用代理模式疾党,可以將功能劃分得更加清晰惨奕,有助于后期維護(hù)

與裝飾器模式的比較:

  • 裝飾器模式通常的做法是將原始對象作為一個參數(shù)傳給裝飾者的構(gòu)造器,而代理模式通常在一個代理類中創(chuàng)建一個被代理類的對象

  • 裝飾器模式關(guān)注于在一個對象上動態(tài)的添加方法雹洗,而代理模式關(guān)注于控制對對象的訪問

  • 模板模式:指一個抽象類中封裝了一個固定流程,流程中的具體步驟可以由不同子類進(jìn)行不同的實(shí)現(xiàn)时肿,通過抽象類讓固定的流程產(chǎn)生不同的結(jié)果


    模板模式類圖
/**
 * 定義一個抽象類
 */
public abstract class AbstractCalculator {

    // 自定義成員方法實(shí)現(xiàn)將參數(shù)指定的表達(dá)式按照參數(shù)指定的規(guī)則進(jìn)行切割并返回計(jì)算結(jié)果

    public int splitExpression(String exp, String op) {
        String[] sArr = exp.split(op);
        return calculate(Integer.parseInt(sArr[0]), Integer.parseInt(sArr[1]));
    }
    // 自定義抽象方法實(shí)現(xiàn)運(yùn)算
    public abstract int calculate(int ia, int ib);
}
/**
 * 定義加法
 */
public class Plus extends AbstractCalculator{
    @Override
    public int calculate(int ia, int ib) {
        return ia + ib;
    }
}
/**
 * 定義減法
 */
public class Minus extends AbstractCalculator{
    @Override
    public int calculate(int ia, int ib) {
        return ia - ib;
    }
}
/**
 * 測試類
 */
public class AbstractCalculatorTest {

    public static void main(String[] args) {

        AbstractCalculator abstractCalculator = new Plus();
        int res = abstractCalculator.splitExpression("1+1", "\\+");
        System.out.println("最終的運(yùn)算結(jié)果是:" + res);
    }
}
輸出

實(shí)際意義:

  • 將多個子類共有且邏輯基本相同的內(nèi)容提取出來實(shí)現(xiàn)代碼復(fù)用
  • 不同的子類實(shí)現(xiàn)不同的效果形成多態(tài)螃成,有助于后期維護(hù)
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市顷霹,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌淋淀,老刑警劉巖覆醇,帶你破解...
    沈念sama閱讀 206,723評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異永脓,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)常摧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評論 2 382
  • 文/潘曉璐 我一進(jìn)店門落午,熙熙樓的掌柜王于貴愁眉苦臉地迎上來谎懦,“玉大人溃斋,你說我怎么就攤上這事」=伲” “怎么了?”我有些...
    開封第一講書人閱讀 152,998評論 0 344
  • 文/不壞的土叔 我叫張陵蛉威,是天一觀的道長猫妙。 經(jīng)常有香客問我,道長割坠,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,323評論 1 279
  • 正文 為了忘掉前任彼哼,我火速辦了婚禮,結(jié)果婚禮上剪菱,老公的妹妹穿的比我還像新娘。我一直安慰自己孝常,他們只是感情好蚓哩,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,355評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著岸梨,像睡著了一般。 火紅的嫁衣襯著肌膚如雪曹阔。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,079評論 1 285
  • 那天寂拆,我揣著相機(jī)與錄音抓韩,去河邊找鬼纠永。 笑死园蝠,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的彪薛。 我是一名探鬼主播茂装,決...
    沈念sama閱讀 38,389評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼善延!你這毒婦竟也來了少态?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,019評論 0 259
  • 序言:老撾萬榮一對情侶失蹤易遣,失蹤者是張志新(化名)和其女友劉穎彼妻,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,519評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡侨歉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,971評論 2 325
  • 正文 我和宋清朗相戀三年屋摇,在試婚紗的時候發(fā)現(xiàn)自己被綠了幽邓。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片炮温。...
    茶點(diǎn)故事閱讀 38,100評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖牵舵,靈堂內(nèi)的尸體忽然破棺而出柒啤,到底是詐尸還是另有隱情,我是刑警寧澤畸颅,帶...
    沈念sama閱讀 33,738評論 4 324
  • 正文 年R本政府宣布担巩,位于F島的核電站,受9級特大地震影響没炒,放射性物質(zhì)發(fā)生泄漏涛癌。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,293評論 3 307
  • 文/蒙蒙 一窥浪、第九天 我趴在偏房一處隱蔽的房頂上張望祖很。 院中可真熱鬧,春花似錦漾脂、人聲如沸假颇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽笨鸡。三九已至,卻和暖如春坦冠,著一層夾襖步出監(jiān)牢的瞬間形耗,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評論 1 262
  • 我被黑心中介騙來泰國打工辙浑, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留激涤,地道東北人。 一個月前我還...
    沈念sama閱讀 45,547評論 2 354
  • 正文 我出身青樓判呕,卻偏偏與公主長得像倦踢,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子侠草,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,834評論 2 345