当我们使用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实例。
答案 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这样的框架中,Driver
,Connection
等的所有实现都不是事先知道的,对于那里的所有数据库,所以使用这种技术和单个枚举引用所有实现是不可能,也不可扩展。因此,他们使用不同的机制,在运行时动态注册和加载实现。
为什么我们不能让传递参数始终成为一个对象,并且在工厂的对象中有逻辑?
你可以。如果您不需要动态加载像JDBC这样的实现(很可能不是这样),那么使用枚举的方式有一些优点。
例如,您的原始实现执行rt = ...
,这不如执行return ...
。这样的错误"无法使用enum
解决方案。另一方面,如果你想要动态加载,那么使用enum
将没有多大意义。
最重要的是,您提出的两种替代方案之间没有太大区别。两者都很好。
答案 2 :(得分:0)
在设计软件时,要考虑的一个方面是关注点分离让CountryCode
创建RetailFactory
对我来说听起来不太合理。这两个概念对彼此的凝聚力都很低,应该避免。
此外,如果您已经拥有国家/地区代码,为什么还需要工厂呢?是什么阻止您直接调用getFactory方法?这根本没有意义。
CountryCode仅仅是FactoryMaker getFactory
方法的提示,以及如何创建工厂。它甚至可能完全忽略国家代码。如果有没有RetailFactory的国家怎么办?你回来了null
吗?一个DefaultFactory或另一个国家的工厂?
当然可能这样做,但如果你从现在起半年后查看你的代码,你可能会认为" Wtf?为什么我在国家代码中创建了工厂?"
此外,您提供的第一个示例似乎更多是 Factory Method 而不是 Factory ,因为根本不使用FactoryMaker。