思维导图
工厂模式
平常编程过程中创建对象的方式就是new一个对象,每new一次对象调用者就相当于多知道了一个类,增加了耦合性。因此可以把对象的生产交给工厂去完成。
简单工厂模式
比如需要外科口罩和N95口罩,生成这两个对象可以交给工厂完成。
1 2
| public abstract class Mask { }
|
1 2 3 4 5 6
| public class N95Mask extends Mask { @Override public String toString() { return "这是N95口罩"; } }
|
1 2 3 4 5
| public class SurgicalMask extends Mask { public String toString() { return "这是医用口罩"; } }
|
生产口罩的工厂:
1 2 3 4 5 6 7 8 9 10 11 12
| public class MaskFactory { public Mask create(String type) { switch (type) { case "Surgical": return new SurgicalMask(); case "N95": return new N95Mask(); default: throw new IllegalArgumentException("Unsupported mask type"); } } }
|
测试:
1 2 3 4 5 6 7 8 9
| public class Client { @Test public void test() { MaskFactory factory = new MaskFactory(); System.out.println(factory.create("Surgical")); System.out.println(factory.create("N95")); } }
|
输出:
- 优点:
- 降低耦合
- 隐藏生产细节提高代码复用
- 缺点:
- 生产产品过多时,工厂类过大,承担过多职责,会形成超级类,违背单一职责原则
- 需要生产新产品时,需要在工厂类中添加新分支,不符合开闭原则。类应该对修改封闭,添加新功能时应添加新的类
工厂方法模式
每个对象由单独的工厂来生产,符合开闭与单一职责原则
1 2 3 4 5
| public class N95Factory { public Mask create() { return new N95Mask(); } }
|
1 2 3 4 5
| public class SurgicalFactory { public Mask create() { return new SurgicalMask(); } }
|
测试:
1 2 3 4 5 6 7 8 9
| public class Client { @Test public void test() { SurgicalFactory surgicalFactory = new SurgicalFactory(); N95Factory n95Factory = new N95Factory(); System.out.println(surgicalFactory.create()); System.out.println(n95Factory.create()); } }
|
抽象工厂模式
提取工厂方法模式中各个工厂的公共接口
1 2 3
| public interface IFactory { Mask create(); }
|
实现该接口:
1 2 3 4 5 6
| public class N95MaskFactory implements IFactory { @Override public Mask create() { return new N95Mask(); } }
|
1 2 3 4 5 6
| public class SurgicalFactory implements IFactory { @Override public Mask create() { return new SurgicalMask(); } }
|
测试:
1 2 3 4 5 6 7
| @Test public void test() { IFactory factory = new SurgicalFactory(); System.out.println(factory.create()); factory = new N95MaskFactory(); System.out.println(factory.create()); }
|
优点
- 调用者只需要使用接口而不需要知道接口的具体实现是哪个工厂,这样替换工厂变得很方便
- 很好的发挥了开闭原则,和依赖倒置原则
缺点
- 需要新增抽象方法时,需要修改所有的抽象类,纵向扩展不方便
单例模式
单例模式分为
- 饿汉式:变量在声明时就初始化
- 懒汉式:变量在需要使用时初始化
饿汉式
1 2 3 4 5 6 7 8 9
| public class Singleton { private static Singleton instance = new Singleton(); private Singleton() {
} public static Singleton getInstance() { return instance; } }
|
缺点: 占内存、启动慢
懒汉式
1 2 3 4 5 6 7 8 9 10 11
| public class Singleton { private static Singleton instance = null; private Singleton() {
} public static Singleton getInstance() { if (instance == null) instance = new Singleton(); return instance; } }
|
以上写法线程不安全,在多线程调用时instance会被实例化多次因此需要给判空过程加锁
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class Singleton { private static Singleton instance = null; private Singleton() {
} public static Singleton getInstance() { synchronized (Singleton.class){ if (instance == null) instance = new Singleton(); } return instance; } }
|
改进后的问题:多个线程调用getInstance时每次都需要执行synchronized同步化方法,严重影响程序执行效率,所以在同步之前需要加上一层检查
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class Singleton { private static Singleton instance = null; private Singleton() {
} public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class){ if (instance == null) instance = new Singleton(); } } return instance; } }
|
改进后的问题: 由于JVM的底层优化,可能会改变指令执行顺序
1
| instance = new Singleton();
|
这一行代码中,执行了三条重要的指令:
- 分配对象的内存空间
- 初始化对象
- 将变量 instance 指向刚分配的内存空间
如果第二条指令和第三条指令发生了重排序,可能导致 instance 还未初始化时,其他线程提前通过双检锁外层的 null 检查,获取到“不为 null,但还没有执行初始化”的 instance 对象,发生空指针异常。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class Singleton { private static volatile Singleton instance = null; private Singleton() {
} public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class){ if (instance == null) instance = new Singleton(); } } return instance; } }
|
还可以使用静态内部类实现懒汉式:
1 2 3 4 5 6 7 8 9
| public class Singleton { private static class SingletonHolder { public static Singleton instance = new Singleton(); }
private static Singleton getInstance() { return SingletonHolder.instance; } }
|
静态内部类在使用时才会被加载,JVM保证了线程安全
建造者模式
将复杂的构建与其表示分离,使同样的构建过程能够创建不同的表示。
就比如制作一杯奶茶的过程是一样的,但是奶茶的规格、是否加冰、是否加珍珠是可选的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| public class MilkTea { private String type; private String size; private boolean pearl; private boolean ice;
private MilkTea() {}
private MilkTea(Builder builder) { this.type = builder.type; this.size = builder.size; this.ice = builder.ice; this.pearl = builder.pearl; }
public String getType() { return type; }
public String getSize() { return size; }
public boolean isPearl() { return pearl; }
public boolean isIce() { return ice; }
public static class Builder { private final String type; private String size = "中杯"; private boolean pearl = true; private boolean ice = false;
public Builder(String type) { this.type = type; }
public Builder size(String size) { this.size = size; return this; }
public Builder pearl(boolean pearl) { this.pearl = pearl; return this; }
public Builder ice(boolean ice) { this.ice = ice; return this; }
public MilkTea builde() { return new MilkTea(this); } } }
|
对于必须配置的属性,通过 Builder 的构造方法传入,可选的属性通过 Builder 的链式调用方法传入,如果不配置,将使用默认配置,也就是中杯、加珍珠、不加冰。根据不同的配置可以制作出不同的奶茶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| public class User { private void buyMilkTea() { MilkTea milkTea = new MilkTea.Builder("原味").builde(); show(milkTea);
MilkTea chocolate = new MilkTea.Builder("巧克力味") .ice(false) .builde(); show(chocolate);
MilkTea strawberry = new MilkTea.Builder("草莓味") .size("大杯") .pearl(false) .ice(true) .builde(); show(strawberry); }
private void show(MilkTea milkTea) { String pearl; if (milkTea.isPearl()) { pearl = "加珍珠"; } else { pearl = "不加珍珠"; } String ice; if (milkTea.isIce()) { ice = "加冰"; } else { ice = "不加冰"; } System.out.println("一份" + milkTea.getSize() + "、" + pearl + "、" + ice + "的" + milkTea.getType() + "奶茶"); }
public static void main(String[] args) { User user = new User(); user.buyMilkTea(); } }
|
原型模式
对已有的对象进行拷贝,创建相同的对象更加方便
1 2 3 4 5 6 7 8 9 10 11
| public class MilkTea { public String type; public boolean ice;
public MilkTea clone() { MilkTea milkTea = new MilkTea(); milkTea.ice = this.ice; milkTea.type = this.type; return milkTea; } }
|
Java有一个语法糖,不需要手写clone,只需要实现Cloneable接口
1 2 3 4 5 6 7 8 9 10
| public class MilkTea implements Cloneable{ public String type; public boolean ice;
@NonNull @Override protected MilkTea clone() throws CloneNotSupportedException { return (MilkTea) super.clone(); } }
|
Git仓库
https://github.com/wuzheng228/designPatternGuide
参考
《深入浅出设计模式》