Java:可以访问超类字段和方法的策略模式?



我有一个抽象类Parent,它包含抽象方法foo()bar()以及其他非抽象方法和字段。我需要创建4个子类(稍后还会创建更多子类(来覆盖foo()bar()上不同变体的每个组合:fooA()fooB()barA()barB()。这些变体需要访问CCD_ 10的其他字段和方法。换句话说,如果Java支持多重继承,那么我会得到这样的东西:

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{
}

我找到了两种解决方案,每种方案都很有效,但都差不多。有没有更好的方法来实现这种行为?我的解决方案如下:

1( 第一个解决方案:

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 */ }
}

这样做的问题是,它复制了fooB()的代码以及仅fooB()需要的所有其他方法。当需要更多的变化时,问题会成倍地恶化。

2( 环顾四周后,我发现了设计模式Strategy,它可以用来实现行为,但很尴尬,因为变体需要访问Parent的字段和方法:

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

这消除了变化的重复,并且可以扩展以适应更多的变化。然而,现在Parent的领域和方法是公开的,整个事情感觉非常复杂。我还担心性能开销,因为FooAFooBBarABarB间接访问Parent方法,尽管我还没有测试过

有没有更好的方法来实现这种行为?

您过于依赖继承IMO。Clean Code的一般规则是更喜欢组合而不是继承。

你想要的是这样的东西:

interface Foo {
void foo();
}
interface Bar {
void bar();
}
interface FooBar extends Foo, Bar {}

有几种方法可以创建它。

父类中的内部类和工厂方法

Parent类不需要实现这些;它可以提供这样做的内部类,以便这些类能够访问受保护的成员。

class Parent {
protected int neededByFoo;
protected int neededByBar;
class FooA implements Foo {
public void foo() {
doStuffWithNeededByFoo();
}
}
class FooB implements Foo {
public void foo() {
doStuffWithNeededByFoo();
}
}
// same for the BarA and BarB implementations
}

借助Parent中的实用程序委托类(与解决方案B没有什么不同(和工厂方法,您可以将它们组合到实现这两个接口的实例中。

private static class FooBarDelegate implements FooBar {
Foo fooDelegate;
Bar barDelegate;
private FooBarDelegate(Foo f, Bar b) { fooDelegate = f; barDelegate = b; }
public void foo() { fooDelegate.foo(); }
public void bar() { barDelegate.bar(); }
}
public FooBar fooAbarA() {
return new FooBarDelegate(new FooA(), new BarA());
}
public FooBar fooBbarA() {
return new FooBarDelegate(new FooB(), new BarA());
}
public FooBar fooAbarB() {
return new FooBarDelegate(new FooA(), new BarB());
}
public FooBar fooBbarA() {
return new FooBarDelegate(new FooB(), new BarB());
}
}

这是该代码的运行版本。

现在,内部类本质上是被组装到FooBar实例中的策略。根本不需要对Parent进行子类化。

在界面中使用组合方法

您可能根本不想在Parent类中进行组合,而是在接口中:

interface FooBar extends Foo, Bar {
public static FooBar combine(Foo f, Bar b) {
return new FooBar() {
foo() { f.foo(); }
bar() { b.bar(); }
}
}
}

然后你会像这个一样使用

Parent p = new Parent();
FooBar fb = FooBar.combine(new p.FooA(), new p.BarA());
fb = FooBar.combine(new p.FooA(), new p.BarB());

等等

方法参考

因为FooBar是函数接口,所以可以组合Parent对象的方法,而不是使用内部类。

class Parent {
public void fooA() { // do stuff }
public void fooB() { // do stuff }
public void barA() { // do stuff }
public void barB() { // do stuff }
}

然后

FooBar fb = FooBar.combine(p::fooA, p::barA);
fb = FooBar.combine(p::fooA, p::barB);
// and so on

就像这样。

然而,现在Parent的字段和方法是公共的事情感觉很复杂。

您的Barable和Fooable实现本身不包含上下文,因此它们必须使用上下文对象中的public方法(此处为Parent子类(来查询和操作它。
但只有Parent方法必须是公共的,而不是其字段。

例如,这将是ParentFooA实现的公平实现:

abstract class Parent{
private Fooable fooable;  // internals 
private Barable barable;  // internals
private String sharedString;  // internals
private Integer sharedInteger;  // internals
// public access        
public foo(){ fooable.foo(); }
public bar(){ barable.bar(); }
public String getSharedString(){
return sharedString;
}
public Integer getSharedInteger(){
return sharedInteger;
}
public String updateSharedData(String string, Integer integer){
// apply some logic and controls if required
this.string = string;
this.integer = integer;
}
}

class FooA extends ImplementableParent implements Fooable{
FooA(Parent p){ 
super(p); 
}
@Override 
foo(){  
if (p.getSharedString().equals("...)){
// some logic
p.updateSharedData("new value", newIntegerValue);
}
}
}

顺便说明一下,将Parent定义为封装的FooableBarable实例的依赖项意味着Fooable可以反向操作Barable
您的实际要求中没有说明。如果您想防止这种情况发生,您应该为上下文定义一个特定的类,该类包含要在两种约定(FooableBarable(之间共享的数据和方法,并为ChildXXX子类的通用约定定义另一个类
而不是在构造函数中传递Parent实例,而是传递Context实例。

相关内容

最新更新