软件开发设计模式之【6】个【创建型】设计模式

代码码云仓库地址:https://gitee.com/dzxmy/design_pattern

常用的创建型设计模式有:工厂方法模式,抽象工厂模式,建造者模式,单例模式。

不常用的创建型设计模式有:简单工厂,原型模式

一、简单工厂

定义:由一个工厂对象决定创建出哪一种产品类的实例

类型:创建型,但不属于GOF23种设计模式

适用场景:

  • 工厂类负责创建的对象比较少
  • 客户端应用层只知道传入工厂类的参数对于如何创建对象不关心

优点:只需要传入一个正确的参数,就可以获取你所需要的对象,而无须知道其创建细节

缺点:工厂类的职责相对过重,增加新的产品,需要修改工厂类的判断逻辑,违背开闭原则

com.dzx.design.creational.simplefactory 包下代码: 简单工厂
 

 

二、工厂方法

 定义:定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类,工厂方法让类的实例化推迟到子类中进行

类型:创建型

适用场景:创建对象需要大量重复的代码,客户端应用层不依赖于产品类实例如何被创建、实现等细节,一个类通过其子类来指定创建哪个对象

优点:用户只需要关心所需产品对应的工厂,无须关心创建细节,加入新产品符合开闭原则,提高可扩展性

缺点:类的个数容易过多,增加复杂度,增加了系统的抽象性和理解难度

com.dzx.design.creational.factorymethod 包下代码:工厂方法
 

三、抽象工厂

定义:抽象工厂模式提供一个创建一系列相关或相互依赖对象的接口

无须指定它们具体的类

类型:创建型 

适用场景:

  • 客户端应用层不依赖于产品类实例如何被创建、实现等细节
  • 强调一系列相关的产品对象(属于同一产品族)一起使用创建对象需要大量重复的代码
  • 提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于具体实现

优点:具体产品在应用层代码隔离,无需关心创建细节,将一个系列的产品族统一到一起创建

缺点:规定了所有可能被创建的产品集合,产品族中扩展新的产品困难,需要修改抽象工厂的接口

增加了系统的抽象性和理解难度

com.dzx.design.creational.abstractfactory  包下代码:抽象工厂
 

 四、建造者模式

定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示

用户只需要指定建造的类型就可以得到它们,建造过程及细节不需要知道

类型:创建型

适用场景:

  • 如果一个对象有非常复杂的内部结构(很多属性)
  • 想把复杂对象的创建和使用分离

优点:封装性好,创建和使用分离,扩展性好、建造类之间一定程度上解耦

缺点:产生多余的builder对象,产品内部发生变化,建造者都要修改,成本较大 

com.dzx.design.creational.builder  包下代码: 建造者模式
 

 建造者模式改进版本(更加常用)

com.dzx.design.creational.builder.v2  包下代码: 建造者模式改进版本
 

 

 五、单例模式

com.dzx.design.creational.singletonexample 包下代码:单例模式
 

单例模式的 几种方式比较
懒汉模式


  
  1. /**
  2. * 懒汉模式
  3. * 单例的实例在第一次使用的时候进行创建
  4. * <p>
  5. * 在单线程的环境下,没有问题,但是在多线程的环境下就会出问题。
  6. */
  7. @NotThreadSafe
  8. @Slf4j
  9. public class SingletonExample1 {
  10. //私有构造函数
  11. private SingletonExample1() {
  12. }
  13. //单例对象
  14. private static SingletonExample1 instance = null;
  15. //静态的工厂方法
  16. //懒汉模式本身时线程不安全的,加上了synchronized关键字之后就可以实现安全
  17. //但是降低了性能,因此并不推荐这种写法
  18. public static synchronized SingletonExample1 getInstance() {
  19. if (instance == null) {
  20. instance = new SingletonExample1();
  21. }
  22. return instance;
  23. }
  24. }


