【问题标题】:Abstract class - children type抽象类-儿童类型
【发布时间】:2012-05-03 20:58:45
【问题描述】:

我正在尝试设计一些类层次结构,但我在这部分“卡住”了。

假设我有以下课程

abstract class Video 
{
    const TYPE_MOVIE = 1;
    const TYPE_SHOW  = 2;

    abstract public function getTitle();
    abstract public function getType();
}

class Movie extends Video 
{
    // ...

    public function getType() 
    {
        return self::TYPE_MOVIE;
    }
}

class Show extends Video 
{
    // ...

    public function getType() 
    {
        return self::TYPE_SHOW;
    }
}

在系统的不同部分,我有(解析器)类,它封装了 电影和节目对象并返回 obj。给客户。

问题:获取 obj 类型的最佳方法是什么。从解析器/工厂类返回,以便客户端可以执行类似的操作

$video = $parser->getVideo('Dumb and Dumber');

echo $video->getTitle();

// Way 1
if($video->getType == 'show') {
    echo $video->getNbOfSeasons();
}

// Way 2
if($video instanceof Show) {
    echo $video->getNbOfSeasons();
}

// Current way
if($video->getType == Video::TYPE_SHOW) {
    echo $video->getNbOfSeasons();
}

有没有比我的解决方案更好的方法(读作:我的解决方案很烂吗?)?

【问题讨论】:

  • +1 解释任何问题的完美示例...

标签: php oop abstract-class hierarchy children


【解决方案1】:

有没有比我的解决方案更好的方法(读作:我的解决方案很烂吗?)?

您的解决方案本身并不糟糕。但是,每当有人试图确定执行某些操作的子类型时,我往往会想知道;为什么?这个答案可能有点理论,甚至可能有点迂腐,但还是这样吧。

你不应该关心。父类和子类之间的关系是子类覆盖了父类的行为。一个parent class should always be substitutable by it's children, regardless which one。如果您发现自己在问:我如何确定子类型,您通常会做两件“错误”的事情之一:

  1. 您正在尝试根据子类型执行操作。通常,人们会选择将该动作移动到班级本身,而不是班级的“外部”。这也使得代码更易于管理。

  2. 您正试图通过使用继承来解决您自己介绍的问题,其中不保证继承。如果有父,也有子,每一个要不同的使用,每一个有不同的方法,就停止使用继承。他们不是同一类型。电影与电视剧不一样,甚至不相近。当然,您可以在电视上看到两者,但相似之处仅止于此。

如果您遇到问题 2,您可能使用继承不是因为它有意义,而只是为了减少代码重复。这本身就是一件好事,但您尝试这样做的方式可能不是最佳的。如果可以的话,你可以使用组合来代替,尽管我怀疑重复的行为会在哪里,除了一些任意的 getter 和 setter。

也就是说,如果您的代码有效,并且您对它感到满意:那就去做吧。这个答案在如何处理 OO 方面是正确的,但我对你的应用程序的其余部分一无所知,所以答案是通用的。

【讨论】:

  • 我完全同意你的看法。 2.正确指出扩展和实现的区别。电影和连续剧可能会遵循一个通用界面以允许在电视上播放,但它们可能不再共享任何内容。
  • @Berry Langerak 处理这个问题是客户端通过传递一些电影的名称来请求视频,在后面我搜索/获取/解析数据,然后创建和填充正确的 obj。取决于获取的数据(它可以是电影、电视节目,将来也可能是一些其他类型),问题是我不在乎什么类型的 obj。我创建,但请求 obj 的客户。关心。他需要知道他可以调用什么方法。
  • ...所以即使 Movie 和 TV show 不共享同一个基类,客户端仍然需要检查哪个 obj。 (视频类型)他得到了。也许我会把整件事都放在 Facade 类中。无论如何,你让我从一开始就停下来思考整个事情:)
  • @MarkoJovanovic 如果对象上有许多不同的方法,那么继承是错误的工具。你只是有两个不同的实体;)
【解决方案2】:

我会选择方式 2。它抽象出您需要在 Video 添加另一个常量,以防您可能想要添加 class SoapOpera extends Show(例如)。

使用方式#2,您对常量的依赖更少。无论您无需硬编码就可以获得任何信息,这意味着如果您想扩展,将来可能会发生更少的问题。了解Tight an Loose Coupling

【讨论】:

  • 同意 - 以他现在的方式做事的唯一好处是,如果您的客户开发人员“不允许”改变抽象类 - 在抽象中使用常量可以让其他开发人员知道他们是如何被允许扩展基础的。
【解决方案3】:

我认为第二种选择更好,使用 instanceof。这通常是所有 OOP 设计的共同点,而不仅仅是 PHP。

使用您的第一个选项,您在基类中有派生类的详细信息,因此必须为您添加的每个新派生类修改基类,这应该始终避免。

在添加新的派生类时保持基类不变可以促进代码重用。

【讨论】:

    【解决方案4】:

    如果有一种“正确”的方式,当然,在编码中一切都是主观的(只要它不会对性能/可维护性产生不利影响;)),那么它是“Truth”和“Brady”的第二种方式已经指出了。

    以您现在的方式做事(抽象中的类常量)的好处是,当您与其他开发人员一起工作时,它可以提供有关您期望抽象类如何交互的提示。

    例如:

    $oKillerSharkFilm = Video::factory(Video::MOVIE, 'Jaws', 'Dundundundundundun');
    $oKillerSharkDocumentary = Video::factory(Video::DOCUMENTARY, 'Jaws', 'A Discovery Shark Week Special');
    

    当然,缺点是你必须在抽象类中维护“允许的扩展”。

    您仍然可以使用问题中演示的instanceof 方法,并在摘要中维护允许的扩展名列表,主要用于控制/类型修复。

    【讨论】:

      猜你喜欢
      • 2017-08-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-13
      相关资源
      最近更新 更多