问题描述
我有一个工厂方法,该方法返回接口的实现.问题是 - 实现具有不同的构造函数参数.
我的问题是 - 如何通过工厂方法传递参数到接口的不同实现?
我有一个想法,但是我不确定是否有意义 - 将属性对象传递给工厂方法?这样,每个接口实现都可以获取其构造函数所需的属性,而工厂接口将被统一.
这是有道理的,还是有更好的解决方案?
我决定加一个示例,这样我就可以更好地澄清问题.
假设我们有接口SomeAlgorithm,并且具有具体算法,其中每个算法可能具有不同的参数,例如
SomeAlgorithm algo = new Algo1(); SomeAlgorithm algo = new Algo2(noOfIterations); SomeAlgorithm algo = new Algo3(precision, boundary);
我希望能够做
之类的事情SomeAlgorithm algo = AlgoFactory.getAlgo("algoName");
我处理不同参数的方法是
SomeAlgorithm algo = AlgoFactory.getAlgo("algoName", properties);
然后,如果算法完全具有参数(例如,algo1没有参数),则算法可以将适当的属性传递给具体算法构造器.如果某些属性不存在,则可以传递默认值(如果在算法中需要该值).
您可以看到,我希望能够动态更改算法.用户将在运行时选择算法并传递适当的参数,以将其放入属性对象.
这有意义吗?
推荐答案
我认为您需要实现建筑商 tatter tatter.
构建器模式是对象创建软件设计模式.与抽象的工厂模式和工厂方法模式不同,其意图是实现多态性,建造者模式的意图是找到望远镜构造函数抗模式的解决方案[需要引用].
当对象构造函数参数组合的增加导致构造函数的指数列表时,望远镜构造函数抗模式发生.
而不是使用许多构造函数,而是使用另一个对象,一个构建器,该对象逐步接收每个初始化参数,然后一次返回结果构造的对象.
请查看此示例代码.
class SomeAlgorithm{ // Make it or class or interface } class Algo extends SomeAlgorithm{ private int noOfIterations; private double precision; private double boundary; public Algo(Builder builder){ this.noOfIterations = builder.noOfIterations; this.precision= builder.precision; this.boundary= builder.boundary; } public String toString(){ return new StringBuilder("Algo:Iterations:precision:boundary:").append(noOfIterations).append(":"). append(precision).append(":").append(boundary).toString(); } static class Builder { private int noOfIterations; // Mandatory parameter private double precision = 1.0; // Optional parameter private double boundary = 2.0; // Optional parameter public Builder ( int noOfIterations){ this.noOfIterations = noOfIterations; } public Builder precision(double precision){ this.precision = precision; return this; } public Builder boundary(double boundary){ this.boundary = boundary; return this; } public Algo build(){ return new Algo(this); } } } public class BuilderDemo{ public static void main(String args[]){ Algo algo = new Algo.Builder(2).precision(3.0).boundary(4.0).build(); System.out.println(algo); algo = new Algo.Builder(10).build(); System.out.println(algo); } }
输出:
java BuilderDemo 2 Algo:Iterations:precision:boundary:2:3.0:4.0 Algo:Iterations:precision:boundary:10:1.0:2.0
如果您必须实现具有构造函数和没有IF-else语句参数相同参数的工厂方法,请查看此替代方案
,但我偏爱获得相同的结果是:
public static Algo newInstance(String algoClassType) { return Class.forName(algoClassType).newInstance(); }
其他推荐答案
编辑问题的更新(Rev43552065-8EE8-47E8-47E8-BC96-C660C3836998):
您的示例不是典型的工厂模式.如果您有三种算法需要通过名称引用并为特定算法提供特定参数,为什么要使用工厂?您可能应该从著名的书"有效Java"中读取"项目1:考虑静态工厂方法,而不是构造函数".它描述了工厂方法的优势,在您的示例中我都看不到.
有很多解决这个问题的解决方案,您可以在各种流行的项目中找到数百个示例.
例如,DriverManager类使用类似URL的字符串,该字符串包含可变格式的连接详细信息和带有高级选项的附加Properties对象(示例).
工厂方法通常应该足够抽象的,以获得工作实现,而无需为特定实现指定任何其他参数.它应该"隐藏"实现细节.
如果有必要传递其他/可选属性,则通过Properties对象是很常见的.
有不同的策略.例如,UrlConnection是一个抽象类.可以通过调用URL.openConnection()来检索实例,但是,只能通过将返回的UrlConnection投入到特定的子类型,例如HttpUrlConnection.
我相信没有一个适合所有案例的解决方案,而且我很确定那里的许多解决方案,甚至可能在Java Standard库中,都不是完美的,但是您应该真正实施简单的东西而不是浪费太多东西有这样的问题的时间.
其他推荐答案
一种可能的方法,它比通过Properties确定结果类型更具有类型的安全性,是使用Abstract Factory模式,例如:
// we will be creating subtypes of Vehicle interface Vehicle { void move(); } class Car implements Vehicle { Car(String vehicleId, int seatsNumber) {} } class Motorcycle implements Vehicle { Motorcycle(String vehicleId) {} } // ... via subtypes of VehicleFactory interface VehicleFactory<T extends Vehicle> { T create(String vehicleId); } class FourSeatedCarFactory implements VehicleFactory<Car> { @Override public Car create(String vehicleId) { return new Car(vehicleId, 4); } } class MotorcycleFactory implements VehicleFactory<Motorcycle> { @Override public Motorcycle create(String vehicleId) { return new Motorcycle(vehicleId); } } class FactoryClient { void useVehicle(VehicleFactory<?> factory) { factory.create("COOL_PLATE_NAME").move(); } }
问题描述
I have a factory method which returns implementation of an interface. The thing is - implementations have different constructor parameters.
My question is - how to pass parameters through factory method to different implementations of the interface?
I have an idea, but I'm not sure if it makes sense - pass Properties object to factory method? This way each of interface implementations can get the properties that needs for its constructor, while factory interface will be unified.
Does this make sense, or there is a better solution?
I decided to add-up an example, so I could clarify the problem better.
Let's say we have interface SomeAlgorithm and we have concrete algorithms, where each algorithm may have different parameters, e.g.
SomeAlgorithm algo = new Algo1(); SomeAlgorithm algo = new Algo2(noOfIterations); SomeAlgorithm algo = new Algo3(precision, boundary);
I would like to be able to do something like
SomeAlgorithm algo = AlgoFactory.getAlgo("algoName");
My approach to handle different parameters would be
SomeAlgorithm algo = AlgoFactory.getAlgo("algoName", properties);
Then, AlgoFactory could pass appropriate properties to concrete algorithm constructor, if the algorithm has parameters at all (e.g. Algo1 doesn't have parameters). If some property is not present a default value could be passed (if that value is required in the algorithm).
As you can see I would like to be able to dynamically change algorithm. User would select algorithm in runtime and pass appropriate parameters which would be put into properties object.
Would this make sense?
推荐答案
I think you need to implement Builder pattern.
The builder pattern is an object creation software design pattern. Unlike the abstract factory pattern and the factory method pattern whose intention is to enable polymorphism, the intention of the builder pattern is to find a solution to the telescoping constructor anti-pattern[citation needed].
The telescoping constructor anti-pattern occurs when the increase of object constructor parameter combination leads to an exponential list of constructors.
Instead of using numerous constructors, the builder pattern uses another object, a builder, that receives each initialization parameter step by step and then returns the resulting constructed object at once.
Have a look at this example code.
class SomeAlgorithm{ // Make it or class or interface } class Algo extends SomeAlgorithm{ private int noOfIterations; private double precision; private double boundary; public Algo(Builder builder){ this.noOfIterations = builder.noOfIterations; this.precision= builder.precision; this.boundary= builder.boundary; } public String toString(){ return new StringBuilder("Algo:Iterations:precision:boundary:").append(noOfIterations).append(":"). append(precision).append(":").append(boundary).toString(); } static class Builder { private int noOfIterations; // Mandatory parameter private double precision = 1.0; // Optional parameter private double boundary = 2.0; // Optional parameter public Builder ( int noOfIterations){ this.noOfIterations = noOfIterations; } public Builder precision(double precision){ this.precision = precision; return this; } public Builder boundary(double boundary){ this.boundary = boundary; return this; } public Algo build(){ return new Algo(this); } } } public class BuilderDemo{ public static void main(String args[]){ Algo algo = new Algo.Builder(2).precision(3.0).boundary(4.0).build(); System.out.println(algo); algo = new Algo.Builder(10).build(); System.out.println(algo); } }
output:
java BuilderDemo 2 Algo:Iterations:precision:boundary:2:3.0:4.0 Algo:Iterations:precision:boundary:10:1.0:2.0
If you have to implement Factory method with same set of parameters for constructor & without if-else statement, have a look at this alternative
But my preference to achieve the same result is :
public static Algo newInstance(String algoClassType) { return Class.forName(algoClassType).newInstance(); }
其他推荐答案
Update for edited question (rev43552065-8ee8-47e8-bc96-c660c3836998):
Your example is not a typical factory pattern. If you have three algorithms which you need to reference by name AND provide specific parameters for a particular algorithm, why would you want to use a factory? You should probably read "Item 1: Consider static factory methods instead of constructors" from the famous book "Effective Java". It describes the advantages of factory methods, none of which I can see in your example.
There are many solutions to this problem, and you can find hundreds of examples in all kinds of popular projects.
For example, the DriverManager class, uses an URL-like string which contains connection details in a variable format and an additional Properties object with advanced options (example).
A factory method should usually be abstract enough to get a working implementation without having to specify any additional parameters for a specific implementation. It is supposed to "hide" implementation details.
If it turns out to be necessary to pass additional / optional properties, it is quite common to pass a Properties object.
There are different strategies. For example, UrlConnection is an abstract class. Instances can be retrieved by calling URL.openConnection(), however, many options can only be set by casting the returned UrlConnection to a specific sub-type, e.g. HttpUrlConnection.
I believe there is no single solution which suits all cases and I am pretty sure that many solutions out there, possibly even in the Java standard library, are far from perfect, but you should really implement something simple instead of wasting too much time with such problems.
其他推荐答案
One possible way, which is more type-safe than passing Properties around to determine result type, is to use Abstract Factory pattern, e.g.:
// we will be creating subtypes of Vehicle interface Vehicle { void move(); } class Car implements Vehicle { Car(String vehicleId, int seatsNumber) {} } class Motorcycle implements Vehicle { Motorcycle(String vehicleId) {} } // ... via subtypes of VehicleFactory interface VehicleFactory<T extends Vehicle> { T create(String vehicleId); } class FourSeatedCarFactory implements VehicleFactory<Car> { @Override public Car create(String vehicleId) { return new Car(vehicleId, 4); } } class MotorcycleFactory implements VehicleFactory<Motorcycle> { @Override public Motorcycle create(String vehicleId) { return new Motorcycle(vehicleId); } } class FactoryClient { void useVehicle(VehicleFactory<?> factory) { factory.create("COOL_PLATE_NAME").move(); } }