带接口的 PHP 多重继承

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/13966054/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-25 06:22:03  来源:igfitidea点击:

PHP Multiple Inheritance with Interfaces

phpoopinheritanceinterface

提问by tdbui22

I'm trying to understand how using interfaces gives me multiple inheritance as I've been googling.

我正在尝试了解如何使用接口为我提供多重继承,因为我一直在使用谷歌搜索。

class A
{
 function do1(){}
 function do2(){}
 function do3(){}
}

class B extends A
{
 function do4(){}
 function do5(){}
 function do6(){}
}

class C extends B
{
}

In the above example, class C has all the methods from class A and B. However, class B also has all the methods of class A, which is not necessary desired.

在上面的例子中,类 C 拥有类 A 和 B 的所有方法。然而,类 B 也拥有类 A 的所有方法,这是不必要的。

My searches have come up to use interfaces to solve this issue by moving methods to a class and creating interfaces, as below.

我的搜索结果是通过将方法移动到类并创建接口来使用接口来解决这个问题,如下所示。

interface A
{
     function do1();
     function do2();
     function do3();
}

interface B
{
     function do4();
     function do5();
     function do6();
}

class C implements A, B
{
     function do1(){}
     function do2(){}
     function do3(){}
     function do4(){}
     function do5(){}
     function do6(){}
}

I don't really see how this solves the issue because all the code is in the new class. If I just wanted to use class A as originally, I would have to create a new class that implement interface A and copy the same code to the new class.

我真的不明白这是如何解决问题的,因为所有代码都在新类中。如果我只想像最初那样使用类 A,则必须创建一个实现接口 A 的新类并将相同的代码复制到新类中。

Is there something I'm missing?

有什么我想念的吗?

回答by cHao

PHP doesn't have multiple inheritance. If you have PHP 5.4, though, you can use traitsto at least avoid every class having to copy code.

PHP 没有多重继承。但是,如果您有 PHP 5.4,则可以使用特征来至少避免每个类都必须复制代码。

interface A {
    public function do1();
    public function do2();
    public function do3();
}

trait Alike {
    public function do1() { }
    public function do2() { }
    public function do3() { }
}


interface B {
    public function do4();
    public function do5();
    public function do6();
}

trait Blike {
    public function do4() { }
    public function do5() { }
    public function do6() { }
}


class C implements A, B {
    use Alike, Blike;
}

class D implements A {
    use Alike;

    // You can even "override" methods defined in a trait
    public function do2() { }
}

Note, though, you have to both implement the interface and use the trait (or, of course, provide your own implementation). And C and D are not related at all, except in both implementing the A interface. Traits are basically just interpreter-level copy and paste, and do not affect inheritance.

但是请注意,您必须实现接口并使用特征(或者,当然,提供您自己的实现)。并且 C 和 D 完全没有关系,除了两者都实现了 A 接口。Traits 基本上只是解释器级别的复制和粘贴,不影响继承。

回答by AgmLauncher

The first thing to understand about interfaces is that they are NOT used for inheritance. That is a very important thing to understand. If you're trying to make several classes share the same concrete code, that is not what an interface is for.

了解接口的第一件事是它们不用于继承。这是一件非常重要的事情。如果您试图让多个类共享相同的具体代码,那不是接口的用途。

The second thing to understand is the difference between client code, and service code.

第二件要理解的事情是客户端代码和服务代码之间的区别。

Client code is essentially the "last step" in a sequence of requests for data. A controller or a view in MVC can be considered client code. The model, meanwhile can be considered service code.

客户端代码本质上是数据请求序列中的“最后一步”。MVC 中的控制器或视图可以被视为客户端代码。该模型同时可以被认为是服务代码。

Interfaces are intended for client code to enforce consistency in the types of data it gets from services. Or another way to think about it - interfaces are a way for services to make sure they will be compatible with a request from client code. That is ALL they do. They quite literally provide an interface by which data is accessed, not an implementation that multiple classes can share.

接口旨在让客户端代码在从服务获取的数据类型中强制执行一致性。或者另一种思考方式 - 接口是服务确保它们与来自客户端代码的请求兼容的一种方式。这就是他们所做的一切。它们确实提供了访问数据的接口,而不是多个类可以共享的实现。

So to give you a concrete example:

所以给你一个具体的例子:

Client Code - a ProfileViewController class for a user's forum profile

客户端代码 - 用户论坛个人资料的 ProfileViewController 类

class ProfileViewController
{
    public function showProfile(User $user)
    {
         $user->getProfile();
    }
}

Service Code - a User model that retrieves data and passes it on to the client code that is requesting it

服务代码 - 检索数据并将其传递给请求它的客户端代码的用户模型

class User
{
    public function getProfile()
    {
         $profile = Do some SQL query here or something
         return $profile;
    }
}

Now suppose later on you decide to break up Users into Members, Administrators, Referees, Moderators, Writers, Editors etc, and that each has their own unique type of profile. (e.g. its own custom query, or data, or what have you)

现在假设稍后您决定将用户分为成员、管理员、裁判、版主、作家、编辑等,并且每个人都有自己独特的个人资料类型。(例如它自己的自定义查询,或数据,或者你有什么)

There are now two problems present here:

现在这里存在两个问题:

  1. You need to guarantee that whatever you pass in there will contain a getProfile() method.
  2. showProfile() will fail if you pass in anything other than a User object.
  1. 您需要保证您传入的任何内容都将包含 getProfile() 方法。
  2. 如果传入 User 对象以外的任何内容, showProfile() 将失败。

1 is easy to solve through abstract classes and methods (or through Interfaces). 2 at first sounds easy as well, because you can just make Moderators, Admins, and Members all subclasses of a User base class.

