Quel est l'intérêt des interfaces en PHP?


224

Les interfaces vous permettent de créer du code qui définit les méthodes des classes qui l'implémentent. Vous ne pouvez cependant pas ajouter de code à ces méthodes.

Classes abstraites vous permettent de faire la même chose, tout en ajoutant du code à la méthode.

Maintenant, si vous pouvez atteindre le même objectif avec des classes abstraites, pourquoi avons-nous même besoin du concept d'interfaces?

On m'a dit que cela avait à voir avec la théorie OO de C ++ à Java, sur laquelle est basé le matériel OO de PHP. Le concept est-il utile en Java mais pas en PHP? Est-ce juste un moyen d'éviter d'avoir des espaces réservés jonchés dans la classe abstraite? Suis-je en train de manquer quelque chose?


4
Vous devez lire ceci: stackoverflow.com/a/384067/14673
Luc M

à peu près sûr que c'est une aide mentale et une aide à la communication. Les interfaces agissent comme d'excellents outils pédagogiques pour votre API, car elles regroupent les services que votre API expose ensemble d'une manière abstraite qui peut être lue sans connaître l'implémentation. C'est cher, mais en tant que personnes à l'aise avec l'interface, vous pouvez déjà utiliser les fonctions directement sans avoir besoin de classes, économisant ainsi un pointeur. sans interfaces définies par langage, il peut être difficile de planifier à l'avance pour de nombreux programmeurs, car les gens habitués aux «langages oop» préfèrent souvent concevoir avec des interfaces plutôt que sur papier.
Dmitry

Réponses:


142

L'intérêt des interfaces est de vous donner la flexibilité de forcer votre classe à implémenter plusieurs interfaces, mais ne pas autoriser l'héritage multiple. Les problèmes d'héritage de plusieurs classes sont nombreux et variés et la page wikipedia qui les résume assez bien.

Les interfaces sont un compromis. La plupart des problèmes d'héritage multiple ne s'appliquent pas aux classes de base abstraites, de sorte que la plupart des langages modernes désactivent l'héritage multiple tout en appelant des interfaces de classes de base abstraites et permettent à une classe "d'implémenter" autant de ceux qu'ils le souhaitent.


39
On peut également dire que * les interfaces fournissent la conception d'une classe sans implémentation. * Les classes abstraites fournissent une certaine conception et une certaine implémentation. Les classes abstraites sont plus utiles lorsque les classes enfants partagent certaines similitudes d'implémentation, mais diffèrent dans certaines implémentations.
Jrgns

@Craig, Il n'y a pas de problème inhérent à l'héritage multiple, juste que les langages actuels ne sont pas assez puissants pour les implémenter correctement. Les «problèmes» peuvent en fait être résolus assez facilement, par exemple, en indiquant le chemin explicite de l'héritage pour les fonctions héritées du même nom peut résoudre le dilemme du diamant.
Pacerier

15
Ce n'est pas du tout ce que sont les interfaces. Il ne s'agit pas d'un compromis sur l'héritage multiple, il s'agit de créer un contrat conceptuel et abstrait pour les objets à implémenter et pour d'autres objets / méthodes à consommer. les interfaces sont un outil pour le polymorphisme et non pour l'héritage direct.
Madara's Ghost

123

Le concept est utile partout dans la programmation orientée objet. Pour moi, je considère une interface comme un contrat. Tant que ma classe et votre classe sont d'accord sur ce contrat de signature de méthode, nous pouvons "interfacer". En ce qui concerne les classes abstraites, je les considère comme plus de classes de base qui suppriment certaines méthodes et j'ai besoin de remplir les détails.


Cela m'a fait comprendre un peu. C'est comme avec les espaces de noms - le code partagé est donc plus facile à utiliser et sans conflits. C'est plus facile quand deux personnes font des cours sur la même base, non?
RedClover

Ne faut-il pas distinguer le concept général d'une «interface» des interfaces concrètes dans un langage comme PHP? Toute fonction, par exemple, a une "interface" qui définit comment vous l'utilisez et masque son implémentation. Ce type d'interface "contractuelle" ne nécessite donc pas de fonction linguistique spéciale. Par conséquent, la fonction de langue doit être pour quelque chose d'autre (ou quelque chose en plus).,
UuDdLrLrSs

