dimanche 5 août 2018

Java: Strategy pattern with access to superclass' fields and methods?

I have an abstract class Parent with abstract methods foo() and bar() and other non-abstract methods and fields. I need to create 4 subclasses (and later more) to cover each combination of different variations on foo() and bar(): fooA(), fooB(), barA(), barB(). These variations need access to other fields and methods of Parent. In other words, if Java supported multiple inheritance, then I would have something like:

abstract class Parent{
    abstract foo(){}
    abstract bar(){}
    //other fields and methods that will be accessed foo and bar are PROTECTED
}

abstract class FooA extends Parent{
    @Override
    foo(){ ... }
}
abstract class FooB extends Parent{
    @Override
    foo(){ ... }
}
abstract class BarA extends Parent{
    @Override
    bar(){ ... }
}
abstract class BarB extends Parent{
    @Override
    bar(){ ... }
}

class ChildAA extends FooA, BarA{   
}

class ChildAB extends FooA, BarB{
}

class ChildBA extends FooB, BarA{
}

class ChildBB extends FooB, BarB{
}

I have found two solutions, each of which sort of works, but just about. Are there better ways to achieve this behaviour? My solutions are as follows:

1) First solution:

abstract class Parent {
    foo(){ 
        /* behaves like fooA */
    }
    //other fields and methods that will be accessed foo and bar are PROTECTED
}

class ChildAA extends Parent{
    barA(){ ... }
}

class ChildAB extends Parent{
    barB(){ ... }
}

class ChildBA extends ChildAA{
    @Override
    foo(){ /* behaves like fooB */ }
|

class ChildBB extends ChildAB{
    @Override 
    foo(){ /* behaves like fooB */ }
}

The problem with this is that it duplicates the code for fooB() and all additional methods that only fooB() needs. The problem gets exponentially worse when more variations are needed.

2) After looking around I found the design pattern Strategy, which can be used to achieve the behaviour but is awkward because the variations need to access Parent's fields and methods:

abstract class Parent{
    Fooable fooable;
    Barable barable;
    foo(){ fooable.foo(); }
    bar(){ barable.bar(); }
    //other fields and methods that will be accessed foo and bar are PUBLIC
}

abstract class ImplementableParent{
    Parent p;
    ImplementableParent(Parent p) { this.p = p; }
}

interface Fooable{
    foo();
}
class FooA extends ImplementableParent implements Fooable{
    FooA(Parent p){ super(p); }
    @Override 
    foo(){ /* behaves like FooA */ }
}
class FooB extends ImplementableParent implements Fooable{
    FooB(Parent p){ super(p); }
    @Override 
    foo(){ /* behaves like FooB */ }
}

interface Barable{
    bar();
}
class BarA extends ImplementableParent implements Barable{
    BarA(Parent p) { super(p); }
    @Override 
    bar() { /* behaves like BarA */ }
}
class BarB extends ImplementableParent implements Barable{
    BarB(Parent p) { super(p); }
    @Override 
    bar() { /* behaves like BarB */ }
}

class ChildAA extends Parent{
    fooable = new FooA(this);
    barable = new BarA(this);
}

class ChildAB extends Parent{
    fooable = new FooA(this);
    barable = new BarB(this);
}

class ChildBA extends Parent{
    fooable = new FooB(this);
    barable = new BarA(this);
}

class ChildBB extends Parent{
    fooable = new FooB(this);
    barable = new BarB(this);
}

This gets rid of repetitions of variations and can be extended to accommodate more variations. However, now the fields and methods of Parent are public and the whole thing feels very convoluted. I am also concerned about performance overhead, since FooA, FooB, BarA and BarB access the Parent methods indirectly, although I haven't tested it.

Is there a better way to achieve the behaviour?

Aucun commentaire:

Enregistrer un commentaire