Избегайте статического связывания в операциях, которые используют аргументы иерархии

Я обнаружил проблему со статическим связыванием.

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

Мы предполагаем, что имеем следующую иерархию.

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, не меняя его. Это расширение, а не модификация.

Другие вопросы по тегам