引言
自己在项目中有这样一个代码逻辑,有一个接口,原来只有一个实现类,在创建它时就直接创建了。后来新增了需求,这个接口多了一个实现类。再对定义接口的实际对象时,需要根据传入的参数,判断到底利用哪一个实现类来创建。于是自己就在原来的代码处加了if-else,将对象的创建逻辑完全混杂在了原来代码处。当时自己觉得已经完美的解决了这个问题。
后来,请有经验的员工看了一下,他一眼就看出来,说到,你这么写代码看上去太乱了,完全可以把对象的创建单独写成一个工厂,这样在这边就看不到具体的逻辑了,其实也不需要看到,这边用统一的接口就行。自己也恍然大悟,原来把代码杂糅进来,看着是比较乱,现在封装后,清爽了很多很多。
再仔细看自己抽象出来的代码,其实就是根据参数的不同创建不同的对象,而它的学名叫“简单工厂方法”,再查一下Gof的设计模式,并没有这种模式。关于工厂,它有工厂方法模式(Factory Method)和抽象工厂模式(Abstract Factory)两种。本文将从简单工厂方法入手,去整理下工厂方法模式。
一、简单工厂模式
我想之所以叫简单工厂模式,的确这个类可以写的非常简单,只需要根据传入参数的不同,利用if-else或者switch,把对象进行实例化即可。也就是在这个工厂类里,基本只要一个函数就可以解决问题。
其结构类似于下图。接口ISimple当然也可以是抽象类。图中的意思是,将具体对象的实例化,放到工厂类SimpleFactory中进行,在Factory里判断具体是创建SimpleA还是SimpleB。
从上面的分析可以看出,它的特点是:简单,快速,当具体的实例相对较少时还比较方便。但是它的缺点也很明显,就是扩展性不够,如果新加一种实现,就得去修改工厂类,这于我们常说的“开放-关闭原则”不符。
我个人理解为什么GOF没有将这种模式写进他们的书里也是上面所说的原因,这种模式更多是一种简化,但并不是所要推荐的方式。也是针对这个问题,我们看下工厂方法模式是怎么做的,怎么解决了这样的瑕疵。
其实细看GOG的书,发现上面这段话并不准确,这种方式与书中说的“参数化工厂方法”类似,根据传入的参数来决定具体采用哪种实例。
二、工厂方法模式
可能更好的效果是书中讲的“连接平行的类层次”,也就是说,针对接口或者抽象类不同的实现,工厂类也进行抽象,提供不同的实现,只要工厂类实现同一个接口就可以。在GOF中给出的结构图如下:
如果有多个不同的实例,则可以对应创建多个不同的工厂创建类,生成如下类似的图:
可以看到,这种模式解决了类的扩展性问题,但是随之而来的就是,可能会产生多个附加的类,会使类的数量大增。
三、抽象工厂模式
对于工厂方法模式,其一个工厂类最后只能“生产”出同一种类型的产品,也就是说继承自统一个类或者实现的是同一个接口。而抽象工厂模式是在这个基础上的进一步封装,一个工厂类可以有多个方法,返回不同类型的对象,而这些对象也是会有不同的实现。
还是参照GOF的书,其给出的结构图如下:
这样,调用方只需要通过抽象工厂类的接口来返回具体的实例,而不需要关注具体的产品实现方式。
四、总结
如果一个接口只有很少的实现,未来也不太可能扩展,就可以选择使用简单工厂模式;如果以后不想去修改工厂类,那么就改用工厂方法模式,去抽象工厂类,针对不同的产品实现不同的工厂类。
而如果有多种不同的产品,各个产品又有不同的实现,那么可以考虑使用抽象工厂模式。
参考:
GOF,《设计模式—可复用面向对象软件的基础》