哇塞!這麼全的工廠模式你見過嗎

設計模式二–工廠模式

舉例:

女媧採集黃土捏成人的形狀,然後放到八卦爐中燒製,然後放到土地上生長,但是意外隨時都會發生。

  • 第一次烤人,感覺應該熟了,往地上一放,哇,沒烤熟,白人誕生了。
  • 第二次烤人,上一次沒有烤熟,這次多烤一會,放到世間一看,哇,熟過頭了,於是黑人誕生了。
  • 第三次烤人,一邊燒製一遍看,知道表皮發黃,嘿,剛剛好,於是黃種人誕生了。

思考:

  • 在面向對象編程的思想中,萬物皆對象。於是我們抽象出具體的三個對象。
  • 女媧,八卦爐,三種不同膚色的人。
  • 女媧可以用場景類Client來表示,八卦爐可以用工廠來表示,三個不同膚色的人,他們都是同一個接口下面的不同實現類。
    於是我們生成類圖。
    在這裏插入圖片描述
  • AbstarctHummanFactory是一個抽象類,HumanFactoruy是一個實現類,完成具體的任務,創建人類。Human是人類- 總稱,三個人種是其實現類,Nawa是一個場景,負責模擬這個場景,發起任務。

人類接口

/**
 * @atuhor sha1024
 */
public interface Human {
    void getTalk();
    void getColor();
}

白人人類

/**
 * @atuhor sha1024
 */
public class WhiteHuman implements Human{

    @Override
    public void getTalk() {
        System.out.println("我是一個白人");
    }

    @Override
    public void getColor() {
        System.out.println("我的皮膚是白色的");
    }
}

黃色人種

/**
 * @atuhor sha1024
 */
public class YellowHuman implements Human{

    @Override
    public void getTalk() {
        System.out.println("黃種人說話最好聽");
    }

    @Override
    public void getColor() {
        System.out.println("我的皮膚是黃色的");
    }
}

黑色人種

/**
 * @atuhor sha1024
 */
public class BlackHuman implements Human{

    @Override
    public void getTalk() {
        System.out.println("黑人說話聽不懂");
    }

    @Override
    public void getColor() {
        System.out.println("我的皮膚是黑色的");
    }
}

定義一個抽象工廠,定義了一個泛型實現了Human

/**
 * @atuhor sha1024
 */
public abstract  class AbstarctHummanFactory {
    public abstract <T extends  Human> T createHuman(Class<T> c);
}
定義了一個人類工廠實現抽象人類工廠
public class HummanFactory extends AbstarctHummanFactory {
    @Override
    public <T extends Human> T createHuman(Class<T> c) {
        //定義一個人種
        Human human = null;
        try{
            human = (T)Class.forName(c.getName()).newInstance();
        }catch (Exception e){
            throw new RuntimeException("定義人種錯誤");
        }

        return (T)human;
    }
}

女媧類,實現具體的場景

public static void main(String[] args) {
    AbstarctHummanFactory  hummanFactory = new HummanFactory();
    Human whiteHuman = hummanFactory.createHuman(WhiteHuman.class);
    whiteHuman.getColor();
    whiteHuman.getTalk();
    Human blackHuman = hummanFactory.createHuman(BlackHuman.class);
    blackHuman.getTalk();
    blackHuman.getColor();
    Human yellowHuman = hummanFactory.createHuman(YellowHuman.class);
    yellowHuman.getColor();
    yellowHuman.getTalk();
    SpringApplication.run(FactoryApplication.class, args);
}

執行結果
在這裏插入圖片描述

工廠方法模式的定義

  • 定義一個用於創建對象的接口,讓子類來決定實例化哪一個。工廠方法使一個類的實例化延遲到其子類。
    在這裏插入圖片描述

在工廠模式中,抽象產品類Product負責定義產品的共性,實現對事物最抽象的定義;Creator爲抽象創建類,也就是抽象工廠,具體如何創建產品類是由具體的實現工廠ConcreateCreator完成的。
具體的產品類可以有多個,都繼承抽象產品類

/**
 * 抽象產品類
 * @atuhor sha1024
 */
public abstract class Product {
    //產品類的公共方法
    public void method1(){
        System.out.println("我是抽象工廠方法1");
    }

    /**
     * 抽象方法
     */
    public abstract void method2();
}

產品類1

/**
 * @atuhor sha1024
 */
public class ConcreateProduct1 extends Product {
    @Override
    public void method2() {
        /**
         * 具體業務邏輯
         */
    }
}

產品類2

/**
 * @atuhor sha1024
 */
public class ConcreateProduct2 extends Product {
    @Override
    public void method2() {
        /**
         * 業務邏輯
         */
    }
}

抽象工廠

/**
 * @atuhor sha1024
 */
public abstract class Creator {
    /**
     * 創建一個產品對象,其輸入任何參數類型都可以自行設置
     * 通常爲String,Enum,Class,也可以爲空
     */
    public abstract <T extends Product> T createProduct(Class<T> c);
}

具體如何產生一個產品對 對象是由具體的工廠實現類來決定的

/**
 * 通過發射獲取到實例
 * @atuhor sha1024
 */
