(Java)設計模式:建立型

紫邪情發表於2022-11-25

前言

這篇內容是從另一篇:UML建模、設計原則 中分離出來的,原本這個建立型設計模式是和其放在一起的

但是:把這篇建立型設計模式放在一起讓我賊彆扭,看起來賊不舒服,越看念頭越不通達,導致老衲躺在床上腦海中冒出來時都睡不著了

因此:最後實在受不了了,還是將其抽離出來


3、設計模式

分類:

  • image

  • 注:使用設計模式的規範,類名 = 需求名+使用的對應設計模式名,如:StringBuilder,這就是使用了Builder建造者模式

  • 設計模式不是一成不變的,主要是思想,至於形不需要在意,形只是便於理解罷了


3.1、建立型

這個型別的模式是專門針對於建立物件的,也就是它的適用機制
換言之:這些設計模式提供了一種在建立物件的同時隱藏建立邏輯的方式,而不是使用 new 運算子直接例項化物件


3.1.1、單例模式

定義:保證物件全域性唯一,即:保證一個類只有一個例項,哪怕是多執行緒來進行訪問,向外提供一個訪問此例項的方法即可

使用場景

  • 1、資料庫連線池不會反覆建立
  • 2、Spring中一個單例模式Bean的建立
  • 3、開發中設定一些全域性的屬性進行儲存(當然:用Redis更好)

3.1.1.1、static實現

package com.zixieqing.o1static;

import java.util.HashMap;
import java.util.Map;

/**
 * <p>@description  : 該類功能  使用static的方式</p>
 * <p>@package      : com.zixieqing.o1static</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public class StaticSingleton {

    private static Map<String, String> CACHE = new HashMap<String, String>();
}

  • 這種方式在第一次執行時就初始化Map了,不需要延遲載入
  • 缺點:需要被繼承 或 需要維持一些特定狀態時就不適合了

3.1.1.2、懶漢模式

定義:體現在一個“懶”字上,即:需要時才去建立物件

package com.zixieqing.o2lazy;

/**
 * <p>@description  : 該類功能  懶漢式
 *  此種方式不安全:好比多個人搶廁所,會造成不安全,可能有多個人搶到
 * </p>
 * <p>@package      : com.zixieqing.o2lazy</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public class LazySingleton {

    /**
     * 1、private static 當前類的屬性
     */
    private static LazySingleton LAZY_SINGLETON_INSTANCE;

    /**
     * 2、private 的構造
     */
    private LazySingleton() {
    }

    /**
     * 3、提供public static 建立當前類物件的方法
     */
    public static LazySingleton getInstance() {
        if (LAZY_SINGLETON_INSTANCE != null) return LAZY_SINGLETON_INSTANCE;

        LAZY_SINGLETON_INSTANCE = new LazySingleton();
        return LAZY_SINGLETON_INSTANCE;

    }

    /**
     * 4、要想稍微安全就加synchronized同步鎖
     * 但是:此種方式因為把synchronized加在了方法上,導致所有訪問爭鎖而出現 資源的浪費
     */
/*    public static synchronized lazy_unsafe_singleton getInstance() {
        if (LAZY_UNSAFE_INSTANCE_SINGLETON != null) return LAZY_UNSAFE_INSTANCE_SINGLETON;

        LAZY_UNSAFE_INSTANCE_SINGLETON = new Singleton_lazy_unsafe();
        return LAZY_UNSAFE_INSTANCE_SINGLETON;

    }*/
}


3.1.1.3、餓漢模式

定義:體現在“餓”字上,即:一開始就初始化

package com.zixieqing.o3hunger;

/**
 * <p>@description  : 該類功能  餓漢式實現
 * 這種方式和利用static的方式是異曲同工的
 * </p>
 * <p>@package      : com.zixieqing.o3hunger</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public class HungerSingleton {

    private static HungerSingleton HUNGER_SINGLETON_INSTANCE = new HungerSingleton();

    private HungerSingleton() {}

    /**
     * 這種方式其實也不安全
     * 因為當多執行緒在if判斷時如果在同一時刻二者都判斷成立,就會建立不同的例項
     */
    public static HungerSingleton getInstance() {
        if (HUNGER_SINGLETON_INSTANCE != null) return HUNGER_SINGLETON_INSTANCE;

        HUNGER_SINGLETON_INSTANCE = new HungerSingleton();
        return HUNGER_SINGLETON_INSTANCE;
    }
}


3.1.1.4、內部類


package com.zixieqing.o4innerclass;

/**
 * <p>@description  : 該類功能  使用內部類實現 - 推薦的一種</p>
 * <p>@package      : com.zixieqing.o4innerclass</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public class InnerClassSingleton {

    /**
     * 1、private的構造
     */
    private InnerClassSingleton() {}

    /**
     * 2、private static的內部類
     * 巧妙之處:使用static修飾,則:做到執行緒安全,也巧妙藉助了第一種實現方式:使用static的形式
     * 同時:內部類可以訪問外部類的靜態屬性和靜態方法
     */
    private static class NewInstance{
        public static InnerClassSingleton INSTANCE = new InnerClassSingleton();
    }

    /**
     * 3、public static對外提供獲取當前類例項的方法
     */
    public static InnerClassSingleton getInstance() {
        return NewInstance.INSTANCE;
    }
}



3.1.1.5、雙重鎖驗證


package com.zixieqing.o5twinlock;

/**
 * <p>@description  : 該類功能  雙重鎖校驗(執行緒安全)</p>
 * <p>@package      : com.zixieqing.o5twinlock</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public class TwinLockSingleton {
    /**
     * 最好加上volatile關鍵字,它的作用是:不進行指令重排(指令重排是JVM中的)
     */
    private static volatile TwinLockSingleton INSTANCE;

    private TwinLockSingleton() {}

    public static TwinLockSingleton getInstance() {

        if (null != INSTANCE) return INSTANCE;

        // 雙重驗證:synchronized 和 if
        synchronized (TwinLockSingleton.class) {
            if (null == INSTANCE) return INSTANCE = new TwinLockSingleton();
        }
        return INSTANCE;
    }
}



3.1.1.6、CAS

package com.zixieqing.o6cas;

import java.util.concurrent.atomic.AtomicReference;

/**
 * <p>@description  : 該類功能  利用CAS演算法實現
 * 好處:CAS的忙等演算法是靠底層硬體,所以:保證了執行緒安全 和 不會產生執行緒的切換和阻塞的開銷,從而提高效能
 *      並且:可以支援較大的併發性
 * </p>
 * <p>@package      : com.zixieqing.o6cas</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public class CASSingleton {

    private static CASSingleton CAS_SINGLETON_INSTANCE;

    /**
     * AtomicReference<K> 原子引用  儲存“一個”K例項
     */
    private static final AtomicReference<CASSingleton> INSTANCE = new AtomicReference<>();

    public static CASSingleton getInstance() {

        /*
            缺點就在這裡:CAS的忙等   從而造成:如果一直沒有獲取就會處於死循壞當中
         */
        while (true) {
            CAS_SINGLETON_INSTANCE = INSTANCE.get();
            if (null != CAS_SINGLETON_INSTANCE) return CAS_SINGLETON_INSTANCE;
        /*
            boolean compareAndSet(V expect, V update)
                expect 預期值
                update 要改成的新值
                如果當前值和預期值相等,那麼就以原子的方式將值改為新值

             下列邏輯:期望INSTANCE是null,所以將INSTANCE的值改為new Singleton_CAS()
         */
            INSTANCE.compareAndSet(null, new CASSingleton());
            // 獲取INSTANCE的值 返回值就是AtomicReference<Singleton_CAS>中的泛型型別
            return INSTANCE.get();
        }
    }
}


3.1.1.6、列舉

package com.zixieqing.o7num;

