在客户端程序中过多的使用new去实例化就会造成耦合过强的问题。使用工厂模式可以有效地将这些实例化工作抽离到客户程序之外,使程序更多的依赖抽象类而不是具体的类,从而解耦。
简单工厂
简单工厂将同源类的实例化工作(new)归类一个独立的“工厂类”中执行,好让具体实例化的过程从客户代码中移除,起到解耦的作用。
书中以Pizza店为例,架构写的比较庞大,因为需要偷懒,这里以文具店卖笔为例。
Stationer为文具店实例,一共卖3种笔,Pen是所有笔的超类。客户需要获取其中一种笔时,只需向Stationer.getPen方法传入具体的type,即可获取到对应的Pen实例。
public class SimplePenFactory {
public static Pen getPen(String type){
if("art".equals(type)){
return new ArtPen();
}else if ("ink".equals(type)){
return new InkPen();
}else if("ballpoint".equals(type)){
return new BallPointPen();
}
System.out.println("Invalid Type");
return null;
}
}
简单工厂是常见的一种用法,但书中认为“简单工厂”并不算真正的设计模式,而更像一种编程习惯。
工厂方法模式
“工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。”
通俗地来讲,就是将“工厂类”中创建对象的方法变成抽象的,并由继承它的子“工厂类”来实现,这样就可以由子类来具体决定实例化哪些类。
接着以文具店的例子来看,如果Pen的子类增多并有质量一般和质量较好的差别,商家增开到2家“文具店”并各自售卖不同质量的笔。使用工厂方法模式,这里要将“工厂类”分成2个(同超类),各自负责实例化不同质量的笔。
那么将原来的工厂类的接口getPen(String type)转成abstract,并创建2个子类GeneralStationer和DeluxeStationer,各自售卖一般和贵的文具笔,Pen的实例化工作也由这2个子类来实现(各自实现了getPen接口)。
public abstract class Stationer {
public abstract Pen getPen(String type);
}
2个Stationer的子类,各自负责实例化不同质量的笔。尽管对客户来说,传入的type参数是一样的,但是实际上这2个“工厂”产生的实例并不同:
class GeneralStationer extends Stationer{
@Override
public Pen getPen(String type) {
if("art".equals(type)){
return new ArtPen();
}else if ("ink".equals(type)){
return new InkPen();
}else if("ballpoint".equals(type)){
return new BallPointPen();
}
System.out.println("Invalid Type");
return null;
}
}
class DeluxeStationer extends Stationer{
@Override
public Pen getPen(String type) {
if("art".equals(type)){
return new GoodArtPen();
}else if ("ink".equals(type)){
return new GoodInkPen();
}else if("ballpoint".equals(type)){
return new GoodBallPointPen();
}
System.out.println("Invalid Type");
return null;
}
}
工厂方法模式通过让工厂子类决定创建的具体对象时哪些,来达到将对象创建过程封装的目的。组成元素有:
1)创建者(工厂类)
2)产品类(单一产品类型,所有产品均继承自同一个父类)。
抽象工厂模式
“抽象工厂模式提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类”
抽象工厂与前面2种概念有点区别,抽象工厂模式目的是创建一组对象。
文具店当然不止有笔(Pen)卖,现在种类增加了橡皮擦(Eraser)和笔盒(PencilCase),而所有这些种类的文具都有好坏,有2个不同的文具工厂生产:
工厂方法模式只负责一种类型的创建,而抽象工厂模式可以创建一个产品的家族。事实上它们比较容易互相配合的,试想Pen类需要笔芯,笔头,笔壳组装而成,那么抽象工厂模式就可以为上游笔厂提供这些原料。
PS:例子中将Factory和各种文具设计成接口,是这本书推崇的原则——“少用继承多用组合”
“依赖倒置原则”
作者特意提到了这个原则:
“要依赖抽象,不要依赖具体类”
尽量依赖抽象,不要依赖具体类,便于得到更松耦合并有弹性的实现。
小结
- 所有工厂都是用来封装对象创建的
- 工厂方法使用继承,将创建对象的任务交给子类。
- 抽象工厂使用对象组合,且创建对象的任务实现在接口中。
- 工厂帮助我们针对抽象编程,不依赖具体类。所有工厂模式都通过减少程序和具体类之间的依赖促进松耦合。