天天看点

【设计模式】设计模式之创建型模式(单例、原型、工厂、建造者)1、设计模式2、创建型模式

【设计模式】设计模式之创建型模式(单例、原型、工厂、建造者)

  • 1、设计模式
        • 1.1 设计模式介绍
        • 1.2 分类
  • 2、创建型模式
        • 2.1 概述
        • 2.2 五大创建型设计模式
          • 2.2.1 单例模式
          • 2.2.2 工厂模式(简单工厂,工厂方法。抽象工厂)
          • 2.2.3 建造者模式
          • 2.2.4 原型模式

1、设计模式

1.1 设计模式介绍

  • 设计模式(Design Pattern)是前辈们对代码开发经验的总结,是解决特定问题的一系列套路。它不是语法规定,而是一套用来提高代码可复用性、可维护性、可读性、稳健性以及安全性的解决方案。
  • 1995 年,GoF(Gang of Four,四人组/四人帮)合作出版了《设计模式:可复用面向对象软件的基础》一书,共收录了 23 种设计模式,从此树立了软件设计模式领域的里程碑,人称「GoF设计模式」。

1.2 分类

  • 根据模式是用来完成什么工作来划分,这种方式可分为创建型模式、结构型模式和行为型模式 3 种:
1、创建型模式:用于描述“怎样创建对象”,它的主要特点是“将对象的创建与使用分离”。GoF 中提供了单例、原型、工厂方法、抽象工厂、建造者等 5 种创建型模式。
2、 结构型模式:用于描述如何将类或对象按某种布局组成更大的结构,GoF 中提供了代理、适配器、桥接、装饰、外观、享元、组合等 7 种结构型模式。
3、行为型模式:用于描述类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,以及怎样分配职责。GoF 中提供了模板方法、策略、命令、职责链、状态、观察者、中介者、迭代器、访问者、备忘录、解释器等 11 种行为型模式。
  • 这篇文章主要聊的是创建型模式。

2、创建型模式

2.1 概述

  • 创建型模式(Creational Pattern)对类的实例化过程进行了抽象,将对象的创建和使用分离。
  • 创建型模式在创建什么,由谁创建,何时创建等方面都为软件设计者提供了尽可能大的灵活性。
  • 创建型模式隐藏了类的实例的创建细节,通过隐藏对象如何被创建和组合在一起达到使整个系统独立的目的。

2.2 五大创建型设计模式

2.2.1 单例模式

2.2.1.1 概念

  • 一个类全局仅有一个实例,一个访问点。
  • 像线程池、缓存、日志对象,这类对象通常只有一个实例,如果多个实例就可能会导致一些问题,例如:程序异常、资源浪费、或者数据不一致性。

2.2.1.2 两种单例模式的实现

  • 饿汉方式:全局的单例实例在类装载时构建
  • 懒汉方式:全局的单例实例在第一次被使用时构建。
  • 饿汉方式代码案例:
/**
 * 饿汉单例模式
 *
 * @author wangjie
 * @version V1.0
 * @date 2019/12/14
 */
public class SingletonDemo {
    private static volatile SingletonDemo instance = new SingletonDemo();

    private SingletonDemo(){
        System.out.println(Thread.currentThread().getName()+"\t 构造方法");
    }

    /**
     * @return
     */
    public static SingletonDemo getInstance(){
        return instance;
    }
}

           
  • 懒汉方式代码案例:
/**
 * 懒汉单例模式
 *
 * @author wangjie
 * @version V1.0
 * @date 2019/12/14
 */
public class SingletonDemo {
    private static volatile SingletonDemo instance=null;

    private SingletonDemo(){
        System.out.println(Thread.currentThread().getName()+"\t 构造方法");
    }

    /**
     * 双重检测机制
     * @return
     */
    public static SingletonDemo getInstance(){
        if(instance==null){
            synchronized (SingletonDemo.class){
                if(instance==null){
                    instance=new SingletonDemo();
                }
            }
        }
        return instance;
    }
           
