(Java)设计模式:创建型

发布时间 2023-03-22 19:05:46作者: 紫邪情

前言

这篇内容是从另一篇:UML建模、设计原则 中分离出来的,原本这个创建型设计模式是和其放在一起的

但是:把这篇创建型设计模式放在一起让我贼别扭,看起来贼不舒服,越看念头越不通达,导致老衲躺在床上脑海中冒出来时都睡不着了

因此:最后实在受不了了,还是将其抽离出来


3、设计模式

分类:

  • image

  • 注:使用设计模式的规范,类名 = 需求名+使用的对应设计模式名,如:StringBuilder,这就是使用了Builder建造者模式

  • 设计模式不是一成不变的,主要是思想,至于形不需要在意,形只是便于理解罢了

  • 若是想要知道Java什么地方用到了对应模式,那就去Spring中直接全局搜索模式的名字,就会出来一堆的实现类,Spring中基本涵盖了所有设计模式


3.1、创建型

这个类型的模式是专门针对于创建对象的,也就是它的适用机制
换言之:这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用 new 运算符直接实例化对象


3.1.1、singleton 单例模式

定义:保证对象全局唯一,即:保证一个类只有一个实例,哪怕是多线程来进行访问,向外提供一个访问此实例的方法即可


使用场景

  • 1、数据库连接池不会反复创建
  • 2、Spring中一个单例模式Bean的创建
  • 3、开发中设置一些全局的属性进行保存(当然:用Redis更好)

3.1.1.1、static实现

View Code

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<>();
}

  • 这种方式在第一次运行时就初始化Map了,不需要延迟加载
  • 缺点:需要被继承 或 需要维持一些特定状态时就不适合了

3.1.1.2、懒汉模式

定义:体现在一个“懒”字上,即:需要时才去创建对象


View Code

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 (null != LAZY_SINGLETON_INSTANCE) return LAZY_SINGLETON_INSTANCE;

        LAZY_SINGLETON_INSTANCE = new LazySingleton();
        return LAZY_SINGLETON_INSTANCE;

    }

    /**
     * 4、要想稍微安全就加synchronized同步锁
     * 但是:此种方式因为把synchronized加在了方法上,导致所有访问争锁而出现 资源的浪费
     */
/*    public static synchronized lazy_unsafe_singleton getInstance() {
        if (null != LAZY_UNSAFE_INSTANCE_SINGLETON) return LAZY_UNSAFE_INSTANCE_SINGLETON;

        LAZY_UNSAFE_INSTANCE_SINGLETON = new Singleton_lazy_unsafe();
        return LAZY_UNSAFE_INSTANCE_SINGLETON;

    }*/
}


3.1.1.3、饿汉模式

定义:体现在“饿”字上,即:一开始就初始化


View Code

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判断时如果在同一时刻二者都判断成立为null,就会创建不同的实例
     */
    public static HungerSingleton getInstance() {
        if (null != HUNGER_SINGLETON_INSTANCE) return HUNGER_SINGLETON_INSTANCE;

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


3.1.1.4、内部类

View Code

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、双重锁验证

View Code

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

View Code

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的忙等   从而造成:如果一直没有获取就会处于死循坏当中
         */
        for (;;) {
            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、枚举

View Code

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、protoType 原型模式

定义:根据一个已有对象(原型实例) 创建 新的对象(就是克隆)

解决的问题:创建重复对象,而这部分对象本身比较复杂,生成过程从库或者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对象,修改之后不会影响原对象

View Code

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;
    }
}


View Code

package com.zixieqing;


