【问题标题】:A method can only be called after the object is initialized - how to proceed?方法只能在对象初始化后调用 - 如何进行?
【发布时间】:2016-01-12 16:11:51
【问题描述】:

我在日常编码中发现了一个反复出现的模式,如下所示:

var foo = new Foo();
foo.Initialize(params);
foo.DoSomething();

在这些情况下,foo.Initialize 是绝对需要的,以便它实际上可以DoSomething,否则某些foo 属性仍将是null/非初始化。

它有规律吗?如何安全地确定DoSomething 只会/总是被称为之后 Initialize?如果没有,如何继续:我应该引发异常,静默忽略它,检查一些标志...?

【问题讨论】:

  • 可以在构造函数中执行初始化逻辑吗?有一个单独的方法来完成对象的构造似乎很愚蠢。
  • @David 我读过一些关于“构造函数中没有真正的工作”、“构造函数中没有new”的文章,这些让我有点不愿意做所有必须做的事情.替代方案似乎是放松类的合同(它不会一直准备好工作),但不知道这种放松应该/将如何完成......
  • “构造函数中没有new”实际上更多的是依赖管理问题,而不是“工作量太大”。您需要了解为什么某些事情不被推荐以及它们会带来什么问题,然后选择最适合您情况的解决方案。教条是最糟糕的决策者。

标签: oop language-agnostic


【解决方案1】:

基本上你说Initialize 是一个构造函数。所以这段代码真的应该是构造函数的一部分:

var foo = new Foo(params);
foo.DoSomething();

这正是构造函数的用途:它是保证在任何对象方法运行之前运行的代码,它的工作是检查前置条件并为其他对象方法运行提供一个健全的环境。

【讨论】:

  • 如果需要的工作很繁重,最好创建一个.Init(...) 方法。
  • 如果 required 工作是绝对 required 在任何其他方法可以运行之前,真的没有区别无论是否采用其他方法。在您可以对对象执行任何其他操作之前,它必须 运行。如果您的构造函数如此繁重,则可能需要进行一些常规重构。也许你真的想要一个工厂。
  • 构造函数必须使对象处于可用状态。这是一个绝对的、无可争议的要求。如果这需要真正的工作,那么这个构造函数必须做真正的工作。
  • @n.m.:这引出了问题...... “可用”?私有构造函数可以使对象处于工厂方法完全可用的状态,而工厂方法又负责将处于可用状态的对象返回给调用该方法的任何人。多阶段初始化过程并不少见。一个很好的例子是 ASP.NET WebForms 页面生命周期。
  • 拥有初始化函数本身并没有错!参见例如fstream::打开。您不能使用未打开的流。在初始化之前抛出一个异常有人试图使用你的对象。这是一个逻辑错误,应该这样对待。现在,当您拥有单独的 ctor 和 init 函数时,编写一个重载的 ctor 来完成这两件事(就像 fstream 中的一样)。
【解决方案2】:

如果在初始化过程中确实有很多工作发生,那么我当然可以看到“放入构造函数太多”的论点。 (我敢肯定,对底层语言机制有更深入了解的人可以就此事提供一些令人信服的解释,但我不是那种人。)

在我看来,工厂在这里会很有用。像这样的:

public class Foo
{
    private Foo()
    {
        // trivial initialization operations
    }

    private void Initialize(SomeType params)
    {
        // non-trivial initialization operations
    }

    public static Foo CreateNew(SomeType params)
    {
        var result = new Foo();
        result.Initialize(params);
        return result;
    }
}

而消费代码变成:

var foo = Foo.CreateNew(params);
foo.DoSomething();

所有形式的附加逻辑都可以放入该工厂,包括对params 的各种健全性检查或验证繁重的初始化操作是否成功完成(例如它们是否依赖外部资源)。这也是注入依赖项的好地方。

这基本上归结为一个清晰分离关注点的问题。构造器的工作是创建对象的实例,初始化器的工作是让复杂对象准备好用于预期用途,工厂的工作是协调这些工作并只返回准备好使用的对象(相应地处理任何错误) .

【讨论】:

  • 这很有趣。如果CreateNew 出现问题该怎么办?
  • @heltonbiker:您可以在CreateNew 中放置您喜欢的任何错误处理。这与在代码中的其他任何地方处理异常或错误情况没有什么不同。
猜你喜欢
  • 2016-09-30
  • 1970-01-01
  • 2011-04-01
  • 2010-11-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多