抽象工厂设计:用枚举代替
当我们使用Abstract Factory Pattern
,我们一般有FactoryMaker
其中有一类getFactory
中,我们通过参数的功能,我们有switch
或if-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这样的框架中,对于所有的数据库, Driver
, Connection
等等的所有实现都是事先不知道的,所以使用这种技术和单枚枚举引用所有实现是不可能的,也不可扩展。 所以他们使用不同的机制,在运行时动态注册和加载实现。
为什么我们不能让传入的参数总是一个对象,并让工厂内部的对象具有逻辑?
您可以。 如果你不需要动态加载像JDBC这样的实现(很可能不需要),你使用枚举的方式有一些优点。 例如,你原来的实现是rt = ...
,这不如return ...
这种“错误”是不可能的使用你的enum
解决方案。 另一方面,如果你想动态加载,那么使用enum
就没有多大意义。
底线是,你提出的两种选择之间没有太大的区别。 两者都很好。
在设计软件时,一方面要考虑的是分离关注它听起来不是对我来说很合理,让一个CountryCode
创建RetailFactory
。 这两个概念对彼此的凝聚力都很低,应该避免。
此外,如果您已经有了一个国家代码,为什么您需要一个工厂?是什么阻止您直接调用getFactory方法? 这根本没有意义。
getFactory
仅仅是getFactory
的getFactory
方法的一个提示,如何创建工厂。 它甚至可能完全忽略国家代码。 如果有一个国家没有RetailFactory怎么办? 你是否返回null
? 一个DefaultFactory或另一个国家的工厂?
当然可以这样做,但是如果你从现在开始半年后看看你的代码,你可能会想:“为什么我在国家代码中创建了工厂?!”
此外,您提供的第一个示例似乎更多地是工厂方法而不是工厂,因为FactoryMaker完全没有使用。
链接地址: http://www.djcxy.com/p/96759.html