在设计模式的的七大原则中提到过,要针对接口编程,而不是针对实现编程。比如这样写:
List<String> list = new ArrayList<>();
等号左侧用的是List类型(接口),而不是ArrayList类型(实现)。
但是,在等号右侧,我们不得不new一个具体类型,这样从某种程度上来说也是面向实现编程了,如果说以后我自己写了个新的ArrayList,想要替换掉原有的ArrayList。这时,就不得不修改这一行代码了,而且因为这个类使用很频繁,要修改的地方会非常多,这就是针对实现编程的缺点所在了,一旦发生改变,代码改动很大。
如果换下面这种方式呢?
List<String> list = ListFactory.newList(ListType.ARRAY_LIST);
注:这里ListType.ARRAY_LIST代表一个常量
在这里,通过一个静态方法去获取List,在该方法中,大致的实现是这样的:
public class ListFactory{
public static List newList(String listType){
switch(listType){
case ListType.ARRAY_LIST:
return new ArrayList();
case .....
...
}
}
}
这样,下次要把ArrayList的实现换掉,只需要修改ListFactory中的实现即可,且只需要改这一个地方。
上述就是一个简单工厂了。
需求:现有一披萨店,提供订购披萨的功能,顾客可以选择多种披萨,如cheese pizza,greek pizza等。
看看订购pizza的方法怎么写:
Pizza orderPizza(String type){
Pizza pizza;
switch(type){
case "cheese":
pizza = new CheesePizza();
break;
case "greek":
pizza = new GreekPizza();
break;
case ...
...
}
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
但是,pizza的种类是会不断增减的,那么orderPizza这个方法就需要不断修改。这时就可以使用简单工厂了。实现方法和上面说的差不多,将创建pizza的过程抽到工厂中即可。
这样似乎并没有解决多少问题,只是将代码的修改从orderPizze移到了工厂中,但是别忘了,创建pizza会出现在很多地方,就如同上面的创建ArrayList一样。把创建工作集中到工厂中,方便统一管理。
在上面的例子中,简单工厂是用静态方法来实现的,这种方法也被称为静态工厂。也可实例化一个工厂。如下:
public class PizzaStore{
private PizzaFactory pizzaFactory;
public PizzaStore(PizzaFactory pizzaFactory){
this.pizzaFactroy = pizzaFactory;
}
public Pizza orderPizza(String type){
Pizza pizza = pizzaFactory.createPizza(type);
...
return pizza;
}
}
两种方法各有所长,静态工厂使用简单,但不能通过继承来改变创建行为,而实例化的实现方式可以。
接着pizza的例子,现在因为披萨的生意好,有人希望加盟,每家加盟店都可能想要提供不同风味的披萨,这收到开店地区的影响。
首先考虑使用简单工厂。每个PizzaStore都有一个工厂来创建披萨,即将上例中的PizzaStore和PizzaFactory多实现几遍。这显然是一个很糟糕的设计,每个加盟店都各自独立,且总店无法管理。比如总店要求所有的pizza制作流程都分为prepare,bake,pizza,box四步。这时可以考虑使用继承,所有的加盟店都要继承总店提供的PizzaStore,在pizzaStore中定义一个抽象的createPizza方法,交给子类实现,如下:
public abstract class PizzaStore{
public Pizza orderPizza(String type){
Pizza pizza = createPizza(type);
...
return pizza;
}
public abstract Pizza createPizza(String type);
}
这就是工厂方法模式了。它的核心是将对象的实例化推迟到子类实现。
在2中谈到用简单工厂方法来实现该需求的时候,每一个加盟店都有一个独立的披萨工厂,这样没法管理。如果在此基础上完成抽象一个工厂接口,所有的披萨工厂都要实现该接口,这样,PizzaStore就可以不用做成抽象的
public interface PizzaFactory{
Pizza createPizza(String type);
}
public class PizzaStore{
private PizzaFactory pizzaFactory;
public PizzaStore(PizzaFactory pizzaFactory){
this.pizzaFactroy = pizzaFactory;
}
public Pizza orderPizza(String type){
Pizza pizza = pizzaFactory.createPizza(type);
...
return pizza;
}
}
这样,所有的加盟店都使用同一个PizzaStore,但传入的工厂不一样,可以创建的pizza也不一样。这就是抽象工厂模式了。
工厂模式的意义在于将实例化对象的代码提取出来,放到一个类中统一管理和维护,达到和主项目的解耦,从而提高项目的扩展性和维护性。