/**
 * <p>@description  : 該類功能  使用列舉來實現(極度推薦)
 * </p>
 * <p>@package      : com.zixieqing.o7num</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public enum EnumSingleton {

    /**
     * 物件
     */
    INSTANCE;

    /**
    * 根據需要自行配置getter也行
    */
}


3.1.2、原型模式

定義:根據一個已有物件(原型例項) 建立 新的物件(就是克隆)

解決的問題:建立重複物件,而這部分物件本身比較複雜,生成過程從庫或者RPC介面中獲取資料的耗時時長可能會很長,因此:採用克隆的方式會節省時間,總之:當系統中需要建立相同或相似的物件時,就可以用原型模式


場景(在開發中貌似都沒用到過):

  • 1、CV大師(ctrl+cctrl+v
  • 2、JavaSE中的Object.clone()

瞭解兩個名詞:淺複製和深複製

  • 淺複製
    • 1、當類的成員變數是基本資料型別時,淺複製會將原物件的屬性值賦值給新物件
    • 2、當類中成員變數是引用資料型別時,淺複製 會將 原物件的引用資料型別的地址 賦值給新物件的成員變數。也就是說 兩個物件共享了同一個資料。當其中一個物件修改成員變數的值時,另外一個的值也會隨之改變
  • 深複製
    • 無論是 基本資料型別還是引用資料型別,都會去開闢額外的空間給新物件

3.1.2.1、用Object.clone() API(不推薦)

3.1.2.1.1、克隆基本資料型別
  • 淺複製會將原物件的屬性值賦值給新物件(複製的是值)

  • 注:String底層被final修飾了的,修改值之後是重新建立了一個Sting物件,修改之後不會影響原物件

package com.zixieqing;

/**
 * <p>@description  : 該類功能  原型類(屬性都是基本資料型別時)
 * Cloneable 標誌Object.clone()方法可以對Person該類的例項進行欄位的複製
 * </p>
 * <p>@package      : com.zixieqing</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public class Person implements Cloneable{

    private String name;

    public Person() {
    }

    public Person(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                '}';
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Person person = null;
        try {
            person = (Person) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return person;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

package com.zixieqing;


/**
 * <p>@description  : 該類功能  測試
 * </p>
 * <p>@package      : com.zixieqing</p>
 * <p>@author       : MeiChengsong</p>
 * <p>@version      : V1.0.0</p>
 */

public class Test {

    public static void main(String[] args) throws CloneNotSupportedException {

        Person person = new Person();
        person.setName("紫邪情");

        Person clonePerson = (Person) person.clone();
        System.out.println( "原型物件:" + person);
        System.out.println( "克隆物件:" + clonePerson);

        clonePerson.setName("小紫");

        System.out.println("==========修改之後=============");
        System.out.println(person);
        System.out.println(clonePerson);

    }
}

image


3.1.2.1.2、克隆引用資料型別
  • 淺複製 會將 原物件的引用資料型別的地址 賦值給新物件的成員變數(複製的是地址)。也就是說 兩個物件共享了同一個資料。當其中一個物件修改成員變數的值時,另外一個的值也會隨之改變
package com.zixieqing;

/**
 * <p>@description  : 該類功能  原型類(屬性是引用資料型別時)
 * </p>
 * <p>@package      : com.zixieqing</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public class Peron2 implements Cloneable{
    private Person person;

    public Peron2() {
    }

    public Peron2(Person person) {
        this.person = person;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Peron2 peron2 = null;

        try {
            peron2 = (Peron2) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }

        return peron2;
    }

    @Override
    public String toString() {
        return "Peron2{" +
                "person=" + person +
                '}';
    }

    public Person getPerson() {
        return person;
    }

    public void setPerson(Person person) {
        this.person = person;
    }
}

package com.zixieqing;


/**
 * <p>@description  : 該類功能  測試
 * </p>
 * <p>@package      : com.zixieqing</p>
 * <p>@author       : MeiChengsong</p>
 * <p>@version      : V1.0.0</p>
 */

public class Test {

    public static void main(String[] args) throws CloneNotSupportedException {

        Peron2 peron2 = new Peron2();
        Person person = new Person("法外狂徒");
        peron2.setPerson(person);

        Peron2 clonePerson2 = (Peron2) peron2.clone();

        System.out.println(peron2);
        System.out.println(clonePerson2);

        person.setName("張三");
        clonePerson2.setPerson(person);

        System.out.println("修改之後");
        System.out.println(peron2);
        System.out.println(clonePerson2);
    }
}

image


3.1.2.2、使用序列化(推薦)

package com.zixieqing.o2useserialize;

import com.zixieqing.o1useclone.Person;

import java.io.*;

/**
 * <p>@description  : 該類功能  測試
 * </p>
 * <p>@package      : com.zixieqing.o2useserialize</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public class Test {

    public static void main(String[] args) throws IOException {

        Person3 person3 = new Person3(0, new Person("紫邪情"));
        Person3 cloneInstance = cloneInstance(person3);

        System.out.println(person3);
        System.out.println(cloneInstance);

        cloneInstance.setSex(1);

        System.out.println("===========修改之後=============");

        System.out.println(person3);
        System.out.println(cloneInstance);
    }


    /**
     * <p>@description  : 該方法功能 物件序列化克隆
     * </p>
     * <p>@methodName   : cloneInstance</p>
     * <p>@author: ZiXieqing</p>
     * <p>@version: V1.0.0</p>
     * @param person3 要進行序列化克隆的物件
     * @return com.zixieqing.o2useserialize.Person3
     */
    private static Person3 cloneInstance(Person3 person3) throws IOException {
        ByteArrayOutputStream bos = null;
        ObjectOutputStream oos = null;
        ByteArrayInputStream bis;
        ObjectInputStream ois;
        Person3 person = null;

        try {
            // 序列化
            bos = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(bos);
            oos.writeObject(person3);

            bis = new ByteArrayInputStream(bos.toByteArray());
            ois = new ObjectInputStream(bis);
            person = (Person3) ois.readObject();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (null != bos) bos.close();

            if (null != oos) oos.close();
        }

        return person;
    }
}

image-20221020163706137


* 3.1.3、工廠模式

3.1.3.1、簡單工廠(工廠基礎)

定義:把對類的建立初始化全都交給一個工廠來執行,而使用者不需要關心建立的過程是什麼樣的,只需要告訴工廠,我想要什麼就行了

設計模式中並沒有所謂的簡單工廠,這玩意兒嚴格來說是一種編碼規範,但是:也是學工廠模式的基礎

簡單工廠的角色

  • 抽象產品:定義產品的規則,即產品有哪些特性和功能,可以是介面、抽象類、普通類也行(但一般不會這麼幹)
  • 具體產品:實現或繼承抽象產品的子類
  • 產品工廠:提供建立產品的方法,讓使用者透過該方法獲取產品

3.1.3.1.1、簡單邏輯

開發場景:網上買商品,假設有三種購買方式(前面的數字對應其型別):1、透過優惠卡;2、透過快播兌換卡;3、啥也沒用,直接購買實物商品,根據前面說的三角色來整活


準備工作:依賴匯入

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.62</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <!-- LOGGING begin -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.5</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
            <version>1.7.5</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.0.9</version>
            <exclusions>
                <exclusion>
                    <artifactId>slf4j-api</artifactId>
                    <groupId>org.slf4j</groupId>
                </exclusion>
            </exclusions>
        </dependency>


1、抽象產品:定義規則

package com.zixieqing.o1simplefactory.o1simplelogic;

/**
 * <p>@description  : 該類功能  抽象產品:購物
 * </p>
 * <p>@package      : com.zixieqing.o1simplefactory.o1simplelogic</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public interface IShopping {

    /**
     * <p>@description  : 該方法功能 傳送商品
     * </p>
     * <p>@methodName   : sendCommodity</p>
     * <p>@author: ZiXieqing</p>
     * <p>@version: V1.0.0</p>
     * @param userId 使用者id
     * @param goodsName  商品名
     */
    void sendCommodity(String userId, String goodsName) throws Exception;
}


