津津乐道设计模式 - 工厂模式详解

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也有用到了这个设计模式;总结一波:

工厂方法模式优点:

  1. 用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程。
  2. 灵活性增强,对于新产品的创建,只需多写一个相应的工厂类。
    典型的解耦框架。
  3. 高层模块只需要知道产品的抽象类,无须关心其他实现类,满足迪米特法则、依赖倒置原则和里氏替换原则。

工厂方法模式缺点:

  1. 类的个数容易过多,增加复杂度
  2. 增加了系统的抽象性和理解难度

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也需要全部调整),因此看出抽象工厂模式也是有它优缺点的。

缺点:

  1. 当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改
  2. 增加了系统的抽象性和理解难度

优点:

  1. 可以在类的内部对产品族中相关联的多等级产品共同管理,而不必专门引入多个新的类来进行管理。
  2. 当需要产品族时,抽象工厂可以保证客户端始终只使用同一个产品的产品组。
  3. 抽象工厂增强了程序的可扩展性,当增加一个新的产品族时,不需要修改原代码,满足开闭原则。

4、结语

本文主要介绍了设计模式的工厂模式,并以实际的生活场景为案例进行讲解,希望对大家对工厂模式有一个更清晰的理解;

样例代码:https://github.com/lhmyy521125/toher-designmode

下一节:津津乐道设计模式 - 单例模式详解

  • 16
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 17
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Micro麦可乐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值