天天看点

架构师备战(三)-软件工程(十二) 设计模式之创建型模式

作者:IT动力

我们传统的23种设置模式如下

  • 创建型模式:用于创建对象
    • 工厂方法(Factory Method) 模式
    • 抽象工厂(Abstract Factory) 模式
    • 原型(Protptype) 模式
    • 单例(Singleton) 模式
    • 构建器模式
  • 结构型模式:建立更大的结构
    • 适配器(Adapter)模式
    • 桥接(Bridge)模式
    • 组合(Composite)模式
    • 装饰(Decorator)模式
    • 外观(Facade)模式
    • 享元(Flyweight)模式
    • 代理(Proxy)模式
  • 行为型模式:交互及职责分配
    • 责任链(Chain of Responsibility)模式
    • 命令(Command)模式
    • 解释器(Interpreter)模式
    • 迭代器(Iterator)模式
    • 中介者(Mediator)模式
    • 备忘录(Memento)模式
    • 观察者(Observer)模式
    • 状态(State)模式
    • 策略(Strategy)模式
    • 模板方法(Temmlate)模式
    • 访问者(Visotor)模式

本篇文章主要说明创建型模式,它的类图,设计思想以及Java代码的实现。

1、工厂方法模式

简要说明

定义一个创建对象的接口,但是子类决定需要实例化哪一个类。工厂方法使得子类实例化的过程推迟。

速记关键字

动态生产对象

通俗的讲,就是我们创建一个工厂类,我们要创建什么对象,传一个参数给工厂,工厂就负责创建一个对象返回给我。

类图如下

架构师备战(三)-软件工程(十二) 设计模式之创建型模式

Java代码实现

/**
 * 产品接口
 */
public interface IProduct {

    /**
     * 生产产品的方法
     */
    void product();

}

public class Product1 implements IProduct{
    @Override
    public void product() {
        System.out.println("生产产品1:手机");
    }
}

public class Product2 implements IProduct{
    @Override
    public void product() {
        System.out.println("生产产品2:电脑");
    }
}

public class Product3 implements IProduct{
    @Override
    public void product() {
        System.out.println("生产产品3:平板");
    }
}

/**
 * 工厂方法类
 */
public class Creator {

    /**
     * 根据产品类型生产产品 该类一般是静态类
     * @param productType 产品类型
     * @return 产品
     */
    public static IProduct factory(Integer productType){
        if (productType == 1){
            return new Product1();
        }
        if (productType == 2){
            return new Product2();
        }
        if (productType == 3){
            return new Product3();
        }
        throw new RuntimeException("请传入正确的产品类型");
    }
}

/**
 * 客户端
 */
public class Client {
    public static void main(String[] args) {
        // 创建产品1
        IProduct product1 = Creator.factory(1);
        // 创建产品2
        IProduct product2 = Creator.factory(2);
        // 创建产品3
        IProduct product3 = Creator.factory(3);

        // 打印一下,看一下是不是对应的产品
        product1.product();
        product2.product();
        product3.product();
    }
}           

注意:这里的工厂方法,也就是Creator的factory方法,一般是静态的,因为它为了方便不用再去实例化工厂了,因为所有的事情都交给这一个工厂做了

输出结果

架构师备战(三)-软件工程(十二) 设计模式之创建型模式

2、抽象工厂模式

由上面的工厂方法知道,我们的工厂方法讲所有的事情都交给了工厂。但是我们如果要针对手机我们要生产一系列的产品,比如华为手机,苹果手机。此时工厂方法就不能够满足了。我们需要一个华为手机工厂,苹果手机工厂。这里抽象工厂模式应运而生。它就是为了生成这样的系列产品的工厂模式。

简要说明

提供一个接口,可以创建一系列相关或者相互依赖的对象,而无需指定它们的具体的类。

速记关键字

生产成系列对象

类图如下

架构师备战(三)-软件工程(十二) 设计模式之创建型模式

Java代码实现

/**
 * 抽象产品A, 手机工厂,用于生产手机
 */
public interface AbstractProductA {
    void product();
}

public class ConcreteProductA1 implements AbstractProductA {
    @Override
    public void product() {
        System.out.println("生产产品A:华为手机");
    }
}

public class ConcreteProductA2 implements AbstractProductA {
    @Override
    public void product() {
        System.out.println("生产产品A:苹果手机");
    }
}

/**
 * 抽象产品B, 笔记本电脑工厂,用于生产笔记本
 */
public interface AbstractProductB {
    void product();
}