2、具體產品:實現或繼承抽象產品的子類

  • 優惠卡方式

    • 
        package com.zixieqing.o1simplefactory.o1simplelogic.impl;
      
        import com.zixieqing.o1simplefactory.o1simplelogic.IShopping;
        import org.slf4j.Logger;
        import org.slf4j.LoggerFactory;
      
        /**
         * <p>@description  : 該類功能  具體商品:優惠卡
         * </p>
         * <p>@package      : com.zixieqing.o1simplefactory.o1simplelogic.impl</p>
         * <p>@author       : ZiXieqing</p>
         * <p>@version      : V1.0.0</p>
         */
      
        public class CouponService implements IShopping {
      
            private Logger logger = LoggerFactory.getLogger(CouponService.class);
      
            @Override
            public void sendCommodity(String userId, String goodsName) throws Exception {
                logger.info("使用者:{},透過優惠卡xxxxxx,購買了:{}", userId, goodsName);
            }
        }
      
      
  • 快播兌換卡方式

    • 
        package com.zixieqing.o1simplefactory.o1simplelogic.impl;
      
        import com.zixieqing.o1simplefactory.o1simplelogic.IShopping;
        import org.slf4j.Logger;
        import org.slf4j.LoggerFactory;
      
        /**
         * <p>@description  : 該類功能  具體商品:快播兌換卡
         * </p>
         * <p>@package      : com.zixieqing.o1simplefactory.o1simplelogic.impl</p>
         * <p>@author       : ZiXieqing</p>
         * <p>@version      : V1.0.0</p>
         */
      
        public class QvodCardService implements IShopping {
      
            private Logger logger = LoggerFactory.getLogger(QvodCardService.class);
      
            @Override
            public void sendCommodity(String userId, String goodsName) throws Exception {
                logger.info("使用者:{},透過快播兌換卡yyyyy,購買了:{}", userId, goodsName);
            }
        }
      
      
  • 實物夠買的方式

    • 
        package com.zixieqing.o1simplefactory.o1simplelogic.impl;
      
        import com.zixieqing.o1simplefactory.o1simplelogic.IShopping;
        import org.slf4j.Logger;
        import org.slf4j.LoggerFactory;
      
        /**
         * <p>@description  : 該類功能  具體商品:啥也不用,直接實物購買
         * </p>
         * <p>@package      : com.zixieqing.o1simplefactory.o1simplelogic.impl</p>
         * <p>@author       : ZiXieqing</p>
         * <p>@version      : V1.0.0</p>
         */
      
        public class GoodsService implements IShopping {
      
            private Logger logger = LoggerFactory.getLogger(GoodsService.class);
      
            @Override
            public void sendCommodity(String userId, String goodsName) throws Exception {
                logger.info("使用者:{},實物購買了:{}", userId, goodsName);
            }
        }
      
      

3、產品工廠:提供建立產品的方法,讓呼叫者透過該工廠獲取產品

package com.zixieqing.o1simplefactory.o1simplelogic;

import com.zixieqing.o1simplefactory.o1simplelogic.impl.CouponService;
import com.zixieqing.o1simplefactory.o1simplelogic.impl.GoodsService;
import com.zixieqing.o1simplefactory.o1simplelogic.impl.QvodCardService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * <p>@description  : 該類功能  產品工廠:購物工廠
 * </p>
 * <p>@package      : com.zixieqing.o1simplefactory.o1simplelogic</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public class ShoppingFactory {

    private Logger logger = LoggerFactory.getLogger(ShoppingFactory.class);

    /**
     * <p>@description  : 該方法功能 購物
     * </p>
     * <p>@methodName   : shopping</p>
     * <p>@author: ZiXieqing</p>
     * <p>@version: V1.0.0</p>
     * @param type  購物方式 1、優惠卡;2、快播兌換卡;3、實物購買
     * @return com.zixieqing.o1simplefactory.o1simplelogic.IShopping
     */
    public IShopping shopping(Integer type) {
        if (null == type) return null;

        logger.info("正在揀貨.....");

        if (1 == type) return new CouponService();

        if (2 == type) return new QvodCardService();

        if (3 == type) return new GoodsService();

        throw new RuntimeException("不存在的商品服務型別");
    }
}

  • 注:如果上面這個工廠的方法加上了static就變成了靜態工廠(靜態方法能被繼承、但不能被重寫)

4、測試:呼叫者透過工廠獲取對應產品

package com.zixieqing;

import com.zixieqing.o1simplefactory.o1simplelogic.IShopping;
import com.zixieqing.o1simplefactory.o1simplelogic.ShoppingFactory;

/**
 * <p>@description  : 該類功能  測試
 * </p>
 * <p>@package      : com.zixieqing</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public class APITest {

    public static void main(String[] args) throws Exception{

        ShoppingFactory shoppingFactory = new ShoppingFactory();

        // 1、優惠卡型別
        IShopping shopping_1 = shoppingFactory.shopping(1);
        shopping_1.sendCommodity(System.nanoTime()+"", "充氣娃娃");

        System.out.println("================華麗的分割線===================");

        // 2、快播兌換卡
        IShopping shopping_2 = shoppingFactory.shopping(2);
        shopping_2.sendCommodity(System.nanoTime()+"", "AI女票");

        System.out.println("================華麗的分割線===================");

        IShopping shopping_3 = shoppingFactory.shopping(3);
        shopping_3.sendCommodity(System.nanoTime()+"", "枸杞");

    }
}

image-20221023230202114


上面就是理解簡單工廠的邏輯,總結一丟丟

  • 簡單工廠的邏輯:
    • image-20221027102226203
  • 由上圖也可知,簡單工廠優點就是呼叫者可以免除直接建立產品物件的責任,而僅僅"消費"產品,明確責任邊界,降低耦合性,當然其缺點也很明顯
    • 1、違背了開閉原則
      • image-20221023232059889
      • image-20221027102536949
      • 所以從上圖可知:簡單工廠就是橫向發展(不斷加實現類、工廠類中不斷加邏輯判斷)

3.1.3.1.2、更加貼合開發場景的邏輯

1、先決條件:先簡單搞個返回結果集的工具類

package com.zixieqing.o1simplefactory.o2complex.util;

/**
 * <p>@description  : 該類功能  返回結果集工具類
 * </p>
 * <p>@package      : com.zixieqing.o1simplefactory.o2complex.util</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public class ResultUtil {

    /**
     * 編碼
     */
    private String code;

    /**
     * 資訊
     */
    private String info;

    public ResultUtil(String code, String info) {
        this.code = code;
        this.info = info;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getInfo() {
        return info;
    }

    public void setInfo(String info) {
        this.info = info;
    }
}


2、優惠卡服務

package com.zixieqing.o1simplefactory.o2complex.coupon;

import com.zixieqing.o1simplefactory.o2complex.util.ResultUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * <p>@description  : 該類功能  模擬發放優惠券業務
 * </p>
 * <p>@package      : com.zixieqing.o1simplefactory.o2complex.coupon</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public class CouponService {

    private Logger logger = LoggerFactory.getLogger(CouponService.class);

    /**
     * <p>@description  : 該方法功能 發放優惠券
     * </p>
     * <p>@methodName   : sendCoupon</p>
     * <p>@author: ZiXieqing</p>
     * <p>@version: V1.0.0</p>
     * @param uId 使用者id
     * @param couponNumber 分配的優惠券號碼
     * @param uuid  隨機生成的uuid號
     * @return com.zixieqing.o1simplefactory.o2complex.util.ResultUtil
     */
    public ResultUtil sendCoupon(String uId, String couponNumber, String uuid) {
        logger.info("發放優惠券業務準備啟動..........");

        logger.info("使用者:{},獲得了優惠券:{}", uId, uId + couponNumber + uuid);

        return new ResultUtil("0000", "優惠券發放成功");
    }
}


