2012-04-23 160 views
4

我试图设计一些类层次结构,并在此部分“卡住”。抽象类 - 儿童类型

比方说,我有以下类

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(); 
} 

难道还有比我的解决方案更好的办法(读作:没有我的解决方案吸)?

+2

+1完美的例子来解释任何问题...... – 2012-04-23 08:50:28

回答

2

有没有比我的解决方案更好的办法(读作:我的解决方案是吸?)?

您的解决方案本身并不吸引人。但是,每当有人试图确定子类型以执行某些操作时,我倾向于怀疑;为什么?这个答案可能有点理论上的,甚至有点迂腐,但是这里有。

你应该不在乎。父类和子类之间的关系是子类覆盖父类的行为。 A parent class should always be substitutable by it's children, regardless which one。如果你发现自己在问:我怎么确定亚型,你平时做的两两件事之一“错误”:

  1. 您正在尝试完成基于亚型的作用。通常情况下,人们会选择将该行动转移到班级本身,而不是班级的“外部”。这也使得代码更易于管理。

  2. 您试图通过使用继承来解决您自己介绍的问题,其中继承不受保证。如果有父母,并且有孩子,每个孩子都有不同的使用方式,每个孩子都有不同的方法,只是停止使用继承。它们不是同一类型。一部电影与电视连续剧不太一样,甚至没有接近。当然,你可以在电视上看到两者,但相似之处在那里停止。

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

也就是说,如果你的代码有效,并且你对此感到满意:那就去吧。这个答案在如何处理面向对象的时候是正确的,但我对你的其他应用程序一无所知,所以答案是通用的。

+0

我完全同意你的看法。 2.正确地指出了扩展和实现之间的区别。一部电影和一部电影可能会遵循一个共同的界面,允许在电视上播放,但他们可能不会再分享更多内容。 – 2012-04-23 09:22:36

+0

@Berry Langerak与此相关的交易是客户通过传递一些电影名称来请求视频,在后面我搜索/获取/解析数据,并创建并填充正确的obj。取决于所获取的数据(它可以是电影,电视节目,以及将来也许还有一些附加类型),问题是我不在乎什么类型的obj。我创建了,但是请求obj的客户端。问津。他需要知道他可以调用什么方法。 – 2012-04-23 12:34:43

+0

...所以即使电影和电视节目不共享相同的基类,客户端仍然需要检查哪个obj。 (视频类型)他得到了。也许我会把整个事情都包装在Facade类中。无论如何,你让我停下来,从头开始考虑整个事情:) – 2012-04-23 12:34:56

2

我会走的方式2.它摘要你需要添加另一个常数在Video万一你可能想要添加class SoapOpera extends Show(例如)。

使用方式#2,您对常量的依赖性降低。无需对其进行硬编码即可获得的任何信息,意味着未来可能发生的问题在希望扩展的情况下更少。阅读关于Tight an Loose Coupling

+1

同意 - 做他们现在做的事情的唯一好处是,它允许一个额外的控制 - 极度的水平,如果你有客户开发谁不是't“允许”改变抽象类 - 使用抽象中的常量可以让其他开发人员知道它们如何被允许扩展基础。 – CD001 2012-04-23 08:53:58

1

我认为第二个选择更好,使用instanceof。这对于所有OOP设计来说通常是普遍的,而不仅仅是PHP。

第一个选项是基类中派生类的具体细节,因此必须修改每个添加的新派生类的基类,这应该总是避免。

当添加新的派生类时,保持基类不变,促进了代码重用。

1

如果有一个“正确”的方式,并且在编码过程中一切都是主观的(只要它不会对性能/可维护性产生不利影响)),那么它就是作为“真相”和“布雷迪”的第二种方式“已经指出。

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

例如:

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

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

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