70

Pourquoi auriez-vous besoin d'une interface, s'il existe déjà des classes abstraites? Pour éviter l'héritage multiple (peut provoquer plusieurs problèmes connus).

Un de ces problèmes:

Le «problème des diamants» (parfois appelé «diamant mortel de la mort») est une ambiguïté qui survient lorsque deux classes B et C héritent de A et que la classe D hérite à la fois de B et de C. S'il existe une méthode dans A qui B et C ont remplacé, et D ne le remplace pas, alors quelle version de la méthode D hérite: celle de B, ou celle de C?

Source: https://en.wikipedia.org/wiki/Multiple_inheritance#The_diamond_problem

Pourquoi / quand utiliser une interface? Un exemple ... Toutes les voitures du monde ont la même interface (méthodes) ... AccelerationPedalIsOnTheRight(), BrakePedalISOnTheLeft(). Imaginez que chaque marque de voiture aurait ces "méthodes" différentes d'une autre marque. BMW aurait les freins sur le côté droit, et Honda aurait les freins sur le côté gauche de la roue. Les gens devraient apprendre comment ces "méthodes" fonctionnent à chaque fois qu'ils achètent une marque de voiture différente. C'est pourquoi c'est une bonne idée d'avoir la même interface dans plusieurs "endroits".

Que fait une interface pour vous (pourquoi quelqu'un en utiliserait-il une)? Une interface vous empêche de faire des "erreurs" (elle vous assure que toutes les classes qui implémentent une interface spécifique, auront toutes les méthodes qui sont dans l'interface).

// Methods inside this interface must be implemented in all classes which implement this interface.
interface IPersonService
{   
    public function Create($personObject);
}

class MySqlPerson implements IPersonService
{
    public function Create($personObject)
    {
        // Create a new person in MySql database.
    }
}

class MongoPerson implements IPersonService
{
    public function Create($personObject)
    {
        // Mongo database creates a new person differently then MySQL does. But the code outside of this method doesn't care how a person will be added to the database, all it has to know is that the method Create() has 1 parameter (the person object).
    }
}

