1

我想在可能的情况下使用组合来扩展类。但是我遇到了一个特殊问题。如何使用需要更专门的构造函数参数的组合来扩展一个类?

可以说我有一个基类和一个扩展类。该扩展类需要在基类的一个实例,而不是如果我们使用组成从它继承,我们要注入我们的依赖关系:

public class Base 
{ 
    private ISomeDependency dependency; 

    public Base(ISomeDependency dependency) 
    { 
     this.dependency = dependency; 
    } 

    public ISomeDependency getDependency() 
    { 
     return dependency; 
    } 
} 
public class Extending 
{ 
    private Base base; 

    public Extending(Base base) 
    { 
     this.base = base; 
    } 
} 

基类发生在一些依赖然后我们可以在使用使用getDependency()方法扩展类。

但是,如果我们想要专门化扩展类中需要的ISomeDependency的子类,该怎么办。

如果我们使用继承,我们可以简单的亚型传递到父类的构造像这样:

public class Extending extends Base 
{ 
    private ISomeDependencySubtype dependency; 

    public Extending(ISomeDependencySubtype dependency) 
    { 
     super(dependency); 
     this.dependency = dependency; 
    } 
} 

但是,如果我们使用成分那么这变得更加困难。我已经想了多种选择:

在基地有一个构造函数的初始化方法,而不是

public class Extending 
{ 
    private ISomeDependencySubtype dependency; 
    private Base base; 

    public Extending(Base base, ISomeDependencySubtype dependency) 
    { 
     base.init(dependency); 
     this.base = base; 
     this.dependency = dependency; 
    } 
} 

此选项隐藏的扩展类使用init()方法的基本事实所以界面变得不清楚。在使用Extending类时,另一个程序员可能会假设Base实例必须在它传递到Extending实例之前调用init()。

添加一般的参数

public class Base<T extends ISomeDependency> 
{ 
    private T dependency; 

    public Base(T dependency) 
    { 
     this.dependency = dependency; 
    } 

    public T getDependency() 
    { 
     return dependency; 
    } 
} 

public class Extending 
{ 
    private ISomeDependencySubtype dependency; 
    private Base<ISomeDependencySubtype> base; 

    public Extending(Base<ISomeDependencySubtype> base) 
    { 
     this.base = base; 
     this.dependency = base.getDependency(); 
    } 
} 

这似乎是仿制药的滥用。如果Base类本身就是一个类,并且可以在没有Extending类的情况下使用,那么它不需要关心将ISomeDependency的哪个子类型传递给它。尽管所有的东西都需要重构以适应它,但通用的参数只存在于这个特定的场景中。

实例化基底延伸

public class Extending 
{ 
    private ISomeDependencySubtype dependency; 
    private Base base; 

    public Extending(ISomeDependencySubtype dependency) 
    { 
     this.base = new Base(dependency); 
     this.dependency = dependency; 
    } 
} 

这里面意味着基实例不能被传递,这意味着依赖不能注射。由于可能对大多数人显而易见的原因,这是不灵活的。

所以我想知道人们认为什么是最好的选择使用组合来克服这个问题(请不要继承的答案)。

+1

你的例子有时会包含一个虚假的'extends Base'。 – 5gon12eder 2014-10-03 02:36:50

+0

斑点。将编辑。 – Jonathan 2014-10-04 02:10:25

回答

0

这个 - 像所有协变依赖关系的问题 - 是一个有点病态的问题,可能没有完美的解决方案。

除了你的解决方案,你也可以通过只Base的类型和ISomeDependencyExtending的构造实例,动态实例化Base对象。

public class Extending { 

    // Keep an additional copy (ugly and probably unsafe). 
    final dependendcy dependendcy; 
    final Base base; 

    public Extending(final Class<? extends Base> cls, 
        final ISomeDependencySubtype dependendcy) throws Exception { 
     this.dependency = dependency; 
     this.base = cls.getConstructor(ISomeDependency.class).newInstance(dependency); 
    } 
} 

这有明显的缺点,你必须承担其不能被Java的类型系统静态强制执行Base的合适的构造函数。

如果你愿意,你也可以假设一个默认的构造函数(可能看起来更容易),然后用依赖项调用init方法。 [我不喜欢init方法。]通过这种方法,您将消除调用者是否应该初始化Base的歧义性。

依赖于保持一个额外的参照依赖的所有解决方案都在我看来不安全,因为没有保证该Base不会取代对象,以便

assert this.dependency == this.base.getDependency(); 

很可能会失败。当然,没有任何安全的向下转型,因为出于同样的原因被getDependency返回的引用,

assert this.base.getDependency() instanceof ISomeDependencySubtype; 

可能会失败也是如此。

即使Extending extends Base(你说你不想要),这个问题没有得到任何改善。

泛型方法可以解决这个问题,但它会为您带来所有其他类型的问题。

由于我不认为有完美的解决方案存在,它可能很大程度上取决于实际的使用情况哪个邪恶接受。

+0

谢谢,我会选择正确的答案以防万一,但我认为你是对的,没有魔法解决方案。 – Jonathan 2014-10-09 19:05:37

相关问题