设计模式
工厂方法模式与抽象工厂模式
假定场景
有一个披萨店要卖披萨,这需要一段程序。由于披萨有多个类型,可以实现为以下代码:
public class PizzaStore {
public void orderPizza(String type) {
Pizza pizza;
if ("a".equals(type)) {
pizza = new APizza();
} else if ("b".equals(type)) {
pizza = new BPizza();
} else {
pizza = null;
}
if (pizza != null) {
pizza.cut();
pizza.box();
pizza.finish();
}
}
}
上述程序看上去貌似没什么问题。不过,程序写完后首先要考虑这部分的需求是否会发生变化!
假设:店家想增加2种新口味的披萨,删除1种卖的不好的披萨。
此时,就需要修改上述代码。但是,这违反了设计原则–对扩展开放,对修改关闭
。要知道,需求通常是会发生变化的,而反复地修改代码通常会使得代码难以维护!
考虑策略设计模式
的设计原则之一 – 封装需要变化的部分
。新建一个“工厂类”来处理创建对象
的需求,如下:
public class PizzaFactory {
/*
并非静态方法
*/
public Pizza createPizza(String type) {
Pizza pizza;
if ("a".equals(type)) {
pizza = new APizza();
} else if ("b".equals(type)) {
pizza = new BPizza();
} else {
pizza = null;
}
return pizza;
}
}
相应地,修改PizzaStore.java的代码,如下:
public class PizzaStore {
private PizzaFactory factory;
public PizzaStore(PizzaFactory factory) {
this.factory = factory;
}
public void orderPizza(String type) {
//采用PizzaFactory来创建对象
Pizza pizza = factory.createPizza(type);
if (pizza != null) {
pizza.cut();
pizza.box();
pizza.finish();
}
}
}
上述代码封装了变化的部分,这有利于代码复用。现在需求又发生了变化,如下:
假设:披萨店火了,国内很多地区都想要加盟,但是不同省份需要制作不同口味的披萨来满足当地人的需求。
此时,可以让每个加盟店对应一个特定的工厂来创建披萨。如下:
public abstract class PizzaFactory {
public abstract Pizza createPizza(String type);
}
public class APizzaFactory extends PizzaFactory{
@Override
public Pizza createPizza(String type) {
Pizza pizza;
if ("c".equals(type)) {
pizza = new APizza();
} else if ("d".equals(type)) {
pizza = new BPizza();
} else {
pizza = null;
}
return pizza;
}
}
上述流程中,a地区的商家可以直接使用自己的APizzaFactory
获取一个披萨对象,然后商家自己
就可以决定是否box,cut,即商家自己
可以不需要PizzaStore
。因此,我们无法控制披萨制作的整个流程。为了加强质量控制,可以把PizzaStore
抽象出来,做成一个抽象类,然后把制作披萨的工厂方法
加入进来,让各个商家实现该类。
public abstract class PizzaStore {
public void orderPizza(String type) {
//解耦
Pizza pizza = createPizza(type);
if (pizza != null) {
pizza.cut();
pizza.box();
pizza.finish();
}
}
//工厂方法
protected abstract Pizza createPizza(String type);
}
public class APizzaStore extends PizzaStore {
@Override
public Pizza createPizza(String type) {
Pizza pizza;
if ("c".equals(type)) {
pizza = new APizza();
} else if ("d".equals(type)) {
pizza = new BPizza();
} else {
pizza = null;
}
return pizza;
}
}
可以看到,工厂方法
让子类决定需要创建的具体对象,从而实现对象创建过程的封装。
工厂方法模式
在类中定义一个创建对象的接口,让子类决定到底创建哪个具体的对象。从而把对象的实例化推出到了子类。
依赖倒置
依赖抽象,而不要依赖具体类。
意思是说,不要让高层组件
依赖低层组件
,且两种组件都应该依赖抽象。
最开始的代码,PizzaStore
依赖着所有具体的Pizza
。后续代码的依赖性有所降低,因为我们让高层组件PizzaStore
依赖于了低层组件具体的Pizza
的抽象。这是工厂方法模式
的效果。
倒置思维方式
从顶端到低端的思维是:需要一个披萨店类,这个类可以制作各种披萨,剪切,打包…
倒置:需要制作各种披萨,它们应该有一个共同的接口Pizza
.披萨的制作依赖该抽象接口即可,具体的披萨类也依赖该接口,创建具体披萨的接口定义为返回该类型即可。
指导方针
- 变量不要持有
具体类
的引用
我们要针对接口编程,即依赖抽象。 - 不要让类继承具体类
如果继承具体,就必然依赖具体类。 - 不要覆盖基类中已经实现的方法
如果子类覆盖了基类中的方法,就说明基类不适合被继承
假设
制作披萨需要各种原料,我们不希望各个加盟店自行决定,而是希望指定的原料工厂来提供。
新增一个方法prepare,来准备原材料。
public abstract class Pizza {
//某种原材料
private Sault sault;
public void finish() {
}
public void cut() {
}
public void box() {
}
public abstract void prepare();
}
public interface Sault {
}
不同地区采用不同工厂的原材料,因此可以设置一个原材料工厂的接口:
public interface IngredientFactory {
Sault createSault();
}
public class AIngredientFactory implements IngredientFactory {
@Override
public Sault createSault() {
return new Sault(){
@Override
public String toString() {
return "a sault";
}
};
}
}
具体披萨的制作:
public class APizza extends Pizza {
private Sault sault;
private IngredientFactory ingredientFactory = new AIngredientFactory();
@Override
public void prepare() {
sault = ingredientFactory.createSault();
}
}
抽象工厂模式
提供接口来创建相关对象或依赖对象的家族,而不需要明确指明具体的类。
比如,上述的ingredientFactory.createSault(),并没有指定创建哪种具体的Sault
。我们的客户端代码APizza
也从具体的Sault
中解耦。