>我正在尝试设计一些类层次结构,但我"卡"在这部分。
假设我有以下课程
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. 类型的最佳方法是什么,以便客户端可以执行以下操作
$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 个问题,您可能不是因为它有意义,而只是为了减少代码重复。这本身就是一件好事,但你尝试这样做的方式可能不是最佳的。如果可以的话,你可以改用组合,尽管我怀疑除了一些任意的getter和setter之外,重复的行为会在哪里。
也就是说,如果你的代码有效,并且你对它感到满意:那就去做吧。这个答案在如何处理 OO 方面是正确的,但我对您的应用程序的其余部分一无所知,所以答案是通用的。
我会选择方式 2。它抽象出您需要在Video
添加另一个常量,以防您可能想要添加class SoapOpera extends Show
(例如)。
使用方式#2,您不太依赖常量。无论您在不对其进行硬编码的情况下可以获得什么信息,都意味着将来在您想要扩展的情况下可能发生的问题更少。阅读有关紧固松耦合的信息。
我认为第二个选项更好,使用实例。这通常是所有OOP设计所共有的,而不仅仅是PHP。
使用第一个选项时,您有关于基类中派生类的细节,因此必须修改您添加的每个新派生类的基类,应始终避免这样做。
添加新派生类时保持基类不变可促进代码重用。
如果有一个"正确"的方法,并且编码中的一切都是主观的(只要它不会对性能/可维护性产生不利影响;)),那么这是"真相"和"布雷迪"指出的第二种方式。
按照你现在的方式做事(抽象中的类常量)的好处是,当你与其他开发人员合作时,它可以提供关于你期望如何与抽象类交互的提示。
例如:
$oKillerSharkFilm = Video::factory(Video::MOVIE, 'Jaws', 'Dundundundundundun');
$oKillerSharkDocumentary = Video::factory(Video::DOCUMENTARY, 'Jaws', 'A Discovery Shark Week Special');
当然,缺点是你必须在抽象类中维护"允许的扩展"。
您仍然可以使用问题中演示的instanceof
方法,并在抽象中维护允许的扩展列表,主要用于控件/类型修复。