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

举报
小米粒-biubiubiu 发表于 2020/11/29 00:33:17 2020/11/29
3.4k+ 0 0
【摘要】 代码码云仓库地址:https://gitee.com/dzxmy/design_pattern 常用的创建型设计模式有:工厂方法模式,抽象工厂模式,建造者模式,单例模式。 不常用的创建型设计模式有:简单工厂,原型模式 一、简单工厂 定义:由一个工厂对象决定创建出哪一种产品类的实例 类型:创建型,但不属于GOF23种设计模式 适用场景: 工厂类负责创建的对象比较少...

代码码云仓库地址: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 包下代码:单例模式
 

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


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


饿汉模式 


      /**
       * 饿汉模式
       */
      public class SingletonExample2 {
      private SingletonExample2() {
       }
      private static SingletonExample2 singletonExample2 = new SingletonExample2();
      public static SingletonExample2 getInstance() {
      return singletonExample2;
       }
      }
  
 



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


      /**
       * 懒汉模式 -》》双重同步锁单例模式
       */
      @NotThreadSafe
      @Slf4j
      public class SingletonExample3 {
      //私有构造函数
      private SingletonExample3() {
       }
      /**
       * 1.分配对象的内存空间
       * 2.初始化对象
       * 3.设置instance 指向刚分配的内存
       * 在单线程的情况下没有问题,但是在多线程的情况下
       *
       * jvm 和cpu 优化,发生指令重排
       *
       * 1.分配对象的内存空间
       * 2.设置instance 指向刚分配的内存
       * 3.初始化对象
       *
       * 因此我们需要限制SingletonExample3类的创建的时候的指令重排,添加volatile关键字
       */
      //单例对象
      //volatile 加上 双重检测机制,禁止指令重排
      private volatile static SingletonExample3 instance = null;
      //静态的工厂方法
      public static  SingletonExample3 getInstance() {
      if (instance == null) { //双重检测机制
      synchronized (SingletonExample3.class) { //同步锁
      if (instance == null) {
       instance = new SingletonExample3();
       }
       }
       }
      return instance;
       }
      }
  
 



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


      /**
       * static 静态块的饿汉模式
       */
      public class SingletonExample6 {
      private SingletonExample6(){}
      private static SingletonExample6 instance =null;
      static  {
       instance = new SingletonExample6();
       }
      private static SingletonExample6 getInstance(){
      return instance;
       }
      public static void main(String[] args){
       System.out.println(getInstance());
       System.out.println(getInstance());
       }
      }
  
 



枚举模式(最推荐) 


      /**
       * 枚举模式,最安全
       * 利用枚举的特性,一个枚举的构造方法只会被调用一次实现单例模式,推荐使用这种方式
       * <p>
       * 优点:1、相比懒汉模式,更能保证线程安全性
       * 2、相比饿汉模式,也只会在第一次使用的时候进行创建实例,不会造成资源浪费
       */
      @ThreadSafe
      @Recommend
      public class SingletonExample7 {
      private SingletonExample7() {
       }
      public static SingletonExample7 getInstance() {
      return Singleton.INSTANCE.getInstance();
       }
      private enum Singleton {
       INSTANCE; //只有一个枚举变量,保证构造方法只调用一次
      private SingletonExample7 singletonExample7;
      //jvm保证这个方法绝对只调用一次
       Singleton() {
       System.out.println("我只会被调用一次");
       singletonExample7 = new SingletonExample7();
       }
      public SingletonExample7 getInstance() {
      return singletonExample7;
       }
       }
      public static void main(String[] args) {
       System.out.println(SingletonExample7.getInstance());
       System.out.println(SingletonExample7.getInstance());
       System.out.println(SingletonExample7.getInstance());
       }
      }
  
 

 六、原型模式

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

类型:创建型

适用场景:

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

优点:

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

缺点:

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

深克隆:

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

浅克隆:

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

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

      package com.dzx.design.creational.prototype.clone;
      import java.util.Date;
      /**
       * @author dzx
       * @ClassName:
       * @Description: 原型模式
       * @date 2019年07月30日 10:58:53
       */
      public class Pig implements Cloneable {
      private String name;
      private Date birthday;
      @Override
      public String toString() {
      return "Pig{" +
      "name='" + name + '\'' +
      ", birthday=" + birthday +
      '}' + super.toString();
       }
      public Pig(String name, Date birthday) {
      this.name = name;
      this.birthday = birthday;
       }
      public String getName() {
      return name;
       }
      public void setName(String name) {
      this.name = name;
       }
      public Date getBirthday() {
      return birthday;
       }
      public void setBirthday(Date birthday) {
      this.birthday = birthday;
       }
      @Override
      protected Object clone() throws CloneNotSupportedException {
       Pig pig = (Pig) super.clone();
       pig.birthday = (Date) pig.getBirthday().clone();
      return pig;
       }
      }
  
 

      package com.dzx.design.creational.prototype.clone;
      /**
       * @author dzx
       * @ClassName:
       * @Description: 原型模式
       * @date 2019年07月15日 10:02:06
       */
      import java.io.Serializable;
      /**
       * 饿汉式+静态代码块
       */
      public class SingletonExample4 implements Serializable,Cloneable {
      private SingletonExample4() {
       }
      /**
       * 防止通过反射和克隆方法破坏单例模式
       * @return
       * @throws CloneNotSupportedException
       */
       @Override
      protected Object clone() throws CloneNotSupportedException {
      return getInstance();
       }
      private static SingletonExample4 singletonExample4 = null;
      static {
       singletonExample4 = new SingletonExample4();
       }
      public static SingletonExample4 getInstance() {
      return singletonExample4;
       }
      }
  
 

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


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

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

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

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

作者其他文章

评论(0

抱歉,系统识别当前为高风险访问,暂不支持该操作

    全部回复

    上滑加载中

    设置昵称

    在此一键设置昵称,即可参与社区互动!

    *长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

    *长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。