Passing Properties to Factory method
Asked Answered
A

4

4

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?

Arliearliene answered 11/1, 2016 at 16:26 Comment(6)
What about overloading the factory method?Subsellium
To the properties vary by interface implementation only, or do they vary by call?Switzer
If you need to pass different count of arguments to constructor most likely your design is not perfect. It would be great if you provide examples of two classes with different paramsStillhunt
@hege_hegedus Wouldn't help, see the examples. I wan't uniform way of creating algorithms.Arliearliene
@Switzer See the edited answer, I have explained.Arliearliene
@DenisEfimov Here are the classes, as you required.Arliearliene
S
2

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();      
}
Seguidilla answered 12/1, 2016 at 19:26 Comment(0)
B
1

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.

Brendis answered 11/1, 2016 at 16:37 Comment(2)
Thank you for your explanation and for the examples. Could you just consider my edited question and see would you still hold to your answer?Arliearliene
@Marko I edited my answer in response to your updated question.Brendis
C
0

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();
    }
}
Celanese answered 11/1, 2016 at 16:45 Comment(1)
Thank you for your answer, but this is not what I'm aiming for. I don't actually have families of algorithms, but different implementations of an interface.Arliearliene
S
0

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

Stillhunt answered 12/1, 2016 at 9:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.