最近在学习
一书的工厂模式这一部分,在这里记录一下学习的体会心得。
首先还是来看工厂方法的概念:定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method使一个类的实例化延迟到其子类。(工厂方法是创建型设计模式)
下面进入我们的故事:
。。。。
先看一个不好的方法
现在你是一个专门做披萨的大厨,能做出各种美味的披萨!你现在有一个披萨类,以及你是如何制作这个披萨的方法都在这个披萨类里面:
public class Pizza{
void prepare(){};//做披萨前的准备
void bake(){};//烘焙你的披萨
void cut(){};//切割你的披萨
void box(){};//把披萨装盒
}
披萨类里面的方法的作用我都用注释写在旁边了。
现在你自己开了一家披萨店,而且你凭借你的手艺让你的小店生意兴隆!你的披萨店类的代码是这样的:
public class PizzaStore{
Pizza orderPizza(){//定制披萨的方法
Pizza pizza = new Pizza();
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
当然,你是一个手艺高超的披萨大厨,在你的披萨店里面不仅只有一种披萨,所以你需要通过一个参数来选择制作不同的披萨,并且还需要不同的披萨对象的代码,下面是修改后的代码:
起司披萨和意大利腊香肠披萨:
public class CheesePizza extends Pizza{
String name ="CheesePizza";
}
public class PepperoniPizza extends Pizza{
String name = "PepperoniPizza";
}
public class PizzaStore{
Pizza orderPizza(String type){//根据输入的名字定制披萨的方法
Pizza pizza =null;
if(type.equals("cheese")){//如果选择是起司披萨就制作起司披萨
pizza = new CheesePizza();
}
else if(type.equals("pepperoni")){//意大利腊肠也不错
pizza = new PepperoniPizza();
}
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
这样,你的披萨店就可以根据不同的要求做出不同的披萨了!!
但是!突然有一天,一种新口味的披萨流行了,你为了让你点披萨店也能跟上时代的步伐,作为大厨的你很快就学会了,这时候,你需要在你的披萨店里面添加着一种披萨。又过了几天,由于禽流感什么的突发情况,某些食材不能用了,所以你不得不把它们从你的菜单中移除。。。。等等这些变动的因素都会使得你PizzaStore类里orderPizza()方法的代码不停的变更,这样非常不好,违背我们软件设计的原则!
用一下简单工厂试试
在我们的PizzaStore类里面,我们注意到,变化的部分是披萨的选择,但是所有披萨的制作方法是大致相同的,如下图所示:
如何让我们的设计更加合理呢?我们可能会想到,将上面那些经常变动部分的代码移到另一个只关心制作什么样的披萨这么一个对象中去。我们这里把这个对象就叫做工厂。
工厂负责我们制作出来的披萨的细节!下面我们来看看这个工厂对象如何改变原来PizzaStore里面的orderPizza()方法的。
简单工厂类的代码:
public class SimplePizzaFactory{
Pizza createPizza(String type){//根据输入的名字制作披萨的方法 注意!!这里是直接制作!
Pizza pizza =null;
if(type.equals("cheese")){//如果选择是起司披萨就制作起司披萨
pizza = new CheesePizza();
}
else if(type.equals("pepperoni")){//意大利腊肠也不错
pizza = new PepperoniPizza();
}
return pizza;
}
}
public class PizzaStore{
SimplePizzaFactory factory;
public PizzaStore(SimplePizzaFactory factory){
this.factory = factory;
}
Pizza orderPizza(String type){//这里是定制披萨
Pizza pizza =null;
pizza = factory.createPizza(type);//这里制作披萨,负责披萨制作的具体环节
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
这里申明一下,与其说简单工厂是一种设计模式,不如果简单工厂是一种设计习惯。它并没有列入设计模式的行列中去。
下面进入我们的工厂方法!FactoryMethod!!
有了简单工厂,你的披萨店生意越来越好!!在你当地,你的披萨店非常有名气了!现在你想要开许多连锁店,这样,不仅仅你当地的人可以尝到你的手艺,许多其他地区的人也能够品尝。但是当你开了连锁店以后,你发现你的店在有的地区很受欢迎,但是在有的地区却并不是那么受欢迎。你分析了原因后得知,由于地域的不同,当地人们的口味也不同。比如四川那边的人喜欢重口味的(没有地域黑的意思),比如厚厚的披萨壳(thick crust)、方许多的调味料(rich sauce)和大量的起司(tons of cheese)。同时,上海人喜欢清淡口味的,比如薄壳(thin crust)、少量调味料(tasty sauce)、少量起司(little cheese)。
仅仅根据上面两个地域的口味,我们来用简单工厂画一个图:
当有多少种不同口味的地区,我们就制作出多少种不同口味的工厂。这是解决这种问题的一个办法。
现在我们分别在四川和上海开了两家分店:
SiChuanPizzaFactory scFactory = new SiChuanPizzaFactory(); //在建立四川披萨店的时候必须现有四川披萨工厂
PizzaStore scStore = new PizzaStore(scFactory);//生成四川披萨店
scStore.orderPizza("cheese");//四川披萨店里面预定一个起司披萨
ShangHaiPizzaFactory scFactory = new ShangHaiPizzaFactory(); //同样的,建立上海披萨店的时候必须现有上海的披萨工厂
ShangHaiPizzaStore shStore = new PizzaStore(shFactory);//生成上海披萨店
shStore.orderPizza("cheese");//上海披萨店里面预定一个起司披萨
但是问题还是存在的,我们会发现我们对于createPizza已经可以根据不同的需要而改变了,但是,这里的bake()、cut()、box()等也有可能需要根据不同的人有不同的操作,比如有的人喜欢用圆的盒子装,有的喜欢用方的盒子,有的人喜欢将整块披萨放进盒子里,有人喜欢把披萨从中间开始切到半径一半处。这些根据不同的人有不同的操作,我们如何解决呢?
也许你会想到了,这不就和我们之前解决披萨种类一样吗,定义一个对象,这个对象专门负责某一个问题。这里,我们如果把PizzaStore里所有的操作都移到另一个对象里面去,其实等同于将所有的这些过程方法做成一个抽象类,让具体需要实现的子类去实现符合自己特殊需求的方法。(可能听的一头雾水,不过没关系,慢慢看完)
现在的PizzaStore类是这个样子:
public abstract class PizzaStore{//定义了一个抽象类(为什么详见后面)
Pizza orderPizza(String type){
Pizza pizza =null;
pizza = createPizza(type);//现在制作披萨又回到了PizzaStore类里面,而不再使用工厂了
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
abstract Pizza createPizza(String type);//我们将工厂对象转移到这个抽象方法里
}
现在的情况我用下面的图来表示:
我们首先有一个抽象的PizzaStore,之后我们每定义一个子类(成立一个披萨分店)就需要继承它然后重写createPizza()方法,这里每个披萨分店就可以根据自己的地域风格,制作出独特的披萨。当然,如果需要不同的切割方法或者包装方法,那就重写cut()和box()即可。
下面我们来展示一下用这样的方式成立的上海披萨店:
首先你必须有上海口味的披萨:
public class ShangHaiCheesePizza extends Pizza{
String name ="ShangHaiCheesePizza";
}
public class ShangHaiPepperoniPizza extends Pizza{
String name ="ShangHaiPepperoniPizza";
}
然后你的上海披萨店的createPizza()里面就生产上海口味的披萨:
public class ShangHaiPizzaStore extends PizzaStore{
Pizza createPizza(String type){
Pizza pizza = null;
if(type.equals("cheese")){//上海口味的披萨!
pizza = new ShangHaiCheesePizza();
}
else if(type.equals("pepperoni")){
pizza = new ShangHaiPepperoniPizza();
}
return pizza;
}
}
(提醒一下啊,我们的orderPizza()方法在父类里面,orderPizza()方法并不知道所预定的披萨是什么披萨,它只管prepare,bake,cut,box。 换而言之,这就是解耦合)
这样,我们的上海分店就开张了!
这里具体制作披萨的过程是在我们的子类(上海店)里实现的,生成披萨的过程被封装到了createPizza()方法里!这个createPizza()就是一个工厂方法。
现在让我们来做一个披萨吧!
现在的披萨类我们修改一下:
import java.util.ArrayList;
public abstract class Pizza{//改为抽象类,以后所有披萨都要继承它
String name="pizza";
String dough;//披萨的面团分类
String sauce;//披萨的酱汁
ArrayList toppings = new ArrayList();//添加到披萨上面的配菜可能有很多,所以用ArrayList
void prepare(){System.out.println("preparing!");};//做披萨前的准备
void bake(){System.out.println("baking!");};//烘焙你的披萨
void cut(){System.out.println("cutting into 6 part!");};//切割你的披萨
void box(){System.out.println("boxing!");};//把披萨装盒
public String getName(){return name;}
}
现在的上海起司披萨是这样的:
public class ShangHaiCheesePizza extends Pizza{
public ShangHaiCheesePizza(){
name = "上海的起司披萨";//构造方法里面我们赋予上海披萨特有的风格!
dough="薄壳儿";
sauce="美味上海酱汁";
toppings.add("上海人喜欢的起司");
}
}
现在在main函数里面试试让上海分店做一个披萨!
class factorymethod{
public static void main(String arg[]){
ShangHaiPizzaStore shps = new ShangHaiPizzaStore();//生成上海披萨店
Pizza pizza = shps.orderPizza("cheese");//在我们的上海分店里面预定一个起司披萨
System.out.println("order pizza name:"+pizza.getName()+" ,ps:"+pizza.sauce);
}
}
得到的结果是:
preparing!
baking!
cutting into 6 part!
boxing!
order pizza name:上海的起司披萨 ,ps:美味上海酱汁
以上就是我们的工厂方法了!
站在全局角度看看我们的披萨店(总结)
最后我们用专业一点的语言总结:我们定义了一个创建对象的接口(PizzaStore里面的createPizza()方法),但由子类(披萨分店)决定要实例化的类是哪一个(上海披萨还是四川披萨等等)。工厂方法让类把实例化推迟到子类。
工厂方法就是把生成对象的具体实现封装到了工厂方法中,这个工厂方法在父类里面都定义为抽象方法。还有一点就是,这里子类决定生产什么,并不是说这个模式能让子类它们自己在运行时“决定”生产什么,而是我们的父类方法在生产这个产品的时候并不知道是什么产品,而是由子类它所设定好的。
工厂方法就是这样一个创建型设计模式。看完了如果对你有收获的话,可以点个赞!