抽象工厂设计:用枚举替换if else

时间:2016-05-07 12:04:33

标签: java design-patterns abstract-factory

当我们使用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实例。

3 个答案:

答案 0 :(得分:0)

我认为抽象工厂是所有OOP语言的通用模式。当人们描述它时,它们应该显示可以在所有这些语言中应用的一般实现。然后人们遵循这种模式,他们遵循一般的实施。

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

答案 1 :(得分:0)

在实践中,工厂方法通常不会事先知道实施。在创建工厂时可能不存在实现类。例如,在服务提供者框架中就是这种情况,例如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将没有多大意义。

最重要的是,您提出的两种替代方案之间没有太大区别。两者都很好。

答案 2 :(得分:0)

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

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

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

当然可能这样做,但如果你从现在起半年后查看你的代码,你可能会认为" Wtf?为什么我在国家代码中创建了工厂?"

此外,您提供的第一个示例似乎更多是 Factory Method 而不是 Factory ,因为根本不使用FactoryMaker。