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?
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();
}
}
I see that Strategy pattern is more suitable here. You don't need to pass parameters to constructor. They look like params for calculation.
If your algorithms use the same work, for example calculate taxes, it's ok go this way. But if they do different things - consider using other approach or provide more details to see what could be done.
So, for tax calculation it can be:
taxCalculator = taxCalculatorFactory.Get(currentCountry);
taxAmount = taxCalculator.Calculate(countrySpecificTaxProperties);
Just use some interface for your countrySpecificTaxProperties, for example ITaxParams
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();
}
来源:https://stackoverflow.com/questions/34726422/passing-properties-to-factory-method