工厂模式到底该不该被使用?

1.工厂的使用场景和解决的问题

工厂模式是一种建造模式,是对象创建的一种范式,它通过将对象创建的逻辑封装到工厂类中,来降低客户端代码的复杂性,使客户端不用过度关注对象创建的细节问题。使用工厂模式可以让客户端在需要创建对象时,只需要依赖相关的工厂即可,而无需依赖繁多的被创建的对象。因为存在工厂这个中间层,可以实现 对象创建逻辑的变化,对客户端无感知,实现客户端在创建对象的过程中,面向工厂而非具体对象编程。

为了方便下面的讨论,我们举例如下:客户端需要根据key获取对应的value值,k-v键值对可以存放在多种存储系统中,包括但不限于db,redis,file三种。

在不使用工厂模式时,对象创建耦合在客户端代码中。根据key获取value的具体代码如下:

public String getValueWithoutFacory(String storeType) {
            IStore store = null;
            if ("db".equalsIgnoreCase(storeType)) {
                store = new DBStorage();
            } else if ("file".equalsIgnoreCase(storeType)) {
                store = new FileStore();
            } else if ("redis".equalsIgnoreCase(storeType)) {
                store = new RedisStore();
            } else {
                throw new IllegalArgumentException(
                    "store config  type is not supported: " + storeType);
            }
    
            String key = "xxx_key";

            String value = store.getValue(key);
            return value;
        }

从上面的代码来看,客户端本来只需要根据key获取value,结果大部分代码都是在创建需要的IStore对象,有点本末倒置了。

2.工厂分类

工厂模式有2中常用的变种:简单工厂工厂方法

(1)简单工厂

简单工厂就是将客户端创建对象的逻辑直接搬到工厂类中,也即是将客户端代码中对象创建的复杂度转移工厂类中。

public static IStorager getStorageWithoutCache(String storeType) {
        IStore store = null;
        if ("db".equalsIgnoreCase(storeType)) {
            store = new DBStorage();
        } else if ("file".equalsIgnoreCase(storeType)) {
            store = new FileStore();
        } else if ("redis".equalsIgnoreCase(storeType)) {
            store = new RedisStore();
        } else {
            throw new IllegalArgumentException(
                    "store config  type is not supported: " + storeType);
        }
       return store;
    }

如果被创建的对象可以复用的话,那么可以将被创建的对象缓存起来,减少对象创建的性能和内存消耗。 

private static Map<String, IStorager> map = new HashMap<String, IStorager>();

    static {
        map.put("db",new DBStorage());
        map.put("file",new FileStore());
        map.put("redis",new RedisStore());
    }

    public static IStore getStorage(String storeType) {
        IStore store = map.get(storeType);

        if (null == store)  {
            throw new IllegalArgumentException(
                    "store config  type is not supported: " + storeType);
        }
        return store;
    }

(2)工厂方法

工厂方法相对简单工厂来说会更复杂一些。他主要用来解决简单工厂中,当被创建的对象不可复用时,出现的大量 if-else 问题。工厂方法的主要实现思路是:为每种类型的对象创建一个单独的工厂,该工厂只创建该类型的对象。具体代码如下:

public class DBStoreFactory implements IStoreFactory{

    @Override
    public IStore createStore() {
        return new DBStorage();
    }
}

public class FileStoreFactory implements IStoreFactory{

    @Override
    public IStore createStore() {
        return new FileStore();
    }

}

public class RedisStoreFactory implements IStoreFactory{

    @Override
    public IStore createStore() {
        return new RedisStore();
    }

}

不过客户端在使用工厂方法时还是会出现大量 if-else 的问题,具体代码如下:

public String getValueWithFactoryMethod(String storeType) {
            IStoreFactory storeFactory = null;
            if ("db".equalsIgnoreCase(storeType)) {
                storeFactory = new DBStoreFactory();
            } else if ("file".equalsIgnoreCase(storeType)) {
                storeFactory = new FileStoreFactory();
            } else if ("redis".equalsIgnoreCase(storeType)) {
                storeFactory = new RedisStoreFactory();
            } else {
                throw new IllegalArgumentException(
                        "store config  type is not supported: " + storeType);
            }

            IStore store = storeFactory.createStorage();
            String key = "xxx_key";
            String value = store.getValue(key);
            return value;
        }

这个和不使用工厂模式时存在的问题是相同,只不过,不使用工厂模式的方式,if-else 出现在创建IStroe时,而使用工厂方法的时候,if-else出现在创建IStoreFactory。针对简单工厂出现的if-else问题,我们可以再次采用简单工厂的方式,只不过这个简单工厂,是工厂的工厂。

到这里你可能会产生疑问:首先我们使用工厂方法就是为了解决简单工厂中出现大量if-else的问题,而使用工厂方法虽然解决了简单工厂中if-else的问题,但是工厂方法本身也带来了if-else问题。那么我们再使用简单工厂去解决工厂方法的if-else的问题,那岂不是就陷入了恶性循环了吗?其实并不会,首先我们第一轮使用简单工厂时,之所以会引入if-else的问题,是因为简单工厂创建的对象,存在不可复用的问题。而在第二轮使用简单工厂创建的对象,本身就是一个工厂,工厂肯定是可以复用的,也就不会再出现if-else的问题。

具体代码如下:

public class StoreFactoryMap {

    private static Map<String,IStoreFactory> factoryMap = new HashMap<>();

    static {
        factoryMap.put("db",new DBStoreFactory());
        factoryMap.put("file",new FileStoreFactory());
        factoryMap.put("redis",new RedisStoreFactory());
    }

    public static IStoreFactory getStoreFactory(String factoryType){
        IStoreFactory iStoreFactory = factoryMap.get(factoryType);
        if (null == iStoreFactory)  {
            throw new IllegalArgumentException(
                    "store config  type is not supported: " + iStoreFactory);
        }
        return iStoreFactory;
    }

3.工厂方法的lambda方式优化

上面的工厂方法,虽然可以解决简单工厂中对于不可复用对象创建过程中的多个if-else,但是会使代码的复杂度提高很多,通过观察工厂方法的实现,我们发现,工厂方法中:针对每种类型的对象,都会定义一个工厂类,这些工厂类功能都很简单,其内部只定义了一个方法,方法做的事情就是创建对象,因此可以用一个函数式接口来替换定义的多个工厂类,降低代码层面实现的复杂度。

具体代码如下:

public class StoreFactoryMapOpt {
    
    private static Map<String, Supplier<IStore>> map = new HashMap<>();

    static {
        map.put("db",()->new DBStore());
        map.put("file",()->new FileStore());
        map.put("redis",()->new RedisStore());
    }

    public static IStore getStoreFactory(String factoryType){
        Supplier<IStore> iStoragerSupplier = map.get(factoryType);
        if (null == iStoragerSupplier)  {
            throw new IllegalArgumentException(
                    "store config  type is not supported: " + factoryType);
        }
        return iStoragerSupplier.get();
    }

    public String getValueWithFactoryMapOpt(String storeType) {
        IStore iStore = StoreFactoryMapOpt.getStoreFactory(storeType);
        String key = "xxx_key";
        String value = iStore.getValue(key);
        return value;
    } 
}


4.工厂模式使用的思考

使用工厂模式,主要为了降低客户端创建对象的复杂度。但是复杂度本身并没有因为使用工厂而消失,只是从客户端转移到了工厂,而是用工厂方法,可以降低简单工厂的复杂度,但是仍然没有将复杂度消除,只是将复杂度转移到了工厂的工厂。那么我们此时再回过头来看,使用简单工厂 和 工厂方法,需要我们创建大量的且与业务本身无关的工厂相关的类,带来的复杂度,和没有使用工厂时,仅有的几个if-else带来的复杂度相比,哪个更复杂?你品,你细品。


 

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值