/**
 * <p>@description  : 该类功能  测试
 * </p>
 * <p>@package      : com.zixieqing</p>
 * <p>@author       : ZiXieqing</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、克隆引用数据类型
  • 浅拷贝 会将 原对象的引用数据类型的地址 赋值给新对象的成员变量(拷贝的是地址)。也就是说 两个对象共享了同一个数据。当其中一个对象修改成员变量的值时,另外一个的值也会随之改变
View Code

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;
    }
}


View Code

package com.zixieqing;


/**
 * <p>@description  : 该类功能  测试
 * </p>
 * <p>@package      : com.zixieqing</p>
 * <p>@author       : ZiXieqing</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、使用序列化(推荐)

View Code

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、factory 工厂模式

3.1.3.1、简单工厂(工厂基础)

定义:把对类的创建初始化全都交给一个工厂来执行,而用户不需要关心创建的过程是什么样的,只需要告诉工厂,我想要什么就行了

设计模式中并没有所谓的简单工厂,这玩意儿严格来说是一种编码规范,但是:也是学工厂模式的基础


简单工厂的角色

  • 抽象产品:定义产品的规则,即产品有哪些特性和功能,可以是接口、抽象类、普通类也行(但一般不会这么干)
  • 具体产品:实现或继承抽象产品的子类
  • 产品工厂:提供创建产品的方法,让使用者通过该方法获取产品

简单工厂逻辑草图

image-20230106170150396


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、抽象产品:定义规则

View Code

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、具体产品:实现或继承抽象产品的子类

  • 优惠卡方式
View Code

        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);
            }
        }

  • 快播兑换卡方式
View Code

        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);
            }
        }

  • 实物够买的方式
View Code

        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、产品工厂:提供创建产品的方法,让调用者通过该工厂获取产品

View Code

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、测试:调用者通过工厂获取对应产品

View Code

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、先决条件:先简单搞个返回结果集的工具类

View Code

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、优惠卡服务

View Code

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、快播兑换卡业务

View Code

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、实物购买商品业务

  • 辅助对象
View Code

        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;
            }
        }

  • 业务
View Code
        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、抽象产品:定义规则

View Code

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、具体商品:实现或继承抽象产品的子类

  • 优惠卡
View Code
        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());
            }
        }

  • 快播兑换卡
View Code
        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());
            }
        }

  • 实物购买
View Code
        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、产品工厂:提供创建产品的方法,让调用者通过该方法获取产品

View Code

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、测试

View Code

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、factory method 工厂方法模式

定义:定义一个创建对象的接口,但由子类(具体工厂)决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类
解决的问题:明确地计划不同条件下创建不同实例时就可以考虑是否采用工厂模式,从而提高扩展性,减少以后的改代码量


废话文学:

  • 工厂方法模式的角色

    • 抽象工厂角色:工厂方法模式的核心,是具体工厂角色必须实现的接口或者继承的父类
    • 具体工厂角色:含有和具体业务逻辑有关的代码,由业务调用创建对应的具体产品的对象
    • 抽象产品角色:是具体产品继承的父类或者是实现的接口
    • 具体产品角色:具体工厂角色所创建的对象就是此角色的实例
  • 工厂方法模式这玩意儿其实就是在简单工厂的基础上稍微变了一下而已,多了一层罢了(没什么是加一层解决不了的,一层不行,那就再来一层),这里加的这一层就是对业务层再抽象了一下而已

  • 简单工厂是纵向发展(不断添加实现类),而工厂方法模式本质是为了解决简单工厂模式的问题(违背开闭原则),所以优点和简单工厂模式一样,其是横向发展(不断添加工厂类+实现类)

  • 简单工厂模式

    • 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、抽象产品

View Code

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解析
View Code

        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解析
View Code

        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、抽象工厂

View Code

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解析工厂
View Code

        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解析工厂
View Code

        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、abstract factory 抽象工厂模式

定义:在工厂方法模式的基础上,对工厂进行变化一下

解决的问题:产品簇的问题(一个中心工厂来创建其他工厂)

对于产品簇百度有一个通俗易懂的解释:指具有相同或相似的功能结构或性能,共享主要的产品特征、组件或子结构,并通过变型配置来满足特定市场的一组产品的聚类

废话文学对产品簇的解释:产品簇是指工厂生产出的产品们之间彼此具备强关联。比如:AK47工厂生产的 AK47步枪、AK47专配的子弹,一旦 AK47装错了子弹是无法正常开枪的(甚至会炸膛)


抽象工厂模式的角色

  • 抽象工厂角色:工厂方法模式的核心,是具体工厂角色必须实现的接口或者继承的父类
  • 具体工厂角色:含有和具体业务逻辑有关的代码,由业务调用创建对应的具体产品的对象
  • 抽象产品角色:是具体产品继承的父类或者是实现的接口
  • 具体产品角色:具体工厂角色所创建的对象就是此角色的实例

抽象工厂模式逻辑举例(上面的四角色自行对应)

image-20221026164247527


3.1.3.3.1、简单逻辑实现

就用上面说的家具来举例


1、椅子接口(抽象产品)

View Code

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、木质桌子(具体产品)
View Code

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、塑料桌子(具体产品)
View Code

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、桌子接口(抽象产品)

View Code

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();

}

  • 木质桌子
View Code

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 "木质桌子";
    }
}

  • 塑料桌子
View Code

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、家具抽象工厂

View Code

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();
}

  • 木质家具工厂
View Code

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();
    }
}

  • 塑料家具工厂
View Code

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、测试

View Code

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、builder 建造者模式

定义:将一个复杂对象的构建与表示分离,使得同样的构建过程可以创建不同的表示;另外:建造者模式又叫生成器模式

换言之:将多个简单对象通过一步一步组装,最后变成复杂对象的过程;同时:简单对象之间的组装(相同的物料)通过相似的组装过程可以得到不一样的复杂对象(即:相同 / 相似的物料,不同的繁琐组装,得到不同的对象[此对象是一个复杂对象])

典型例子: 计算机有显示器、鼠标、键盘等等部件,不同用户想要的电脑样子、配置不一样,但都只需要和售卖员讲一下自己要的,最后店铺就会组装好用户想要的电脑给用户

注意:核心点是“相同 或 相似的组成成分”,即:创建对象需要很多步骤,但是步骤的顺序不一定固定,这种场景才是使用建造者模式的最佳场景(开发经典例子:一个类的内部结构很复杂,如有很多属性时,就可以考虑用建造者模式改造一下)


建造者角色

  • 产品(Product):就是最终要创建的产品对象
  • 抽象建造者(Builder):规范产品对象的各个组成部分的建造;可以是接口或抽象类;相当于:老板,专门约束下级的
  • 具体建造者(ConcreteBuilder):实现或继承抽象建造者的子类;是创建产品对象的执行者;相当于:真正干活的人
  • 指挥者(Director):负责产品对象的构建次序(注:这个叼毛可有可无),就相当于:有图纸的工程师;它和抽象建造者是关联关系(即:这里面的方法返回值类型是抽象建造者类型)
    • 可有可无的原因:调用者万一就是个懂行的,那还要专门找个看图纸的干嘛,直接上场指挥产品应该按什么顺序构建就可以了
    • 由于这个指挥者可有可无,所以这个建造者模式的写法相当灵活,建造者模式的核心是抽象建造者和具体建造者,其他的随意变(结合其他设计模式、结合自己认为不影响逻辑的设计都可以),要么接口+实现类,要么抽象类+继承类,但是也得注意:抽象建造者里面的成员需要满足的条件,即:方法或属性的设计(换言之:就是依赖倒置原则,只是对属性或方法进行了要求,从而做到让对象的构建独立(产品对象的属性构建顺序:可以通过指挥者、也可以直接完全交给调用者),表示分离(直接交给具体构建者,最后得到一个产品对象即可)

3.1.4.1、简单逻辑

3.1.4.1.1、有指挥者的情况

注:以下逻辑不是固定的,可以随意变化

  • 大体类图如下

image

  • 1-1、产品对象
View Code

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、抽象建造者
View Code

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、具体建造者
View Code

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、指挥者
View Code

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、测试
View Code

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

  • 上面这个指挥者中的代码多此一举了,这种应该直接交给调用者就行了,由调用者自己来安排产品对象的构建顺序是怎么样的,因此:稍微变一下,把指挥者去掉,然后调用者直接去找建造者即可(抽象建造者、具体建造者代码不变,直接去掉指挥者),那调用就变成如下的样子
View Code

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("王麻子");

  • 看到上面这种是自己传值进去的,所以抽象建造者就又可以变一下了呗(产品对象的各部件对象的构建,值由调用者传进去),如:
View Code

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的自动转配呢?需要的一些东西在配置文件中,可不可以结合工厂模式去进行加载呢(单例模式随便一个地方都可以扔进去)?

4、下一篇:行为型设计模式

链接:https://www.cnblogs.com/xiegongzi/p/16860825.html