软件开发设计模式之【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);
-
-
-
}
-
}
文章来源: blog.csdn.net,作者:血煞风雨城2018,版权归原作者所有,如需转载,请联系作者。
原文链接:blog.csdn.net/qq_31905135/article/details/93748355
- 点赞
- 收藏
- 关注作者
评论(0)