设计模式(一)——简单工厂、工厂与抽象工厂

本文详细介绍了简单工厂模式、工厂模式和抽象工厂模式。简单工厂模式将实例创建交给工厂类,但违反开放封闭原则;工厂模式将工厂抽象,解决了部分问题;抽象工厂模式针对多种抽象产品。还分析了抽象工厂模式优缺点,介绍了简单工厂优化及反射结合的方法,并对比了三种模式的区别与使用场景。

简单工厂模式

简单工厂模式是指:使用一个单独的类去实现创造实例的过程,该类就被称为工厂类

举个栗子

假设这里要实现一个计算器,为了将计算器的功能进行隔离(对加减乘除的方法进行隔离,使其互不干扰,又为了提取公共的运算部分),将公共部分放在一个超类,然后对应去实现不同运算功能的子类

一个功能就对应一个类

在这里插入图片描述
简单工厂类根据页面传来的类型来调用不同模块
在这里插入图片描述
也就是将实例对象的能力交给了简单工厂类去进行

这个模式遵循了单一职责,却违反了开放封闭原则,一旦我要添加新的计算功能,就要去修改工厂里面的switch分支,修改了原有的类。

工厂模式

简单工厂模式违反了开放封闭原则,所以就有了工厂模式

工厂模式是指:定义一个用于创建对象的接口,让子类去决定实例化哪一个类,让一个类的实例延迟到其子类去进行

关键就在于,逻辑判断的代码放在了简单工厂类里面,这是简单工厂模式的优点,也是其缺点

  • 优点在于工厂类里面包含了必要的逻辑判断,根据客户端的选择条件去动态实例化相关的类,对于客户端来说,去除了客户端对具体产品的依赖性
  • 缺点也在于工厂类里面包含了必要的逻辑判断,一旦需要增加新的实例化类时,工厂类自己也要需要去进行修改,违反了开放封闭原则

缺点的问题就出现在工厂并不是面向抽象的,而是面向实际,解决的思路就是将工厂也抽象起来,不同的实例要由不同的工厂去进行创建

将工厂抽象起来,这里共有4个工厂

在这里插入图片描述
对应的产品如下
在这里插入图片描述
那么页面交互的地方要调用的代码如下(页面掌控了具体调用工厂的逻辑)
在这里插入图片描述
那么此时,如果我们要新增产品(Product)时,要去添加两个新类(一个实现Product,另一个实现IFactory),一个是新产品,另外一个是新工厂,那么就符合了开放封闭原则,对原有工厂类的修改封闭,对工厂类的拓展新类进行开放

简单来说,工厂把简单工厂的内部逻辑判断转移到了客户端上,本来新增产品是要修改工厂类的,现在是修改客户端上面

抽象工厂模式

抽象工厂模式是指:提供一个创建一系列相关或相互依赖对象的接口,而无需去指定它们具体的类

其实抽象工厂模式跟工厂模式类似,只不过工厂模式是针对一种抽象产品有不同实例的,所以工厂接口里面只能生成一个抽象产品,而抽象工厂模式是针对多种抽象产品,而抽象的工厂接口里面包含所有的抽象产品的建造方法,也就是该工厂不局限于只有一个抽象产品了,该工厂有一系列产品

比如这里有两个抽象产品

在这里插入图片描述
在这里插入图片描述
那么对应的抽象工厂应该有这两个产品的抽象生产方法

/**
 * @Author: Ember
 * @Date: 2021/6/1 22:03
 * @Description:
 */
public interface IFactory {
    public ProductA createA();
    public ProductB createB();
}
class AccessFactory implements IFactory{

    @Override
    public ProductA createA() {
        return new ProductAOne();
    }

    @Override
    public ProductB createB() {
        return new ProductBOne();
    }
}
class SQLFactory implements IFactory{

    @Override
    public ProductA createA() {
        return new ProductATwo();
    }

    @Override
    public ProductB createB() {
        return new ProductBTwo();
    }
}

而客户端的调用依然是只要去修改对应实例的工厂即可,与工厂模式的客户端修改几乎一样

在这里插入图片描述

