又来了新的项目
这一天,小明主管找到小明说:我们之前的项目里面其实有很多通过new的方式去创建对象,而且是创建的一些实际的对象,这样的做法是不太好的。小明很诧异,不通过new方式,还有其他的方式创建?主管对小明说:没事,做了我们的项目你就明白这一切了。这次我们的新项目是做给一家披萨店的,我和你一起来做这个项目,你先做,我再做进一步的改进。
小明很快做了一个版本出来:
Pizza orderPizza(String type) {
Pizza pizza;
if(type.equals("cheese")){
pizza = new CheesePizza();
}else if(type.equals("greek")){
pizza = new GreekPizza();
}else if(type.equals("durian")){
pizza = new DurianPizza();
}
pizza.prepare();
pizza.make();
pizza.cut();
pizza.box();
return pizza;
}
主管对小明说:如果我现在有新种类的Pizza,你的程序会犯了什么问题?小明一下发觉了问题所在,代码里面除了make()方法以外,上面的都是变化的,我应该把它们独立出来:
public class SimplePizzaFactory{
public Pizza createPizza(String type) {
Pizza pizza;
if(type.equals("cheese")){
pizza = new CheesePizza();
}else if(type.equals("greek")){
pizza = new GreekPizza();
}else if(type.equals("durian")){
pizza = new DurianPizza();
}
return pizza;
}
}
public class PizzaStore{
SimplePizzaFactory factory;
public PizzaStore(SimplePizzaFactory factory){
this.factory = factory;
}
public Pizza orderPizza(String type) {
Pizza pizza = factory.createPizza(type);
pizza.prepare();
pizza.make();
pizza.cut();
pizza.box();
return pizza;
}
}
主管继续对小明说,嗯,不错,已经慢慢走上正轨了,你现在使用的就是不算是一种设计模式的模式—简单工厂
:将对象的创建放在另外的类中去解决,这样能够简单的将变化的部分独立出来,我们再有新的Pizza或者其中一种Pizza不在制作时,可以不用去修改PizzaStore的代码,因为PizzaStore是不应该被经常修改的。但是,简单工厂还不是我们想要的,如果出现了其他加盟店,我们需要将其他加盟店的整个流程都管理起来,那么仅仅只有生成Pizza的工厂是不够的,从准备到装盒的整个流程我们都需要规范其起来(prepare()、make()、cut()、box()方法),不然其他的加盟店只利用我们提供的工厂生产Pizza,而这些流程有自己的方式,那Pizza店的服务就不能统一标准了。那小明问那我们应该怎么改进呢?主管说,很简单,我们使用工厂方法模式
:
public abstract class PizzaStore{
public Pizza orderPizza(String type) {
Pizza pizza = createPizza(type);
pizza.prepare();
pizza.make();
pizza.cut();
pizza.box();
return pizza;
}
abstract Pizza createPizza(String type);
}
public class ChengDuPizzaStore extends PizzaStore{
Pizza createPizza(String type){
if(type.equals("cheese")){
return new CheesePizza();
}else if(type.equals("greek")){
return new GreekPizza();
}else if(type.equals("durian")){
return new DurianPizza();
}else {
return null;
}
}
}
小明说,等等,这种模式怎么又出现了可能会改变的部分啊?而且这和简单工厂的方式区别就是新的创建对象类成了PizzaStore的子类。主管说,你说到关键了,这和简单工厂的方式很相似,但是仅仅只是相似,它和简单工厂的区别很大:简单工厂仅仅只是把对象的创建封装到另一个类中,而工厂方法模式提供了一个框架,所有子类都依靠这个框架(prepare()、make()、cut()、box()),然后把对象的创建交给了子类,因为在Pizza店和其他的一些实际的需求中,子类可能只需要决定产生什么要的东西,而其他的流程都是统一规范的。
这里面体现了一个很重要的原则:依赖倒置原则,披萨店在使用Pizza时,没有具体依赖某一种披萨实现自己的流程(prepare()、make()、cut()、box()),而是一个通过依赖于一个Pizza接口。
工厂方法模式定义
定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类的实例化推迟到子类。
角色
- 抽象产品
- 产品实现
- 抽象工厂
- 具体工厂
优缺点
优点
- 在工厂方法中,用户只需要知道所要产品的具体工厂,无须关系具体的创建过程,甚至不需要具体产品类的类名
- 在系统增加新的产品时,我们只需要添加一个具体产品类和对应的实现工厂,无需对原工厂进行任何修改,很好地符合了“开闭原则”
缺点
- 每次增加一个产品时,都需要增加一个具体类和对象实现工厂,是的系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事
披萨店的新问题
没过多久,主管找到小明说,披萨店在使用我们的程序时,出现了一个问题,披萨店的出售流程是规范了,但是有一些加盟店开始在原料上偷工减料,造成披萨在顾客中的口碑褒贬不一,所以我们需要改进一下,让每个店使用的原料是可以被监控的。小明很快想到了办法,我们将披萨生成(new的时候)统一为一个简单工厂,那么就可以统一知道披萨的生成了。主管说这样表面是解决了问题,但其实这样会使每个店生成的都是一样的,我们的目的是为了监控原料,而非统一原料。所以我们真正需要使用的是抽象工厂模式:
public interface PizzaIngredientFactory{
public Dough createDough();
public Sauce createSauce();
public Cheese createCheese();
public Veggies[] createVeggies();
public Clams createClams();
}
public abstract class Pizza{
String name;
Dough dough;
Sauce sauce;
Cheese cheese;
Veggies[] veggies;
Clams clams;
public void make(){
}
public void cut(){
}
public void box(){
}
abstract void prepare();
}
public class CheesePizza extends Pizza{
PizzaIngredientFactory factory;
public CheesePizza(PizzaIngredientFactory factory){
this.factory = factory;
}
void prepare(){
dough = factory.createDough();
sauce = factory.createSauce();
cheese = factory.createCheese();
veggies = factory.createVeggies();
clams = factory.createClams();
}
}
这样一来,我们的披萨名字是一样的,但是不同的地方制作相同披萨的原料可以不一样(自己实现PizzaIngredientFactory),这就是抽象工厂模式。
抽象工厂模式定义
提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。
角色
- 抽象工厂
- 实现工厂
- 抽象产品
- 具体产品
产品族
不同的产品等级结构,功能相关联的产品组成的家族。抽象工厂就是创建出分属于不同产品等级机构的对象组成产品族。
优缺点
优点:
- 抽象工厂模式隔离了具体类的生产,使得客户并不需要知道什么被创建
- 当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象
- 增加新的具体工厂和产品族很方便,无须修改已有系统,符合“开闭原则”
缺点:
- 增加新的产品等级结构很复杂,需要修改抽象工厂和所有的具体工厂类,对“开闭原则”的支持呈现倾斜性