问题
The following code runs, but my implementation violates SOLID principles. I'm hoping someone can help me redesign my code so that it follows SOLID principles, particularly by avoiding the use of instanceof
.
I have a parent class, Parent
, with three child classes: Element
, Container
, and Wrapper
.
Element
simply holds an integer, like a math scalar.
Container
has a parent array, Parent[] contents
, which may be any of the child classes, like a math vector. It can be a vector of scalars (elements), a vector of vectors (containers), or a vector of functions (wrappers).
Wrapper
has an interface, Function
, that allows the programmer to define a unique method per wrapper. For example, one wrapper could contain a function that squares an element, while another wrapper could contain a function that takes the dot product between two containers.
Wrapper
is parameterized with two generic types, Wrapper<Domain extends Parent,Range extends Parent>
, which signify the input type, Domain, and the output type, Range.
My problem arises when I try to access the Parent[] contents
array within the Container
class. Since contents can be any of the child classes, and since each child class has a different definition for their add
methods, I find myself having to use instanceof, which violates SOLID principles.
How can I change the structure of my code?
public class Parent {
public Parent add(Parent p){return null;}
}
public class Element extends Parent{
protected int value;
public void set(int x){ value=x; }
public int get(){ return value; }
public Element(){ set(0); }
public Element(int x){ set(x); }
@Override
public Parent add(Parent p){
if (p instanceof Element){
Element e = (Element) p;
return add(e);
}
else if (p instanceof Container){
Container c = (Container) p;
return add(c);
}
else return null;
}
public Element add(Element e){ return new Element( get()+e.get()); }
public Container add(Container c){ return c.add(this); }
}
///I would prefer for this class to be parameterized Container<Type>, but I run into the problem of instantiating generic arrays
public class Container extends Parent{
protected Parent[] contents;
public int length(){ return contents.length; }
public Container(int x){ contents = new Parent[x]; }
public void set(int k, Parent p){ contents[k]=p; }
public Parent get(int k){ return contents[k]; }
///Have to use instanceof to determine which type of output it will be
public Parent sum(){
Parent p;
if(get(0) instanceof Element)
p = new Element();
else if (get(0) instanceof Wrapper)
p = new Wrapper();
else p = new Parent();
for(int k=0;k<contents.length;++k)
p = p.add(contents[k]);
return p;
}
///Have to use instanceof to distinguish which add to use
@Override
public Parent add(Parent p){
if (p instanceof Element){
Element e = (Element) p;
return add(e);
}
else if (p instanceof Container){
Container c = (Container) p;
return add(c);
}
else return null;
}
///adds element to the first entry in the container
public Container add(Element e){
Container out = new Container(contents.length);
for(int k=0;k<contents.length;++k)
out.set(k, contents[k]);
out.set(0, out.get(0).add(e));
return out;
}
///adds component by component
public Container add(Container c){
int minLength;
if(c.length() < contents.length)
minLength = c.length();
else minLength = contents.length;
Container out = new Container(minLength);
for(int k=0;k<minLength;++k)
out.set(k, contents[k].add( c.get(k) ));
return out;
}
}
public interface Function <Domain extends Parent, Range extends Parent> {
public Range of(Domain point);
}
public class ZeroFunction<Domain extends Parent,Range extends Parent> implements Function<Domain,Range>{
@Override
public Range of(Domain point) {
return (Range) new Element();
}
}
public class Wrapper<Domain extends Parent, Range extends Parent> extends Parent{
protected Function<Domain,Range> function;
public Wrapper(){ function = new ZeroFunction<Domain,Range>(); }
public Wrapper(Function<Domain,Range> x) { function = x; }
public Range of(Domain t){ return function.of(t); }
@Override
public Parent add(Parent p){
if (p instanceof Wrapper)
return add( (Wrapper) p);
else return null;
}
public Wrapper<Domain,Range> add(final Wrapper<Domain,Range> w){
return new Wrapper<Domain,Range>( new Function<Domain,Range>(){
public Range of(Domain point){
try{
Range term = function.of(point);
Range summand = w.of(point);
Range sum = (Range) term.add(summand);
return sum;
} catch(Exception e){
e.printStackTrace();
return null;
}
}
});
}
}
public class Main {
public static void main(String[] args){
Wrapper<Container, Element> wrapper1 = new Wrapper<Container, Element>(new Function<Container, Element>() {
@Override
public Element of(Container c) {
return (Element) c.sum();
}
});
Wrapper<Container, Element> wrapper2 = new Wrapper<Container, Element>(new Function<Container, Element>() {
@Override
public Element of(Container c) {
return (Element) c.sum();
}
});
Container wContainer = new Container(2);
wContainer.set(0, wrapper1);
wContainer.set(1, wrapper2);
Wrapper<Container,Element> wrapper3 = (Wrapper<Container,Element>) wContainer.sum();
Container eContainer = new Container(2);
eContainer.set(0, new Element(1));
eContainer.set(1, new Element(2));
Element solution = wrapper3.of(eContainer);
System.out.println(solution.get());
}
}
来源:https://stackoverflow.com/questions/52684258/how-to-determine-type-of-child-class-from-method-of-parent-class-without-instanc