1.简单工厂模式
考虑这样一个需求:
有一个CoffeeStore可以提供Coffee;有一个Coffee接口,有AmericanCoffee和LatteCoffee两个具体的实现类.在一个CoffeeStore内,有orderCoffee(String type)这样一个方法,需要根据传入的类型,来确定返回的具体coffee类型,那么在你的客户端代码中可能会有这样的代码:
Coffee coffee = null;
if("americano".equals(type)) {
coffee = new AmericanoCoffee();
} else if("latte".equals(type)) {
coffee = new LatteCoffee();
}
return coffee;
类图结构:
但是这样的代码,灵活性很差,违反了"开闭原则".
可以将创建一个新的SimpleFactory,工厂里提供一个工厂方法,提供创建Coffee的工厂方法:
public class SimpleCoffeeFactory {
public Coffee createCoffee(String type) {
Coffee coffee = null;
if("americano".equals(type)) {
coffee = new AmericanoCoffee();
} else if("latte".equals(type)) {
coffee = new LatteCoffee();
}
return coffee;
}
}
类图结构:
然后在你的客户端代码创建简单工厂对象来调用方法创建对象.或者将这个工厂方法定义成static的,直接通过类名来调用createCoffee()方法来创建coffee对象(这种叫静态工厂,静态工厂不能通过继承来重载工厂方法).
这么做有什么好处,似乎只是把问题搬到了另一个地方罢了?
1.如果在客户端很多地方调用了SimpleFactory方法来创建Coffee对象,一旦创建Coffee对象的逻辑改变了,你只需要修改SimpleFactory这个工厂类的代码就可以了.如果将那些判断的逻辑写在了客户端代码中,就需要所有创建Coffee对象的代码了.
2.它实现了客户端代码和Coffee对象的解耦合.客户端只需要知道调用工厂方法产生一个Coffee对象,它不在需要知道这个对象具体怎么实现.这个对象的实现被"封装"在了工厂类里.具体怎么产生于客户端无关了.
简单工厂不是一个设计模式,更像是一种编程习惯.但是它确实能带来很多好处.但是缺点也很明显,当我们想要增加一种coffee类型的时候,就需要修改simpleFactory的代码逻辑,违背"开闭原则".
2.工厂方法模式
针对上面简单工厂的问题,可以通过工厂方法模式来解决,实现开闭原则.
工厂方法模式的定义:
定义一个用于创建对象的接口,让子类决定实例化哪一个类.工厂方法让一个类的实例化延迟到其工厂的子类.
工厂方法模式有四个角色:
抽象工厂:提供了创建产品的接口(方法),调用者通过它访问具体工厂的工厂方法来创建产品。
具体工厂:实现抽象工厂的中的抽象方法,完成具体产品的创建.
抽象产品:定义了产品的规范,描述了产品的特性和功能.就是工厂方法所创建的对象的接口.
具体产品:实现了抽象产品所定义的接口,由具体的工厂创建.
类图如下:
抽象工厂CoffeeFactory,提供了创建产品的方法createCoffee(),返回工厂生产的抽象产品Coffee.
public interface CoffeeFactory {
Coffee createCoffee();
}
具体工厂AmericanCoffeeFactory,实现抽象工厂中的createCoffee(),返回具体的产品AmericanCoffee,LatteCoffee.
public class LatteCoffeeFactory implements CoffeeFactory {
public Coffee createCoffee() {
return new LatteCoffee();
}
}
public class AmericanCoffeeFactory implements CoffeeFactory {
public Coffee createCoffee() {
return new AmericanCoffee();
}
现在回头理解一下工厂方法模式定义.
对于简单工厂的违反开闭原则,工厂方法完美解决.现在如果需要另外一种类型的BlueMountainCoffee,只需要定义一个BlueMountainCoffeeFactory,返回具体的BlueMountainCoffee即可.在不改变原有代码基础上实现扩展.缺点是,一个工厂方法只能生产一种具体的Coffee,如果很有多类型Coffee,就会有很多对应的Factory,类爆炸问题.
工厂方法的一个具体应用:
ArrayList类
类结构图:
在ArrayList源码中的位置:
ArrayList的内部类Itr(具体产品)实现了Iterator接口(抽象产品).ArrayList(具体工厂)实现Collection接口(抽象工厂)的iterator()(工厂方法),返回具体产品Itr.
3. 抽象工厂模式
前面工厂方法模式中考虑的是一类产品的生产,如电视机厂只生产电视机,咖啡店只生产咖啡.这些工厂只生产同种类产品,同种类产品称为同等级产品,也就是说,工厂方法只生产同等级产品.AmericanCoffeeFactory和LatteCoffeeFactory生产的都是Coffee这个产品.但是现实生活中,很多工厂都是综合性的工厂,如小米工厂即可以生产小米手机,还可生产小米电视,小米笔记本.这属于同一个产品族的不同等级产品.
抽象工厂模式将考虑多等级产品的生产.将一个工厂生产的位于不同等级的产品称为一个产品族.
抽象工厂的概念:
提供一个创建一系列相关或者相互依赖对象的接口,而无需指定他们具体的类.
注意,这个接口内地额方法不是任意的,而是一系列相关或者相互依赖的方法.(即生产的产品是同一个产品族.)
抽象工厂的主要角色:
抽象工厂:提供创建产品的接口,他有多个创建产品的方法,可以创建不同等级的产品.
具体工厂:实现抽象工厂的方法,完成具体产品的创建.
抽象产品:定义了产品的规范,描述了产品的主要特性和功能.抽象工厂有多个抽象产品.
具体产品:实现了抽象产品所定义的接口,由具体工厂来创建,他和具体工厂之间的关系是多对一的关系.
对于上面的案例,现咖啡店业务发生改变,不仅要生产咖啡还要生产甜点,如提拉米苏、抹茶慕斯等,要是按照工厂方法模式,需要定义提拉米苏类、抹茶慕斯类、提拉米苏工厂、抹茶慕斯工厂、甜点工厂类,很容易发生类爆炸情况。其中拿铁咖啡、美式咖啡是一个产品等级,都是咖啡;提拉米苏、抹茶慕斯也是一个产品等级;拿铁咖啡和提拉米苏是同一产品族(也就是都属于意大利风味),美式咖啡和抹茶慕斯是同一产品族(也就是都属于美式风味)。所以这个案例可以使用抽象工厂模式实现。类图如下:
抽象工厂DessertFactory提供了生产Coffee和Dessert两个抽象产品的接口,AmericanDessertFactory和ItalyDessertFactory这两个具体工厂实现了抽象工厂的接口,分别生产具体产品AmericanCoffee,MatchaMousse和LatteCoffee,Tiramisu.注意AmericanDessertFactory生产的都属于美式风味(产品族),ItalyDessertFactory生产都是意大利风味.
抽象工厂的优点:当一个产品族的多个产品被设计在一起工作时,它能保证客户端只使用同一产品族中的对象.
缺点:当抽象工厂的产品族要添加一个新等级的产品时,所有的工厂类(具体工厂)都要修改.