Passing Properties to Factory method

99封情书 提交于 2019-11-28 13:50:47

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();      
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!