3、快播兌換卡業務

package com.zixieqing.o1simplefactory.o2complex.qvod;

import com.zixieqing.o1simplefactory.o2complex.util.ResultUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * <p>@description  : 該類功能  快播兌換卡業務
 * </p>
 * <p>@package      : com.zixieqing.o1simplefactory.o2complex.qvod</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public class QvodService {

    private Logger logger = LoggerFactory.getLogger(QvodService.class);

    /**
     * <p>@description  : 該方法功能 授予兌換卡
     * </p>
     * <p>@methodName   : grentToken</p>
     * <p>@author: ZiXieqing</p>
     * <p>@version: V1.0.0</p>
     * @param phone 使用者手機號
     * @param cardId  隨機生成的卡號
     * @return com.zixieqing.o1simplefactory.o2complex.util.ResultUtil 
     */
    public ResultUtil grentToken(String phone, String cardId) {
        logger.info("授予的兌換卡為:{}", phone + cardId);
        return new ResultUtil("0000", phone + cardId);
    }
}


4、實物購買商品業務

  • 輔助物件

    • 
        package com.zixieqing.o1simplefactory.o2complex.goods;
      
        /**
         * <p>@description  : 該類功能  實物購買:支付要求物件
         * </p>
         * <p>@package      : com.zixieqing.o1simplefactory.o2complex.goods</p>
         * <p>@author       : ZiXieqing</p>
         * <p>@version      : V1.0.0</p>
         */
      
        public class DeliverRequest {
      
      
            /**
             * 使用者姓名
             */
            private String userName;
            /**
             * 使用者手機
             */
            private String userPhone;
            /**
             * 商品SKU:庫存保有單位
             */
            private String sku;
            /**
             * 訂單ID
             */
            private String orderId;
            /**
             * 收貨人姓名
             */
            private String consigneeUserName;
            /**
             * 收貨人手機
             */
            private String consigneeUserPhone;
            /**
             * 收穫人地址
             */
            private String consigneeUserAddress;
      
            public String getUserName() {
                return userName;
            }
      
            public void setUserName(String userName) {
                this.userName = userName;
            }
      
            public String getUserPhone() {
                return userPhone;
            }
      
            public void setUserPhone(String userPhone) {
                this.userPhone = userPhone;
            }
      
            public String getSku() {
                return sku;
            }
      
            public void setSku(String sku) {
                this.sku = sku;
            }
      
            public String getOrderId() {
                return orderId;
            }
      
            public void setOrderId(String orderId) {
                this.orderId = orderId;
            }
      
            public String getConsigneeUserName() {
                return consigneeUserName;
            }
      
            public void setConsigneeUserName(String consigneeUserName) {
                this.consigneeUserName = consigneeUserName;
            }
      
            public String getConsigneeUserPhone() {
                return consigneeUserPhone;
            }
      
            public void setConsigneeUserPhone(String consigneeUserPhone) {
                this.consigneeUserPhone = consigneeUserPhone;
            }
      
            public String getConsigneeUserAddress() {
                return consigneeUserAddress;
            }
      
            public void setConsigneeUserAddress(String consigneeUserAddress) {
                this.consigneeUserAddress = consigneeUserAddress;
            }
        }
      
      
  • 業務

    •   package com.zixieqing.o1simplefactory.o2complex.goods;
      
        import com.alibaba.fastjson.JSON;
        import com.zixieqing.o1simplefactory.o2complex.util.ResultUtil;
        import org.slf4j.Logger;
        import org.slf4j.LoggerFactory;
      
        /**
         * <p>@description  : 該類功能  實物購買商品業務
         * </p>
         * <p>@package      : com.zixieqing.o1simplefactory.o2complex.goods</p>
         * <p>@author       : ZiXieqing</p>
         * <p>@version      : V1.0.0</p>
         */
      
        public class GoodsService {
      
            private Logger logger = LoggerFactory.getLogger(GoodsService.class);
      
            /**
             * <p>@description  : 該方法功能 發貨
             * </p>
             * <p>@methodName   : deliverGoods</p>
             * <p>@author: ZiXieqing</p>
             * <p>@version: V1.0.0</p>
             * @param deliverRequest  輔助:支付物件
             * @return com.zixieqing.o1simplefactory.o2complex.util.ResultUtil 
             */
            public ResultUtil deliverGoods(DeliverRequest deliverRequest) {
                logger.info("模擬傳送實物商品一個:{}", JSON.toJSONString(deliverRequest));
                return new ResultUtil("0000", "發貨成功:" + deliverRequest);
            }
        }
      
      

5、抽象產品:定義規則

package com.zixieqing.o1simplefactory.o2complex;

import java.util.Map;

/**
 * <p>@description  : 該類功能  商品
 * </p>
 * <p>@package      : com.zixieqing.o1simplefactory.o2complex</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public interface ICommodityService {

    /**
     * <p>@description  : 該方法功能 傳送商品
     * </p>
     * <p>@methodName   : sendCommodity</p>
     * <p>@author: ZiXieqing</p>
     * <p>@version: V1.0.0</p>
     * @param uId 使用者id
     * @param commodityId 商品id
     * @param bizId 業務id
     * @param extMap  擴充套件資訊
     */
    void sendCommodity(String uId, String commodityId, String bizId, Map<String, String> extMap) throws Exception;
}


