2014-10-24 1 views
1

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

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

Предположим, что у нас есть следующая иерархия.

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). Однако это невозможно для меня, потому что переключатель нарушает Open/Close Principle, и у меня много (много) конкретных элементов.

Другой вариант, я мог бы использовать что-то вроде Visitor Pattern. Я мог бы отправить объект 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). ОДНАКО !!, у меня есть дублирующий код в конкретных элементах, и у меня будет несколько конкретных элементов.

Итак, я хотел бы знать, знаем ли мы какой-либо шаблон или какую-либо передовую практику для решения этой проблемы. (Другой вариант, возможно, использовать отражение, но я бы не хотел его использовать).

+0

абстрактные методы не могут содержать код внутри них. Вот почему они являются абстрактными «public abstract void stockOperation (Stock stock)) { stock.операция (это); } 'даже не компилируется –

+0

Конечно, этот код не компилируется, потому что Stock не определяет метод, который включает в себя элемент как аргумент - почему, по вашему мнению, это не будет компилироваться? Вы передаете элемент, который расширяется как element1 & 2 – 6ton

+0

@ 6ton. Потому что это перегрузка, а не полиморфизм. Когда происходит перегрузка, компилятор должен иметь возможность определять ** во время компиляции **, который вызывается перегруженным методом. И это не может. – ajb

ответ

2

Проблема в том, что вы размещаете отдельные операции с запасами в складе. Независимо от того, используете ли вы переключатель или нет, как только у вас будет другой тип элемента, вам нужно будет изменить запас, добавив к нему новый перегруженный operation. И, как вы сказали, ваш класс акций должен быть закрыт для модификаций.

Таким образом, вы должны сделать все, что бы то ни было, операции с запасом для самого элемента 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, создав доступные методы, позволяющие элементам получать или устанавливать все необходимое внутри объекта запаса, например, результат операции.

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

+0

Я не вижу преимущества этого. Он потенциально помещает код, который должен принадлежать 'Stock' в неправильном классе, который, как я полагаю, приводит к увеличению связи (и может не работать, если ему нужно ссылаться на поля членов, определенные в' Stock'). – ajb

+0

У вас очень плотная связь, в любом случае у вас есть метод на каждый элемент Element. Если код на элемент настолько отличается, что ему нужен отдельный перегруженный метод в запасе, то он принадлежит элементу. И вы можете обращаться к полям участников на складе с помощью аксессуаров. – RealSkeptic

+0

Я согласен с тобой @RealSkeptic. Это хороший подход, однако я ненавижу дубликат кода, но я не знаю, как удалить. Я нашел планшет для решения этой ситуации, но я не нашел. –