1

假设我有两个这样的课程

public class Foo
{
    protected final Bar child;

    public Foo()
    {
        child = new Bar(this);
    }
}

public class Bar
{
    protected final Foo parent;

    public Bar(Foo parent)
    {
        this.parent = parent;
    }
}

我想创建一个 , 的子类FooFoo2它的子类 aBar2是 的子类Bar。我可以这样做:

public class Foo
{
    protected final Bar child;

    public Foo()
    {
        child = new makeChild();
    }

    protected Bar makeChild()
    {
        return new Bar(this);
    }
}

public class Foo2 extends Foo
{
    @Override
    protected Bar makeChild()
    {
        return new Bar2(this);
    }
}

但是,这应该是一个非常糟糕的主意。但是这样的事情是行不通的:

public class Foo
{
    protected final Bar child;

    public Foo()
    {
        this(new Bar(this));
    }

    protected Foo(Bar child)
    {
        this.child = child;
    }
}

因为在调用超类型构造函数之前new Bar(this)引用。this

我看到了两种处理方法:

1)我可以将成员设为私有和非最终成员,然后让设置器在它们已经设置时抛出异常,但这看起来很笨拙,只能在运行时检测到任何编码问题。

2)我使Foo构造函数将要使用Class的类型的对象作为参数Bar,然后使用反射来调用该类的构造函数。但是,对于我正在尝试做的事情来说,这似乎是重量级的。

我缺少任何编码技术或设计模式吗?

4

3 回答 3

2

我缺少任何编码技术或设计模式吗?

想到了依赖注入模式。只需将参数完全从构造函数中取出,在接口之上创建构成类的类型,然后在需要时注入适当的具体类型。

Foo接口

interface Foo {
  public void setBar(Bar bar);
  public Bar getBar();
}

酒吧界面

interface Bar {
  public void setFoo(Foo foo);
  public Foo getFoo();
}

我一直在研究使用Guice或类似的东西来进一步解耦并自动进行注入。

于 2013-06-17T01:17:58.760 回答
1

你的问题是关于循环引用的简单问题。

首先,您应该设计您的类以避免循环引用,原因有很多here

接下来,如果您别无选择,那么最好的解决方案是使用“依赖注入”。为什么?这样想:

  1. 您必须创建 Foo 对象
  2. 在创建 foo 时,您还需要创建 Bar
  3. 但是出于明显的原因,您不想对 Bar 实例化进行硬编码
  4. 由于您提到的原因,在构造函数中调用“可覆盖”方法是有问题的

因此,依赖注入可以安全地进行救援。

于 2013-06-17T01:36:29.053 回答
0

一种选择是为初始化创建一个单独的函数,并在子类 ctor 中调用 init 方法:

例如

class Foo {
    private Bar child;

    public Foo() {
        initWithChild(new Bar(this));
    }

    protected final void initWithChild(Bar child) {
        this.child = child;
    }
}

class Foo2 {
    public Foo2() {
        initWithChild(new Bar2(this));
    }
}

当然,它假设 Foo 和 Bar 是耦合的,依靠 Foo 来实例化 Bar 是合理的。但是在大多数情况下,您应该考虑通过 DI 框架将 Bar 注入 Foo。


在评论中,您提到了 final 的必要性。

虽然我不认为在成员 var 现在是私有的情况下对于非最终版本来说这不是一个大问题,但如果初始化工作很简单,这里有另一种方式(实际上类似于上面描述的方式),您可以考虑正如你的问题:

class Foo {
    protected final Bar child;

    public Foo() {
        this.child = new Bar(this);
    }
}

class Foo2 {
    public Foo2() {
        this.child = new Bar2(this);
    }
}

尚未测试代码,但我相信它应该可以工作:P

于 2013-06-17T01:36:00.867 回答