关于oop:PHP与接口的多重继承

PHP Multiple Inheritance with Interfaces

我在努力理解,在我谷歌搜索的过程中,使用接口是如何给我多重继承的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class A
{
 function do1(){}
 function do2(){}
 function do3(){}
}

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

class C extends B
{
}

在上面的例子中,C类有A类和B类的所有方法,但是B类也有A类的所有方法,这是不需要的。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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(){}
}

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

我有什么东西不见了吗?


PHP没有多重继承。但是,如果您有PHP5.4,那么至少可以使用特性来避免每个类都必须复制代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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() { }
}

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


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

第二个需要理解的是客户机代码和服务代码之间的区别。

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

接口用于客户端代码,以强制其从服务获取的数据类型保持一致性。或者考虑它的另一种方式——接口是服务确保它们与来自客户机代码的请求兼容的一种方式。他们就是这么做的。它们实际上提供了一个访问数据的接口,而不是多个类可以共享的实现。

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

客户端代码-用户论坛配置文件的profileviewcontroller类

1
2
3
4
5
6
7
class ProfileViewController
{
    public function showProfile(User $user)
    {
         $user->getProfile();
    }
}

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

1
2
3
4
5
6
7
8
class User
{
    public function getProfile()
    {
         $profile = Do some SQL query here or something
         return $profile;
    }
}

现在假设稍后您决定将用户分成成员、管理员、推荐人、版主、作者、编辑等,并且每个人都有自己独特的配置文件类型。(例如,它自己的自定义查询或数据,或您拥有的内容)

现在有两个问题:

  • 您需要确保在其中传递的任何内容都将包含getprofile()方法。
  • 如果传递的不是用户对象,showProfile()将失败。
  • 1很容易通过抽象类和方法(或通过接口)来解决。2一开始听起来也很简单,因为您只需使版主、管理员和成员成为用户基类的所有子类。

    但接下来,除了用户配置文件之外,您还需要一些通用的配置文件。也许你想展示体育运动员的个人资料,甚至名人的个人资料。他们不是用户,但他们仍然有个人资料/详细信息页面。

    因为他们不是用户,所以认为他们是用户的子类可能没有任何意义。

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

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

    首先是一些服务代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    // 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;
        }
    }

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

    1
    2
    3
    4
    5
    6
    7
    class ProfileViewController
    {
        public function showProfile(IHasProfile $obj)
        {
             $obj->getProfile();
        }
    }

    就在这里。ShowProfile()现在已经被抽象得足够多,以至于它不关心它得到了什么对象,它只关心对象有一个公共的getProfile()方法。因此,现在您可以创建新类型的对象以满足您的心意,如果它们打算具有配置文件,您只需给它们"实现iHasProfile",它们将自动与ShowProfile()一起工作。

    有点做作,但它至少应该说明接口的概念。

    当然,您可以只是"懒惰",根本不排版对象,从而允许任何对象被传入。但这完全是一个单独的话题;)


    只能对接口进行多重继承!

    比如我的输出:

    1
    2
    3
    4
    5
    6
    7
    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
    • 我创建了A和B接口,C接口扩展了它们。
    • 在我们有了实现C接口的D类之后
    • 最后,我问$d对象是否是接口的实例,是的。
    • 小精灵

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

      1
      2
      3
      4
      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


      在许多支持OOP的语言中,在php中不可能有多个继承。

      请参阅此处的类似主题。主题在AS3中,但给出了答案。

      特别是关于使用接口解决问题的答案在这里的同一篇文章中给出。

      希望这有帮助


      作为对由@ tonicospinelli似乎在这里,事实上,它允许多个inheritance interfaces,PHP的,但它不是清楚解释给定的,只是一个例子

      多个inheritance作品的方式,使用PHP,implement interfaces passes这些特质。

      一旦你declare实施"一类的多接口"(1),你可能使用已经定义好的特点,是进行大assure inheritance -。

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