对象A引用了对象B,它将对象A作为公共属性 - 糟糕的设计?

时间:2011-03-22 18:22:28

标签: c# oop

我有一个上下文对象,它有几个有用的属性和方法。其他对象在其构造函数中接受此上下文对象。但是,上下文对象可以将这些其他对象作为公共属性。见下文:

public class Foo {
    private IContext Context { get; private set; }

    public Foo( IContext context ) {
        Context = context;
    }
}

public class SomeContext : IContext {
    public Foo SomeProperty { get; set; }
    /*
        Other useful properties and methods as defined in IContext
    */

    public SomeContext () {
        SomeProperty = new Foo( this );
    }
}

现在我可以在Foo上的方法中做一些有用的事情:

public void FooIt() {
    IUseful bar = this.Context.GetUsefulInterface();
    bar.DoUsefulThing();
}

然而,它可能导致一些非常奇怪的东西。在Foo上考虑这些方法:

public void DoSomething() {
    /* useful stuff */
}

public void DoSomethingElse() {
    this.Context.SomeProperty.DoSomething();  // could just call this.DoSomething();
    this.Context.SomeProperty.DoSomethingElse();  // stack overflow!
}

这被认为是一个糟糕的设计/代码味道?上下文对象的原因有所涉及,我想将问题更多地放在周期性参考上。如果认为不好,打破这种周期性关系有哪些方法?

4 个答案:

答案 0 :(得分:2)

不错,但绝对不理想。我对此的看法是,您不应该在构造函数中为对象创建上下文,其中上下文对象将创建对象。上下文应该是您正在创建的对象外部的东西。

但是,使用上下文对象创建“无限”循环排序没有问题。 ASP.NET MVC ControllerContext在方式上做了非常相似的事情;你通常不会这样称呼它:

ControllerContext.Controller.ControllerContext.Controller // etc

答案 1 :(得分:2)

如果没有领域知识,在设计循环依赖时很难知道从哪里开始,所以我只是概括。

当您没有表达层次关系时,循环引用很糟糕,在这种关系中,返回父级是必需的或有用的。这是因为它促进了循环中类型之间的强耦合,很难正确构造/删除,并且在遍历时容易出错。

但是,如果您具有层次关系,并且遍历层次结构是必需/有用的,那么拥有循环引用是完全合理的。

您可能想要避免的一件事是尝试一次调用太深的属性。这会增加您的耦合,并且很容易导致错误,例如您发现的堆栈溢出异常。

// Reaches too far.  Makes this depend on the interface of SomeProperty
this.Context.SomeProperty.DoSomething();

// ...

// Not reaching too far.  Only depends on Context.
// This might forward to SomeProperty.DoSomething()
this.Context.DoSomething();

根据您的修复方式,这也可能有助于您打破堆栈溢出。

请参阅:http://en.wikipedia.org/wiki/Law_of_Demeter

答案 2 :(得分:0)

循环关系在非托管环境中很糟糕,因为您需要弱引用来打破循环关系(否则您将不可避免地泄漏循环中涉及的对象)。我无法想出给他们贴上标签的具体理由"糟糕"但是,在托管环境中。

事实上,有很多充分理由要求循环引用。例如,在UI树中,父UI元素知道他们的孩子,孩子也知道他们的父母。

答案 3 :(得分:-1)

通常,周期性参考是不良设计的气味。但是有几种情况,循环引用的方式很好(在Winforms中:Control.Controls与Control.Parent)。

如果您有周期性参考,则应检查是否有正确的值。如果你有父子关系,并且从父母那里删除了孩子,那么孩子不应该引用父母,父母不应该引用孩子。