抽象工厂设计:用枚举代替

当我们使用Abstract Factory Pattern ,我们一般有FactoryMaker其中有一类getFactory中,我们通过参数的功能,我们有switchif-else上传递的参数的功能逻辑来决定要返回的工厂。 创建一个枚举或一个对象的传递参数,然后让这些对象内的哪个工厂返回的逻辑会更好。 例如 :

让我们说我们这个工厂制造商,它通过enum CountryCode来决定工厂。


    public class FacoryMaker {

        public final static FacoryMaker fctry= new FacoryMaker();

        public static RetailFactory getFactory(CountryCode code){
            RetailFactory rt = null;
            if(code == CountryCode.UK){
                rt = new UKFactory();
            }
            if(code == CountryCode.US){
                rt = new USFactory();
            }
            return rt;
        }
    }

取而代之的是,我们将拥有:


    public class FacoryMaker {

        public final static FacoryMaker fctry= new FacoryMaker();

        public static RetailFactory getFactory(CountryCode code){
            return code.getFactory();
        }
    }

和枚举将被修改如下:


    public enum CountryCode {
        US(){
            @Override
            public RetailFactory getFactory() {
                return new USFactory();
            }
        },  
        UK(){
            @Override
            public RetailFactory getFactory() {
                return new UKFactory();
            }
        };  
        public abstract RetailFactory getFactory();
    }

但我没有看到这一般被遵循。 为什么这样? 为什么我们不能让传入的参数总是一个对象,并让工厂内部的对象具有逻辑? 在任何抽象工厂设计下它能否失败? 它看起来非常通用。 此外,通过这种方式甚至可以删除工厂制造商,并直接使用该对象来获取Factory实例。


我认为Abstract Factory是所有OOP语言的一般模式。 当人们描述它时,他们应该展示一个可能适用于所有这些语言的通用实现。 然后人们遵循这个模式,他们遵循一般的实施。

您的实现使用的是Enum,它在Java中特别支持,但不包括其他OOP语言。


在实践中,工厂方法通常不会事先知道实现。 工厂创建时实施类可能不存在。 例如在诸如Java数据库连接API(JDBC)之类的服务提供者框架中就是这种情况。 JDBC定义了服务提供者必须实现的接口,但具体的实现并未提前知道。 此框架允许稍后添加实施,例如用于新的尖端数据库的数据库驱动程序。 服务提供者框架包括注册实现的提供者注册API(例如: DriverManager.registerDriver )和用于客户端获取服务实例的服务访问API(例如: DriverManager.getConnection )。 通常使用反射实例化服务实现(例如: Class.forName("org.blah.Driver") )。

你的例子是不同的。 你知道你想要的所有实现类。 而且你还没有考虑其他实现的可插拔性。 无论您是使用switch还是enum创建实例,它都没有什么区别。 两种选择都很好,相当。

另一个相关的选择是Collections中的各种方法,例如Collections.emptyList()Collections.singletonList(...)等。 这些实现不是由交换机决定的,但是通过使用专门的方法具有明确的名称。

如果您希望能够使用您的接口的实现未提前知道,并且不在工厂中进行硬编码,请查看服务提供商框架(如JDBC)。

但我没有看到这一般被遵循。 为什么这样?

您的技术仅适用于您事先了解RetailFactory所有实施。 在像JDBC这样的框架中,对于所有的数据库, DriverConnection等等的所有实现都是事先不知道的,所以使用这种技术和单枚枚举引用所有实现是不可能的,也不可扩展。 所以他们使用不同的机制,在运行时动态注册和加载实现。

为什么我们不能让传入的参数总是一个对象,并让工厂内部的对象具有逻辑?

您可以。 如果你不需要动态加载像JDBC这样的实现(很可能不需要),你使用枚举的方式有一些优点。 例如,你原来的实现是rt = ... ,这不如return ... 这种“错误”是不可能的使用你的enum解决方案。 另一方面,如果你想动态加载,那么使用enum就没有多大意义。

底线是,你提出的两种选择之间没有太大的区别。 两者都很好。


在设计软件时,一方面要考虑的是分离关注它听起来不是对我来说很合理,让一个CountryCode创建RetailFactory 。 这两个概念对彼此的凝聚力都很低,应该避免。

此外,如果您已经有了一个国家代码,为什么您需要一个工厂?是什么阻止您直接调用getFactory方法? 这根本没有意义。

getFactory仅仅是getFactorygetFactory方法的一个提示,如何创建工厂。 它甚至可能完全忽略国家代码。 如果有一个国家没有RetailFactory怎么办? 你是否返回null ? 一个DefaultFactory或另一个国家的工厂?

当然可以这样做,但是如果你从现在开始半年后看看你的代码,你可能会想:“为什么我在国家代码中创建了工厂?!”

此外,您提供的第一个示例似乎更多地是工厂方法而不是工厂,因为FactoryMaker完全没有使用。

链接地址: http://www.djcxy.com/p/96759.html

上一篇: Abstract Factory Design: Replacing if else with enum

下一篇: Java Abstract class Unusual behavior