上帝对象

上帝对象是糟糕的软件设计和糟糕的面向对象实现的诱人后果。

从本质上讲,上帝对象 是一个拥有太多方法或太多属性的对象;从本质上讲,它是一个知道太多或做了太多事情的类。上帝对象很快就会与应用程序中的大量其他代码紧密耦合(被引用)。

那么,这究竟有什么问题呢?简而言之,当你将一段代码与其他所有代码捆绑在一起时,你很快就会发现维护方面的灾难。如果你为一个用例调整了上帝对象中一个方法的逻辑,你可能会发现这会给另一个元素带来意想不到的后果。

在计算机科学中,采取分而治之的策略通常是个好主意。通常,大问题只是一系列小问题。解决了这一系列小问题,就能迅速解决整个问题。对象通常应该是自足的;它们应该只知道关于自身的问题,也应该只解决一组问题,即自身的问题。任何与这一目标无关的东西都不属于这一类。

可以说,与物理对象有关的对象应该是实例化的,而与之无关的对象应该是抽象类。

在开发嵌入式系统时,上帝对象反其道而行之。嵌入式系统用于处理从计算器到 LED 标牌等任何设备上的数据;它们是小型芯片,本质上是独立的计算机,而且成本相当低。在这种使用情况下,由于计算能力有限,你经常会发现编程的优雅性和可维护性成了无关紧要的问题。性能的轻微提高和控制的集中化可能更为重要,这意味着使用上帝对象可能是明智之举。幸运的是,PHP 很少用于嵌入式系统的编程,所以你不太可能遇到这种特殊情况。

处理这些类的最有效方法是手动将它们拆分成独立的类。

另一种名为 "害怕添加类" 的反模式也会造成这种情况,同时也会导致无法缓解这种情况。这就是开发人员不愿创建必要类的原因。

下面是一个上帝类的例子:

<?php

class God
{
    public function getTime(): int
    {
        return time();
    }

    public function getYesterdayDate(): string
    {
        return date("F j, Y", time() - 60 * 60 * 24);
    }

    public function getDaysInMonth(): int
    {
        return cal_days_in_month(CAL_GREGORIAN, date('m'), date('Y'));
    }

    public function isCacheWritable(): bool
    {
        return is_writable(CACHE_FILE);
    }

    public function writeToCache($data): bool
    {
        return (file_put_contents(CACHE_FILE, $data) !== false);
    }

    public function whatIsThisClass(): string
    {
        return "Pure technical debt in the form of a God Class.";
    }
}

因此,正如你所看到的,在这个类中,我们基本上合并了很多无关的方法。为了解决这个问题,我们可以把这个类分成两个子类,一个是 Watch 类,另一个是 CacheManager 类。

下面是 Watch 类;该类的目的只是向我们显示各种格式的时间:

<?php

class Watch
{
    public function getTime(): int
    {
        return time();
    }

    public function getYesterdayDate(): string
    {
        return date("F j, Y", time() - 60 * 60 * 24);
    }

    public function getDaysInMonth(): int
    {
        return cal_days_in_month(CAL_GREGORIAN, date('m'), date('Y'));
    }
}

这是 CacheManager 类;这个类将所有缓存的功能分开,因此它与 Watch 类完全分开:

<?php

class CacheManager
{
    public function isCacheWritable(): bool
    {
        return is_writable(CACHE_FILE);
    }

    public function writeToCache($data): bool
    {
        return (file_put_contents(CACHE_FILE, $data) !== false);
    }
}