抽象工厂模式的优点与缺点

  • 便于交换产品系列,只要去改动客户端使用的抽象工厂实例即可,那么就可以获取同种抽象,但不同系列的产品

  • 让具体的产品创建实例与客户端分离,客户端仅仅是通过工厂的抽象接口来创建实例的,对产品是不可见的(连产品实例的名字也不知道)。

  • 缺点在于,每当新增一个新的抽象产品时,要增加的其他类也加多了,比如该新抽象产品的对应各个工厂的具体实例,同时要修改的地方也加多了,各个工厂都要增加对于该抽象产品的实现创建方法(因为抽象工厂增加了该抽象产品的创建方法)

  • 客户端对产品虽然仍然不可视,但破坏了封闭开放原则

所以设计的时候,就一定要提前想好抽象产品,不要随意进行新增。

简单工厂优化抽象工厂

抽象工厂之所以新增产品如此困难,原因就在于工厂太多了,所以可以将所有的工厂合并成一个简单工厂,那么只需要去修改一个简单工厂类即可。

public class ConcreateFactory implements IFactory{
    private static final String TYPE = "SQL";
    //private static final String TYPE = "ACCESS";
    @Override
    public ProductA createA() {
        switch (TYPE){
            case "SQL":{
                return new ProductAOne();
            }
            case "ACCESS":{
                return new ProductATwo();
            }
        } 
        return null;
    }
    @Override
    public ProductB createB() {
        switch (TYPE){
            case "SQL":{
                return new ProductBOne();
            }
            case "ACCESS":{
                return new ProductBTwo();
            }
        }
        return null;
    }
}

这里使用简单工厂模式还将工厂名字(TYPE)给封装起来,那么客户端那边对工厂名字甚至不可见,所以客户端什么都不用改,所有的修改都在简单工厂里面进行修改就可以了

但缺点仍然存在,存在大量的switch,破坏了开放封闭原则

反射+抽象工厂

通过反射,我们就可以根据类名动态去创建产品,减少一大堆的switch去进行切换,当然这里实现的前提是,产品的类名与工厂名字有所关联

举个栗子

这是抽象产品及其对应的实例

在这里插入图片描述
那么我们的简单工厂就变成这样,通过反射去获取对应的实际产品

/**
 * @Author: Ember
 * @Date: 2021/6/1 22:43
 * @Description:
 */
public class DataAccessFactory implements IFactory{
    private static final String TYPE = "SQL";
    //这里也是可以改动,然后对应生产同一下列不同实际的产品
    //private static final String TYPE = "Access";
    @Override
    public ProductOne getOne() {
        try {
            Class<?> aClass = Class.forName(TYPE + "Product");
            Object o = aClass.newInstance();
            return (ProductOne) o;
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public ProductTwo getTwo() {
        try {
            Class<?> aClass = Class.forName(TYPE + "ProductTwo");
            Object o = aClass.newInstance();
            return (ProductTwo) o;
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
        return null;
    }
}

没有了大量的switch,那么我们就可以不需要改动原有产品的生产代码,添加新产品的时候,只需要添加对应反射生产实际新产品的代码即可。

这里还能做一个优化就是,我们可以利用配置文件来去修改工厂里面的TYPE配置,那么就不必改动源代码文件了

所有在用简单工厂的地方,考虑使用反射技术来去除switch或if,解除由于大量的switch带来的耦合

三种模式的区别,与使用场景

  • 工厂模式克服了简单工厂违背开放-封闭原则的缺点,而且保持了封装对象创建过程的优点(封装了产品的创建)

  • 工厂模式与简单工厂模式都实现了客户端与产品对象的解耦合(抽象工厂模式的客户端修改的是工厂类,创建产品的细节依然在工厂类里面,所以仍然是与产品对象解耦合)

  • 工厂模式是简单工厂模式的进一步抽象和推广,但缺点在于,工厂模式会导致更多的类生成,因为不仅要新系列产品还要新工厂

  • 在业务中,抽象产品的系列越来越多,一个抽象工厂也不再只是单独生产一个系列的抽象产品,那么工厂模式就变成了抽象工厂模式

  • 抽象工厂模式保留了工厂模式的优点,就是对于同一系列的衍生产品符合开放封闭原则的

  • 但是当要新增抽象产品的时候,就要去进行改动很多类,抽象工厂类、实际工厂类、抽象产品类,都要去进行新增或改动,不符合开放封闭原则

  • 使用简单工厂模式来优化抽象工厂模式,通过将工厂合并起来,可以减少改动的抽象工厂类,只需要改动简单工厂即可

  • 再利用反射和配置文件,可以减少简单工厂里面大量的switch或if分支带来的耦合,让已有的产品创建方法不需要进行改动

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值