  • 懒汉方式中的双重检测机制–DCL:也被称为"双重检查加锁优化",“锁暗示”(Lock hint) 是一种软件设计模式用来减少并发系统中竞争和同步的开销。双重检查锁定模式首先验证锁定条件(第一次检查),只有通过锁定条件验证才真正的进行加锁逻辑并再次验证条件(第二次检查)
  • DCL(双端检锁) 机制不一定线程安全,原因是有指令重排的存在,加入volatile可以禁止指令重排
  • 关于volatile关键字,更多详见:【JUC】volatile关键字相关整理

2.2.1.3 单例模式分析

  • 单例模式的目的是保证一个类仅有一个实例,并提供一个访问它的全局访问点。单例模式包含的角色只有一个,就是单例类——Singleton。单例类拥有一个私有构造函数,确保用户无法通过new关键字直接实例化它。除此之外,该模式中包含一个静态私有成员变量与静态公有的工厂方法,该工厂方法负责检验实例的存在性并实例化自己,然后存储在静态成员变量中,以确保只有一个实例被创建。
  • 在单例模式的实现过程中,需要注意如下三点:
1、单例类的构造函数为私有;
2、提供一个自身的静态私有成员变量;
3、提供一个公有的静态工厂方法。

2.2.1.4 单例模式适用环境

  • 系统只需要一个实例对象,如系统要求提供一个唯一的序列号生成器,或者需要考虑资源消耗太大而只允许创建一个对象。
  • 客户调用类的单个实例只允许使用一个公共访问点,除了该公共访问点,不能通过其他途径访问该实例。
  • 在一个系统中要求一个类只有一个实例时才应当使用单例模式。反过来,如果一个类可以有几个实例共存,就需要对单例模式进行改进,使之成为多例模式。
2.2.2 工厂模式(简单工厂,工厂方法。抽象工厂)

2.2.2.1 概念

  • 在基类中定义一个创建产品对象的工厂接口,将产品对象的实际创建工作推迟到具体子工厂类当中。
  • 满足创建型模式中所要求的“创建与使用相分离”的特点。

2.2.2.2 分类

  • 简单工厂模式(Simple Factory Pattern):又称为静态工厂方法(Static Factory Method)模式,它属于类创建型模式。在简单工厂模式中,可以根据参数的不同返回不同类的实例。简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。
  • 工厂方法模式(Factory Method Pattern)又称为工厂模式,也叫虚拟构造器(Virtual Constructor)模式或者多态工厂(Polymorphic Factory)模式,它属于类创建型模式。在工厂方法模式中,工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象,这样做的目的是将产品类的实例化操作延迟到工厂子类中完成,即通过工厂子类来确定究竟应该实例化哪一个具体产品类。
  • 抽象工厂模式(Abstract Factory Pattern):提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。抽象工厂模式又称为Kit模式,属于对象创建型模式。

2.2.2.3 实现

2.2.2.3.1 简单工厂实现

  • 简单工厂模式并不是23种常用的设计模式之一,它只算工厂模式的一个特殊实现。简单工厂模式在实际中的应用相对于其他2个工厂模式用的还是相对少得多,因为它只适应很多简单的情况。
  • 适用场景:
1、需要创建的对象较少。
2、客户端不关心对象的创建过程。
  • 简单工厂代码案例:
/**
 * 绘图工具
 *
 * @author wangjie
 * @version V1.0
 * @date 2020/4/3
 */
public interface Shape {
    /**
     * 绘图
     */
    void draw();
}

/**
 * 画圆
 *
 * @author wangjie
 * @version V1.0
 * @date 2020/4/3
 */
@Slf4j
public class Circle implements Shape {

    public Circle() {
        log.info("【Circle init】");
    }

    /**
     * 圆
     */
    @Override
    public void draw() {
        log.info("【Draw Circle】");
    }
}

/**
 * 正方形
 *
 * @author wangjie
 * @version V1.0
 * @date 2020/4/3
 */
@Slf4j
public class Square implements Shape {
    public Square() {

        log.info("【Square init】");
    }

    @Override
    public void draw() {
        log.info("【Draw Square】");
    }
}
/**
 * 绘画简单工厂
 *
 * @author wangjie
 * @version V1.0
 * @date 2020/4/3
 */
public class ShapeFactory {