6、具體商品:實現或繼承抽象產品的子類

  • 優惠卡

    •   package com.zixieqing.o1simplefactory.o2complex.impl;
      
        import com.zixieqing.o1simplefactory.o2complex.ICommodityService;
        import com.zixieqing.o1simplefactory.o2complex.coupon.CouponService;
        import com.zixieqing.o1simplefactory.o2complex.util.ResultUtil;
        import org.slf4j.Logger;
        import org.slf4j.LoggerFactory;
      
        import java.util.Map;
        import java.util.UUID;
      
        /**
         * <p>@description  : 該類功能  TODO
         * </p>
         * <p>@package      : com.zixieqing.o1simplefactory.o2complex.impl</p>
         * <p>@author       : ZiXieqing</p>
         * <p>@version      : V1.0.0</p>
         */
      
        public class CouponCommodityServiceImpl implements ICommodityService {
      
            private Logger logger = LoggerFactory.getLogger(CouponCommodityServiceImpl.class);
      
            /**
             * 模擬@autowried注入
             */
            private CouponService couponService = new CouponService();
      
            @Override
            public void sendCommodity(String uId, String commodityId, String bizId, Map<String, String> extMap) throws Exception {
                ResultUtil result = couponService.sendCoupon(uId, commodityId, UUID.randomUUID().toString());
      
                if (!"0000".equals(result.getCode()))
                    throw new RuntimeException(result.getInfo());
            }
        }
      
      
  • 快播兌換卡

    •   package com.zixieqing.o1simplefactory.o2complex.impl;
      
        import com.zixieqing.o1simplefactory.o2complex.ICommodityService;
        import com.zixieqing.o1simplefactory.o2complex.qvod.QvodService;
        import com.zixieqing.o1simplefactory.o2complex.util.ResultUtil;
        import org.slf4j.Logger;
        import org.slf4j.LoggerFactory;
      
        import java.util.Map;
      
        /**
         * <p>@description  : 該類功能  快播兌換卡發貨
         * </p>
         * <p>@package      : com.zixieqing.o1simplefactory.o2complex.impl</p>
         * <p>@author       : ZiXieqing</p>
         * <p>@version      : V1.0.0</p>
         */
      
        public class QvodCommodityServiceImpl implements ICommodityService {
      
            private Logger logger = LoggerFactory.getLogger(CouponCommodityServiceImpl.class);
      
            /**
             * 模擬注入
             */
            private QvodService qvodService = new QvodService();
      
            @Override
            public void sendCommodity(String uId, String commodityId, String bizId, Map<String, String> extMap) throws Exception {
                // 這裡把電話號碼定死,模擬而已
                ResultUtil result = qvodService.grentToken("12345678910", bizId + commodityId);
      
                logger.info("透過快播兌換卡:{},獲取商品:{}", bizId + commodityId, commodityId);
      
                if (!"0000".equals(result.getCode()))
                    throw new RuntimeException(result.getInfo());
            }
        }
      
      
  • 實物購買

    •   package com.zixieqing.o1simplefactory.o2complex.impl;
      
        import com.zixieqing.o1simplefactory.o2complex.ICommodityService;
        import com.zixieqing.o1simplefactory.o2complex.goods.DeliverRequest;
        import com.zixieqing.o1simplefactory.o2complex.goods.GoodsService;
        import com.zixieqing.o1simplefactory.o2complex.util.ResultUtil;
        import org.slf4j.Logger;
        import org.slf4j.LoggerFactory;
      
        import java.util.Map;
      
        /**
         * <p>@description  : 該類功能  實物購買商品
         * </p>
         * <p>@package      : com.zixieqing.o1simplefactory.o2complex.impl</p>
         * <p>@author       : ZiXieqing</p>
         * <p>@version      : V1.0.0</p>
         */
      
        public class GoodsCommodityServiceImpl implements ICommodityService {
      
            private Logger logger = LoggerFactory.getLogger(GoodsCommodityServiceImpl.class);
      
            /**
             * 模擬注入
             */
            private GoodsService goodsService = new GoodsService();
      
            @Override
            public void sendCommodity(String uId, String commodityId, String bizId, Map<String, String> extMap) throws Exception {
                DeliverRequest deliverRequest = new DeliverRequest();
                // 下面這些資訊去資料庫搞出來
                deliverRequest.setUserName("紫邪情");
                deliverRequest.setUserPhone("123143124342");
                deliverRequest.setSku(commodityId);
                deliverRequest.setOrderId(bizId);
                deliverRequest.setConsigneeUserName(extMap.get("consigneeUserName"));
                deliverRequest.setConsigneeUserPhone(extMap.get("consigneeUserPhone"));
                deliverRequest.setConsigneeUserAddress(extMap.get("consigneeUserAddress"));
      
                ResultUtil result = goodsService.deliverGoods(deliverRequest);
      
                if (!"0000".equals(result.getCode()))
                    throw new RuntimeException(result.getInfo());
            }
        }
      
      

7、產品工廠:提供建立產品的方法,讓呼叫者透過該方法獲取產品

package com.zixieqing.o1simplefactory.o2complex;

import com.zixieqing.o1simplefactory.o2complex.impl.CouponCommodityServiceImpl;
import com.zixieqing.o1simplefactory.o2complex.impl.GoodsCommodityServiceImpl;
import com.zixieqing.o1simplefactory.o2complex.impl.QvodCommodityServiceImpl;

/**
 * <p>@description  : 該類功能  產品工廠
 * </p>
 * <p>@package      : com.zixieqing.o1simplefactory.o2complex</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public class CommodityFactory {

    /**
     * <p>@description  : 該方法功能 獲取產品
     * </p>
     * <p>@methodName   : getCommodity</p>
     * <p>@author: ZiXieqing</p>
     * <p>@version: V1.0.0</p>
     * @param type  產品型別
     * @return com.zixieqing.o1simplefactory.o2complex.ICommodityService 
     */
    public ICommodityService getCommodity(Integer type) {
        if (1 == type) return new CouponCommodityServiceImpl();

        if (2 == type) return new QvodCommodityServiceImpl();

        if (3 == type) return new GoodsCommodityServiceImpl();

        throw new RuntimeException("不合法的商品型別");
    }
}


8、測試

package com.zixieqing;

import com.zixieqing.o1simplefactory.o1simplelogic.IShopping;
import com.zixieqing.o1simplefactory.o1simplelogic.ShoppingFactory;
import com.zixieqing.o1simplefactory.o2complex.CommodityFactory;
import com.zixieqing.o1simplefactory.o2complex.ICommodityService;

import java.util.UUID;