public class ConcreteProductB1 implements AbstractProductB {
    @Override
    public void product() {
        System.out.println("生产产品B:华为笔记本");
    }
}

public class ConcreteProductB2 implements AbstractProductB {
    @Override
    public void product() {
        System.out.println("生产产品B:苹果笔记本");
    }
}

/**
 * 抽象工厂 可以是接口 也可以是抽象类 总的定义需要生产哪些系列产品
 */
public interface AbstractFactory {
    // 创建产品A(返回的对象是抽象产品A)
    AbstractProductA createProductA(Integer type);

    // 创建产品B(返回的对象是抽象产品B)
    AbstractProductB createProductB(Integer type);
}

/**
 * 具体工厂A,实现了抽象工厂,但是只实现抽象产品A的具体对象创建
 */
public class ConcreteFactoryA implements AbstractFactory{
    @Override
    public AbstractProductA createProductA(Integer type) {
        if (type == 1){
            return new ConcreteProductA1();
        }
        if (type == 2){
            return new ConcreteProductA2();
        }
        throw new RuntimeException("请传入正确的产品A类型");
    }

    @Override
    public AbstractProductB createProductB(Integer type) {
        return null;
    }
}

/**
 * 具体工厂B,实现了抽象工厂,但是只实现抽象产品B的具体对象创建
 */
public class ConcreteFactoryB implements AbstractFactory{
    @Override
    public AbstractProductA createProductA(Integer type) {
        return null;
    }

    @Override
    public AbstractProductB createProductB(Integer type) {
        if (type == 1){
            return new ConcreteProductB1();
        }
        if (type == 2){
            return new ConcreteProductB2();
        }
        throw new RuntimeException("请传入正确的产品B类型");
    }
}

/**
 * 客户端
 */
public class Client {
    public static void main(String[] args) {
        // 先实例化某个具体的工厂
        AbstractFactory factoryA = new ConcreteFactoryA();
        AbstractFactory factoryB = new ConcreteFactoryB();

        // 比如我要通过两个工厂分别生产一个华为手机和一个华为笔记本
        AbstractProductA productA = factoryA.createProductA(1);
        AbstractProductB productB = factoryB.createProductB(1);

        // 输出
        productA.product();
        productB.product();
    }
}           

运行结果

架构师备战(三)-软件工程(十二) 设计模式之创建型模式

由类图和代码可以清晰的看出来,我们的抽象工厂定义了要创建哪些系列的产品,这些产品交给哪些具体的工厂去实现。而每个具体的工厂都会根据产品的类型参数,去创建不同的产品。这样我们新增一个具体的产品非常的容易,我们增加一个具体的工厂也改动不大,这就是设计模式的魅力所在。

3、原型模式

简要说明

用原型实例指定创建对象的类型,并且通过拷贝这个原型来创建新的对象

速记关键字

克隆对象

类图如下

架构师备战(三)-软件工程(十二) 设计模式之创建型模式

浅克隆与深克隆

浅克隆表示只克隆对象的直接属性,如果对象里面包含另一个对象,不会连这个对象下面的属性也克隆,而是设置的这个对象的引用。 深克隆表示除了克隆对象的直接属性,如果对象里面包含另一个对象,也会连这个对象下面的属性也克隆,而不是设置的这个对象的引用。

Java代码实现

@Data
public class User implements Cloneable{
    private String name;
    private Integer age;
    // 地址对象,浅克隆之后的这个对象不会克隆,使用的是原来的引用,深克隆则会克隆
    private Address address;

    // 浅克隆,实现Cloneable接口即可
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    // 深克隆,在实现浅克隆的基础上,将Address手动克隆一遍,Address本身也需要实现Cloneable接口
    protected Object deepClone() throws CloneNotSupportedException {
        User clone = (User)super.clone();
        // 把里里面是对象的属性,执行一遍克隆即可
        Address address = (Address)clone.getAddress().clone();
        clone.setAddress(address);
        return clone;
    }
}

