Избегайте статического связывания в операциях, которые используют аргументы иерархии
Я обнаружил проблему со статическим связыванием.
Мой реальный класс очень расширен, поэтому я буду использовать несколько игрушечных классов, чтобы выразить свою проблему.
Мы предполагаем, что имеем следующую иерархию.
public class Element{}
public class Element1 extends Element{}
public class Element2 extends Element{}
у меня есть Stock
класс, который использует разные Element
специализация определяется Element
иерархия.
public class Stock{
public void operation(Element1 e1){
System.out.println("Operation - " + e1.getClass().getName());
}
public void operation(Element2 e2){
System.out.println("Operation - " + e2.getClass().getName());
}
}
Наконец, у меня есть StockManager
который позволяет управлять Stock
,
public StockManager{
Stock stock;
public StockManager(Stock stock){
this.stock=stock;
}
public void manage(List<Element> elements){
for(Element element: elements){
stock.operation(element);
}
}
}
Конечно, этот код не компилируется, потому что Stock
не определяет метод, который включает в себя Element
в качестве аргумента. В этом случае мы могли бы исправить код, используя разные подходы.
Во-первых, я смогу определить метод, который будет определять Element
в качестве входного аргумента, например
public void operation(Element e){
System.out.println("Operation - " + e.getClass().getName());
}
Этот метод может определить переключатель для управления различными конкретными элементами (Element1
, Element2
). Тем не менее, это невозможно для меня, потому что переключатель нарушает принцип открытия / закрытия, и у меня есть много (много) конкретных элементов.
Другой вариант, я мог бы использовать что-то вроде шаблона посетителя. Я мог бы отправить Stock
возражать против конкретного элемента. И конкретный элемент будет отвечать за использование Stock
операции. Таким образом, класс может измениться на:
public abstract class Element{
public abstract void stockOperation(Stock stock);
}
public class Element1 extends Element{
public abstract void stockOperation(Stock stock){
stock.operation(this);
}
}
public class Element2 extends Element{
public abstract void stockOperation(Stock stock){
stock.operation(this);
}
}
И StockManager
,
public StockManager{
Stock stock;
public StockManager(Stock stock){
this.stock=stock;
}
public void manage(List<Element> elements){
for(Element element: elements){
element.stockOperation(stock);
}
}
}
Это позволяет определить во время компиляции статический тип конкретных элементов. И динамическое связывание будет отвечать за вызов stockOperation
метод правильного конкретного элемента (Element1
или же Element2
). ОДНАКО!!, у меня есть дублирующий код в конкретных элементах, и у меня будет несколько конкретных элементов.
Итак, я хотел бы знать, знаем ли мы какой-либо дизайн шаблона или лучшую практику для решения этой проблемы. (Другая альтернатива, это может быть использование отражения, но я бы не хотел его использовать).
1 ответ
Проблема заключается в том, что вы размещаете отдельные операции со складами в объекте запаса. Независимо от того, используете ли вы переключатель или нет, как только у вас появится другой тип элемента, вам нужно будет изменить запас, добавив новый перегруженный operation
к этому. И, как вы сказали, ваш класс Stock должен быть закрыт для изменений.
Таким образом, вы должны отнести все операции со складами к самому объекту Element. Это в основном ваше второе предложение, но вы делаете реализацию в каждом отдельном элементе.
public abstract class Element{
public abstract void stockOperation(Stock stock);
}
public class Element1 extends Element{
@Override
public void stockOperation(Stock stock){
System.out.println("Operation - Element1");
}
}
public class Element2 extends Element{
@Override
public void stockOperation(Stock stock){
System.out.println("Operation - Element2");
}
}
Вам, вероятно, нужно будет общаться с объектом на складе для реальных операций. Сделайте это с запасом, который был передан каждому stockOperation
создание доступных методов, позволяющих элементам получать или устанавливать все, что необходимо внутри объекта stock, например, результат операции.
Таким образом, если у вас есть новый тип элемента, вам нужно только написать новую операцию в новом элементе, и вы можете сохранить тот же класс Stock, не меняя его. Это расширение, а не модификация.