De cette façon, la Create()méthode sera toujours utilisée de la même manière. Peu importe si nous utilisons la MySqlPersonclasse ou leMongoPerson classe. La façon dont nous utilisons une méthode reste la même (l'interface reste la même).

Par exemple, il sera utilisé comme ceci (partout dans notre code):

new MySqlPerson()->Create($personObject);
new MongoPerson()->Create($personObject);

De cette façon, quelque chose comme ça ne peut pas arriver:

new MySqlPerson()->Create($personObject)
new MongoPerson()->Create($personsName, $personsAge);

Il est beaucoup plus facile de se souvenir d'une interface et d'utiliser la même partout, que de plusieurs différentes.

De cette façon, l'intérieur de la Create()méthode peut être différent pour différentes classes, sans affecter le code "extérieur", qui appelle cette méthode. Tout ce que le code extérieur doit savoir, c'est que la méthode Create()a 1 paramètre ( $personObject), car c'est ainsi que le code extérieur utilisera / appellera la méthode. Le code extérieur ne se soucie pas de ce qui se passe à l'intérieur de la méthode; il suffit de savoir comment l'utiliser / l'appeler.

Vous pouvez également le faire sans interface, mais si vous utilisez une interface, c'est "plus sûr" (car cela vous empêche de faire des erreurs). L'interface vous assure que la méthode Create()aura la même signature (mêmes types et même nombre de paramètres) dans toutes les classes qui implémentent l'interface. De cette façon, vous pouvez être sûr que TOUTE classe qui implémente l' IPersonServiceinterface aura la méthode Create()(dans cet exemple) et n'aura besoin que de 1 paramètre ( $personObject) pour être appelée / utilisée.

Une classe qui implémente une interface doit implémenter toutes les méthodes que l'interface possède / possède.

J'espère que je ne me suis pas trop répété.


Très belle analogie avec les pédales de voiture!
James

24

La différence entre l'utilisation d'une interface et d'une classe abstraite a plus à voir avec l'organisation du code que l'application par le langage lui-même. Je les utilise beaucoup lors de la préparation de code pour que d'autres développeurs puissent travailler afin qu'ils restent dans les modèles de conception prévus. Les interfaces sont une sorte de «conception par contrat» par laquelle votre code accepte de répondre à un ensemble prescrit d'appels d'API qui peuvent provenir de code auquel vous n'avez pas accès.

Alors que l'héritage de la classe abstraite est une relation "est une", ce n'est pas toujours ce que vous voulez, et l'implémentation d'une interface est plus une relation "agit comme une" Cette différence peut être assez importante dans certains contextes.

Par exemple, disons que vous avez un compte de classe abstraite à partir duquel de nombreuses autres classes s'étendent (types de comptes, etc.). Il a un ensemble particulier de méthodes qui ne s'appliquent qu'à ce groupe de types. Cependant, certaines de ces sous-classes de comptes implémentent Versionable, Listable ou Editable afin qu'elles puissent être lancées dans des contrôleurs qui s'attendent à utiliser ces API. Le contrôleur ne se soucie pas de quel type d'objet il s'agit

En revanche, je peux également créer un objet qui ne s'étend pas depuis Account, par exemple une classe abstraite User, et toujours implémenter Listable et Editable, mais pas Versionable, ce qui n'a pas de sens ici.

De cette façon, je dis que la sous-classe FooUser n'est PAS un compte, mais agit comme un objet modifiable. De même, BarAccount s'étend à partir de Account, mais n'est pas une sous-classe User, mais implémente Editable, Listable et également Versionable.

L'ajout de toutes ces API pour Editable, Listable et Versionable dans les classes abstraites elle-même serait non seulement encombré et laid, mais dupliquerait les interfaces communes dans Account et User, ou forcerait mon objet User à implémenter Versionable, probablement juste pour lancer un exception.


C'est ici. Appliquez rigoureusement les développeurs à utiliser vos méthodes sans les étendre ou les écraser
Nick

21

Les interfaces sont essentiellement un modèle pour ce que vous pouvez créer. Ils définissent les méthodes qu'une classe doit avoir , mais vous pouvez créer des méthodes supplémentaires en dehors de ces limitations.

Je ne sais pas ce que vous entendez par ne pas pouvoir ajouter de code aux méthodes - parce que vous le pouvez. Appliquez-vous l'interface à une classe abstraite ou à la classe qui l'étend?

Une méthode dans l'interface appliquée à la classe abstraite devra être implémentée dans cette classe abstraite. Cependant, appliquez cette interface à la classe d'extension et la méthode n'a besoin que d'être implémentée dans la classe d'extension. Je peux me tromper ici - je n'utilise pas les interfaces aussi souvent que je pourrais / devrais.

J'ai toujours pensé aux interfaces comme un modèle pour les développeurs externes ou un ensemble de règles supplémentaire pour garantir que les choses sont correctes.


9
En PHP, les interfaces contiennent uniquement la déclaration de méthode et non l'implémentation réelle. Les classes abstraites, cependant, vous permettent «d'ajouter du code» aux méthodes à hériter par les classes qui l'étendent. Je crois que cette différence est ce à quoi mk faisait référence.
nocash

13

Vous utiliserez des interfaces en PHP:

  1. Pour masquer l'implémentation - établissez un protocole d'accès à une classe d'objets et modifiez l'implémentation sous-jacente sans refactoriser tous les endroits où vous avez utilisé ces objets
  2. Pour vérifier le type - comme pour vous assurer qu'un paramètre a un type spécifique $object instanceof MyInterface
  3. Pour appliquer la vérification des paramètres lors de l'exécution
  4. Pour implémenter plusieurs comportements dans une seule classe (créer des types complexes)

    classe Car implémente EngineInterface, BodyInterface, SteeringInterface {

de sorte qu'un Carobjet peut maintenant start(), stop()(EngineInterface) ou goRight(), goLeft()(Interface de direction)

et d'autres choses auxquelles je ne peux pas penser en ce moment

Le numéro 4 est probablement le cas d'utilisation le plus évident que vous ne pouvez pas aborder avec des classes abstraites.

De Penser en Java:

Une interface dit: «Voici à quoi ressembleront toutes les classes qui implémentent cette interface particulière.» Ainsi, tout code qui utilise une interface particulière sait quelles méthodes peuvent être appelées pour cette interface, et c'est tout. L'interface est donc utilisée pour établir un «protocole» entre les classes.


10

Les interfaces existent non pas comme une base sur laquelle les classes peuvent s'étendre mais comme une carte des fonctions requises.

Voici un exemple d'utilisation d'une interface dans laquelle une classe abstraite ne convient pas:
disons que j'ai une application de calendrier qui permet aux utilisateurs d'importer des données de calendrier à partir de sources externes. J'écrirais des classes pour gérer l'importation de chaque type de source de données (ical, rss, atom, json) Chacune de ces classes implémenterait une interface commune qui garantirait qu'elles ont toutes les méthodes publiques communes dont mon application a besoin pour obtenir les données.

<?php

interface ImportableFeed 
{
    public function getEvents();
}

Ensuite, lorsqu'un utilisateur ajoute un nouveau flux, je peux identifier le type de flux qu'il s'agit et utiliser la classe développée pour ce type pour importer les données. Chaque classe écrite pour importer des données pour un flux spécifique aurait un code complètement différent, sinon il pourrait y avoir très peu de similitudes entre les classes en dehors du fait qu'elles sont requises pour implémenter l'interface qui permet à mon application de les consommer. Si je devais utiliser une classe abstraite, je pourrais très facilement ignorer le fait que je n'ai pas outrepassé la méthode getEvents () qui casserait alors mon application dans ce cas, alors que l'utilisation d'une interface ne laisserait pas mon application s'exécuter si l'une des méthodes définies dans l'interface n'existent pas dans la classe qui l'a implémentée. Mon application n'a pas à se soucier de la classe qu'elle utilise pour obtenir les données d'un flux,

Pour aller plus loin, l'interface se révèle extrêmement utile lorsque je reviens à mon application de calendrier avec l'intention d'ajouter un autre type de flux. L'utilisation de l'interface ImportableFeed signifie que je peux continuer à ajouter plus de classes qui importent différents types de flux en ajoutant simplement de nouvelles classes qui implémentent cette interface. Cela me permet d'ajouter des tonnes de fonctionnalités sans avoir à ajouter inutilement de volume à mon application principale, car mon application principale ne dépend que des méthodes publiques disponibles dont l'interface a besoin, tant que mes nouvelles classes d'importation de flux implémentent l'interface ImportableFeed, puis je sais que je peux simplement le laisser en place et continuer à bouger.

Ce n'est qu'un début très simple. Je peux ensuite créer une autre interface que toutes mes classes d'agenda peuvent être tenues d'implémenter et qui offre plus de fonctionnalités spécifiques au type de flux géré par la classe. Un autre bon exemple serait une méthode pour vérifier le type de flux, etc.

Cela va au-delà de la question, mais puisque j'ai utilisé l'exemple ci-dessus: les interfaces sont livrées avec leur propre ensemble de problèmes si elles sont utilisées de cette manière. Je me retrouve à avoir besoin d'assurer la sortie qui est retournée par les méthodes implémentées pour correspondre à l'interface et pour y parvenir j'utilise un IDE qui lit les blocs PHPDoc et ajoute le type de retour comme indice de type dans un bloc PHPDoc de l'interface qui sera ensuite traduire dans la classe concrète qui l'implémente. Mes classes qui consomment les données de sortie des classes qui implémentent cette interface sauront alors au moins qu'elles attendent un tableau renvoyé dans cet exemple:

<?php
interface ImportableFeed 
{
    /**
     * @return array
     */
    public function getEvents();
}

Il n'y a pas beaucoup de place pour comparer les classes abstraites et les interfaces. Les interfaces sont simplement des cartes qui, une fois implémentées, nécessitent que la classe ait un ensemble d'interfaces publiques.


Bonne explication :) Merci!
Razvan.432

Juste pour ajouter des informations: dans la classe abstraite, si vous déclarez la méthode comme abstraite, elle se comporte comme une interface, vous ne pouvez donc pas ignorer le fait que vous n'avez pas remplacé getEvents (). L'application échouerait de la même manière qu'avec l'interface.
Rikudou_Sennin

8

Les interfaces ne sont pas seulement destinées à garantir que les développeurs implémentent certaines méthodes. L'idée est que parce que ces classes sont garanties d'avoir certaines méthodes, vous pouvez utiliser ces méthodes même si vous ne connaissez pas le type réel de la classe. Exemple:

interface Readable {
  String read();
}

List<Readable> readables; // dunno what these actually are, but we know they have read();
for(Readable reader : readables)
  System.out.println(reader.read());

Dans de nombreux cas, il n'est pas logique de fournir une classe de base, abstraite ou non, car les implémentations varient énormément et ne partagent rien en plus de quelques méthodes.

Les langages typés dynamiquement ont la notion de "typage de canard" où vous n'avez pas besoin d'interfaces; vous êtes libre de supposer que l'objet possède la méthode que vous appelez. Cela contourne le problème dans les langages typés statiquement où votre objet a une méthode (dans mon exemple, read ()), mais n'implémente pas l'interface.


6

À mon avis, les interfaces devraient être préférées aux classes abstraites non fonctionnelles. Je ne serais pas surpris s'il y aurait même un impact sur les performances, car il n'y a qu'un seul objet instancié, au lieu d'analyser deux, de les combiner (bien que, je ne peux pas être sûr, je ne suis pas familier avec le fonctionnement interne de OOP PHP).

Il est vrai que les interfaces sont moins utiles / significatives que, par exemple, Java. D'un autre côté, PHP6 introduira encore plus d'indications de type, y compris des indications de type pour les valeurs de retour. Cela devrait ajouter de la valeur aux interfaces PHP.

tl; dr: interfaces définit une liste de méthodes qui doivent être suivies (pensez à l'API), tandis qu'une classe abstraite donne des fonctionnalités de base / communes, que les sous-classes affinent à des besoins spécifiques.


PHP 6 ne sera jamais publié. PHP 6 était un projet en cours de développement de 2005 à 2010, mais il a été retardé et finalement annulé. PHP 7 est la prochaine version, principalement pour éviter toute confusion avec l'ancien projet PHP 6.
Ashu Jha

5

Je ne me souviens pas si PHP est différent à cet égard, mais en Java, vous pouvez implémenter plusieurs interfaces, mais vous ne pouvez pas hériter de plusieurs classes abstraites. Je suppose que PHP fonctionne de la même manière.

En PHP, vous pouvez appliquer plusieurs interfaces en les séparant par une virgule (je pense, je ne trouve pas cela une solution propre).

En ce qui concerne plusieurs classes abstraites, vous pouvez avoir plusieurs résumés s'étendant les uns aux autres (encore une fois, je ne suis pas totalement sûr de cela, mais je pense avoir déjà vu cela quelque part auparavant). La seule chose que vous ne pouvez pas prolonger est une classe finale.


4

Les interfaces ne donneront à votre code aucune amélioration des performances ou quelque chose comme ça, mais elles peuvent grandement contribuer à le rendre maintenable. Il est vrai qu'une classe abstraite (ou même une classe non abstraite) peut être utilisée pour établir une interface avec votre code, mais les interfaces appropriées (celles que vous définissez avec le mot-clé et qui ne contiennent que des signatures de méthode) sont tout simplement plus faciles à trier et lire.

Cela étant dit, j'ai tendance à faire preuve de discrétion pour décider d'utiliser ou non une interface sur une classe. Parfois, je veux des implémentations de méthodes par défaut, ou des variables qui seront communes à toutes les sous-classes.

Bien sûr, le point sur la mise en œuvre de plusieurs interfaces est également valable. Si vous avez une classe qui implémente plusieurs interfaces, vous pouvez utiliser un objet de cette classe sous différents types dans la même application.

Le fait que votre question concerne PHP, cependant, rend les choses un peu plus intéressantes. Taper sur les interfaces n'est toujours pas incroyablement nécessaire en PHP, où vous pouvez à peu près tout alimenter n'importe quelle méthode, quel que soit son type. Vous pouvez taper statiquement des paramètres de méthode, mais certains d'entre eux sont cassés (String, je crois, provoque quelques hoquets). Ajoutez à cela le fait que vous ne pouvez pas taper la plupart des autres références, et il n'y a pas beaucoup de valeur à essayer de forcer la saisie statique en PHP ( à ce stade ). Et à cause de cela, la valeur des interfaces en PHP , à ce stadeest beaucoup moins que dans les langues plus fortement typées. Ils ont l'avantage de la lisibilité, mais pas grand-chose d'autre. L'implémentation multiple n'est même pas bénéfique, car vous devez toujours déclarer les méthodes et leur donner des corps dans l'implémenteur.


2

Voici les points pour l'interface PHP

  1. Il est utilisé pour définir le nombre requis de méthodes dans la classe [si vous souhaitez charger du code HTML, l'ID et le nom sont requis. Dans ce cas, l'interface inclut setID et setName].
  2. L'interface force strictement la classe à inclure toutes les méthodes qui y sont définies.
  3. Vous ne pouvez définir une méthode qu'en interface avec l'accessibilité publique.
  4. Vous pouvez également étendre l'interface comme une classe. Vous pouvez étendre l'interface en php en utilisant le mot-clé extend.
  5. Étendez plusieurs interfaces.
  6. Vous ne pouvez pas implémenter 2 interfaces si les deux partagent une fonction avec le même nom. Cela générera une erreur.

Exemple de code:

interface test{
    public function A($i);
    public function B($j = 20);
}

class xyz implements test{
    public function A($a){
        echo "CLASS A Value is ".$a;
    }
    public function B($b){
        echo "CLASS B Value is ".$b;
    }
}
$x = new xyz();
echo $x->A(11);
echo "<br/>";
echo $x->B(10);

2

Nous avons vu que les classes abstraites et les interfaces sont similaires en ce qu'elles fournissent des méthodes abstraites qui doivent être implémentées dans les classes enfants. Cependant, ils présentent toujours les différences suivantes:

1.Les interfaces peuvent inclure des méthodes abstraites et des constantes, mais ne peuvent pas contenir de méthodes et de variables concrètes.

2.Toutes les méthodes de l'interface doivent être dans la portée de la visibilité publique .

3.Une classe peut implémenter plusieurs interfaces, alors qu'elle ne peut hériter que d'une seule classe abstraite.

                                  interface                      abstract class
the code                     - abstract methods               - abstract methods
                             - constants                      - constants                  
                                                              - concrete methods
                                                              - concrete variables

access modifiers             
                             - public                         - public
                                                              - protected
                                                              - private
                                                                etc.
number of parents          The same class can implement
                           more than 1 interface              The child class can 
                                                              inherit only from 1 abstract class

J'espère que cela vous aidera à comprendre!


2

Les interfaces sont comme vos gènes.

Les cours abstraits sont comme vos vrais parents.

Leurs objectifs sont héréditaires, mais dans le cas des classes abstraites vs interfaces, ce qui est hérité est plus spécifique.


2

Je ne connais pas les autres langues, quel est le concept d'interface là-bas. Mais pour PHP, je ferai de mon mieux pour l'expliquer. Soyez patient et veuillez commenter si cela a aidé.

Une interface fonctionne comme un "contrat", spécifiant ce que fait un ensemble de sous-classes, mais pas comment elles le font.

La règle

  1. Une interface ne peut pas être instanciée.

  2. Vous ne pouvez implémenter aucune méthode dans une interface, c'est-à-dire qu'elle ne contient que la signature de la méthode mais pas les détails (corps).

  3. Les interfaces peuvent contenir des méthodes et / ou des constantes, mais aucun attribut. Les constantes d'interface ont les mêmes restrictions que les constantes de classe. Les méthodes d'interface sont implicitement abstraites.

  4. Les interfaces ne doivent pas déclarer de constructeurs ou de destructeurs, car ce sont des détails d'implémentation au niveau de la classe.

  5. Toutes les méthodes d'une interface doivent avoir une visibilité publique.

Prenons maintenant un exemple. Supposons que nous ayons deux jouets: l'un est un chien et l'autre est un chat.

Comme nous le savons, un chien aboie et un chat miaule.Ces deux ont la même méthode de parole, mais avec des fonctionnalités ou une mise en œuvre différentes. Supposons que nous donnons à l'utilisateur une télécommande dotée d'un bouton de prise de parole.

Lorsque l'utilisateur appuie sur le bouton Parler, le jouet doit parler, peu importe qu'il s'agisse d'un chien ou d'un chat.

C'est un bon cas pour utiliser une interface, pas une classe abstraite car les implémentations sont différentes. Pourquoi? Rappelles toi

Si vous devez prendre en charge les classes enfants en ajoutant une méthode non abstraite, vous devez utiliser des classes abstraites. Sinon, les interfaces seraient votre choix.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.