/**
 * <p>@description  : 該類功能  測試
 * </p>
 * <p>@package      : com.zixieqing</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public class APITest {

    public static void main(String[] args) throws Exception{

        System.out.println("================華麗的分割線:簡單結合業務開發進行測試===================");

        CommodityFactory commodityFactory = new CommodityFactory();

        // 1、優惠卡型別
        ICommodityService commodity_1 = commodityFactory.getCommodity(1);
        commodity_1.sendCommodity(System.nanoTime() + "",
                System.currentTimeMillis() + "",
                UUID.randomUUID() + "",
                null);

        // 2、快播兌換卡
        ICommodityService commodity_2 = commodityFactory.getCommodity(2);
        commodity_2.sendCommodity(System.nanoTime() + "",
                System.currentTimeMillis() + "",
                UUID.randomUUID() + "",
                null);

        // 3、實物購買
        ICommodityService commodity_3 = commodityFactory.getCommodity(3);
        HashMap<String, String> extMap = new HashMap<>();
        extMap.put("consigneeUserName", "紫邪情");
        extMap.put("consigneeUserPhone", "31343214321432");
        extMap.put("consigneeUserAddress", "浙江省.杭州市.餘杭區.XX街道.YY小區.324134321431");
        commodity_3.sendCommodity(System.nanoTime() + "",
                System.currentTimeMillis() + "",
                UUID.randomUUID() + "",
                extMap);
    }
}

image-20221024142958332


3.1.3.1.3、構建出簡單工廠的方式

廢話文學

  • 1、思考要建立的幾個例項(具體產品)之間有什麼共通性
  • 2、將多個例項的共通性抽取成一個介面(抽象產品)
  • 3、使用一個工廠來對建立的例項進行判斷,從而讓呼叫者根據條件得到想要的例項物件(還可以結合反射來建立物件)
  • 4、然後根據下圖進行架構即可

image-20221027102226203


3.1.3.2、工廠方法模式

定義:定義一個建立物件的介面,但由子類(具體工廠)決定要例項化的類是哪一個。工廠方法讓類把例項化推遲到子類
解決的問題:明確地計劃不同條件下建立不同例項時就可以考慮是否採用工廠模式,從而提高擴充套件性,減少以後的改程式碼量


廢話文學:

  • 工廠方法模式的角色

    • 抽象工廠角色:工廠方法模式的核心,是具體工廠角色必須實現的介面或者繼承的父類
    • 具體工廠角色:含有和具體業務邏輯有關的程式碼,由業務呼叫建立對應的具體產品的物件
    • 抽象產品角色:是具體產品繼承的父類或者是實現的介面
    • 具體產品角色:具體工廠角色所建立的物件就是此角色的例項
  • 工廠方法模式這玩意兒其實就是在簡單工廠的基礎上稍微變了一下而已,多了一層罷了(沒什麼是加一層解決不了的,一層不行,那就再來一層),這裡加的這一層就是對業務層再抽象了一下而已

  • 簡單工廠是橫向發展(不斷橫向新增實現類),而工廠方法模式本質是為了解決簡單工廠模式的問題(違背開閉原則),所以優點和簡單工廠模式一樣,其是縱向發展(不斷縱向新增工廠類+實現類)

  • 簡單工廠模式

    • image-20221027102226203
  • 工廠方法模式:

    • image-20221027105341110
    • 從上圖可以看出:工廠方法模式做的是同一類產品(一條流水線開機時不會還搞多產品加工吧),這個點就是和下面抽象工廠的最大區別(抽象工廠針對的是產品簇)

3.1.3.2.1、Java中使用工廠方法模式的地方

這個東西其實在剛剛前面的程式碼中有一個東西就用到了,即:SLF4J日誌門面,前面用了一個LoggerFactory,它裡面就用到了工廠方法模式

1、進入getLogger()

// 進入getLogger()
private Logger logger = LoggerFactory.getLogger(QvodService.class);


// 得到的程式碼
    public static Logger getLogger(Class clazz) {
        // 進入這裡的getLogger()
        return getLogger(clazz.getName());
    }

// 得到程式碼
    public static Logger getLogger(String name) {
        // 再看一下getILoggerFactory()
        ILoggerFactory iLoggerFactory = getILoggerFactory();
        return iLoggerFactory.getLogger(name);
    }

// getILoggerFactory()的程式碼
    public static ILoggerFactory getILoggerFactory() {
        if (INITIALIZATION_STATE == 0) {
            INITIALIZATION_STATE = 1;
            performInitialization();
        }

        switch(INITIALIZATION_STATE) {
        case 1:
			// 重點1、static SubstituteLoggerFactory TEMP_FACTORY = new SubstituteLoggerFactory();
            return TEMP_FACTORY;
        case 2:
            throw new IllegalStateException("org.slf4j.LoggerFactory could not be successfully initialized. See also http://www.slf4j.org/codes.html#unsuccessfulInit");
        case 3:
            return StaticLoggerBinder.getSingleton().getLoggerFactory();
        case 4:
			// 重點2、static NOPLoggerFactory NOP_FALLBACK_FACTORY = new NOPLoggerFactory();
            return NOP_FALLBACK_FACTORY;
        default:
            throw new IllegalStateException("Unreachable code");
        }
    }

  • 上面的兩個:TEMP_FACTORYNOP_FALLBACK_FACTORY都實現了ILoggerFactory

2、看一眼ILoggerFactory

// 這個就是根工廠:定義最大規則的那個叼毛
public interface ILoggerFactory {
    Logger getLogger(String var1);
}


逆向回來,邏輯就變成如下的樣子

  • image

廢話文學

  • 在開發中怎麼使用工廠方法模式(下列邏輯不止適用於此模式,建立型、行為型、結構型均適合,瞭解每種型別針對的是對什麼做架構即可,如:是對類中方法、還是類本身等等做架構來進行抽離,最後時間長了一看需求就知道咋個設計了)

    • 1、思考自己要建立的幾個例項( 即:具體產品)有什麼共通性

    • 2、將共通性抽取出來變成一個介面或抽象類(即:抽象產品)

    • 3、思考對每一個例項(產品)弄一個工廠類之後(即:具體工廠),那這幾個工廠類之間又有什麼共通性

    • 4、將工廠類的共通性抽取出來變成一個介面(即:抽象工廠)

    • 5、當然:再考慮以後應該會在什麼地方新增功能、是否會擴充套件的話更好,但這一條需要經驗積累

    • 6、然後使用下圖方式做架構即可

image-20221027105341110


3.1.3.2.2、例項

場景:檔案解析,假設有兩種:1、xml檔案解析;2、json檔案解析

邏輯如下

  • image-20221026120019726

1、抽象產品

package com.zixieqing.o2factorymethod;

/**
 * <p>@description  : 該類功能  檔案解析器
 * </p>
 * <p>@package      : com.zixieqing.o2factorymethod</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public interface IFileParser {

    String parse();
}


2、具體產品

  • json解析

    • 
        package com.zixieqing.o2factorymethod.impl;
      
        import com.zixieqing.o2factorymethod.IFileParser;
      
        /**
         * <p>@description  : 該類功能  json檔案解析器
         * </p>
         * <p>@package      : com.zixieqing.o2factorymethod.impl</p>
         * <p>@author       : ZiXieqing</p>
         * <p>@version      : V1.0.0</p>
         */
      
        public class JsonFileParse implements IFileParser {
            @Override
            public String parse() {
                return "這裡就搞json檔案解析的邏輯";
            }
        }
      
      
  • xml解析

    • 
        package com.zixieqing.o2factorymethod.impl;
      
        import com.zixieqing.o2factorymethod.IFileParser;
      
        /**
         * <p>@description  : 該類功能  xml檔案解析器
         * </p>
         * <p>@package      : com.zixieqing.o2factorymethod</p>
         * <p>@author       : ZiXieqing</p>
         * <p>@version      : V1.0.0</p>
         */
      
        public class XMLFileParse implements IFileParser {
            @Override
            public String parse() {
                return "這裡就搞xml檔案的解析邏輯";
            }
        }
      
      

3、抽象工廠

package com.zixieqing.o2factorymethod.factory;

import com.zixieqing.o2factorymethod.IFileParser;

/**
 * <p>@description  : 該類功能  解析器抽象工廠
 * </p>
 * <p>@package      : com.zixieqing.o2factorymethod</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public interface IParserFactory {

    IFileParser parse();
}


4、具體工廠

  • json解析工廠

    • 
        package com.zixieqing.o2factorymethod.factory.impl;
      
        import com.zixieqing.o2factorymethod.IFileParser;
        import com.zixieqing.o2factorymethod.factory.IParserFactory;
        import com.zixieqing.o2factorymethod.impl.JsonFileParse;
      
        /**
         * <p>@description  : 該類功能  json解析工廠
         * </p>
         * <p>@package      : com.zixieqing.o2factorymethod.factory</p>
         * <p>@author       : ZiXieqing</p>
         * <p>@version      : V1.0.0</p>
         */
      
        public class JsonParseFactory implements IParserFactory {
      
            @Override
            public IFileParser parse() {
                return new JsonFileParse();
            }
        }
      
      
  • xml解析工廠

    • 
        package com.zixieqing.o2factorymethod.factory.impl;
      
        import com.zixieqing.o2factorymethod.IFileParser;
        import com.zixieqing.o2factorymethod.factory.IParserFactory;
        import com.zixieqing.o2factorymethod.impl.XMLFileParse;
      
        /**
         * <p>@description  : 該類功能  xml解析工廠
         * </p>
         * <p>@package      : com.zixieqing.o2factorymethod.factory</p>
         * <p>@author       : ZiXieqing</p>
         * <p>@version      : V1.0.0</p>
         */
      
        public class XMLParseFactory implements IParserFactory {
            @Override
            public IFileParser parse() {
                return new XMLFileParse();
            }
        }
      
      

3.1.3.3、抽象工廠模式

定義:在工廠方法模式的基礎上,對工廠進行變化一下

解決的問題:產品簇的問題(一箇中心工廠來建立其他工廠)

對於產品簇百度有一個通俗易懂的解釋:指具有相同或相似的功能結構或效能,共享主要的產品特徵、元件或子結構,並透過變型配置來滿足特定市場的一組產品的聚類

廢話文學對產品簇的解釋:產品簇是指工廠生產出的產品們之間彼此具備強關聯。比如:AK47工廠生產的 AK47步槍、AK47專配的子彈,一旦 AK47裝錯了子彈是無法正常開槍的(甚至會炸膛)

抽象工廠模式的角色

  • 抽象工廠角色:工廠方法模式的核心,是具體工廠角色必須實現的介面或者繼承的父類
  • 具體工廠角色:含有和具體業務邏輯有關的程式碼,由業務呼叫建立對應的具體產品的物件
  • 抽象產品角色:是具體產品繼承的父類或者是實現的介面
  • 具體產品角色:具體工廠角色所建立的物件就是此角色的例項

抽象工廠模式邏輯舉例(上面的四角色自行對應)

image-20221026164247527


3.1.3.3.1、簡單邏輯實現

就用上面說的傢俱來舉例


1、椅子介面(抽象產品)

package com.zixieqing.o3abstractfactory;

