單例模式、簡單工廠模式和抽象工廠模式初探

摘自《輕量級JavaEE企業(yè)應用開發(fā)》

【模式】是一條由三個部分組裝成的通用規(guī)則:它表示了一個特定環(huán)境、一類問題和一個解決方案之間的關系
【設計模式】是對于特定環(huán)境下,經常出現(xiàn)的的某類軟件開發(fā)問題的一種相對成熟的設計方案

單例模式

如果一個類始終只能創(chuàng)建一個實例,則稱這個類為單例類,這種模式就被稱為單例模式。
Spring推薦獎所有業(yè)務邏輯組件、DAO組件、數(shù)據(jù)源組件等配置為單例的行為方式,因為這些組件不需要保存任何用戶狀態(tài),是所有客戶端都可以通用的組件

代碼實現(xiàn)
package com.singleton;

/**
 * 單例模式Demo
 * 《輕量級JavaEE企業(yè)應用實戰(zhàn)》 P727
 * 如果一個類始終只能創(chuàng)建一個實例,則這個類稱為單例類,這種模式稱為單例模式
 * Spring推薦獎所有業(yè)務邏輯組件、DAO組件、數(shù)據(jù)源組件等配置成單例的行為方式
 * @author Slience
 *
 */
class Singleton {
    private static Singleton instance;
    private Singleton() {
    }
    public static Singleton getInstance() {
        //如果instance等于null,證明還沒有創(chuàng)建過Singleton實例,為其創(chuàng)建Singleton實例
        //如果instance不等于null,說明已經創(chuàng)建過Singleton實例,
        //直接將這個Singleton返回即可,不必在new一個Singleton
        if(instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}
public class SingletonTest {
    public static void main(String[] args) {
        Singleton s1 = Singleton.getInstance();
        Singleton s2 = Singleton.getInstance();
        System.out.println(s1 == s2);
    }
}

單例模式主要有兩個優(yōu)勢

  • 減少創(chuàng)建Java實例所帶來的系統(tǒng)開銷。
  • 便于系統(tǒng)跟蹤單個Java實例的生命周期、實例狀態(tài)等。

簡單工廠模式

A實例調用B實例的方法,則稱A依賴于B
當A實例需要調用B實例的時候,有兩種方式可以實現(xiàn):

  • 一種是使用new 關鍵字創(chuàng)建一個B實例,這種方式的缺點在于如果日后需求有變需要用C實例替代B實例,那么A實例中硬編碼耦合B實例就需要修改,如果有很多實例都用new B()的方式去調用B實例的方法,那么需要修改的地方將會很多。
  • A實例只需要調用B對象的方法,并不關心B對象的實現(xiàn)創(chuàng)建過程,所以我們可以讓B類實現(xiàn)了一個IB借口,A類只需要和IB接口耦合,A類不直接使用new關鍵字來創(chuàng)建B實例,而是重新定義一個IBFactory類,由該類來負責創(chuàng)建IB實例,A類只需要調用IBFactory工廠方法來得到IB實例。

使用后一種設計,可以讓A類和B類解除耦合,只需要和IB結構和IBFactory耦合,這樣當新需求來時C類可以去實現(xiàn)IB接口然后再IBFactory中修改創(chuàng)建IB實例的代碼,就可以實現(xiàn)新需求了,A類中不需要對代碼進行修改。
這種將多個類對象交給工廠類來生成的設計方式被稱為【簡單工廠模式】

代碼實現(xiàn)

假設程序用有一個Computer對象需要依賴一個輸出設備,可以讓Computer對象依賴一個Output(接口)屬性,再用Printer(實現(xiàn)輸出類)對象去實現(xiàn)Output接口,Computer從OutputFactory工廠類生成Printer從而讓Computer和Printer分離開來,日后如果有更好的實現(xiàn)輸出類BetterPrinter,那么直接修改OutputFactory代碼即可。
Computer類

package com.simplefactory;
/**
 * 《輕量級JavaEE企業(yè)應用實戰(zhàn) p728
 * Computer類可以通過Output接口打印內容,讓打印機類實現(xiàn)Output接口
 * Computer使用OutputFactory來獲取合適的打印機從而讓打印機類和電腦類解耦
 * 
 * 使用簡單工廠模式的優(yōu)勢是:讓對象的調用者和對象的創(chuàng)建過程分離,當對象調用者需要對象時,
 * 直接向工廠請求即可;從而避免了對象的調用者與對象的實現(xiàn)類以硬編碼方式耦合,
 * 以提高系統(tǒng)的可維護性、可拓展性。
 * 工廠模式也有一個小小的缺陷:當產品修改是,工廠類也要做相應的修改
 * @author Slience
 *
 */
public class Computer {
    private Output out;
    public Computer(Output out) {
        this.out = out;
    }
    //定義一個模擬獲取字符串輸入的方法
    public void keyIn(String msg) {
        out.getDate(msg);
    }
    //定義一個模擬打印的方法
    public void print() {
        out.out();
    }
    public static void main(String[] args) {
        OutputFactory factory = new OutputFactory();
        //通過工廠類獲取output對象
        //Computer和factory硬編碼而不和output實現(xiàn)類耦合,這樣修改到另一個實現(xiàn)類的時候
        //只需要修改factory中的getOutput方法即可
        Computer computer = new Computer(factory.getOutput());
        computer.keyIn("Hello World");
        computer.keyIn("Slience");
        computer.print();
    }
}

Output輸出接口

package com.simplefactory;

public interface Output {

    //默認的打印隊列大小
    final static int MAX_CACHE_LINE = 1; 
    
    void getDate(String msg);

    void out();

}

之后用Printer去實現(xiàn)Output

package com.simplefactory;

public class Printer implements Output {

    private String[] printData = new String[MAX_CACHE_LINE];
    //用以記錄當前需要打印的作業(yè)數(shù)
    private int dataNum = 0;
    
    @Override
    public void getDate(String msg) {
        // TODO Auto-generated method stub
        if(dataNum >= MAX_CACHE_LINE) {
            System.out.println("輸出隊列已滿,添加失敗");
        } else {
            printData[dataNum++] = msg;
        }
        
    }

    @Override
    public void out() {
        // TODO Auto-generated method stub
        while(dataNum > 0) {
            System.out.println("打印機打印:" + printData[0]);
            //將原數(shù)組第二個到--dataNum個元素前移一位
            System.arraycopy(printData, 1, printData, 0, --dataNum);
        }
    }

}

創(chuàng)建Output對象都通過OutputFactory工廠類去創(chuàng)建

package com.simplefactory;

public class OutputFactory {
    public Output getOutput() {
        
        return new Printer();
        //換了新的打印機之后就可以打印兩行了
        //return new BetterPrinter();
    }
}

當我們運行起Computer的時候會輸出

輸出隊列已滿,添加失敗
打印機打印:Hello World

如果此時來了一個更好地Output實現(xiàn)類——BetterPrinter,他能夠打印原大小兩倍的數(shù)據(jù)量

package com.simplefactory;

public class BetterPrinter implements Output {

    private String[] printData = new String[MAX_CACHE_LINE * 2];
    //用以記錄當前需要打印的作業(yè)數(shù)
    private int dataNum = 0;
    
    @Override
    public void getDate(String msg) {
        // TODO Auto-generated method stub
        if(dataNum >= MAX_CACHE_LINE * 2) {
            System.out.println("輸出隊列已滿,添加失敗");
        } else {
            printData[dataNum++] = msg;
        }
        
    }

    @Override
    public void out() {
        // TODO Auto-generated method stub
        while(dataNum > 0) {
            System.out.println("打印機打印:" + printData[0]);
            //將原數(shù)組第二個到--dataNum個元素前移一位
            System.arraycopy(printData, 1, printData, 0, --dataNum);
        }
    }

}

我們想要給Computer換上新的Output的話,只需要在OutputFactory中修改為return new BetterPrinter()即可,Computer不需要做任何的改變。但是這樣還是有一個小小的缺點,那就是當產品修改時,工廠類也要做相應的修改。
在Spring中可以通過配置XML來實現(xiàn)工廠類的修改

工廠模式

和簡單工廠模式很像,只不過新增了一個OutputFactory接口和BetterPrinterFactory和PrinterFactory工廠類,這樣做的好處在于不同的工廠生產不同的對象,工廠實現(xiàn)類不需要對邏輯進行判斷,邏輯判斷可以在OutputFactoryFactory類中進行,OutputFactoryFactory類是用來根據(jù)需要生成不同的實現(xiàn)了OutputFactory接口的工廠類的類。

代碼實現(xiàn)

Output接口不變,新增OutputFactory接口

package com.factory;

public interface OutputFactory {
    Output getOutput();
}

還有其實現(xiàn)類OutputFactoryFactory

package com.factory;

public class OutputFactoryFactory {
    public static OutputFactory getOutputFactory(String type) {
        //不考慮大小寫
        if(type.equalsIgnoreCase("better")) {
            return new BetterPrinterFactory();
        } else {
            return new PrinterFactory();
        }
    }
}

之后創(chuàng)建BetterPrinterFactory和PrinterFactory工廠實現(xiàn)類,PrintFactory和BetterPrinterFactory只是兩者返回的對象不同。

package com.factory;

public class BetterPrinterFactory implements OutputFactory {

    @Override
    public Output getOutput() {
        // TODO Auto-generated method stub
        return new BetterPrinter();
        //如果是PrinterFactory返回的就是
        //return new Printer();
    }
    
}

在Computer中通過對OutputFactoryFactory傳入不同的參數(shù)來獲取不同的Factory類,再通過不同的工廠類來創(chuàng)建不同的對象

package com.factory;
/**
 * 《輕量級JavaEE企業(yè)應用實戰(zhàn) p737
 * 
 * 
 * 
 * @author Slience
 *
 */
public class Computer {
    private Output out;
    public Computer(Output out) {
        this.out = out;
    }
    //定義一個模擬獲取字符串輸入的方法
    public void keyIn(String msg) {
        out.getDate(msg);
    }
    //定義一個模擬打印的方法
    public void print() {
        out.out();
    }
    public static void main(String[] args) {
        
        //工廠的工廠
        OutputFactory factory = OutputFactoryFactory.getOutputFactory("better");
        //通過工廠類獲取output對象
        //Computer和factory硬編碼而不和output實現(xiàn)類耦合,這樣修改到另一個實現(xiàn)類的時候
        //只需要修改factory中的getOutput方法即可
        Computer computer = new Computer(factory.getOutput());
        computer.keyIn("Hello World");
        computer.keyIn("Slience");
        computer.print();
    }
}

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

相關閱讀更多精彩內容

  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,545評論 19 139
  • 一、設計模式的分類 總體來說設計模式分為三大類: 創(chuàng)建型模式,共五種:工廠方法模式、抽象工廠模式、單例模式、建造者...
    lichengjin閱讀 990評論 0 8
  • 一、設計模式的分類 總體來說設計模式分為三大類: 創(chuàng)建型模式,共五種:工廠方法模式、抽象工廠模式、單例模式、建造者...
    RamboLI閱讀 827評論 0 1
  • 原文鏈接:http://blog.csdn.net/zhangerqing http://www.cnblogs....
    孤獨雜貨鋪閱讀 1,637評論 0 3
  • 1 場景問題# 1.1 選擇組裝電腦的配件## 舉個生活中常見的例子——組裝電腦,我們在組裝電腦的時候,通常需要選...
    七寸知架構閱讀 4,538評論 6 67

友情鏈接更多精彩內容