我有一个工厂方法,它返回一个接口的实现。问题是 - 实现具有不同的构造函数参数。
我的问题是 - 如何通过工厂方法将参数传递给接口的不同实现?
我有一个想法,但我不确定它是否有意义 - 将Properties对象传递给工厂方法?这样,每个接口实现都可以获得其构造函数所需的属性,而工厂接口将统一。
这是否有意义,或者有更好的解决方案?
我决定加上一个例子,所以我可以更好地澄清问题。
假设我们有接口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没有参数),AlgoFactory可以将适当的属性传递给具体的算法构造函数。如果某些属性不存在,则可以传递默认值(如果算法中需要该值)。
如您所见,我希望能够动态更改算法。用户可以在运行时选择算法并传递适当的参数,这些参数将被放入属性对象中。
这会有意义吗?
答案 0 :(得分:1)
已修改问题的更新(rev43552065-8ee8-47e8-bc96-c660c3836998):
您的示例不是典型的工厂模式。如果您需要通过名称引用三种算法并为特定算法提供特定参数,那么您为什么要使用工厂?您应该阅读"项目1:考虑静态工厂方法而不是构造函数"来自着名的书籍#34; Effective Java"。它描述了工厂方法的优点,我在你的例子中没有看到它们。
这个问题有很多解决方案,你可以在各种流行的项目中找到数百个例子。
例如,DriverManager
类使用类似URL的字符串,其中包含变量格式的连接详细信息和带有高级选项(example)的其他Properties
对象。
工厂方法通常应该足够抽象以获得有效的实现,而无需为特定实现指定任何其他参数。应该隐藏"隐藏"实施细节。
如果有必要传递附加/可选属性,则传递Properties
对象是很常见的。
有不同的策略。例如,UrlConnection
是一个抽象类。可以通过调用URL.openConnection()
来检索实例,但是,只能通过将返回的UrlConnection
转换为特定的子类型来设置许多选项,例如, HttpUrlConnection
。
我相信没有适合所有情况的单一解决方案,我很确定许多解决方案,甚至可能在Java标准库中,都远非完美,但你应该真正实现一些简单而不是浪费太多时间问题。
答案 1 :(得分:0)
一种可能的方式,比传递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();
}
}
答案 2 :(得分:0)
我认为策略模式更适合此处。您不需要将参数传递给构造函数。他们看起来像params进行计算。
如果您的算法使用相同的工作,例如计算税,那么就可以这样做了。但如果他们做不同的事情 - 考虑使用其他方法或提供更多细节,看看可以做些什么。
因此,对于税收计算,它可以是:
taxCalculator = taxCalculatorFactory.Get(currentCountry);
taxAmount = taxCalculator.Calculate(countrySpecificTaxProperties);
只需为countrySpecificTaxProperties使用一些界面,例如ITaxParams
答案 3 :(得分:0)
我认为您需要实施Builder模式。
构建器模式是对象创建软件设计模式。与抽象工厂模式和工程方法模式不同,其意图是启用多态,建模模式的目的是找到伸缩构造函数反模式的解决方案[需要引证]。
当对象构造函数参数组合的增加导致构造函数的指数列表时,会发生伸缩构造函数反模式。
构建器模式不是使用众多构造函数,而是使用另一个对象(构建器),它逐步接收每个初始化参数,然后立即返回生成的构造对象。
看看这个示例代码。
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
如果必须使用相同的构造函数和参数集实现Factory方法如果没有if-else语句,请查看this alternative
但我对获得相同结果的偏好是:
public static Algo newInstance(String algoClassType) {
return Class.forName(algoClassType).newInstance();
}