问题
I have found an issue about static binding.
My real class are very extended, so I will use several toy class to express my problem.
We suppose that we have the following hierarchy.
public class Element{}
public class Element1 extends Element{}
public class Element2 extends Element{}
I have a Stock
class which use the different Element
specialization defined by Element
hierarchy.
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());
}
}
Finally, I have a StockManager
which allows to manage a 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);
}
}
}
Of course, this code does not compile, because Stock
does not define a method which includes a Element
as argument. In this case we could fix the code using different approaches.
First, I will be able to define a method which will define a Element
as input arg, e.g.
public void operation(Element e){
System.out.println("Operation - " + e.getClass().getName());
}
This method could define a switch for managing the different concrete elements (Element1
, Element2
). However, this is imposible for me, because the switch violate the Open/Close Principle, and I have a lot (a lot) of concrete elements.
Another alternative, I could use something like the Visitor Pattern.
I could send the Stock
object to a concrete element. And the concrete element will be charge of using the Stock
operations. So, the class could change to:
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);
}
}
And the 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);
}
}
}
It allows determine in compile-time the static type of the concrete elements. And the dynamic binding will be charge of call the stockOperation
method of the correct concrete element (Element1
or Element2
). HOWEVER!!, I have duplicate code in the concrete elements, and I will have several concrete elements.
So, I would like know if we know any pattern design or any best practice for solve this issue. (Another alternative, it maybe to use reflection, but I would not like use it).
回答1:
The problem is that you place the individual stock operations in the stock object. No matter if you use a switch or not, as soon as you have another Element type, you'll need to modify stock by adding a new overloaded operation
to it. And as you said, your Stock class should be closed to modifications.
So you have to do relegate whatever the stock operation is to the Element object itself. This is basically your second suggestion, but you do the implementation in each different 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");
}
}
You will probably need to communicate with the stock object for the real operations. Do that with the stock that was passed to each stockOperation
, making methods available that allow the elements to get or set whatever is needed inside the stock object, like the result of the operation.
This way, if you have a new Element type, you only need to write the new operation in the new Element, and you can keep the same Stock class without changing it. This is extension rather than modification.
来源:https://stackoverflow.com/questions/26553300/avoid-static-binding-in-operations-which-use-hierarchy-arguments