津津乐道设计模式 - 工厂模式详解
1、如何理解工厂模式
很多小伙伴可能对这个工厂模式概念不好理解,这里我用最简单的比喻来让大家明白;
作为一名脑力工作者只有身心愉悦才能让我们代码更有质量…所以我们今天需要出去红浪漫洗浴休闲中心洗脚、按摩放松一下,这个时候无论你是去A休闲中心,还是B休闲中心,你只需要在休闲中心,向前台领班说:按摩!这个时候休闲中心就会给你安排技师。A、B休闲中心就是提供服务(产出产品)的Factory;
任何时候我们需要某种服务,只需向工厂请求即可。消费者无须修改就可以接纳新服务。缺点是当服务修改(如:增加推油项目)时,工厂类也要做相应的修改。
2、简单工厂模式
简单工厂模式适用于工厂类负责创建的对象较少的场景,且客户端只需要传入工厂类的参数(按摩),对于如何构建对象不需要关心;
我们看代码,还是以洗浴中心为例:目前洗浴中心,目前开放了:洗脚、沐浴、按摩等,我们可以定义一个标准的ISerice服务接口:
public interface IService{
/** 提供服务 **/
public void doSerice();
}
创建一个洗脚(footbath)服务的实现类:
public class FootbathService implements IService {
@Override
public void doSerice() {
System.out.println("正在提供洗脚服务");
}
}
//然后一般我们会这样调用
IService services = new FootbathService();
上诉代码,父类IService指向子类FootbathService的引用,这个时候休闲中心为了给客户更多的服务增加了推油(PullOil)服务甚至更多,那么试想一下我们的客户端将变的越来越臃肿,我们用简单工厂模式再对代码进行优化
//新增推油服务
public class PullOilService implements IService {
@Override
public void doSerice() {
System.out.println("正在提供推油服务");
}
}
创建服务的工厂类
public class ServiceFactory {
public IService create(String name){
//洗脚
if(name.equals("footbath")){
return new FootbathService();
}
//推油
else if(name.equals("PullOil")){
return new PullOilService();
}else{
return null;
}
}
}
//这个时候我们这样调用
ServiceFactory serviceFactory = new ServiceFactory();
serviceFactory.create("footbath");
到了这里很多小伙伴要说了,哎呀,要是休闲中心继续增加服务,你这个工厂类又要去增加代码逻辑。没错,小伙伴对服务的增加还是很关心的,下面我们继续优化采用反射,改造ServiceFactory
//利用反射优化ServiceFactory
public class ServiceFactory {
public IService create(String className) {
try {
if (className != null && !className.equals("")) {
return (IService) Class.forName(className).newInstance();
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
//调用如下:create的参数是完整的包类名
ServiceFactory serviceFactory = new ServiceFactory();
IService services = serviceFactory.create("xx.xx.FootbathService");
services.doSerice();
好了经过我们的优化,这个时候休闲中心再增加其它服务,我们都不需要去修改ServiceFactory了,我们可以尽情享受快捷的服务了,或许还有小伙伴问了,哎呀create每次都要传类名,还要强转,又没又更好优化,答案是肯定的,我们继续完善
public IService create(Class<? extends IService> clazz) {
try {
if (clazz!=null) {
return clazz.newInstance();
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
//调用如下:
ServiceFactory serviceFactory = new ServiceFactory();
IService services = serviceFactory.create(PullOilService.class);
services.doSerice();
至此参数只需要传递类的类型即可,实现上面参数传递类名的相同功能。简单工厂模式也有缺点:工厂类并不易于扩展过于复杂的产品结构。
3、工厂方法模式
在简单工厂模式中,随着服务链的丰富,如果每个服务的创建都有不同的逻辑要求,则工厂会越来越臃肿,不便于维护。还是休闲中心的例子:假设按照简单工厂模式,现在推油服务需要VIP客户才可以享受,那么在构建PullOilService时候,工厂类ServiceFactory就需要加入响应的逻辑,一旦各种服务的要求不一样,大家试想ServiceFactory的职责相对就很重了,那么我们这时候休闲中心决定,我有这么些服务,每个服务的类型都由一个部长负责,你要洗脚就找负责洗脚的部长,你要按摩就找按摩的部长… 至于刚才提到的需要VIP客户才可以进行服务,都交给对应的部长来控制,即引出下面的总结:
工厂方法模式是指定义一个创建对象的接口,但让实现这个接口的类决定实例化哪个类,工厂方法模式让类的实例化延迟到子类进行。
根据单一职责原则,我们将职能继续拆分。洗脚服务由洗脚部长创建,按摩服务由按摩部长创建;上代码先构建IService接口
public interface ServiceFactory {
IService create();
}
分别构建子工厂(即部长),洗脚部长类的代码
//洗脚部长工厂类
public class FootbathFactory implements ServiceFactory {
@Override
public IService create() {
return new FootbathService();
}
}
//洗脚部长工厂类
public class PullOilFactory implements ServiceFactory {
@Override
public IService create() {
//模拟需要VIP客户才能获得服务
if(true){
return new PullOilService();
}
return null;
}
}
调用测试代码
public static void main(String[] args) {
//洗脚
ServiceFactory serviceFactory = new FootbathFactory();
IService iService = serviceFactory.create();
iService.doSerice();
//推油
serviceFactory = new PullOilFactory();
iService = serviceFactory.create();
iService.doSerice();
}
哎,相信作JAVA开发的小伙伴看到这个感觉在Spring中经常能看到这类操作,没错Spring也有用到了这个设计模式;总结一波:
工厂方法模式优点:
- 用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程。
- 灵活性增强,对于新产品的创建,只需多写一个相应的工厂类。
典型的解耦框架。 - 高层模块只需要知道产品的抽象类,无须关心其他实现类,满足迪米特法则、依赖倒置原则和里氏替换原则。
工厂方法模式缺点:
- 类的个数容易过多,增加复杂度
- 增加了系统的抽象性和理解难度
3、抽象工厂模式
抽象工厂模式是指提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。对这个模式含义不理解之前,我们还是按照上述的案例来分析两个概念:产品等级和产品族,为了更好理解将华为、苹果、联想的产品线整理,如下图:
红色区域电脑、电话、路由器代表了华为工厂的一个产品族,蓝色区域则是各工厂的产品线(电脑),简单理解就是 每个工厂都有对应的产品族,既然华为、联想、苹果的产品族都是一样的,那么我们就定义一个抽象工厂、抽象产品,由每个实际工厂继承来负责创建多个实际产品。因此,这个抽象工厂可以作用到多个实际工厂(华为、苹果、联想)
通过上述的描述讲解,相信大家已经有了一个形象的理解,我们还是红浪漫休闲中心的案例:洗脚、按摩、推油(都是我们的产品族),现在为了提高服务质量,所有的服务都需要增加采耳、洁面的功能,即:提供洗脚服务不单纯只有洗脚 还要增加 采耳 + 洁面才能完整构成一个服务,在产品等级线中增加两个服务产品采耳 和 洁面接口。
//采耳接口
public interface EarCleaning {
void clean();
}
//洁面接口
public interface FaceWashing {
void washing();
}
创建一个抽象工厂类AbstractFactory
public interface AbstractFactory {
//构建采耳
EarCleaning createEarCleaning();
//构建洁面
FaceWashing createFaceWashing();
}
接下来构建洗脚产品族的 采耳类以及洁面类
//扩展洗脚产品族的采耳类FootbathEarCleaning
public class FootbathEarCleaning implements EarCleaning {
@Override
public void clean() {
System.out.println("洗脚产品族中的采耳产品等级");
}
}
//扩展洗脚产品族的洁面类FootbathEarCleaning
public class FootbathFaceWashing implements FaceWashing {
@Override
public void washing() {
System.out.println("洗脚产品族中的洁面产品等级");
}
}
创建洗脚产品族的实际工厂
public class FootbathFactory implements AbstractFactory {
@Override
public EarCleaning createEarCleaning() {
return new FootbathEarCleaning();
}
@Override
public FaceWashing createFaceWashing() {
return new FootbathFaceWashing();
}
}
我们再来构建推油产品族的 采耳类以及洁面类
//扩展推油产品族的采耳类PullOilEarCleaning
public class PullOilEarCleaning implements EarCleaning {
@Override
public void clean() {
System.out.println("推油产品族中的采耳产品等级");
}
}
//扩展推油产品族的洁面类PullOilEarCleaning
public class PullOilFaceWashing implements FaceWashing {
@Override
public void washing() {
System.out.println("推油产品族中的洁面产品等级");
}
}
创建推油产品族的实际工厂
public class PullOilFactory implements AbstractFactory {
@Override
public EarCleaning createEarCleaning() {
return new PullOilEarCleaning() ;
}
@Override
public FaceWashing createFaceWashing() {
return new PullOilFaceWashing();
}
}
客户端调用方式
public class AbstractFactoryTest {
public static void main(String[] args) {
//洗脚产品族
FootbathFactory footbathFactory = new FootbathFactory();
footbathFactory.createEarCleaning().clean();
footbathFactory.createFaceWashing().washing();
//推油产品族
PullOilFactory pullOilFactory = new PullOilFactory();
pullOilFactory.createEarCleaning().clean();
pullOilFactory.createFaceWashing().washing();
}
}
以上代码完整的描述了两个产品族(洗脚和推油,也描述了两个产品等级采耳和洁面),抽象工厂就是清楚的描述了这层关系,但如果我们我们现在又要拓展一个修甲产品等级,那么AbstractFactory抽象需要引入修甲的构建方法,除此以外所有的实际具体工厂(FootbathFactory、PullOilFactory也需要全部调整),因此看出抽象工厂模式也是有它优缺点的。
缺点:
- 当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改
- 增加了系统的抽象性和理解难度
优点:
- 可以在类的内部对产品族中相关联的多等级产品共同管理,而不必专门引入多个新的类来进行管理。
- 当需要产品族时,抽象工厂可以保证客户端始终只使用同一个产品的产品组。
- 抽象工厂增强了程序的可扩展性,当增加一个新的产品族时,不需要修改原代码,满足开闭原则。
4、结语
本文主要介绍了设计模式的工厂模式,并以实际的生活场景为案例进行讲解,希望对大家对工厂模式有一个更清晰的理解;