如何使用需要更专业的构造函数参数的组合扩展类?

时间:2014-10-03 00:00:00

标签: java constructor composition extending constructor-injection

我正在尝试尽可能使用合成扩展类。但我遇到了一个特殊问题。

假设我有一个Base类和一个Extending类。如果我们使用组合并且我们想要注入依赖项,则Extending类接受Base类的实例而不是继承它:

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

Base类接受一些依赖项,然后我们可以使用getDependency()方法在Extending类中使用它们。

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

如果我们使用继承,我们可以简单地将子类型传递给超类构造函数,如下所示:

public class Extending extends Base
{
    private ISomeDependencySubtype dependency;

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

但是如果我们使用合成,那么这就变得更加困难了。我想到了很多选择:

在Base

中使用初始化方法而不是构造函数
public class Extending
{
    private ISomeDependencySubtype dependency;
    private Base base;

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

此选项隐藏了Extending类在Base类上使用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;
    }
}

这意味着无法传递Base实例,这意味着无法注入依赖项。由于大多数人可能都很明显的原因,这是不灵活的。

所以我想知道人们认为使用组合来解决这个问题的最佳选择(请不要基于继承的答案)。

1 个答案:

答案 0 :(得分:0)

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

除了您的解决方案之外,您还可以只将Base的类型和ISomeDependency的实例传递给Extending的构造函数,并实例化{{1动态对象。

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; (你说你不想要),这个问题也没有好转。

泛型方法可以解决这个问题,但它可以解决所有其他问题。

由于我不认为存在完美的解决方案,因此很可能在很大程度上取决于实际的用例,即接受邪恶。