@Data
public class Address implements Cloneable{
    private String province;
    private String city;
    private String area;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

/**
 * 客户端实现
 */
public class Client {
    public static void main(String[] args) throws CloneNotSupportedException {
        // 创建对象
        Address address = new Address();
        address.setProvince("重庆");
        address.setProvince("重庆市");
        address.setCity("渝中区");
        User user = new User();
        user.setName("IT动力");
        user.setAge(18);
        user.setAddress(address);

        // 对象浅克隆, 浅克隆之后的属性里面的对象属性是不变的,因为存储的是引用
        User clone = (User)user.clone();
        System.out.println("浅克隆不会克隆address属性,结果应该为true: " + (clone.getAddress() == user.getAddress()));

        // 对象深克隆
        User clone2 = (User)user.deepClone();
        System.out.println("深克隆会克隆address属性, 结果应该为false: " + (clone2.getAddress() == user.getAddress()));
    }
}           

输出结果

架构师备战(三)-软件工程(十二) 设计模式之创建型模式

从上面看到,深克隆的实现我们手动去做了克隆Address对象的操作,但是在实际代码中,我们显然不能够去这样做。而实现的方式就是每个对象都实现序列化接口。深克隆时会序列化成流,然后再从流里读取出来进行反序列化。这样就能实现深克隆。

JSON序列化代码实现

@Data
public class Address implements Serializable {
    private String province;
    private String city;
    private String area;
}

@Data
public class User implements Serializable {
    private String name;
    private Integer age;
    private Address address;

    protected Object deepClone() throws CloneNotSupportedException {
        // 这里直接引入fastjson做测试
        return JSON.parseObject(JSON.toJSONString(this), User.class);
    }
}

/**
 * 客户端实现
 */
public class Client {
    public static void main(String[] args) throws CloneNotSupportedException {
        // 创建对象
        com.dp.prototype.Address address = new Address();
        address.setProvince("重庆");
        address.setProvince("重庆市");
        address.setCity("渝中区");
        User user = new com.dp.prototype.User();
        user.setName("IT动力");
        user.setAge(18);
        user.setAddress(address);

        // 对象深克隆
        User clone2 = (User) user.deepClone();
        System.out.println("深克隆会克隆address属性, 结果应该为false: " + (clone2.getAddress() == user.getAddress()));
    }
}           

输出结果

架构师备战(三)-软件工程(十二) 设计模式之创建型模式

4、单例模式

简要说明

保证一个类只有一个实例,并提供一个访问它的全局访问点

速记关键字

单实例

类图如下

架构师备战(三)-软件工程(十二) 设计模式之创建型模式

单例模式,无外乎就是如何创建一个单实例,并且提供一个唯一访问入口。但是单例如何实例化又分为了饿汉式单例模式和懒汉式单例模式。

饿汉式单例模式

饿汉式单例模式,就是我们创建私有的一个静态变量,直接对进行实例化,并且提供一个私有的构造函数,防止对象被新建。最后提供一个公开的获取实例的方法,直接返回声明的对象。

它是从出生就做好了实例化,后面也不需要再去实例化了,所以它是天生线程安全的。

懒汉式单例模式

饿汉式是一开始就把对象创建好了,懒汉式一开始比较懒,它不创建,在方法被调用时才会创建,所以叫懒汉式。但是这样创建在多线程可能会有问题,因此一般需要加锁。

加锁得方式有整个方法加锁,还有一种叫做双重检测锁得单例模式,能够应对高并发。

Java代码实现

/**
 * 饿汉式单例模式
 */
public class HungrySingleton {
    // 私有得静态得实例变量,饿汉式直接初始化对象
    private static final HungrySingleton instants = new HungrySingleton();

    // 私有构造函数,防止其他类通过反射实例化该类
    private HungrySingleton(){
        System.out.println("我是饿汉式单例的私有构造方法,我被调用了");
    }

    // 静态的public方法,供外界网文的唯一入口
    public static HungrySingleton getInstance(){
        // 直接返回上面对应的对象
        return instants;
    }
}

/**
 * 懒汉式单例模式
 */
public class LazySingleton {
    // 私有得静态得实例变量,饿汉式直接初始化对象
    private static LazySingleton instants;

    // 私有构造函数,防止其他类通过反射实例化该类
    private LazySingleton(){
        System.out.println("我是懒汉式单例的私有构造方法,我被调用了");
    }

    // 静态的public方法,供外界网文的唯一入口, 注意懒汉式在这里需要加锁,不然会存在线程安全问题
    public static synchronized LazySingleton getInstance(){
        // 为空 则创建并且赋值给全局变量
        if (instants == null){
            instants = new LazySingleton();
        }
        // 返回上面对应的对象
        return instants;
    }
}

/**
 * 懒汉式单例模式2 双重检测锁(推荐)
 */
public class LazySingleton2 {
    // 私有得静态得实例变量,饿汉式直接初始化对象 注意,这里需要将全局变量加上volatile关键字,从而禁止重排序
    private static volatile LazySingleton2 instants;