饿汉模式 


  
  1. /**
  2. * 饿汉模式
  3. */
  4. public class SingletonExample2 {
  5. private SingletonExample2() {
  6. }
  7. private static SingletonExample2 singletonExample2 = new SingletonExample2();
  8. public static SingletonExample2 getInstance() {
  9. return singletonExample2;
  10. }
  11. }



 懒汉模式+ 双重检测同步锁


  
  1. /**
  2. * 懒汉模式 -》》双重同步锁单例模式
  3. */
  4. @NotThreadSafe
  5. @Slf4j
  6. public class SingletonExample3 {
  7. //私有构造函数
  8. private SingletonExample3() {
  9. }
  10. /**
  11. * 1.分配对象的内存空间
  12. * 2.初始化对象
  13. * 3.设置instance 指向刚分配的内存
  14. * 在单线程的情况下没有问题,但是在多线程的情况下
  15. *
  16. * jvm 和cpu 优化,发生指令重排
  17. *
  18. * 1.分配对象的内存空间
  19. * 2.设置instance 指向刚分配的内存
  20. * 3.初始化对象
  21. *
  22. * 因此我们需要限制SingletonExample3类的创建的时候的指令重排,添加volatile关键字
  23. */
  24. //单例对象
  25. //volatile 加上 双重检测机制,禁止指令重排
  26. private volatile static SingletonExample3 instance = null;
  27. //静态的工厂方法
  28. public static SingletonExample3 getInstance() {
  29. if (instance == null) { //双重检测机制
  30. synchronized (SingletonExample3.class) { //同步锁
  31. if (instance == null) {
  32. instance = new SingletonExample3();
  33. }
  34. }
  35. }
  36. return instance;
  37. }
  38. }



 饿汉模式 (静态块的方式)


  
  1. /**
  2. * static 静态块的饿汉模式
  3. */
  4. public class SingletonExample6 {
  5. private SingletonExample6(){}
  6. private static SingletonExample6 instance =null;
  7. static {
  8. instance = new SingletonExample6();
  9. }
  10. private static SingletonExample6 getInstance(){
  11. return instance;
  12. }
  13. public static void main(String[] args){
  14. System.out.println(getInstance());
  15. System.out.println(getInstance());
  16. }
  17. }



枚举模式(最推荐) 


  
  1. /**
  2. * 枚举模式,最安全
  3. * 利用枚举的特性,一个枚举的构造方法只会被调用一次实现单例模式,推荐使用这种方式
  4. * <p>
  5. * 优点:1、相比懒汉模式,更能保证线程安全性
  6. * 2、相比饿汉模式,也只会在第一次使用的时候进行创建实例,不会造成资源浪费
  7. */
  8. @ThreadSafe
  9. @Recommend
  10. public class SingletonExample7 {
  11. private SingletonExample7() {
  12. }
  13. public static SingletonExample7 getInstance() {
  14. return Singleton.INSTANCE.getInstance();
  15. }
  16. private enum Singleton {
  17. INSTANCE; //只有一个枚举变量,保证构造方法只调用一次
  18. private SingletonExample7 singletonExample7;
  19. //jvm保证这个方法绝对只调用一次
  20. Singleton() {
  21. System.out.println("我只会被调用一次");
  22. singletonExample7 = new SingletonExample7();
  23. }
  24. public SingletonExample7 getInstance() {
  25. return singletonExample7;
  26. }
  27. }
  28. public static void main(String[] args) {
  29. System.out.println(SingletonExample7.getInstance());
  30. System.out.println(SingletonExample7.getInstance());
  31. System.out.println(SingletonExample7.getInstance());
  32. }
  33. }

 六、原型模式

定义:指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象,不需要知道任何创建的细节,不调用构造函数

类型:创建型

适用场景:

  • 类初始化消耗较多资源
  • new产生的一个对象需要非常繁琐的过程(数据准备,访问权限等)
  • 构造函数比较复杂
  • 循环体中生产大量对象时

优点:

  • 原型模式性能比直接new一个对象性能高
  • 简化创建过程

缺点:

  • 必须配备克隆方法
  • 对克隆复杂对象或者对克隆出的对象进行复杂改造时,容易引入风险
  • 深拷贝,浅拷贝要运用得当

深克隆:

一个类实现cloneable接口,并在重写clone方法的时候对该类的引用数据类型对象属性也需要调用clone方法进行拷贝

浅克隆:

一个类实现cloneable接口,默认就是浅克隆

