软件开发设计模式之【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 包下代码:单例模式

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


/**
 * 懒汉模式
 * 单例的实例在第一次使用的时候进行创建
 * <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);


    }
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小米吃辣椒2022

你的鼓励将是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值