Передача свойств в фабричный метод

У меня есть фабричный метод, который возвращает реализацию интерфейса. Дело в том, что у реализаций разные параметры конструктора.

Мой вопрос - как передать параметры через фабричный метод в разные реализации интерфейса?

У меня есть идея, но я не уверен, что это имеет смысл - передать объект свойств фабричному методу? Таким образом, каждая из реализаций интерфейса может получить свойства, необходимые для ее конструктора, а фабричный интерфейс будет унифицирован.

Имеет ли это смысл, или есть лучшее решение?

Я решил добавить пример, чтобы лучше прояснить проблему.

Допустим, у нас есть интерфейс 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); 

Затем AlgoFactory может передать соответствующие свойства конструктору конкретного алгоритма, если алгоритм вообще имеет параметры (например, Algo1 не имеет параметров). Если какое-то свойство отсутствует, может быть передано значение по умолчанию (если это значение требуется в алгоритме).

Как видите, я хотел бы иметь возможность динамически изменять алгоритм. Пользователь выберет алгоритм во время выполнения и передаст соответствующие параметры, которые будут помещены в объект свойств.

Будет ли это иметь смысл?


person Kobe-Wan Kenobi    schedule 11.01.2016    source источник
comment
Как насчет перегрузки фабричного метода?   -  person Tamas Hegedus    schedule 11.01.2016
comment
Свойства зависят только от реализации интерфейса или от вызова?   -  person dbugger    schedule 11.01.2016
comment
Если вам нужно передать конструктору разное количество аргументов, скорее всего, ваш дизайн не идеален. Было бы здорово, если бы вы привели примеры двух классов с разными параметрами   -  person Dzianis Yafimau    schedule 11.01.2016
comment
@hege_hegedus Не поможет, посмотрите примеры. Я не хочу единого способа создания алгоритмов.   -  person Kobe-Wan Kenobi    schedule 12.01.2016
comment
@dbugger Смотрите отредактированный ответ, я объяснил.   -  person Kobe-Wan Kenobi    schedule 12.01.2016
comment
@DenisEfimov Вот классы, которые вам нужны.   -  person Kobe-Wan Kenobi    schedule 12.01.2016


Ответы (4)


Обновление для отредактированного вопроса (rev43552065-8ee8-47e8-bc96-c660c3836998):

Ваш пример не является типичным фабричным шаблоном. Если у вас есть три алгоритма, на которые вам нужно ссылаться по имени И предоставить конкретные параметры для конкретного алгоритма, зачем вам использовать фабрику? Вам, вероятно, следует прочитать "Пункт 1: Рассмотрите статические фабричные методы вместо конструкторов" из известной книги "Эффективная Java". В нем описаны преимущества фабричных методов, ни одного из которых я не вижу в вашем примере.


Решений этой проблемы множество, и вы можете найти сотни примеров во всевозможных популярных проектах.

Например, класс DriverManager использует строку, подобную URL-адресу, которая содержит сведения о соединении в переменном формате и дополнительный объект Properties с расширенными параметрами (пример).

Фабричный метод обычно должен быть достаточно абстрактным, чтобы получить работающую реализацию без указания каких-либо дополнительных параметров для конкретной реализации. Предполагается, что он «скрывает» детали реализации.

Если оказывается необходимым передать дополнительные/необязательные свойства, довольно часто передается объект Properties.

Существуют разные стратегии. Например, UrlConnection — это абстрактный класс. Экземпляры можно получить, вызвав URL.openConnection(), однако многие параметры можно установить только путем приведения возвращаемого UrlConnection к определенному подтипу, например. HttpUrlConnection.

Я считаю, что нет единого решения, подходящего для всех случаев, и я почти уверен, что многие решения, возможно, даже в стандартной библиотеке Java, далеки от совершенства, но вы действительно должны реализовать что-то простое, вместо того, чтобы тратить слишком много времени на такие проблемы.

person Tobias    schedule 11.01.2016
comment
Спасибо за объяснение и за примеры. Не могли бы вы просто рассмотреть мой отредактированный вопрос и посмотреть, будете ли вы по-прежнему придерживаться своего ответа? - person Kobe-Wan Kenobi; 12.01.2016
comment
@Marko Марко, я отредактировал свой ответ в ответ на ваш обновленный вопрос. - person Tobias; 12.01.2016

Один из возможных способов, который более безопасен для типов, чем передача 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();
    }
}
person Victor Sorokin    schedule 11.01.2016
comment
Спасибо за ваш ответ, но это не то, к чему я стремлюсь. На самом деле у меня нет семейств алгоритмов, но есть разные реализации интерфейса. - person Kobe-Wan Kenobi; 12.01.2016

Я вижу, что здесь больше подходит шаблон Стратегия. Вам не нужно передавать параметры в конструктор. Они выглядят как параметры для расчета.

Если ваши алгоритмы используют одну и ту же работу, например, рассчитывают налоги, можно пойти по этому пути. Но если они делают разные вещи - рассмотрите возможность использования другого подхода или предоставьте больше деталей, чтобы увидеть, что можно сделать.

Итак, для расчета налога это может быть:

taxCalculator = taxCalculatorFactory.Get(currentCountry);
taxAmount = taxCalculator.Calculate(countrySpecificTaxProperties);

Просто используйте какой-нибудь интерфейс для вашей страныSpecificTaxProperties, например ITaxParams

person Dzianis Yafimau    schedule 12.01.2016

Я думаю, вам нужно реализовать шаблон 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, посмотрите эта альтернатива

Но я предпочитаю добиться того же результата:

public static Algo newInstance(String algoClassType) {
    return Class.forName(algoClassType).newInstance();      
}
person Ravindra babu    schedule 12.01.2016