com.dzx.design.creational.prototype 包下代码 原型模式
 

  
  1. package com.dzx.design.creational.prototype.clone;
  2. import java.util.Date;
  3. /**
  4. * @author dzx
  5. * @ClassName:
  6. * @Description: 原型模式
  7. * @date 2019年07月30日 10:58:53
  8. */
  9. public class Pig implements Cloneable {
  10. private String name;
  11. private Date birthday;
  12. @Override
  13. public String toString() {
  14. return "Pig{" +
  15. "name='" + name + '\'' +
  16. ", birthday=" + birthday +
  17. '}' + super.toString();
  18. }
  19. public Pig(String name, Date birthday) {
  20. this.name = name;
  21. this.birthday = birthday;
  22. }
  23. public String getName() {
  24. return name;
  25. }
  26. public void setName(String name) {
  27. this.name = name;
  28. }
  29. public Date getBirthday() {
  30. return birthday;
  31. }
  32. public void setBirthday(Date birthday) {
  33. this.birthday = birthday;
  34. }
  35. @Override
  36. protected Object clone() throws CloneNotSupportedException {
  37. Pig pig = (Pig) super.clone();
  38. pig.birthday = (Date) pig.getBirthday().clone();
  39. return pig;
  40. }
  41. }

  
  1. package com.dzx.design.creational.prototype.clone;
  2. /**
  3. * @author dzx
  4. * @ClassName:
  5. * @Description: 原型模式
  6. * @date 2019年07月15日 10:02:06
  7. */
  8. import java.io.Serializable;
  9. /**
  10. * 饿汉式+静态代码块
  11. */
  12. public class SingletonExample4 implements Serializable,Cloneable {
  13. private SingletonExample4() {
  14. }
  15. /**
  16. * 防止通过反射和克隆方法破坏单例模式
  17. * @return
  18. * @throws CloneNotSupportedException
  19. */
  20. @Override
  21. protected Object clone() throws CloneNotSupportedException {
  22. return getInstance();
  23. }
  24. private static SingletonExample4 singletonExample4 = null;
  25. static {
  26. singletonExample4 = new SingletonExample4();
  27. }
  28. public static SingletonExample4 getInstance() {
  29. return singletonExample4;
  30. }
  31. }

克隆测试以及 通过克隆破坏单例模式


  
  1. package com.dzx.design.creational.prototype.clone;
  2. import java.lang.reflect.InvocationTargetException;
  3. import java.lang.reflect.Method;
  4. import java.util.Date;
  5. /**
  6. * @author dzx
  7. * @ClassName:
  8. * @Description: 原型模式
  9. * @date 2019年07月30日 10:59:57
  10. */
  11. public class Test {
  12. public static void main(String[] args) throws CloneNotSupportedException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
  13. Date date = new Date(0);
  14. Pig pig = new Pig("佩奇", date);
  15. Pig clone = (Pig) pig.clone();
  16. System.out.println(pig);
  17. System.out.println(clone);
  18. //默认为浅克隆,当我们修改pig对象的生日时候,clone对象的生日也跟着被修改了
  19. //如果要做到深克隆,就需要重写深克隆,并且在克隆的时候,需要克隆pig类里面属性为引用数据类型的对象
  20. //否则很容易引起bug
  21. pig.getBirthday().setTime(66666666L);
  22. System.out.println(pig);
  23. System.out.println(clone);
  24. //利用反射和克隆破坏单例模式
  25. SingletonExample4 instance = SingletonExample4.getInstance();
  26. Method clone1 = instance.getClass().getDeclaredMethod("clone");
  27. clone1.setAccessible(true);//原本是protected,需要打开访问权限
  28. //执行clone方法
  29. SingletonExample4 invoke = (SingletonExample4) clone1.invoke(instance);
  30. //发现instance 和 invoke 两个对象的引用地址是不同的,说明单例已被破坏掉
  31. //解决防范:1 不实现cloneable接口 2在重写的clone()方法中调用getInstance()方法
  32. System.out.println(instance);
  33. System.out.println(invoke);
  34. }
  35. }

 

 

文章来源: blog.csdn.net,作者:血煞风雨城2018,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/qq_31905135/article/details/93748355

(完)