哪个更邪恶:不必要的单身人士还是上帝对象?

时间:2009-07-03 15:36:50

标签: design-patterns oop singleton anti-patterns god-object

情况就是这样:我的课程做得太多了。它主要用于访问配置信息,但它也具有数据库连接。它是作为一个单独实现的,所以这也使单元测试变得困难,因为大多数代码与它紧密耦合。这甚至更成问题,因为它创建了一个导入时间依赖项(我们在Python中这样做),这意味着某些模块必须按特定顺序导入。理想情况下,我想将它分成两个类,并使其成为非单例。

幸运的是,我的雇主已经热心这样一个事实,即这种测试是好的,并且如果它使代码更易于测试,我愿意允许我做出这样的更改。但是,我怀疑他们是否愿意让我花费太多时间。而且我宁愿逐步修复它,而不是试图过于激进。

所以,我在这里看到三个选择:

  1. 将配置对象分解为(单例)配置对象和(非单例)数据库对象。这至少允许我将数据库作为导入时依赖项删除。
  2. 使配置对象成为非单例并将其传递给需要它的对象。我认为这更好地解决了我们的短期需求,但我认为这需要更多的时间。
  3. 在你的回答中做一些我没有想到的事情。 : - )
  4. 那我该怎么办?

4 个答案:

答案 0 :(得分:5)

如果没有看到您的代码,很难知道,但为什么不按照您的意思去做 - 逐步进行?首先执行步骤1,拆分数据库。

如果那很快就回去,你现在只有一个较小的物体停止成为单身而不是2.所以第2步应该更快。或者在这个阶段,您可能会看到一些其他代码可以从单例中重构。

希望你可以逐步减少单身人士中的内容,直到它消失,而不必在任何一步中缴纳巨额的时间税。

例如,如果配置的各个部分是独立的,那么配置中的一部分或许可以一次单独配置。因此,在重构文件配置时,GUI配置可能会留下单例,还是类似的东西?

答案 1 :(得分:5)

我认为你有望分成两个班级。您可能需要考虑使用工厂根据需要创建数据库上下文/连接。这样,您可以将连接视为根据需要创建/处置的工作单元,而不是在对象的生命周期内保持单个连接。但是YMMV。

至于配置,这是我发现单身人士可能是正确选择的一种情况。我不一定会抛弃它只是因为它很难进行单元测试。但是,您可能需要考虑构建它以实现接口。然后,您可以使用依赖项注入在测试期间提供接口的模拟实例。如果注入的值为null,则构建生产代码以使用单例实例或注入单例实例。或者,您可以构造类以允许通过私有方法重新初始化,并在setup / teardown测试方法中调用它,以确保它具有适合您的测试的配置。我更喜欢前者到后者的实现,但是当我无法直接控制界面时我也使用过它。

逐步进行更改肯定是要走的路。如果可能的话,用测试包装当前的功能,并确保在修改后仍然通过这些测试(当然,不是那些直接处理修改的测试)是确保你不破坏其他代码的好方法

答案 2 :(得分:2)

选项1是我在所有应用程序中执行的操作:配置对象单例和按需创建或注入的数据库对象。

将cofiguration对象作为单身人士一直非常适合我。只有一个配置文件,我总是希望在应用程序启动时读取它。

答案 3 :(得分:2)

请原谅我不知道任何Python的事实,所以我希望任何伪代码都有意义......

我首先将对象分成两部分,以便你的单身人士的责任更小,然后我将剩余的配置单例并将其改为普通的类(就像你要执行你的第二个建议一样) )。

到目前为止,我已经创建了一个新的包装器单例,它公开了配置类的方法:

class ConfigurationWrapper : IConfigurationClass 
{
    public static property ConfigurationWrapper Instance;

    public property IConfigurationClass InnerClass;

    public method GetDefaultWindowWidth()
    {
        return InnerClass.GetDefaultWindowWidth();
    }

    etc...
}

现在应用程序做的第一件事就是将一个ConfigurationClass实例注入包装器。

ConfigurationClass config = new ConfigurationClass()
ConfigurationWrapper.Instance.InnerClass = config;

最后,您可以将当前依赖单例的类移动到包装器单例(应该是快速查找和替换)。现在,您可以一次转换一个类,通过它们的构造函数获取配置对象以完成第2阶段。任何您没有时间做的事情都可以使用包装器单例。一旦你将它们全部移动,就可以摆脱包装。

另一方面,你可以忽略用法的重构,只需将一个模拟的配置类注入到单例包装器中进行测试 - 基本上是一个穷人的依赖注入。