PHP中的多重继承


97

我正在寻找一种好的,简洁的方法来解决以下事实:PHP5仍然不支持多重继承。这是类的层次结构:

消息
-的TextMessage
-------- InvitationTextMessage
- EmailMessage
-------- InvitationEmailMessage

这两种类型的Invitation *类有很多共同点。我很想有一个共同的父类,邀请函,他们两个都可以继承。不幸的是,他们与当前祖先也有很多共同点……TextMessage和EmailMessage。古典对多重继承的渴望。

解决问题的最轻巧的方法是什么?

谢谢!


4
在很多情况下,继承(甚至多重继承)是合理的。查看SOLID原则。优先考虑组成而不是继承。
的Ondrej Mirtes

2
@OndřejMirtes是什么意思-“在很多情况下继承是合理的”。
styler1972

12
我的意思是-继承带来的问题多于收益(请看Liskov替代原理)。您几乎可以通过合成解决所有问题,并且省去了很多麻烦。继承也是静态的-这意味着您无法更改代码中已编写的内容。但是合成可以在运行时使用,您可以动态选择实现-例如,使用不同的缓存机制重用同一类。
的Ondrej Mirtes

5

1
我建议初学者不要使用继承。通常,只有两种允许继承的情况是:1)构建库时,因此用户编写的代码更少; 2)当项目负责人要求您使用它时
gurghet,2016年

Answers:


141

Alex,大多数时候您需要多重继承是一个信号,表明您的对象结构有些不正确。在您概述的情况下,我看到您的班级职责范围太广。如果Message是应用程序业务模型的一部分,则它不应考虑呈现输出。相反,您可以划分责任并使用MessageDispatcher来发送使用文本或html后端传递的消息。我不知道您的代码,但是让我这样模拟一下:

$m = new Message();
$m->type = 'text/html';
$m->from = 'John Doe <jdoe@yahoo.com>';
$m->to = 'Random Hacker <rh@gmail.com>';
$m->subject = 'Invitation email';
$m->importBody('invitation.html');

$d = new MessageDispatcher();
$d->dispatch($m);

这样,您可以向Message类添加一些特殊化:

$htmlIM = new InvitationHTMLMessage(); // html type, subject and body configuration in constructor
$textIM = new InvitationTextMessage(); // text type, subject and body configuration in constructor

$d = new MessageDispatcher();
$d->dispatch($htmlIM);
$d->dispatch($textIM);

请注意,MessageDispatcher将根据type传递的Message对象中的属性来决定是以HTML还是纯文本形式发送。

// in MessageDispatcher class
public function dispatch(Message $m) {
    if ($m->type == 'text/plain') {
        $this->sendAsText($m);
    } elseif ($m->type == 'text/html') {
        $this->sendAsHTML($m);
    } else {
        throw new Exception("MIME type {$m->type} not supported");
    }
}

总结起来,责任分为两类。消息配置在InvitationHTMLMessage / InvitationTextMessage类中完成,并将发送算法委托给调度程序。这称为策略模式,您可以在此处阅读更多内容。


13
惊人的广泛答案,谢谢!我今天学到了一些东西!
亚历克斯·温斯坦

26
...我知道这有点老了(我在寻找PHP是否有MI ...只是出于好奇)我不认为这是Strategy Pattern的一个很好的例子。设计策略模式是为了使您可以随时实施新的“策略”。您提供的实现不具备此功能。相反,Message应该具有“发送”功能,该功能调用MessageDispatcher-> dispatch()(Dispatcher可以是param或成员var),并且新类HTMLDispatcher和TextDispatcher将以各自的方式实现“ dispatch”(这允许其他Dispatchers进行此操作)其他工作)
特伦斯·洪尔斯

12
不幸的是,PHP不适用于实施策略模式。支持方法重载的语言在这里可以更好地工作-假设您有两个同名的方法:dispatch(HTMLMessage $ m)和dispatch(TextMessage $)-现在在强类型语言中,编译器/解释器将根据以下情况自动采用正确的“策略”:参数类型。除此之外,我不认为对新战略实施持开放态度是“战略模式”的本质。当然,这是一件好事,但通常不是必需的。
米哈尔鲁德尼茨基

2
假设您有一个类Tracing(这只是一个示例),您想要在其中使用诸如调试到文件中,针对严重问题发送SMS等通用内容。您所有的班级都是该班级的孩子。现在假设您要创建一个Exception具有这些功能的类(=的子代Tracing)。此类必须是的子级Exception。您如何设计没有多重继承的东西?是的,您始终可以找到解决方案,但您将永远接近黑客。从长远来看,骇客=昂贵的解决方案。故事结局。
奥利维尔·庞斯