    /**
     * 获取形状
     * @param shapeType
     * @return
     */
    public static Shape getShape(String shapeType) {
        if (shapeType == null) {
            return null;
        }
        if (shapeType.equalsIgnoreCase("CIRCLE")) {
            return new Circle();
        } else if (shapeType.equalsIgnoreCase("SQUARE")) {
            return new Square();
        }
        return null;
    }

    public static void main(String[] args) {

        // 获取 Circle 的对象,并调用它的 draw 方法
        Shape circle = ShapeFactory.getShape("CIRCLE");
        circle.draw();
        // 获取 Square 的对象,并调用它的 draw 方法
        Shape square = ShapeFactory.getShape("SQUARE");
        square.draw();
    }
}
           
  • 简单工厂总结:
1、简单工厂模式可以根据参数的不同返回不同类的实例。简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。
2、简单工厂模式包含三个角色:工厂角色负责实现创建所有实例的内部逻辑;抽象产品角色是所创建的所有对象的父类,负责描述所有实例所共有的公共接口;具体产品角色是创建目标,所有创建的对象都充当这个角色的某个具体类的实例。
3、简单工厂模式的要点在于:当你需要什么,只需要传入一个正确的参数,就可以获取你所需要的对象,而无须知道其创建细节。
4、简单工厂模式最大的优点在于实现对象的创建和对象的使用分离,将对象的创建交给专门的工厂类负责,但是其最大的缺点在于工厂类不够灵活,增加新的具体产品需要修改工厂类的判断逻辑代码,而且产品较多时,工厂方法代码将会非常复杂。
5、简单工厂模式适用情况包括:工厂类负责创建的对象比较少;客户端只知道传入工厂类的参数,对于如何创建对象不关心。

2.2.2.3.2 工厂方法实现

  • 工厂方法模式应该是在工厂模式家族中是用的最多模式,一般项目中存在最多的就是这个模式。
  • 工厂方法模式是简单工厂的仅一步深化, 在工厂方法模式中,我们不再提供一个统一的工厂类来创建所有的对象,而是针对不同的对象提供不同的工厂。也就是说 每个对象都有一个与之对应的工厂 。
  • 适用环境
1、一个类不知道它所需要的对象的类:在工厂方法模式中,客户端不需要知道具体产品类的类名,只需要知道所对应的工厂即可,具体的产品对象由具体工厂类创建;客户端需要知道创建具体产品的工厂类。
2、一个类通过其子类来指定创建哪个对象:在工厂方法模式中,对于抽象工厂类只需要提供一个创建产品的接口,而由其子类来确定具体要创建的对象,利用面向对象的多态性和里氏代换原则,在程序运行时,子类对象将覆盖父类对象,从而使得系统更容易扩展。
3、将创建对象的任务委托给多个工厂子类中的某一个,客户端在使用时可以无须关心是哪一个工厂子类创建产品子类,需要时再动态指定,可将具体工厂类的类名存储在配置文件或数据库中
  • 工厂方法代码案例:
/**
 * 获取形状的接口
 *
 * @author wangjie
 * @version V1.0
 * @date 2020/4/3
 */
public interface Factory {
    Shape getShape();
}

/**
 * 画圆的工厂
 *
 * @author wangjie
 * @version V1.0
 * @date 2020/4/3
 */
public class CircleFactory  implements Factory {

    @Override
    public Shape getShape() {
        return new Circle();
    }

}

/**
 * 画正方形的工厂
 *
 * @author wangjie
 * @version V1.0
 * @date 2020/4/3
 */
public class SquareFactory implements Factory{

    @Override
    public Shape getShape() {
        return new Square();
    }

}

/**
 * 工厂方法模型测试
 *
 * @author wangjie
 * @version V1.0
 * @date 2020/4/3
 */
public class Test {
    public static void main(String[] args) {
        Factory circlefactory = new CircleFactory();
        Shape circle = circlefactory.getShape();
        circle.draw();
    }

}
           