/**
 * <p>@description  : 該類功能  抽象產品:椅子介面
 * </p>
 * <p>@package      : com.zixieqing.o3abstractfactory</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public interface IChair {

    /**
     * <p>@description  : 該方法功能 生產椅子
     * </p>
     * <p>@methodName   : createChair</p>
     * <p>@author: ZiXieqing</p>
     * <p>@version: V1.0.0</p>
     *
     * @return java.lang.String
     */
    String create();
}

  • 1.1、木質桌子(具體產品)
package com.zixieqing.o3abstractfactory.impl;

import com.zixieqing.o3abstractfactory.IChair;

/**
 * <p>@description  : 該類功能  具體產品:木質椅子
 * </p>
 * <p>@package      : com.zixieqing.o3abstractfactory.impl</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public class WoodenChair implements IChair {
    @Override
    public String create() {
        return "木質椅子";
    }
}

  • 1.2、塑膠桌子(具體產品)
package com.zixieqing.o3abstractfactory.impl;

import com.zixieqing.o3abstractfactory.IChair;

/**
 * <p>@description  : 該類功能  具體產品:塑膠椅子
 * </p>
 * <p>@package      : com.zixieqing.o3abstractfactory.impl</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public class PlasticChair implements IChair {
    @Override
    public String create() {
        return "塑膠椅子";
    }
}


2、桌子介面(抽象產品)

package com.zixieqing.o3abstractfactory;

/**
 * <p>@description  : 該類功能  抽象產品:桌子介面
 * </p>
 * <p>@package      : com.zixieqing.o3abstractfactory</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public interface IDesk {

    /**
     * <p>@description  : 該方法功能 生產桌子
     * </p>
     * <p>@methodName   : create</p>
     * <p>@author: ZiXieqing</p>
     * <p>@version: V1.0.0</p>
     *
     * @return java.lang.String
     */
    String create();

}

  • 木質桌子
package com.zixieqing.o3abstractfactory.impl;

import com.zixieqing.o3abstractfactory.IDesk;

/**
 * <p>@description  : 該類功能  具體產品:木質桌子
 * </p>
 * <p>@package      : com.zixieqing.o3abstractfactory.impl</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public class WoodenDesk implements IDesk {
    @Override
    public String create() {
        return "木質桌子";
    }
}

  • 塑膠桌子
package com.zixieqing.o3abstractfactory.impl;

import com.zixieqing.o3abstractfactory.IDesk;

/**
 * <p>@description  : 該類功能  具體產品:塑膠桌子
 * </p>
 * <p>@package      : com.zixieqing.o3abstractfactory.impl</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public class PlastidDeck implements IDesk {
    @Override
    public String create() {
        return "塑膠桌子";
    }
}


3、傢俱抽象工廠

package com.zixieqing.o3abstractfactory;

/**
 * <p>@description  : 該類功能  抽象工廠:傢俱工廠
 * </p>
 * <p>@package      : com.zixieqing.o3abstractfactory</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public interface IFurnitureFactory {

    IChair createChair();

    IDesk createDesk();
}

  • 木質傢俱工廠
package com.zixieqing.o3abstractfactory.factory;

import com.zixieqing.o3abstractfactory.IChair;
import com.zixieqing.o3abstractfactory.IDesk;
import com.zixieqing.o3abstractfactory.IFurnitureFactory;
import com.zixieqing.o3abstractfactory.impl.WoodenChair;
import com.zixieqing.o3abstractfactory.impl.WoodenDesk;

/**
 * <p>@description  : 該類功能  具體工廠:專門生產木質傢俱這一產品簇需要的東西
 * </p>
 * <p>@package      : com.zixieqing.o3abstractfactory.factory</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public class WoodenIFurnitureFactory implements IFurnitureFactory {
    @Override
    public IChair createChair() {
        return new WoodenChair();
    }

    @Override
    public IDesk createDesk() {
        return new WoodenDesk();
    }
}

  • 塑膠傢俱工廠
package com.zixieqing.o3abstractfactory.factory;

import com.zixieqing.o3abstractfactory.IChair;
import com.zixieqing.o3abstractfactory.IDesk;
import com.zixieqing.o3abstractfactory.IFurnitureFactory;
import com.zixieqing.o3abstractfactory.impl.PlasticChair;
import com.zixieqing.o3abstractfactory.impl.PlastidDeck;

/**
 * <p>@description  : 該類功能  具體工廠:專門生產塑膠傢俱這一產品簇需要的東西
 * </p>
 * <p>@package      : com.zixieqing.o3abstractfactory</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public class PlasticFurnitureFactory implements IFurnitureFactory {
    @Override
    public IChair createChair() {
        return new PlasticChair();
    }

    @Override
    public IDesk createDesk() {
        return new PlastidDeck();
    }
}


4、測試

package com.zixieqing;

import com.zixieqing.o3abstractfactory.factory.PlasticFurnitureFactory;
import com.zixieqing.o3abstractfactory.factory.WoodenIFurnitureFactory;

/**
 * <p>@description  : 該類功能  測試
 * </p>
 * <p>@package      : com.zixieqing</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public class AbstractFactoryTest {

    public static void main(String[] args) {
        // 木質椅子
        String chair = new WoodenIFurnitureFactory().createChair().create();
        // 木質桌子
        String desk = new PlasticFurnitureFactory().createDesk().create();

        System.out.println(chair + "+" +desk);

        // 想要塑膠傢俱這個產品簇的東西,就去找塑膠傢俱工廠即可
    }
}


3.1.3.2、工廠方法VS抽象工廠模式

廢話文學

  • 1、首先兩個模式都可以生產產品 / 建立例項

  • 2、其次看前面的定義:

    • 工廠方法針對的是“某種產品”,繼而成為“某種產品A” 對應 “某工廠A”
    • 抽象工廠針對的是“某類產品”,繼而變成“某工廠B” 生產 “某類產品”(可以得到這類產品的例項,也可以得到這類產品的零部件)
  • 3、情景理解:

    • 工廠方法模式就像華為手機專賣店一樣,賣的是這一種手機
    • 抽象工廠模式就像手機售賣店一樣,可以買到華為、小米等手機,同時也可以根據自己需要買到自己手機的零部件
  • 4、在場景上(注意看下圖,兩個很像的,但注意看下圖中抽象工廠、抽象產品這兩個地方):

    • 工廠方法模式:它是讓某一條產品線上的產品更易被擴充套件(要擴充套件就是下圖加工廠和實現類唄)
      • image-20221027163542069
    • 抽象工廠模式:它是讓多條產品線中,既保證這多條產品線中的單條產品線的產品更易被擴充套件的同時,又保證多條產品線之間遵循著同樣的約束
      • image-20221027172232726
      • image-20221027164314390

* 3.1.4、建造者模式

定義:將一個複雜物件的構建與表示分離,使得同樣的構建過程可以建立不同的表示;另外:建造者模式又叫生成器模式

換言之:將多個簡單物件透過一步一步組裝,最後變成複雜物件的過程;同時:簡單物件之間的組裝(相同的物料)透過相似的組裝過程可以得到不一樣的複雜物件(即:相同 / 相似的物料,不同的繁瑣組裝,得到不同的物件[此物件是一個複雜物件組合起來的])

典型例子:計算機有顯示器、滑鼠、鍵盤等等部件,不同使用者想要的電腦樣子、配置不一樣,但都只需要和售賣員講一下自己要的,最後店鋪就會組裝好使用者想要的電腦給使用者

注意:核心點是“相同 或 相似的組成成分”,即:建立物件需要很多步驟,但是步驟的順序不一定固定,這種場景才是使用建造者模式的最佳場景(開發經典例子:一個類的內部結構很複雜,如有很多屬性時,就可以考慮用建造者模式改造一下)


建造者角色

  • 產品(Product):就是最終要建立的產品物件
  • 抽象建造者(Builder):規範產品物件的各個組成部分的建造;可以是介面或抽象類;相當於:老闆,專門約束下級的
  • 具體建造者(ConcreteBuilder):實現或繼承抽象建造者的子類;是建立產品物件的執行者;相當於:真正幹活的人
  • 指揮者(Director):負責產品物件的構建次序(注:這個叼毛可有可無),就相當於:有圖紙的工程師;它和抽象建造者是關聯關係(即:這裡面的方法返回值型別是抽象建造者型別)
    • 可有可無的原因:呼叫者萬一就是個懂行的,那還要專門找個看圖紙的幹嘛,直接上場指揮產品應該按什麼順序構建就可以了
    • 由於這個指揮者可有可無,所以這個建造者模式的寫法相當靈活,建造者模式的核心是抽象建造者和具體建造者,其他的隨意變(結合其他設計模式、結合自己認為不影響邏輯的設計都可以),要麼介面+實現類,要麼抽象類+繼承類,但是也得注意:抽象建造者裡面的成員需要滿足的條件,即:方法或屬性的設計(換言之:就是依賴倒置原則,只是對屬性或方法進行了要求,從而做到讓物件的構建獨立(產品物件的屬性構建順序:可以透過指揮者、也可以直接完全交給呼叫者),表示分離(直接交給具體構建者,最後得到一個產品物件即可)

3.1.4.1、簡單邏輯

3.1.4.1.1、有指揮者的情況

注:以下邏輯不是固定的,可以隨意變化

  • 大體類圖如下

image-20221102175358584

  • 1-1、產品物件
package com.zixieqing.o1simple;

/**
 * <p>@description  : 該類功能  產品物件:電腦
 * </p>
 * <p>@package      : com.zixieqing.o1simple</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public class Computer {

    /**
     * 電腦主機
     */
    private String host;

    /**
     * 電腦顯示器
     */
    private String monitor;

    /**
     * 擴充套件資訊
     */
    private String extendMap;

    @Override
    public String toString() {
        return "Computer{" +
                "host='" + host + '\'' +
                ", monitor='" + monitor + '\'' +
                ", extendMap='" + extendMap + '\'' +
                '}';
    }

    public void setHost(String host) {
        this.host = host;
    }

    public void setMonitor(String monitor) {
        this.monitor = monitor;
    }

    public void setExtendMap(String extendMap) {
        this.extendMap = extendMap;
    }
}

  • 1-2、抽象建造者
