web-dev-qa-db-fra.com

PHP Héritage multiple avec interfaces

J'essaie de comprendre en quoi l'utilisation d'interfaces me procure un héritage multiple alors que je cherchais sur Google.

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

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

class C extends B
{
}

Dans l'exemple ci-dessus, la classe C a toutes les méthodes des classes A et B. Cependant, la classe B a aussi toutes les méthodes de la classe A, ce qui n'est pas nécessaire.

Mes recherches ont abouti à l’utilisation d’interfaces pour résoudre ce problème en déplaçant les méthodes dans une classe et en créant des interfaces, comme indiqué ci-dessous.

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(){}
}

Je ne vois pas vraiment comment cela résoudrait le problème car tout le code est dans la nouvelle classe. Si je voulais simplement utiliser la classe A à l'origine, je devrais créer une nouvelle classe qui implémenterait l'interface A et copier le même code dans la nouvelle classe.

Y a-t-il quelque chose qui me manque?

14
tdbui22

PHP n'a pas d'héritage multiple. Si vous avez PHP 5.4, cependant, vous pouvez utiliser traits pour éviter au moins que chaque classe ait à copier du code.

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() { }
}

Notez cependant que vous devez à la fois implémenter l'interface et utiliser le trait (ou bien sûr, fournir votre propre implémentation). Et C et D ne sont pas du tout liés, sauf dans les deux cas implémentant l'interface A. Les traits ne sont fondamentalement que des copier-coller au niveau interprète et n'affectent pas l'héritage.

27
cHao

La première chose à comprendre concernant les interfaces est qu'elles ne sont PAS utilisées pour l'héritage. C'est une chose très importante à comprendre. Si vous essayez de faire en sorte que plusieurs classes partagent le même code concret, ce n'est pas à quoi sert une interface. 

La deuxième chose à comprendre est la différence entre le code client et le code de service. 

Le code client est essentiellement la "dernière étape" d'une séquence de demandes de données. Un contrôleur ou une vue dans MVC peut être considéré comme un code client. Le modèle, quant à lui, peut être considéré comme un code de service.

Les interfaces sont conçues pour que le code client renforce la cohérence des types de données qu’il reçoit des services. Ou une autre façon de penser - les interfaces sont un moyen pour les services de s’assurer qu’ils seront compatibles avec une requête du code client. C'est tout ce qu'ils font. Ils fournissent littéralement une interface permettant d'accéder aux données, et non une implémentation que plusieurs classes peuvent partager.

Donc, pour vous donner un exemple concret:

Code client - une classe ProfileViewController pour le profil de forum d'un utilisateur

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

Code de service - un modèle utilisateur qui récupère des données et les transmet au code client qui les demande

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

Supposons maintenant que vous décidiez plus tard de diviser les utilisateurs en membres, administrateurs, arbitres, modérateurs, rédacteurs, rédacteurs, etc., et que chacun ait son propre type de profil. (par exemple, sa propre requête personnalisée, ses propres données ou ce que vous avez)

Il y a maintenant deux problèmes présents ici:

  1. Vous devez vous assurer que tout ce que vous transmettez contiendra une méthode getProfile (). 
  2. showProfile () échouera si vous transmettez autre chose qu'un objet User. 

1 est facile à résoudre par le biais de classes et méthodes abstraites (ou d’interfaces). 2 au premier abord semble simple, car vous pouvez simplement créer des modérateurs, des administrateurs et des membres de toutes les sous-classes d’une classe de base User. 

Mais que se passe-t-il ensuite, en plus des profils USER, vous souhaitez créer des profils génériques. Peut-être souhaitez-vous afficher les profils des joueurs sportifs, voire des profils de célébrités. Ce ne sont pas des utilisateurs, mais ils ont toujours des pages de profils/détails. 

Parce qu'ils ne sont pas des utilisateurs, cela n'a aucun sens de les considérer comme des sous-classes d'utilisateurs.

Alors maintenant, vous êtes un peu coincé. showProfile () doit pouvoir accepter plus qu'un objet User. En fait, vous ne savez pas quel type d’objet vous souhaitez transmettre. Mais en même temps, puisque vous voulez toujours pouvoir saisir $ user-> getProfile (), tout ce que vous transmettez doit être suffisamment générique pour pouvoir être passé, ET implémenter une méthode concrète getProfile (). 

Solution? Interfaces !!!!!

D'abord un code de service

// 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;
    }
}

Ensuite, le code client qui l'utilisera

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

Et voila. showProfile () a maintenant été suffisamment résumé pour qu’il se moque de l’objet quel il obtient, il se soucie seulement que l’objet possède une méthode getProfile () publique. Alors maintenant, vous pouvez créer de nouveaux types d’objets au contenu de votre coeur, et s’ils sont destinés à avoir des profils, vous pouvez simplement leur donner "implémente IHasProfile" et ils fonctionneront automatiquement avec showProfile () 

C'est un exemple artificiel, mais il devrait au moins illustrer le concept d'interface.

Bien sûr, vous pouvez simplement être "paresseux" et ne pas transtyper l'objet du tout, et permettre ainsi à n'importe quel objet d'être transmis. Mais c'est un sujet à part entière;)

20
AgmLauncher

L'héritage multiple n'est possible que pour les interfaces!

comme ma sortie pour elle:

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
  • J'ai créé les interfaces A et B et l'interface C les étend.
  • Après avoir classe D qui implémente l'interface C
  • Enfin, je demande si $ d objet est une instance de A interface, oui c'est vrai

Pour le lulz, j'essaie de créer la classe E qui étend les classes D et stdclass et reçois une erreur!

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
3
tonicospinelli

L'héritage multiple n'est pas possible dans PHP comme dans beaucoup de langages supportés par OOP 

Voir le sujet similaire ici . Le sujet est dans AS3 mais vous donne la réponse.

Pour répondre particulièrement à propos de la résolution en utilisant des interfaces, il est répondu dans le même message ici

J'espère que cela t'aides

1
Solow Developer

Comme dit ici de @tonicospinelli, il semble que en effet, PHP autorise l'héritage multiple d'interfaces, mais ce n'est pas clairement expliqué, juste à titre d'exemple

La façon dont fonctionne l'héritage multiple fonctionne, PHP les transmet en utilisant Traits qui implémente Interfaces.

Une fois que vous avez déclaré une Classe implémentant une "interface multiple" (1), vous pouvez utiliser les Traits déjà définis pour vous assurer que l'héritage est bien effectué.

(1): Dire "multi-interface", je veux dire une classe implémentant une interface allant de plusieurs autres interfaces

0
xsubira