Leetcode-设计模式-构建型模式
Posted 钢铁-程序猿
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Leetcode-设计模式-构建型模式相关的知识,希望对你有一定的参考价值。
文章目录
设计模式-构建型模式
一、工厂模式
1、简单工厂模式(让一个工厂创建所有对象)
工厂模式缺点:
- 让一个工厂类承担构建所有对象的职责,一是如果需要生产的产品过多,此模式会导致工厂类过于庞大,承担过多的职责,变成超级类。违背了单一职责原则。
- 二是当要生产新的产品时,必须在工厂类中添加新的分支。而开闭原则告诉我们:类应该对修改封闭。我们希望在添加新功能时,只需增加新的类,而不是修改既有的类,所以这就违背了开闭原则。
public class FruitFactory
public Fruit create(String type)
switch (type)
case "苹果":
AppleSeed appleSeed = new AppleSeed();
Sunlight sunlight = new Sunlight();
Water water = new Water();
return new Apple(appleSeed, sunlight, water);
case "梨子":
return new Pear();
default:
throw new IllegalArgumentException("暂时没有这种水果");
2、工厂方法模式(每个产品有一个专属工厂)
虽然每个产品都有自己的工厂,但是还是满足单一职责和开闭原则的。
- 1、如果苹果的生产过程需要修改时,只需修改苹果工厂,调用方不需要修改。梨子的生产过程需要修改时,只需修改梨子工厂。符合单一职责原则。
- 2、当需要生产新的产品时,无需更改既有的工厂,只需要添加新的工厂即可。保持了面向对象的可扩展性,符合开闭原则。
public class User
private void eat()
AppleFactory appleFactory = new AppleFactory();
Fruit apple = appleFactory.create();
PearFactory pearFactory = new PearFactory();
Fruit pear = pearFactory.create();
apple.eat();
pear.eat();
3、抽象工厂模式(定义接口,提取出工厂的接口,使用时无需关心是哪个工厂类,适合横向扩展(新增工厂),不适合纵向扩展(新增功能))
提供接口,我们在创建时指定了具体的工厂类后,在使用时就无需再关心是哪个工厂类,只需要将此工厂当作抽象的 IFactory 接口使用即可。这种经过抽象的工厂方法模式被称作抽象工厂模式。
public class User
private void eat()
IFactory appleFactory = new AppleFactory();
Fruit apple = appleFactory.create();
IFactory pearFactory = new PearFactory();
Fruit pear = pearFactory.create();
apple.eat();
pear.eat();
实际上抽象工厂模式主要用于替换一系列方法。
例如将程序中的 SQL Server 数据库整个替换为 Access 数据库,使用抽象方法模式的话,只需在 IFactory 接口中定义好增删改查四个方法,让 SQLFactory 和 AccessFactory 实现此接口,调用时直接使用 IFactory 中的抽象方法即可,调用者无需知道使用的什么数据库,我们就可以非常方便的整个替换程序的数据库,并且让客户端毫不知情。
抽象工厂模式很好的发挥了开闭原则、依赖倒置原则,但缺点是抽象工厂模式太重了,如果 IFactory 接口需要新增功能,则会影响到所有的具体工厂类。所以抽象工厂模式适用于增加同类工厂这样的横向扩展需求,不适合新增功能这样的纵向扩展。
二、单例模式
1、饿汉式
饿汉式:变量在声明时便初始化。
public class Singleton
private static Singleton instance = new Singleton();
private Singleton()
public static Singleton getInstance()
return instance;
2、懒汉式
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;
问:双检锁单例模式中,volatile 主要用来防止哪几条指令重排序?如果发生了重排序,会导致什么样的错误?
答案:
instance = new Singleton();
这一行代码中,执行了三条重要的指令:
- 分配对象的内存空间
- 初始化对象
- 将变量 instance 指向刚分配的内存空间
在这个过程中,如果第二条指令和第三条指令发生了重排序,可能导致 instance 还未初始化时,其他线程提前通过双检锁外层的 null 检查,获取到“不为 null,但还没有执行初始化”的 instance 对象,发生空指针异常。
三、建造者模式(通过链式调用创建对象,建造者模式主要应用于构建过程稳定,可通过不同配置建造出不同对象的场景。)
意图:将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。
何时使用:一些基本部件不会变,而其组合经常变化的时候。
使用建造者模式表示如下:
public class MilkTea
private final String type;
private final String size;
private final boolean pearl;
private final boolean ice;
private MilkTea()
private MilkTea(Builder builder)
this.type = builder.type;
this.size = builder.size;
this.pearl = builder.pearl;
this.ice = builder.ice;
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 cold)
this.ice = cold;
return this;
public MilkTea build()
return new MilkTea(this);
public class User
private void buyMilkTea()
MilkTea milkTea = new MilkTea.Builder("原味").build();
show(milkTea);
MilkTea chocolate =new MilkTea.Builder("巧克力味")
.ice(false)
.build();
show(chocolate);
MilkTea strawberry = new MilkTea.Builder("草莓味")
.size("大杯")
.pearl(false)
.ice(true)
.build();
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() + "奶茶");
可以看到,我们将 MilkTea 的构造方法设置为私有的,所以外部不能通过 new 构建出 MilkTea 实例,只能通过 Builder 构建。对于必须配置的属性,通过 Builder 的构造方法传入,可选的属性通过 Builder 的链式调用方法传入,如果不配置,将使用默认配置,也就是中杯、加珍珠、不加冰。
四、原型模式
手写clone方法
public class MilkTea
public String type;
public boolean ice;
public MilkTea clone()
MilkTea milkTea = new MilkTea();
milkTea.type = this.type;
milkTea.ice = this.ice;
return milkTea;
private void order()
MilkTea milkTeaOfJay = new MilkTea();
milkTeaOfJay.type = "原味";
milkTeaOfJay.ice = false;
MilkTea yourMilkTea = milkTeaOfJay.clone();
// 一千位粉丝都调用 milkTeaOfJay 的 clone 方法即可
...
java自带的Cloneable(不需手写Clone方法)
public class MilkTea implements Cloneable
public String type;
public boolean ice;
@NonNull
@Override
protected MilkTea clone() throws CloneNotSupportedException
return (MilkTea) super.clone();
值得注意的是,Java 自带的 clone 方法是浅拷贝的。也就是说调用此对象的 clone 方法,只有基本类型的参数会被拷贝一份,非基本类型的对象不会被拷贝一份,而是继续使用传递引用的方式。如果需要实现深拷贝,必须要自己手动修改 clone 方法才行。
总结
- 工厂方法模式:为每一类对象建立工厂,将对象交由工厂创建,客户端只和工厂打交道。
- 抽象工厂模式:为每一类工厂提取出抽象接口,使得新增工厂、替换工厂变得非常容易。
- 建造者模式:用于创建构造过程稳定的对象,不同的 Builder 可以定义不同的配置。
- 单例模式:全局使用同一个对象,分为饿汉式和懒汉式。懒汉式有双检锁和内部类两种实现方式。
- 原型模式:为一个类定义 clone 方法,使得创建相同的对象更方便。
以上是关于Leetcode-设计模式-构建型模式的主要内容,如果未能解决你的问题,请参考以下文章