package com.zixieqing.o1simple.builder;

import com.zixieqing.o1simple.Computer;

/**
 * <p>@description  : 該類功能  抽象建造者:老闆 約束下級
 * </p>
 * <p>@package      : com.zixieqing.o1simple</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public interface IComputerBuilder {

    IComputerBuilder buildHost();

    IComputerBuilder buildMonitor();

    IComputerBuilder buildExtendMap();

    Computer getComputer();
}

  • 1-3、具體建造者
package com.zixieqing.o1simple.builder;

import com.zixieqing.o1simple.Computer;

/**
 * <p>@description  : 該類功能  具體建造者:幹活的員工
 * </p>
 * <p>@package      : com.zixieqing.o1simple.builder</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public class ComputerBuilder implements IComputerBuilder{

    /**
     * 組合產品物件
     */
    private Computer computer = new Computer();


    /**
     * 產品物件零部件:建造主機
     */
    @Override
    public ComputerBuilder buildHost() {
        computer.setHost("主機配置");
      /*
        返回this就是成為鏈式呼叫的關鍵,如:
        computerBuilder.buildMonitor()
                        .buildHost()
                        .buildExtendMap()
                        .getComputer();
      */
        return this;
    }

    /**
     * 產品物件零部件:顯示器型別
     */
    @Override
    public ComputerBuilder buildMonitor() {
        computer.setMonitor("顯示器型別");
        return this;
    }

    /**
     * 產品物件零部件:額外資訊
     */
    @Override
    public ComputerBuilder buildExtendMap() {
        computer.setExtendMap("額外資訊");
        return this;
    }

    /**
     * 返回構建好的複雜物件 / 產品物件
     */
    @Override
    public Computer getComputer() {
        return computer;
    }
}

  • 1-4、指揮者
package com.zixieqing.o1simple.builder;

import com.zixieqing.o1simple.Computer;

/**
 * <p>@description  : 該類功能  指揮者:拿圖紙的工程師,負責產品物件的構建順序
 * </p>
 * <p>@package      : com.zixieqing.o1simple.builder</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public class ComputerDirector {

    private static IComputerBuilder computerBuilder = new ComputerBuilder();

    public static Computer getComputer() {
        // 1、先搞顯示器
        return computerBuilder.buildMonitor()
                // 在搞主機配置(這裡能夠進行鏈式呼叫,就是因為具體建造者中建造各部件(屬性)時使用了return this的原因)
                .buildHost()
                // 最後弄其他配置資訊
                .buildExtendMap()
                .getComputer();
    }
}

  • 1-5、測試
package com.zixieqing;

import com.zixieqing.o1simple.builder.ComputerDirector;

/**
 * <p>@description  : 該類功能  測試
 * </p>
 * <p>@package      : com.zixieqing</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public class APITest {

    public static void main(String[] args) {

        System.out.println(ComputerDirector.getComputer());
    }
}

  • 1-6、結果
Computer{host='主機配置', monitor='顯示器型別', extendMap='額外資訊'}


3.1.4.1.2、沒有指揮者的情況

有的時候會面臨一種情況

image-20221031221043881

  • 上面這個指揮者中的程式碼多此一舉了,這種應該直接交給呼叫者就行了,由呼叫者自己來安排產品物件的構建順序是怎麼樣的,因此:稍微變一下,把指揮者去掉,然後呼叫者直接去找建造者即可(抽象建造者、具體建造者程式碼不變,直接去掉指揮者),那呼叫就變成如下的樣子
package com.zixieqing;

import com.zixieqing.o1simple.Computer;
import com.zixieqing.o1simple.builder.ComputerBuilder;
import com.zixieqing.o1simple.builder.ComputerDirector;
import com.zixieqing.o1simple.builder.IComputerBuilder;

/**
 * <p>@description  : 該類功能  測試
 * </p>
 * <p>@package      : com.zixieqing</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public class APITest {

    public static void main(String[] args) {

        IComputerBuilder computerBuilder = new ComputerBuilder();

        Computer computer = computerBuilder.buildHost()
                .buildExtendMap()
                .buildMonitor()
                .getComputer();

        System.out.println(computer);
    }
}

  • 看到上面這個樣子是不是感覺熟悉了,就是JDK中StringBuilderappend()這個API的設計模式,當然:要是使用過MyBatis的xml開發模式,那裡面有一個使用SqlSessionFactoryBuilder來獲取SqlSessionFactory,這裡也用到了這個設計模式,甚至Spring底層的BeanDefinitionBuilder透過getBeanDefinition獲取BeanDefinition物件也用到了
        StringBuilder builder = new StringBuilder();

        builder.append("張三")
               .append("李四")
               .append("大刀王五")
               .append("王麻子");

  • 看到上面這種是自己傳值進去的,所以抽象建造者就又可以變一下了唄(產品物件的各部件物件的構建,值優呼叫者傳進去),如:
package com.zixieqing.o1simple.builder;

import com.zixieqing.o1simple.Computer;

/**
 * <p>@description  : 該類功能  抽象建造者:老闆 約束下級
 * </p>
 * <p>@package      : com.zixieqing.o1simple</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public interface IComputerBuilder {

    // 這樣甩手掌櫃就可以甩得稍微徹底點了
    IComputerBuilder buildHost(String host);

    IComputerBuilder buildMonitor(String monitor);

    IComputerBuilder buildExtendMap(String extendMap);

    Computer getComputer();
}

  • 甚至說:要是是類似於SpringBoot的自動轉配呢?需要的一些東西在配置檔案中,可不可以結合工廠模式去進行載入呢(單例模式隨便一個地方都可以扔進去)?

相關文章