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

摘自《輕量級JavaEE企業應用開發》

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

單例模式

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

代碼實現
package com.singleton;

/**
 * 單例模式Demo
 * 《輕量級JavaEE企業應用實戰》 P727
 * 如果一個類始終只能創建一個實例,則這個類稱為單例類,這種模式稱為單例模式
 * Spring推薦獎所有業務邏輯組件、DAO組件、數據源組件等配置成單例的行為方式
 * @author Slience
 *
 */
class Singleton {
    private static Singleton instance;
    private Singleton() {
    }
    public static Singleton getInstance() {
        //如果instance等于null,證明還沒有創建過Singleton實例,為其創建Singleton實例
        //如果instance不等于null,說明已經創建過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);
    }
}

單例模式主要有兩個優勢

  • 減少創建Java實例所帶來的系統開銷。
  • 便于系統跟蹤單個Java實例的生命周期、實例狀態等。

簡單工廠模式

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

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

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

代碼實現

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

package com.simplefactory;
/**
 * 《輕量級JavaEE企業應用實戰 p728
 * Computer類可以通過Output接口打印內容,讓打印機類實現Output接口
 * Computer使用OutputFactory來獲取合適的打印機從而讓打印機類和電腦類解耦
 * 
 * 使用簡單工廠模式的優勢是:讓對象的調用者和對象的創建過程分離,當對象調用者需要對象時,
 * 直接向工廠請求即可;從而避免了對象的調用者與對象的實現類以硬編碼方式耦合,
 * 以提高系統的可維護性、可拓展性。
 * 工廠模式也有一個小小的缺陷:當產品修改是,工廠類也要做相應的修改
 * @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實現類耦合,這樣修改到另一個實現類的時候
        //只需要修改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去實現Output

package com.simplefactory;

public class Printer implements Output {

    private String[] printData = new String[MAX_CACHE_LINE];
    //用以記錄當前需要打印的作業數
    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]);
            //將原數組第二個到--dataNum個元素前移一位
            System.arraycopy(printData, 1, printData, 0, --dataNum);
        }
    }

}

創建Output對象都通過OutputFactory工廠類去創建

package com.simplefactory;

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

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

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

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

package com.simplefactory;

public class BetterPrinter implements Output {

    private String[] printData = new String[MAX_CACHE_LINE * 2];
    //用以記錄當前需要打印的作業數
    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]);
            //將原數組第二個到--dataNum個元素前移一位
            System.arraycopy(printData, 1, printData, 0, --dataNum);
        }
    }

}

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

工廠模式

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

代碼實現

Output接口不變,新增OutputFactory接口

package com.factory;

public interface OutputFactory {
    Output getOutput();
}

還有其實現類OutputFactoryFactory

package com.factory;

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

之后創建BetterPrinterFactory和PrinterFactory工廠實現類,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傳入不同的參數來獲取不同的Factory類,再通過不同的工廠類來創建不同的對象

package com.factory;
/**
 * 《輕量級JavaEE企業應用實戰 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實現類耦合,這樣修改到另一個實現類的時候
        //只需要修改factory中的getOutput方法即可
        Computer computer = new Computer(factory.getOutput());
        computer.keyIn("Hello World");
        computer.keyIn("Slience");
        computer.print();
    }
}

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

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