经典设计模式有23个,分为创建型,结构型和行为型。
创建型主要解决对象的创建问题,封装复杂的创建过程,解耦对象的创建代码和使用代码。
创建型的设计模式有工厂模式,抽象工厂模式,单例模式,建造者模式和原型模式。
工厂模式-Factory
简单工厂
package cn.hgy.simplefactory;
/**
* @author guoyu.huang
* @version 1.0.0
*/
public class SimpleFactory {
public BaseClass getClass(String type) {
String classAType = "classA";
if (classAType.equals(type)) {
return new ClassA();
} else {
return new ClassB();
}
}
}
如果要增加类型,需要更新工厂代码,不符合开闭原则。解决方案如下:
工厂模式
工厂接口
package cn.hgy.factory;
/**
* @author guoyu.huang
* @version 1.0.0
*/
public abstract class BaseFactory {
protected abstract boolean handle(String type);
public abstract BaseClass create();
}
工厂接口实现
package cn.hgy.factory;
/**
* @author guoyu.huang
* @version 1.0.0
*/
public class ClassAFactory extends BaseFactory {
private static final String CLASS_A_TYPE = "classA";
@Override
public boolean handle(String type) {
return CLASS_A_TYPE.equals(type);
}
@Override
public BaseClass create() {
return new ClassA();
}
}
工厂类
package cn.hgy.factory;
import java.io.File;
import java.util.*;
/**
* @author guoyu.huang
* @version 1.0.0
*/
public class Factory {
public BaseClass getClass(String type) {
List<BaseFactory> factoryList = getAllClass();
for(BaseFactory baseFactory : factoryList) {
if(baseFactory.handle(type)){
return baseFactory.create();
}
}
return null;
}
/**
* 根据反射获取所有的工厂类
* @return
*/
private List<BaseFactory> getAllClass(){
File directory = new File("D:\\codespace\\design-pattern-study\\factory\\target\\classes\\cn\\hgy\\factory");
File[] files = directory.listFiles();
List<BaseFactory> factoryList = new ArrayList<>();
for (File file : files) {
String filePath = file.getPath().replace("D:\\codespace\\design-pattern-study\\factory\\target\\classes\\", "");
filePath = filePath.replaceAll(".class", "");
filePath = filePath.replaceAll("\\\\", ".");
try {
Class clazz = Class.forName(filePath);
if(clazz.getSuperclass() != null && clazz.getSuperclass().equals(BaseFactory.class)){
BaseFactory factory = (BaseFactory)Class.forName(filePath).newInstance();
factoryList.add(factory);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
return factoryList;
}
}
如果使用spring,做法则更简单,代码如下:
@Component
public class ClassAFactory extends BaseFactory {
// something
}
/**
* @author guoyu.huang
* @version 1.0.0
*/
@Component
public class Factory {
@Autowired
private List<BaseFactory> factoryList;
public BaseClass getClass(String type) {
for(BaseFactory baseFactory : factoryList) {
if(baseFactory.handle(type)){
return baseFactory.create();
}
}
return null;
}
}
使用工厂实现简单spring工厂,参考地址:https://github.com/CNXMBuyu/design-pattern-study.git
抽象工厂
抽象工厂可以理解是工厂的工厂,将同一类型的工厂抽象成一个工厂类,用于大型的创建对象。
单例模式 - Singleton
单例模式不再过多描述,直接上例子。
饿汉式
package cn.hgy.single.eager;
/**
* 饿汉单例模式,先实例化
*
* @author guoyu.huang
* @version 1.0.0
*/
public class EagerSingleton {
private static EagerSingleton eagerSingleton = new EagerSingleton();
private EagerSingleton(){
}
public static EagerSingleton getInstance(){
return eagerSingleton;
}
}
懒汉式
package cn.hgy.single.lazy;
/**
* 懒汉单例模式,需要的时候实例化
*
* @author guoyu.huang
* @version 1.0.0
*/
public class LazySingleton {
private static LazySingleton instance;
private LazySingleton() {
}
public static LazySingleton getInstance() {
if (instance == null) {
return new LazySingleton();
} else {
return instance;
}
}
}
双重校验
package cn.hgy.single.doublecheck;
/**
* 双重校验单例模式
*
* @author guoyu.huang
* @version 1.0.0
*/
public class DoubleCheckSingleton {
private static volatile DoubleCheckSingleton instance;
private DoubleCheckSingleton() {
}
public static DoubleCheckSingleton getInstance() {
if (instance == null) {
synchronized (DoubleCheckSingleton.class) {
instance = new DoubleCheckSingleton();
}
}
return instance;
}
}
内部类
效果等同于双重校验
package cn.hgy.single.inner;
import cn.hgy.single.doublecheck.DoubleCheckSingleton;
/**
* @author guoyu.huang
* @version 1.0.0
*/
public class InnerSingleton {
private InnerSingleton(){}
public static InnerSingleton getInstance() {
return Singleton.instance;
}
private static class Singleton{
private static final InnerSingleton instance = new InnerSingleton();
}
}
建造者-Builder
建造者一般是当构造函数或者set函数无法满足。例如构造函数中参数超过5个,易用性很低;属性之间互相依赖,无法通过set函数来进行逻辑校验。可以考虑建造者模式
package cn.hgy.builder;
/**
* 建造者模式
*
* @author guoyu.huang
* @version 1.0.0
*/
public class BuilderClass {
private String a;
private String b;
private String c;
private String d;
private BuilderClass(Builder builder) {
this.a = builder.a;
this.b = builder.b;
this.c = builder.c;
this.d = builder.d;
}
public static class Builder {
private String a;
private String b;
private String c;
private String d;
public Builder setA(String a) {
this.a = a;
return this;
}
public Builder setB(String b) {
this.b = b;
return this;
}
public Builder setC(String c) {
this.c = c;
return this;
}
public Builder setD(String d) {
this.d = d;
return this;
}
public BuilderClass build() {
if (this.a.equals(b)) {
throw new IllegalArgumentException("a不能和b属性一样");
}
return new BuilderClass(this);
}
}
}
原型模式-Prototype
原型模式就是利用对已有对象(原型)进行复制(或者叫拷贝)的方式,来创建新对象,以达到节省创建时间的目的。
浅拷贝
对象中除了基本类型,引用类型的地址还是指向原对象的地址,所以原对象的属性变更会对其产生印象。
package cn.hgy.prototype;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* @author guoyu.huang
* @version 1.0.0
*/
public class Shallow {
private List<ClassA> list;
public Shallow(List<ClassA> list){
this.list = Arrays.asList(list.toArray(new ClassA[list.size()]).clone());
}
public List<ClassA> getList() {
return list;
}
}
深拷贝
对象中所有属性都是全新的地址,互不干扰。
package cn.hgy.prototype;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* @author guoyu.huang
* @version 1.0.0
*/
public class Deep {
private List<ClassA> list;
public Deep(List<ClassA> list){
this.list = new ArrayList<>(list.size());
list.forEach(classA -> {
this.list.add(deepCopy(classA));
});
}
public List<ClassA> getList() {
return list;
}
public ClassA deepCopy(ClassA object) {
try {
ByteArrayOutputStream bo = new ByteArrayOutputStream();
ObjectOutputStream oo = new ObjectOutputStream(bo);
oo.writeObject(object);
ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
ObjectInputStream oi = new ObjectInputStream(bi);
return (ClassA) oi.readObject();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
通用类
package cn.hgy.prototype;
import java.io.Serializable;
/**
* @author guoyu.huang
* @version 1.0.0
*/
public class ClassA implements Serializable {
private String a;
private String b;
public ClassA(String a, String b) {
this.a = a;
this.b = b;
}
public String getA() {
return a;
}
public void setA(String a) {
this.a = a;
}
public String getB() {
return b;
}
public void setB(String b) {
this.b = b;
}
@Override
public String toString() {
return "ClassA{" +
"a='" + a + '\'' +
", b='" + b + '\'' +
'}';
}
}
package cn.hgy.prototype;
import java.util.ArrayList;
import java.util.List;
/**
* @author guoyu.huang
* @version 1.0.0
*/
public class Prototype {
public static void main(String[] args) {
List<ClassA> list = new ArrayList<>();
list.add(new ClassA("a1", "b1"));
list.add(new ClassA("a2", "b2"));
List<ClassA> shallow = new Shallow(list).getList();
List<ClassA> deep = new Deep(list).getList();
list.get(0).setA("new a1");
System.out.println("shallow foreach");
shallow.forEach(classA -> {
System.out.println(classA.toString());
});
System.out.println("deep foreach");
deep.forEach(classA -> {
System.out.println(classA.toString());
});
}
}
// 打印信息如下:
shallow foreach
ClassA{a='new a1', b='b1'}
ClassA{a='a2', b='b2'}
deep foreach
ClassA{a='a1', b='b1'}
ClassA{a='a2', b='b2'}
总结
创建型设计模式主要是为了解决对象创建问题,当对象的创建通过简单的构造函数实现时,可以考虑创建型设计模式的思路。
- 工厂模式
当对象创建逻辑比较复杂时,可能会有很多的if-else判断时,可以考虑工厂模式。
工厂模式有简单工厂,工厂和抽象工厂。
- 单例模式
当为了防止资源访问冲突时,可以将该资源的访问类设置为单例模式。
单例模式有懒汉式(延迟加载),饿汉式(事先加载),双重校验(懒汉式同步锁加载),内部类(依赖java加载类的特性来事先懒汉式加载)。
- 建造者模式
当一个类的必填属性过多,使用构造函数易用性大大降低,且属性之间有依赖关系,无法通过set函数来控制时,可以考虑建造者模式。
建造者模式会将必填项通过建造者模式类的set函数进行赋值,在实例化具体的类之前,可以编写业务逻辑来进行业务逻辑校验。
- 原型模式
当有大量已存在的对象要额外复制一份时,可以考虑使用原型模式。
原型模式的实现方式有浅拷贝和深拷贝。浅拷贝的对象引用类型的属性指向原对象的地址,s会互相影响;深拷贝的对象引用类型的属性都是全新的地址,互不影响。