  • 工厂方法模式总结:
1 、工厂方法模式中,工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象,这样做的目的是将产品类的实例化操作延迟到工厂子类中完成,即通过工厂子类来确定究竟应该实例化哪一个具体产品类。
2、工厂方法模式包含四个角色:抽象产品是定义产品的接口,是工厂方法模式所创建对象的超类型,即产品对象的共同父类或接口;具体产品实现了抽象产品接口,某种类型的具体产品由专门的具体工厂创建,它们之间往往一一对应;抽象工厂中声明了工厂方法,用于返回一个产品,它是工厂方法模式的核心,任何在模式中创建对象的工厂类都必须实现该接口;具体工厂是抽象工厂类的子类,实现了抽象工厂中定义的工厂方法,并可由客户调用,返回一个具体产品类的实例。
3 、工厂方法模式是简单工厂模式的进一步抽象和推广。由于使用了面向对象的多态性,工厂方法模式保持了简单工厂模式的优点,而且克服了它的缺点。在工厂方法模式中,核心的工厂类不再负责所有产品的创建,而是将具体创建工作交给子类去做。这个核心类仅仅负责给出具体工厂必须实现的接口,而不负责产品类被实例化这种细节,这使得工厂方法模式可以允许系统在不修改工厂角色的情况下引进新产品。
4、工厂方法模式的主要优点是增加新的产品类时无须修改现有系统,并封装了产品对象的创建细节,系统具有良好的灵活性和可扩展性;其缺点在于增加新产品的同时需要增加新的工厂,导致系统类的个数成对增加,在一定程度上增加了系统的复杂性。
5、工厂方法模式适用情况包括:一个类不知道它所需要的对象的类;一个类通过其子类来指定创建哪个对象;将创建对象的任务委托给多个工厂子类中的某一个,客户端在使用时可以无须关心是哪一个工厂子类创建产品子类,需要时再动态指定。

2.2.2.3.3 抽象工厂实现

  • 在工厂方法模式中具体工厂负责生产具体的产品,每一个具体工厂对应一种具体产品,工厂方法也具有唯一性,一般情况下,一个具体工厂中只有一个工厂方法或者一组重载的工厂方法。但是有时候我们需要一个工厂可以提供多个产品对象,而不是单一的产品对象
  • 当系统所提供的工厂所需生产的具体产品并不是一个简单的对象,而是多个位于不同产品等级结构中属于不同类型的具体产品时需要使用抽象工厂模式。
  • 抽象工厂是生产一整套有产品的(至少要生产两个产品),这些产品必须相互是有关系或有依赖的,而工厂方法中的工厂是生产单一产品的工厂。
  • 抽象工厂模式是所有形式的工厂模式中最为抽象和最具一般性的一种形态。
  • 抽象工厂模式与工厂方法模式最大的区别在于,工厂方法模式针对的是一个产品等级结构,而抽象工厂模式则需要面对多个产品等级结构,一个工厂等级结构可以负责多个不同产品等级结构中的产品对象的创建 。当一个工厂等级结构可以创建出分属于不同产品等级结构的一个产品族中的所有对象时,抽象工厂模式比工厂方法模式更为简单、有效率。
  • 适用环境:
1、一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有类型的工厂模式都是重要的。
2、系统中有多于一个的产品族,而每次只使用其中某一产品族。
3、属于同一个产品族的产品将在一起使用,这一约束必须在系统的设计中体现出来。
4、系统提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于具体实现。
  • 抽象工厂代码案例:
/**
 * 裤子
 *
 * @author wangjie
 * @version V1.0
 * @date 2020/4/3
 */
public interface Pants {
    /**
     * 今天搭配那条裤子
     */
    void Wear();
}
/**
 * 今天穿什么鞋子
 *
 * @author wangjie
 * @version V1.0
 * @date 2020/4/3
 */
public interface Shoes {
    /**
     * 今天搭配那双鞋子
     */
    void Wear();
}

/**
 * 耐克的裤子
 *
 * @author wangjie
 * @version V1.0
 * @date 2020/4/3
 */
@Slf4j
public class NikePants  implements Pants {
    @Override
    public void Wear() {
        log.info("今天穿耐克的裤子");
    }
}
/**
 * 耐克的鞋子
 *
 * @author wangjie
 * @version V1.0
 * @date 2020/4/3
 */
@Slf4j
public class NikeShoes implements Shoes {
    @Override
    public void Wear() {
        log.info("今天穿耐克的鞋子");
    }
}
/**
 * 今天穿阿迪的裤子
 *
 * @author wangjie
 * @version V1.0
 * @date 2020/4/3
 */
@Slf4j
public class AdidasPants implements Pants {
    @Override
    public void Wear() {
        log.info("今天穿阿迪的裤子");
    }
}
/**
 * 今天穿阿迪的鞋子
 *
 * @author wangjie
 * @version V1.0
 * @date 2020/4/3
 */
@Slf4j
public class AdidasShoes implements Shoes {
    @Override
    public void Wear() {
        log.info("今天穿阿迪的鞋子");
    }
}
/**
 * 穿搭工厂
 *
 * @author wangjie
 * @version V1.0
 * @date 2020/4/3
 */
public interface AbstractWearFactory {