public  class ConcreateCreator  extends Creator{
    @Override
    public <T extends Product> T createProduct(Class<T> c) {
        Product product = null;
        try{
            product = (T)Class.forName(c.getName()).newInstance();
        }catch (Exception e){
            throw new RuntimeException(e.getMessage());
        }
        return (T)product;
    }
}

場景類

/**
 * @atuhor sha1024
 */
public class Client {
    public static void main(String[] args){
        Creator creator = new ConcreateCreator();
        Product product = creator.createProduct(ConcreateProduct1.class);
        /**
         * 業務邏輯
         */
        product.method2();
    }
}

工廠模式的優點:

  • 良好的封裝性,代碼結果清晰。
  • 工廠方法模式的擴展性非常好。
  • 屏蔽產品類,產品類如何變化,調用者不用關心,它只需要關心產品的接口。更好的實現瞭解耦合。

工廠方法模式使用場景

  • 工廠方法是new一個對象的 替代品,所以在所有需要生成對象的地方都可以使用,但是要考慮是否要增加一個產品類來增加代碼的複雜性。
  • 需要靈活的可擴展的框架時,可以考慮採用工廠方法模式。

工廠模式的擴展

縮小爲簡單工廠模式

在這裏插入圖片描述
上圖中我們去掉了AbstarctHummanFactory
抽象人類工廠,同時把createHuman改爲靜態類型。

/**
 * @atuhor sha1024
 */
public class NvWa {
    public static void main(String[] args){
        Human human = HummanFactory2.createHuman(YellowHuman.class);
        Human human2 = HummanFactory2.createHuman(BlackHuman.class);
    }
}

運行結果沒用發生變化,但是我們的類圖變得更加簡單了,因爲簡單所以我們稱爲簡單工廠模式

升級爲多個工廠模式

在這裏插入圖片描述
當我們在做一個複雜項目的時候,經常會遇到初始化一個對象很費力的情況,所有產品都放到一個工廠方法中進行初始化會使代嗎結構不清晰。例如:一個產品類有5個具體實現類,每個實現類的初始化方法都不相同,如果寫在工廠中,會導致該方法巨大無比。
考慮到要結構清晰,我們就爲每個產品定義一個創造者,然後由調用者去選擇與哪個工廠方法關聯。

定義一個抽象工廠

public abstract  class AbstarctHummanFactory {
    public abstract Human createHuman();
}

黑人人種創建工廠

public class BlackHummanFactory extends AbstarctHummanFactory {
    @Override
    public Human createHuman() {
        return new BlackHuman();
    }
}

黃色人種創建工廠

public class YellowHummanFactory extends AbstarctHummanFactory {
    @Override
    public Human createHuman() {
        return new YellowHuman();
    }
}

場景類

public static void main(String[] args){
    Human yellowHuman = (new YellowHummanFactory()).createHuman();
    yellowHuman.getTalk();
    yellowHuman.getColor();
    Human blackHuman = (new BlackHummanFactory()).createHuman();
    blackHuman.getTalk();
    blackHuman.getColor();
}

運行結果還是相同的,每一個產品類都對應了一個創建類好處就是創建類職責清晰,而且結構簡單,但是給可擴展性和維護性帶來了一定的影響。

當然,在複雜的應用場景中一般採用多工廠模式,然後增加一個協調類,避免調用者和各個子工廠交流,對高層模塊提供統一調用api。

替代單例模式

在這裏插入圖片描述
Singleton定義了一個private 的無參構造函數,目的是不允許通過new 的方式創建一個對象

public class Singleton {
    private Singleton(){}
    public void doSomeThing(){
        //業務邏輯
    }
}

單例類,Singleton保證不能通過正常情況下建立一個對象,只能通過反射方式創建

public class SingletonFactory {
    private static Singleton singleton;
    static {
        try{
            Class c1 = Class.forName(Singleton.class.getName());
            //獲取無參構造函數
            Constructor constructor = c1.getDeclaredConstructor();
            //設置無參是可訪問的
            constructor.setAccessible(true);
            //產生一個實例對象
            singleton = (Singleton) constructor.newInstance();
        }catch(Exception e){

        }
    }
   public static Singleton getSingleton(){
        return singleton;
   }

通過獲得類構造器,然後設置訪問權限,生成一個對象,然後提供外部訪問,保證內部中的對象唯一。當然,其他的類也能通過反射的方式獲得一個單例對象。

延遲初始化

在這裏插入圖片描述
ProductFactory負責產品類對象的創建,並通過prMap創建一個緩存,對需要被重用的對象保留

public class ProductFactory {
    private static final Map<String,Product> prMap = new HashMap<>();
    public static synchronized Product createProduct(String type) throws Exception{
        Product product = null;
        //判斷緩存中是否有這個對象
        if(prMap.containsKey(type)){
            product = prMap.get(type);
        }else{
            if(type.equals("Product1")){
                product = new ConcreateProduct1();
            }else{
                product = new ConcreateProduct2();   
            }
            //同時緩存數據到prMap中
            prMap.put(type,product);
        }
        return product;
    }
}

通過定義一個Map容器,容納所有對象,如果Map容器中已經有的對象,則直接取出;如果沒用,則根據需要的類型產生一個對象並放入到Map容器中。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章