1
Olivier Pons,我认为子类化Tracing不会是您的用例的正确解决方案。就像拥有带有静态方法Debug,SendSMS等的抽象Tracing类一样简单,然后可以使用Tracing :: SendSMS()等从任何其他类中调用它们。您的其他类不是Tracing的“类型”,他们“使用”追踪。注意:有些人可能更喜欢单例而不是静态方法。我更喜欢在单例情况下使用静态方法。

15

也许您可以将“ is-a”关系替换为“ has-a”关系?邀请可能有一条消息,但不一定是“是”消息。可能会确认一个邀请消息,它与消息模型不能很好地配合。

如果您需要了解更多信息,请搜索“组成与继承”。


9

如果我可以在这个话题中引用Phil ...

PHP与Java一样,不支持多重继承。

PHP 5.4中将引入一些特征,这些特征试图为该问题提供解决方案。

同时,您最好重新考虑您的课堂设计。如果您需要扩展类的API,则可以实现多个接口。

还有克里斯...

PHP并不真正支持多重继承,但是有一些(有点混乱)的实现方式。查看此URL以获得一些示例:

http://www.jasny.net/articles/how-i-php-multiple-inheritance/

以为他们俩都有有用的联系。等不及要尝试特征或一些混入...


1
要走特质
Jonathan

6

Symfony框架为此提供了一个mixin插件,您可能想将其检出-甚至只是出于想法,如果不使用它。

“设计模式”的答案是将共享功能抽象为一个单独的组件,并在运行时进行编写。考虑一种将邀请功能抽象为类的方法,该类通过继承以外的其他方式与您的Message类相关联。


4

我正在PHP 5.4中使用特征来解决此问题。 http://php.net/manual/en/language.oop5.traits.php

这允许通过扩展进行经典继承,但也可以将通用功能和属性放入“特征”中。如手册所述:

特性是一种在PHP等单一继承语言中重用代码的机制。特性旨在通过使开发人员在生活在不同类层次结构中的几个独立类中自由重用方法集,从而减少单一继承的某些限制。



3

这既是一个问题,也是一个解决方案。

那神奇的_ call()呢?_get(),__ set()方法?我尚未测试此解决方案,但是如果您创建multiInherit类,该怎么办。子类中的受保护变量可以包含要继承的类的数组。多接口类中的构造函数可以创建每个要继承的类的实例,并将它们链接到私有属性,例如_ext。__call()方法可以在_ext数组中的每个类上使用method_exists()函数来定位要调用的正确方法。__get()和__set可用于定位内部属性,或者如果您的专家具有引用,则可以使子类的属性和继承的类成为对同一数据的引用。您的对象的多重继承对于使用这些对象的代码将是透明的。也,只要_ext数组按类名索引,内部对象就可以直接访问继承的对象。我已经设想过创建这个超类,但还没有实现它,因为我认为如果它能起作用,可能会导致养成各种不良的编程习惯。


我认为这是可行的。它将结合多个类的功能,但实际上不会继承它们(在意义上instanceof
user102008 2011年

并且一旦在内部类中对self进行调用,这肯定将无法允许覆盖:: <whatever>
Phil Lello

1

我有几个问题要问清楚您在做什么:

1)您的消息对象是否包含消息,例如正文,收件人,计划时间?2)您打算如何处理邀请对象?与EmailMessage相比,是否需要对其进行特殊处理?3)如果这样,有什么特别之处?4)如果是这种情况,为什么邀请消息类型需要不同地处理?5)如果要发送欢迎消息或OK消息怎么办?它们也是新物体吗?

听起来确实好像您正在尝试将过多的功能组合到一组对象中,而这些对象仅应与保留消息内容有关,而与如何处理消息内容无关。对我来说,邀请或标准消息没有区别。如果邀请需要特殊处理,则意味着应用程序逻辑而不是消息类型。

例如:我构建的系统具有一个共享的基本消息对象,该对象已扩展为SMS,电子邮件和其他消息类型。但是:这些消息没有进一步扩展-邀请消息只是预定义的文本,可以通过电子邮件类型的消息发送。特定的邀请应用程序将涉及邀请的验证和其他要求。毕竟,您要做的就是将消息X发送给收件人Y,该收件人本身应该是一个离散系统。


0

像Java一样的问题。尝试使用具有抽象功能的接口来解决该问题



-1

在Message类下面的Invitation类怎么样?

因此层次结构如下:

消息
---邀请
------的TextMessage
------ EmailMessage

并在Invitation类中,添加InvitationTextMessage和InvitationEmailMessage中的功能。

我知道,邀请并不是真正的消息类型,而是消息的功能。所以我不确定这是否是好的OO设计。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.