    /**
     * 穿什么裤子
     * @return
     */
    Pants wearPants();

    /**
     * 穿什么鞋
     * @return
     */
    Shoes wearShoes();
}
/**
 * 周一穿阿迪的裤子搭耐克的鞋
 *
 * @author wangjie
 * @version V1.0
 * @date 2020/4/3
 */
public class MondayWearFactory implements AbstractWearFactory{

    @Override
    public Pants wearPants() {
        return new AdidasPants();
    }

    @Override
    public Shoes wearShoes() {
        return new NikeShoes();
    }
}
/**
 * 周二穿耐克的裤子搭阿迪的鞋
 *
 * @author wangjie
 * @version V1.0
 * @date 2020/4/3
 */
public class TuesdayWearFactory implements AbstractWearFactory{

    @Override
    public Pants wearPants() {
        return new NikePants();
    }

    @Override
    public Shoes wearShoes() {
        return new AdidasShoes();
    }
}
/**
 * 抽象工厂测试
 *
 * @author wangjie
 * @version V1.0
 * @date 2020/4/3
 */
@Slf4j
public class Test {
    public static void main(String[] args) {
        AbstractWearFactory factory = new MondayWearFactory();
        Pants pants = factory.wearPants();
        pants.Wear();
        Shoes shoes = factory.wearShoes();
        shoes.Wear();
    }
}
           
  • 抽象工厂模式总结:
1、抽象工厂模式提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。
2、抽象工厂模式包含四个角色:抽象工厂用于声明生成抽象产品的方法;具体工厂实现了抽象工厂声明的生成抽象产品的方法,生成一组具体产品,这些产品构成了一个产品族,每一个产品都位于某个产品等级结构中;抽象产品为每种产品声明接口,在抽象产品中定义了产品的抽象业务方法;具体产品定义具体工厂生产的具体产品对象,实现抽象产品接口中定义的业务方法。
3、抽象工厂模式是所有形式的工厂模式中最为抽象和最具一般性的一种形态。抽象工厂模式与工厂方法模式最大的区别在于,工厂方法模式针对的是一个产品等级结构,而抽象工厂模式则需要面对多个产品等级结构。
4、抽象工厂模式的主要优点是隔离了具体类的生成,使得客户并不需要知道什么被创建,而且每次可以通过具体工厂类创建一个产品族中的多个对象,增加或者替换产品族比较方便,增加新的具体工厂和产品族很方便;主要缺点在于增加新的产品等级结构很复杂,需要修改抽象工厂和所有的具体工厂类,对“开闭原则”的支持呈现倾斜性。
5、抽象工厂模式适用情况包括:一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节;系统中有多于一个的产品族,而每次只使用其中某一产品族;属于同一个产品族的产品将在一起使用;系统提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于具体实现。
2.2.3 建造者模式

2.2.3.1 概念

