0

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());

    }
}
Stanislav Kralin
  • 10,115
  • 4
  • 30
  • 52
user2303321
  • 187
  • 1
  • 10
  • I would suggest you give this a read. https://stackoverflow.com/questions/2723397/what-is-pecs-producer-extends-consumer-super Also to initialize generic arrays, create array of `Object` and typecast it. https://stackoverflow.com/questions/529085/how-to-create-a-generic-array-in-java – Aditya Peshave Oct 07 '18 at 00:36
  • @AdityaPeshave I see. Given that I need to both add things to the array and get things from the array, should I use neither extends nor super? Then I would just implement a generic array? – user2303321 Oct 07 '18 at 01:01

0 Answers0