赞
踩
工厂模式缺点:
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("暂时没有这种水果");
}
}
}
虽然每个产品都有自己的工厂,但是还是满足单一职责和开闭原则的。
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();
}
}
提供接口,我们在创建时指定了具体的工厂类后,在使用时就无需再关心是哪个工厂类,只需要将此工厂当作抽象的 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 接口需要新增功能,则会影响到所有的具体工厂类。所以抽象工厂模式适用于增加同类工厂这样的横向扩展需求,不适合新增功能这样的纵向扩展。
饿汉式:变量在声明时便初始化。
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return instance;
}
}
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 还未初始化时,其他线程提前通过双检锁外层的 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 方法即可
...
}
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 方法才行。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。