  • 建造者模式(Builder Pattern) 又名生成器模式,是一种对象构建模式。它可以将复杂对象的建造过程抽象出来(抽象类别),使这个抽象过程的不同实现方法可以构造出不同表现(属性)的对象。
  • 建造者模式 是一步一步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构建它们,用户不需要知道内部的具体构建细节。
  • 抽象工厂模式不关心构建过程,只关心什么产品由什么工厂生产,而建造者模式则是要求按照指定的蓝图建造产品,它的主要目的是通过组装零配件而产生一个新产品。
  • 例如汽车,它包括车轮、方向盘、发送机等各种部件,对于大多数用户而言,无须知道这些部件的装配细节,也几乎不会使用单独某个部件,而是使用一辆完整的汽车,可以通过建造者模式对其进行设计与描述,建造者模式可以将部件和其组装过程分开,一步一步创建一个复杂的对象。用户只需要指定复杂对象的类型就可以得到该对象,而无须知道其内部的具体构造细节
  • 适用环境:
1、需要生成的产品对象有复杂的内部结构,这些产品对象通常包含多个成员属性。
2、需要生成的产品对象的属性相互依赖,需要指定其生成顺序。
3、对象的创建过程独立于创建该对象的类。在建造者模式中引入了指挥者类,将创建过程封装在指挥者类中,而不在建造者类中。
4、隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品。
  • 抽象工厂代码案例:
/**
 * 汽车
 *
 * @author wangjie
 * @version V1.0
 * @date 2020/4/4
 */
public class Car {

    /**
     * 轮胎
     */
    private String tyre;
    /**
     * 方向盘
     */
    private String steeringWheel;

   //get,set省略
}

/**
 * 汽车创建抽象类
 *
 * @author wangjie
 * @version V1.0
 * @date 2020/4/4
 */
public abstract class CarBuilder {

    protected Car car = new Car();

    public abstract void buildTyre();

    public abstract void buildSteeringWheel();

    public Car getCar(){
        return car;
    }
}

/**
 * 宝马建造者
 *
 * @author wangjie
 * @version V1.0
 * @date 2020/4/4
 */
@Slf4j
public class BMWCarBuilder  extends CarBuilder{

    @Override
    public void buildTyre() {
        car.setTyre("宝马轮胎");
    }

    @Override
    public void buildSteeringWheel() {
        car.setSteeringWheel("宝马方向盘");
    }
}
/**
 * 奔驰建造者
 *
 * @author wangjie
 * @version V1.0
 * @date 2020/4/4
 */
@Slf4j
public class MercedesCarBuilder extends CarBuilder{

    @Override
    public void buildTyre() {
        car.setTyre("奔驰轮胎");
    }

    @Override
    public void buildSteeringWheel() {
        car.setSteeringWheel("奔驰方向盘");
    }
}
/**
 * 指挥者
 *
 * @author wangjie
 * @version V1.0
 * @date 2020/4/4
 */
public class Director {

    private CarBuilder builder;

    public Director(CarBuilder builder){
        this.builder = builder;
    }

    public Car construct(){
        builder.buildTyre();
        builder.buildSteeringWheel();
        return builder.getCar();
    }
}
/**
 * 建造者模式测试
 *
 * @author wangjie
 * @version V1.0
 * @date 2020/4/4
 */
@Slf4j
public class Test {