1 通过抽象类和方法(或通过接口)很容易解决。2 乍一听也很简单,因为您可以将 Moderators、Admins 和 Members 作为 User 基类的所有子类。

But then what happens when down the road, in addition to USER profiles, you want to have generic profiles for things. Perhaps you want to show profiles of sports players, or even profiles of celebrities. They're not users, but they still have profiles/details pages.

但是接下来会发生什么,除了 USER 配置文件之外,您还希望拥有用于事物的通用配置文件。也许您想显示体育运动员的个人资料,甚至名人的个人资料。他们不是用户,但他们仍然有个人资料/详细信息页面。

Because they're not users, it may not make any sense to consider them subclasses of User.

因为它们不是用户,所以将它们视为 User 的子类可能没有任何意义。

So now you're a bit stuck. showProfile() needs to be able to accept more than just a User object. In fact, you don't know what type of object you will ultimately want to pass in there. But at the same time, since you always want to be able to grab $user->getProfile(), anything you pass in there must be generic enough to be passed in, AND implement a concrete getProfile() method.

所以现在你有点卡住了。showProfile() 需要能够接受的不仅仅是 User 对象。事实上,您不知道最终要传入什么类型的对象。但同时,由于您总是希望能够获取 $user->getProfile(),因此您传入的任何内容都必须足够通用才能传入,并实现具体的 getProfile() 方法。

Solution? Interfaces!!!!!

解决方案?接口!!!!!!

First some service code

首先是一些服务代码

// First define an interface for ANY service object that will have a profile

interface IHasProfile
{
    public function getProfile();
}


// Next, define the class for an object that should have a profile. I'll do a bunch for the sake of an example...

class User implements IHasProfile
{
    public function getProfile()
    {
         $profile = Your unique user profile query here
         return $profile;
    }
}


class Celebrity implements IHasProfile
{
    public function getProfile()
    {
         $profile = Your unique celebrity profile query here
         return $profile;
    }
}


class Car implements IHasProfile
{
    public function getProfile()
    {
         $profile = Your unique vehicle profile query goes here
         return $profile;
    }
}

Next, the client code that will use it

接下来,将使用它的客户端代码

class ProfileViewController
{
    public function showProfile(IHasProfile $obj)
    {
         $obj->getProfile();
    }
}

And there you have it. showProfile() has now been abstracted enough that it doesn't care whatobject it gets, it only cares that the object has a public getProfile() method. So now you can create new types of objects to your heart's content, and if they are intended to have profiles, you can just give them "implements IHasProfile" and they will automatically just work with showProfile().

你有它。showProfile() 现在已经足够抽象,它不关心它得到了什么对象,它只关心对象是否有一个公共的 getProfile() 方法。所以现在你可以创建新类型的对象来满足你的需求,如果它们打算有配置文件,你可以给它们“实现 IHasProfile”,它们将自动与 showProfile() 一起工作。

Kind of a contrived example, but it should illustrate at least the concept of interfaces.

一个人为的例子,但它至少应该说明接口的概念。

Of course, you could just be "lazy" and not typecast the object at all, and thus allowing ANY object to be passed in. But that's a separate topic entirely ;)

当然,您可以只是“懒惰”,根本不对对象进行类型转换,从而允许传入任何对象。但这完全是一个单独的主题;)

回答by tonicospinelli

Multiple inheritance is possible only for Interfaces!

多重继承仅适用于接口!

such as my output for it:

比如我的输出:

php > interface A{};
php > interface B{};
php > interface C extends A,B{};
php > class D implements C{};
php > $d = new D();
php > echo ($d instanceof A);
1
  • I created A and B interfaces and C interface extends them.
  • After we have D class which implements C interface
  • Finally, I ask if $d object is instanceof A interface, yeah it's true
  • 我创建了 A 和 B 接口,C 接口扩展了它们。
  • 在我们有了实现 C 接口的 D 类之后
  • 最后问一下 $d 对象是不是 instanceof 接口的,是的

For the lulz, I try to create E class which extends D and stdclass classes and get error!

对于 lulz,我尝试创建扩展 D 和 stdclass 类的 E 类并得到错误!

php > class E extends D, stdclass{};
PHP Parse error:  syntax error, unexpected ',', expecting '{' in php shell code on line 1

Parse error: syntax error, unexpected ',', expecting '{' in php shell code on line 1

回答by dejjub-AIS

Multiple inheritance is not possible in PHP like in many OOPsupported languages

多重继承在 PHP 中是不可能的,就像在许多OOP受支持的语言中一样

See similar topic here. The topic is in AS3but gives you answer.

请参阅此处的类似主题。主题在AS3但给你答案。

To answer particularly about solving using interfaces is answered in the same post here

这里的同一篇文章中回答了特别是关于使用接口解决的问题

Hope this helps

希望这可以帮助

回答by xsubira

As told hereby @tonicospinelli, it seems that indeed, PHP allows multiple inheritance of interfaces, but it isn't clearly explained, just given an example

至于说在这里通过@tonicospinelli,似乎的确,PHP允许的接口多重继承,但它没有明确的解释,只是给出的例子

The way multiple inheritance works, PHP passes these using Traitsthat implement Interfaces.

多重继承的工作方式是,PHP 使用实现Interfaces 的Traits传递这些。

Once you declare a Classimplementing a "multi-interface" (1), you may use already defined Traitsto assure inheritance is well-performed.

一旦声明一个实现一个“多接口”(1),可以使用已定义特征,以确保继承是公进行。

(1): Saying "multi-interface" I mean a class implementing an interface what extends from multiple other interfaces

(1):说“多接口”我的意思是一个实现一个接口的类,它从多个其他接口扩展而来