    // 私有构造函数,防止其他类通过反射实例化该类
    private LazySingleton2(){
        System.out.println("我是懒汉式单例2的私有构造方法,我被调用了");
    }

    // 静态的public方法,供外界网文的唯一入口
    public static LazySingleton2 getInstance(){
        // 第一个if判断是否为空,不为空直接返回,避免synchronized同步代码块的执行,多线程场景下频繁加锁会影响性能
        if(instants == null){
            // 为空的情况才加锁
            synchronized (LazySingleton2.class){
                // 第二个if判断是否为空,当a线程优先获得锁,执行到此处,b线程没竞争到锁会被阻塞在外面,a线程判断实例是否为空,为空则new实例,
                // a线程释放锁之后,b线程拿到锁进来后判断instance是否为null,此时不为null,则释放锁往下
                if(instants == null){
                    instants = new LazySingleton2();
                }
            }
        }

        return instants;
    }
}           

输出结果

架构师备战(三)-软件工程(十二) 设计模式之创建型模式

单例模式的代码实现方式有这么多种,那么工作中会使用哪种呢。如果需要考虑并发问题,就需要使用双重检测锁,这样性能会高一些。

为什么不使用饿汉式呢,因为它一开始就把对象初始化了,也就是内存从一开始就占用了。如果存在胆量的单例,但是却没有使用,那就是对内存的一种极大的浪费。而懒汉式则只有在真正需要时才会去创建对象。

5、构造器模式

简要说明

讲一个复杂类的表示与其构造相分离,使得形同的构建过程能够得出不同的表示。

速记关键字

复杂对现象构造

类图如下

架构师备战(三)-软件工程(十二) 设计模式之创建型模式

java代码实现

/**
 * 房子类 包含了房子的长宽高
 */
@Data
public class Room {

    private String length;

    private String width;

    private String height;


    @Override
    public String toString() {
        return "Room{length='" + length + ", width='" + width + ", height='" + height + '}';
    }
}

/**
 * 构建器接口
 */
public interface Builder {
    // 构建房子的长度
    void buildLength();
    // 构建房子的宽度
    void buildWidth();
    // 构建房子的高度
    void buildHeight();
    // 构建房子
    Room buildRoom();
}

/*
 * 大房子的具体构建者
 */
public class LargeRoomBuilder implements Builder {
    private final Room room = new Room();
    @Override
    public void buildLength() {
        room.setLength("100米");
    }
    @Override
    public void buildWidth() {
        room.setWidth("120米");
    }
    @Override
    public void buildHeight() {
        room.setHeight("5米");
    }
    @Override
    public Room buildRoom() {
        return room;
    }
}

/*
 * 小房子的具体构建者
 */
public class SmallRoomBuilder implements Builder {
    private final Room room = new Room();
    @Override
    public void buildLength() {
        room.setLength("30米");
    }
    @Override
    public void buildWidth() {
        room.setWidth("40米");
    }
    @Override
    public void buildHeight() {
        room.setHeight("3米");
    }
    @Override
    public Room buildRoom() {
        return room;
    }
}

/**
 * 指挥者,引入指挥者的目的:指定构造流程、方式
 */
public class Director {

    // 引入构建器
    private final Builder builder;

    // 构造注入
    public Director(Builder builder) {
        this.builder = builder;
    }

    // 构建房间方法
    public Room buildRoom() {
        // 构建房间,分别构建长宽高,然后就能能够构建一个房间
        builder.buildLength();
        builder.buildWidth();
        builder.buildHeight();
        return builder.buildRoom();
    }
}

/**
 * 客户端
 */
public class Client {
    public static void main(String[] args) {
        // 指定构造者,交由指挥者构建大房子
        Room largeRoom = new Director(new LargeRoomBuilder()).buildRoom();
        System.out.println(largeRoom);

        // 指定构造者,交由指挥者构建小房子
        Room smallRoom = new Director(new SmallRoomBuilder()).buildRoom();
        System.out.println(smallRoom);
    }
}           

输出打印

架构师备战(三)-软件工程(十二) 设计模式之创建型模式

6、小结

这里主要说明了设计模式中的创建型模式的概念,类图,以及代码的实现。而结构型模式和行为型模式,将会在后面去说明,因为创建型模式相对比较简单,也容易理解,所以放在最前面。后买你虽然会难一些,但是我们要知难而上。学无止境,加油!

继续阅读