99

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

Message
-- TextMessage
-------- InvitationTextMessage
-- EmailMessage
-------- InvitationEmailMessage

这两种类型的 Invitation* 课程有很多共同点;我很想有一个共同的父类,邀请,他们都会继承。不幸的是,它们与当前的祖先也有很多共同点……TextMessage 和EmailMessage。这里是多重继承的经典愿望。

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

谢谢!

4

11 回答 11

144

亚历克斯,大多数时候你需要多重继承是一个信号,你的对象结构有些不正确。在您概述的情况下,我认为您的班级责任过于广泛。如果消息是应用程序业务模型的一部分,它不应该关心渲染输出。相反,您可以分担责任并使用 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);

type请注意,MessageDispatcher 将根据传递的 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 类中完成,发送算法委托给 Dispatcher。这称为策略模式,您可以在此处阅读更多信息。

于 2008-09-18T09:53:48.843 回答
15

也许您可以用“has-a”关系替换“is-a”关系?邀请可能有一条消息,但它不一定需要“is-a”消息。Invitation fe 可能会被确认,这与 Message 模型不匹配。

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

于 2008-09-18T08:45:27.790 回答
10

如果我可以在这个线程中引用菲尔......

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

PHP 5.4 中的特性将试图为这个问题提供解决方案。

与此同时,你最好重新考虑你的班级设计。如果您希望为您的类提供扩展 API,则可以实现多个接口。

还有克里斯……

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

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

认为他们都有有用的链接。迫不及待地想尝试一些特征或者一些混合...

于 2012-03-22T07:15:58.597 回答
7

Symfony 框架为此提供了一个 mixin 插件,您可能想检查一下——即使只是为了想法,如果不使用它的话。

“设计模式”的答案是将共享功能抽象为一个单独的组件,并在运行时组合。考虑一种方法将 Invitation 功能抽象为一个类,该类以某种方式与您的 Message 类相关联,而不是继承。

于 2008-09-18T08:43:24.770 回答
4

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

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

Traits 是一种在 PHP 等单继承语言中重用代码的机制。Trait 旨在通过使开发人员能够在生活在不同类层次结构中的多个独立类中自由地重用方法集来减少单继承的一些限制。

于 2012-11-15T12:02:11.673 回答
3

听起来装饰器模式可能很合适,但如果没有更多细节就很难说。

于 2008-09-18T08:43:10.060 回答
3

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

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

于 2010-10-20T07:52:24.523 回答
1

我有几个问题要问以澄清你在做什么:

1) 您的消息对象是否包含一条消息,例如正文、收件人、安排时间?2) 你打算用你的邀请对象做什么?与 EmailMessage 相比,它是否需要特别对待?3)如果是这样,它有什么特别之处?4)如果是这样的话,为什么消息类型需要对邀请进行不同的处理?5) 如果您想发送欢迎消息或 OK 消息怎么办?它们也是新对象吗?

听起来您确实在尝试将过多的功能组合到一组对象中,这些对象应该只关心保存消息内容 - 而不是应该如何处理它。对我来说,你看,邀请或标准消息之间没有区别。如果邀请需要特殊处理,那么这意味着应用程序逻辑而不是消息类型。

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

于 2008-09-18T14:13:19.453 回答
0

像Java一样的问题。尝试使用带有抽象函数的接口来解决该问题

于 2008-09-18T08:45:27.230 回答
0

PHP 确实支持接口。这可能是一个不错的选择,具体取决于您的用例。

于 2008-09-18T08:47:54.400 回答
-1

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

所以层次结构是:

Message
--- Invitation
------ TextMessage
------ EmailMessage

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

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

于 2012-05-23T21:02:12.567 回答