    public static void main(String[] args) {
        BMWCarBuilder bmwCarBuilder = new BMWCarBuilder();
        Director director = new Director(bmwCarBuilder);
        Car car= director.construct();
        log.info("车:{}", JSON.toJSONString(car));
    }
}
           

2.2.3.2 建造者模式总结:

1、建造者模式将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
2、建造者模式包含如下四个角色:抽象建造者为创建一个产品对象的各个部件指定抽象接口;具体建造者实现了抽象建造者接口,实现各个部件的构造和装配方法,定义并明确它所创建的复杂对象,也可以提供一个方法返回创建好的复杂产品对象;产品角色是被构建的复杂对象,包含多个组成部件;指挥者负责安排复杂对象的建造次序,指挥者与抽象建造者之间存在关联关系,可以在其construct()建造方法中调用建造者对象的部件构造与装配方法,完成复杂对象的建造
3、在建造者模式的结构中引入了一个指挥者类,该类的作用主要有两个:一方面它隔离了客户与生产过程;另一方面它负责控制产品的生成过程。指挥者针对抽象建造者编程,客户端只需要知道具体建造者的类型,即可通过指挥者类调用建造者的相关方法,返回一个完整的产品对象。
4、建造者模式的主要优点在于客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象,每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者,符合“开闭原则”,还可以更加精细地控制产品的创建过程;其主要缺点在于由于建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,因此其使用范围受到一定的限制,如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。
5、建造者模式适用情况包括:需要生成的产品对象有复杂的内部结构,这些产品对象通常包含多个成员属性;需要生成的产品对象的属性相互依赖,需要指定其生成顺序;对象的创建过程独立于创建该对象的类;隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同类型的产品。
2.2.4 原型模式

2.2.4.1 概念

  • 用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。
  • 对象的创建过程较为复杂,有时候需要频繁创建,原型模式通过给出一个原型对象来指明所要创建的对象的类型,然后用复制这个原型对象的办法创建出更多同类型的对象,这就是原型模式的意图所在。
  • 适用场景:
1、创建新对象成本较大(例如初始化时间长,占用CPU多或占太多网络资源),新对象可以通过复制已有对象来获得,如果相似对象,则可以对其成员变量稍作修改。
2、系统要保存对象的状态,而对象的状态很小。
3、需要避免使用分层次的工厂类来创建分层次的对象,并且类的实例对象只有一个或很少的组合状态,通过复制原型对象得到新实例可以比使用构造函数创建一个新实例更加方便。

2.2.4.2 分析

  • Java类都继承自java.lang.Object,而Object类提供一个clone()方法,可以将一个Java对象复制一份。因此在Java中可以直接使用Object提供的clone()方法来实现对象的克隆,Java语言中的原型模式实现很简单。

2.2.4.3 原型模式代码示例:

/**
 * 形状原型
 *
 * @author wangjie
 * @version V1.0
 * @date 2020/4/4
 */
public abstract class Shape implements Cloneable {

    private String id;
    protected String type;

    abstract void draw();

    public String getType(){
        return type;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    @Override
    public Object clone() {
        Object clone = null;
        try {
            clone = super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return clone;
    }
}

/**
 * 圆形
 *
 * @author wangjie
 * @version V1.0
 * @date 2020/4/4
 */
public class Circle extends Shape {

    public Circle(){
        type = "Circle";
    }

    @Override
    public void draw() {
        System.out.println("draw Circle");
    }
}

/**
 * 长方形
 *
 * @author wangjie
 * @version V1.0
 * @date 2020/4/4
 */
public class Rectangle extends Shape {

    public Rectangle(){
        type = "Rectangle";
    }

    @Override
    public void draw() {
        System.out.println("draw Rectangle");
    }
}

/**
 * 原型库
 *
 * @author wangjie
 * @version V1.0
 * @date 2020/4/4
 */
public class ShapeCache {

    private static Hashtable<String, Shape> shapeMap = new Hashtable<String, Shape>();


    /**
     * 原型库
     */
    public  ShapeCache() {
        Circle circle = new Circle();
        circle.setId("1");
        shapeMap.put(circle.getId(),circle);

        Rectangle rectangle = new Rectangle();
        rectangle.setId("2");
        shapeMap.put(rectangle.getId(),rectangle);
    }

    public Shape getShape(String shapeId) {
        Shape cachedShape = shapeMap.get(shapeId);
        return (Shape) cachedShape.clone();
    }
}
/**
 * 原型模式测试
 *
 * @author wangjie
 * @version V1.0
 * @date 2020/4/4
 */
public class Test {

    public static void main(String[] args) {
        ShapeCache shapeCache = new ShapeCache();

        Shape clonedShape = (Shape) shapeCache.getShape("1");
        System.out.println("Shape : " + clonedShape.getType());

        Shape clonedShape2 = (Shape) shapeCache.getShape("2");
        System.out.println("Shape : " + clonedShape2.getType());

    }
}