假如有一个披萨店,Pizza的种类有很多,如CheesePizza、VeggiePizza、PepperPizza等。披萨店根据收到的订单制作Pizza,披萨的制作流程有材料的准备材料、烤、切、包装几步。如何设计Pizza的订购呢?按照一般的设计思路:
那么当用户订购时:
Pizza OrderPizza(String orderType) {
Pizza pizza;
if (orderType.equals("veggie")) {
pizza = new VeggiePizza();
} else if (orderType.equals("cheese")) {
pizza = new CheesePizza();
} else if (orderType.equals("pepper")) {
pizza = new PepperPizza();
}
// pizza制作过程
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
然而当披萨店增加或者删除披萨类型时,必须修改OrderPizza的代码。例如想要添加一个“中国披萨”,那么就要添加:
else if (orderType.equals("china")) {
pizza = new ChinaPizza();
}
也就是说,只要披萨菜单存在改动,这段代码就得一改再改,这种设计明显违反了“开放-关闭原则” ,并没有做到对修改“关闭”。同时,如果有多个订单,那么每个订单也必须依赖每个子类,这样就显得冗杂。如何优化呢?这里得引出“简单工厂模式”了。
简单工厂模式(Simple Factory Pattern):又称为静态工厂方法(Static Factory Method)模式,简单工厂模式中专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。
直白地讲,简单工厂把实例化的操作单独放到一个类中,这个类就成为简单工厂类,让简单工厂类来决定应该用哪个具体子类来实例化,这样做能把客户类和具体子类的实现解耦,客户类不再需要知道有哪些子类以及应当实例化哪个子类。
简单工厂模式并不在 GoF 23 种设计模式之列,与其说其是设计模式,不如说是一种编程习惯。
简单工厂模式中包含如下角色:
Factory:工厂角色
工厂角色负责实现创建所有实例的内部逻辑。
Product:抽象产品角色
抽象产品角色是所创建的所有对象的父类,负责描述所有实例所共有的公共接口。
ConcreteProduct:具体产品角色
具体产品角色是创建目标,所有创建的对象都充当这个角色的某个具体类的实例。
类图
时序图
将简单工厂模式运用到上面的披萨店中,那么就得建造一个SimpleFactory工厂类,它被所有需要进行实例化的客户类调用。
public class SimpleFactory {
public Pizza createPizza(String orderType) {
Pizza pizza = null;
if (orderType.equals("veggie")) {
pizza = new VeggiePizza();
} else if (orderType.equals("cheese")) {
pizza = new CheesePizza();
} else if (orderType.equals("pepper")) {
pizza = new PepperPizza();
}
return pizza;
}
}
客户代码为:
public PizzaStore{
SimpleFactory simpleFactory;
public OrderPizza(SimpleFactory simpleFactory) {
this.simpleFactory = simpleFactory;
}
public Pizza orderPizza(String orderType) {
Pizza pizza;
pizza = simpleFactory.createPizza(orderType);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
// 其他方法
}
优点
缺点
适用场景
对于产品种类相对较少的情况,考虑使用简单工厂模式。使用简单工厂模式的客户端只需要传入工厂类的参数,不需要关心如何创建对象的逻辑,可以很方便地创建所需产品。
public final static DateFormat getDateInstance();
public final static DateFormat getDateInstance(int style);
public final static DateFormat getDateInstance(int style,Locale locale);
假如披萨店在各个地儿都有,比如Beijing,Shanghai…他们都保有他们城市的风味。要吃到不同城市风味的披萨,如果运用简单工厂模式:
// 这里创建的工厂,全是在北京地区的工厂
BeijingFactory beijingFactory = new BeijingFactory();
// 创建一个披萨店,将北京工厂的引用作为参数
PizzaStore pizzaStore = new PizzaStore(beijingFactory);
// 制作披萨时,就会得到在北京地区的奶酪披萨
pizzaStore.orderPizza("CheesePizza");
同理,上海的披萨店也类似:
ShanghaiFactory shanghaiFactory = new ShanghaiFactory();
PizzaStore pizzaStore = new PizzaStore(shanghaiFactory);
pizzaStore.orderPizza("CheesePizza");
然而,这样使得代码缺乏弹性。如何弹性地制作出具有一定规范但又不失本地特色的披萨呢?这里就得引出工厂方法模式了。
工厂方法模式(Factory Method Pattern)又称为工厂模式,也叫虚拟构造器(Virtual Constructor)模式或者多态工厂(Polymorphic Factory)模式,它属于类创建型模式。在工厂方法模式中,工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象,这样做的目的是将产品类的实例化操作延迟到工厂子类中完成,即通过工厂子类来确定究竟应该实例化哪一个具体产品类。
直白地讲,“工厂方法模式”是对简单工厂模式的进一步抽象化,只是工厂方法把产品的实例化操作推迟到子类。
工厂方法模式由4个要素构成。
类图
时序图
还是以上面披萨制作为例,那么就可以把createPizza(style)放回PizzaStore中,不过设置为“抽象方法”,然后每个区域设置自己的PizzaStore子类。
public abstract class PizzaStore {
public Pizza orderPizza(String orderType) {
Pizza pizza;
pizza = createPizza(orderType);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
abstract Pizza createPizza(String orderType);
}
那么创造怎么样口味的披萨由子类工厂决定:
public class MyStyleStore extends PizzaStore {
Pizza createPizza(String type) {
if (type.equals("veggie")) {
pizza = new MyStyleVeggiePizza();
} else if (type.equals("cheese")) {
pizza = new MyStyleCheesePizza();
} else if (type.equals("pepper")) {
pizza = new MyStylePepperPizza();
}
}
}
public class BeijingStore extends PizzaStore {
Pizza createPizza(String type) {
if (type.equals("veggie")) {
pizza = new BeijingVeggiePizza();
} else if (type.equals("cheese")) {
pizza = new BeijingCheesePizza();
} else if (type.equals("pepper")) {
pizza = new BeijingPepperPizza();
}
}
}
优点
缺点
适用场景
在工厂方法模式中,客户端不需知道具体产品类的类名,只需知道创建具体产品的工厂类;对于抽象工厂类,只需要提供一个创建产品的接口,而由其子类来确定具体要创建的对象。
工厂方法模式在JDK类库中运用比较多,例如:
为了更清晰地理解工厂方法模式,先理解两个概念:
抽象工厂模式(Abstract Factory Pattern):提供一个接口,用于创建创建一系列相关或相互依赖对象的家族,而无须指定它们具体的类。抽象工厂模式又称为Kit模式,属于对象创建型模式。
抽象工厂模式与工厂方法模式区别在于,工厂方法模式针对的是一个产品等级结构,而抽象工厂模式则需要面对多个产品等级结构,一个工厂等级结构可以负责多个不同产品等级结构中的产品对象的创建 。
抽象工厂模式同工厂方法模式一样,也是由4 个要素构成,但抽象工厂中方法个数不同,抽象产品的个数也不同。
类图
时序图
public class AbstractProductX {
}
public class AbstractProductY {
}
public class ConcreteProductAX extends AbstractProductX {
}
public class ConcreteProductBX extends AbstractProductX {
}
public class ConcreteProductAY extends AbstractProductY {
}
public class ConcreteProductBY extends AbstractProductY {
}
public abstract class AbstractFactory {
abstract AbstractProductX createProductX();
abstract AbstractProductY createProductY();
}
public class ConcreteFactoryA extends AbstractFactory {
AbstractProductX createProductX() {
return new ProductAX();
}
AbstractProductY createProductY() {
return new ProductAY();
}
}
public class ConcreteFactoryB extends AbstractFactory {
AbstractProductX createProductX() {
return new ProductBX();
}
AbstractProductY createProductY() {
return new ProductBY();
}
}
public class Client {
public static void main(String[] args) {
AbstractFactory abstractFactory = new ConcreteFactoryA();
AbstractProductX productX = abstractFactory.createProductX();
AbstractProductY productY = abstractFactory.createProductY();
// do something with productX and productY
}
}
优点
缺点
工厂方法模式在JDK类库中的运用:
严格地讲,简单工厂模式并不在GoF23种设计模式之列,更像是一种编程习惯,也可以理解为工厂方法模式的特殊情况之一。
简单工厂方法实现了客户类与其子类的解耦。
工厂方法模式属于类创建型模式,它把产品的实例化操作也推迟到子类,实现了产品类与其子类的解耦。
抽象工厂模式属于对象创建型模式,相对工厂方法模式针对一个产品等级结构,抽象工厂模式则需要面对多个产品等级结构,一个抽象工厂等级结构可以负责多个不同产品等级结构中的产品对象的创建 。
三种设计模式都各有优缺点,在实际开发中,我们应该根据实际业务需求来选择。