Cechy a Interfejsy

Ostatnio próbowałem studiować na PHP, i znalazłem się zawieszony na cechach. Rozumiem pojęcie ponownego użycia kodu poziomego i nie chcę koniecznie dziedziczyć z klasy abstrakcyjnej. Nie rozumiem, jaka jest zasadnicza różnica między używaniem cech a interfejsami?

Próbowałem szukać przyzwoitego posta na blogu lub artykułu wyjaśniającego, kiedy używać jednego lub drugiego, ale przykłady, które znalazłem do tej pory, wydają się tak podobne identyczne.

Czy ktoś mógłby podzielić się swoją opinią/poglądem na ten temat?

Author: Rahil Wazir, 2012-02-09

13 answers

Interfejs definiuje zestaw metod, które Klasa implementująca musi zaimplementować.

Gdy cecha jest use ' d implementacje metod też się pojawiają--co nie zdarza się w Interface.

To jest największa różnica.

From the Horizontal Reuse for PHP RFC :

Traits jest mechanizmem ponownego użycia kodu w językach dziedziczenia, takich jak PHP. Cecha ma na celu zmniejszenie pewnych ograniczeń pojedynczego dziedziczenia przez umożliwienie programiście swobodnego używania zestawów metod w kilku niezależnych klasach żyjących w różnych hierarchiach klas.

 201
Author: Alec Gorge,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-19 22:41:56

Ogłoszenie O Zamówieniu Publicznym:

Chcę powiedzieć, że uważam, że cechy są prawie zawsze zapachem kodu i należy ich unikać na rzecz kompozycji. Moim zdaniem pojedyncze dziedziczenie jest często nadużywane do tego stopnia, że jest anty-wzorcem, a wielokrotne dziedziczenie tylko wiąże ten problem. Będziesz znacznie lepiej służył w większości przypadków, faworyzując kompozycję nad dziedziczeniem (czy to pojedynczym czy wielokrotnym). Jeśli nadal interesują Cię cechy i ich związek z interfejsami, Czytaj dalej ...


Zacznijmy od tego:

Programowanie obiektowe (OOP) może być trudnym paradygmatem do zrozumienia. To, że używasz klas, nie oznacza, że Twój kod jest Obiektowe (oo).

Aby napisać kod OO musisz zrozumieć, że OOP tak naprawdę chodzi o możliwości Twoich obiektów. Trzeba myśleć o zajęciach w kategoriach tego, co oni can do zamiast czego oni faktycznie tak. Jest to w przeciwieństwie do tradycyjnego programowania proceduralnego, w którym nacisk kładzie się na zrobienie odrobiny kodu "zrób coś"."

Jeśli kod OOP dotyczy planowania i projektowania, interfejs jest schematem, a obiekt jest w pełni zbudowanym domem. Tymczasem cechy są po prostu sposobem na pomoc w budowie domu rozplanowanego przez plan (interfejs).

Interfejsy

Dlaczego więc powinniśmy używać interfejsów? Po prostu interfejsy sprawiają, że nasz kodeks jest mniej kruchy. Jeśli wątpisz w to stwierdzenie, zapytaj każdego, kto został zmuszony do zachowania starszego kodu, który nie został napisany przeciwko interfejsom.

[16]}interfejs jest umową pomiędzy programistą a jego kodem. Interfejs mówi: "tak długo, jak grasz według moich zasad, możesz mnie zaimplementować, jak chcesz i obiecuję, że nie złamię twojego innego kodu." Jako przykład rozważmy scenariusz w świecie rzeczywistym (bez samochodów i widgetów):]}

Chcesz zaimplementować system buforowania dla aplikacji webowej do cięcia down on server load

Zaczynasz od napisania klasy do Cache request responses używając APC:

class ApcCacher
{
  public function fetch($key) {
    return apc_fetch($key);
  }
  public function store($key, $data) {
    return apc_store($key, $data);
  }
  public function delete($key) {
    return apc_delete($key);
  }
}
Następnie, w obiekcie odpowiedzi http, sprawdzasz trafienie pamięci podręcznej przed wykonaniem całej pracy, aby wygenerować rzeczywistą odpowiedź:
class Controller
{
  protected $req;
  protected $resp;
  protected $cacher;

  public function __construct(Request $req, Response $resp, ApcCacher $cacher=NULL) {
    $this->req    = $req;
    $this->resp   = $resp;
    $this->cacher = $cacher;

    $this->buildResponse();
  }

  public function buildResponse() {
    if (NULL !== $this->cacher && $response = $this->cacher->fetch($this->req->uri()) {
      $this->resp = $response;
    } else {
      // build the response manually
    }
  }

  public function getResponse() {
    return $this->resp;
  }
}
To podejście działa świetnie. Ale może kilka tygodni później zdecydujesz, że chcesz użyć systemu pamięci podręcznej opartego na plikach zamiast APC. Teraz musisz zmienić kod kontrolera, ponieważ zaprogramowałeś swój kontroler do pracy z funkcjonalnością klasy ApcCacher, a nie z interfejsem wyrażającym możliwości klasy ApcCacher. Powiedzmy, że zamiast powyższego stworzyłeś Controller klasę opartą na CacherInterface zamiast betonu ApcCacher w ten sposób:
// your controller's constructor using the interface as a dependency
public function __construct(Request $req, Response $resp, CacherInterface $cacher=NULL)

Aby się z tym pogodzić zdefiniuj swój interfejs tak:

interface CacherInterface
{
  public function fetch($key);
  public function store($key, $data);
  public function delete($key);
}

Z kolei masz zarówno swoje ApcCacher, jak i nowe FileCacher klasy zaimplementować CacherInterface i zaprogramować Controller klasę do skorzystaj z możliwości wymaganych przez interfejs.

Ten przykład (miejmy nadzieję) pokazuje, jak programowanie interfejsu pozwala na zmianę wewnętrznej implementacji klas bez martwienia się, czy zmiany zepsują inny kod.

Cechy

Cechy, z drugiej strony, są po prostu metodą ponownego użycia kodu. Interfejsy nie powinny być traktowane jako wzajemnie wykluczająca się alternatywa dla cech. W rzeczywistości, tworzenie cech, które spełniają możliwości wymagane przez interfejs to idealny przypadek użycia.

Powinieneś używać cech tylko wtedy, gdy wiele klas ma tę samą funkcjonalność(prawdopodobnie podyktowaną tym samym interfejsem). Nie ma sensu używać cechy do zapewnienia funkcjonalności dla jednej klasy: to tylko zaciemnia to, co robi Klasa, A lepszy projekt przeniesie funkcjonalność cechy do odpowiedniej klasy.

Rozważ następującą implementację cechy:

interface Person
{
    public function greet();
    public function eat($food);
}

trait EatingTrait
{
    public function eat($food)
    {
        $this->putInMouth($food);
    }

    private function putInMouth($food)
    {
        // digest delicious food
    }
}

class NicePerson implements Person
{
    use EatingTrait;

    public function greet()
    {
        echo 'Good day, good sir!';
    }
}

class MeanPerson implements Person
{
    use EatingTrait;

    public function greet()
    {
        echo 'Your mother was a hamster!';
    }
}

A więcej konkretny przykład: wyobraź sobie, że zarówno twój FileCacher, jak i twój ApcCacher z dyskusji interfejsu używają tej samej metody, aby określić, czy wpis pamięci podręcznej jest przestarzały i powinien zostać usunięty(oczywiście nie jest tak w prawdziwym życiu, ale idź z nim). Możesz napisać cechę i pozwolić obu klasom na użycie jej do wymagań wspólnego interfejsu.

Ostatnie słowo ostrzeżenia: uważaj, aby nie przesadzić z cechami. Często cechy są używane jako podstawa dla słabego projektu, gdy unikalna Klasa implementacje wystarczyłyby. Powinieneś ograniczyć cechy do spełnienia wymagań interfejsu dla najlepszego projektowania kodu.

 452
Author: rdlowrey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-15 20:33:42

A trait jest zasadniczo implementacją PHP mixin i jest w rzeczywistości zestawem metod rozszerzeń, które można dodać do dowolnej klasy poprzez dodanie trait. Metody stają się częścią implementacji tej klasy, ale bez użycia dziedziczenia .

Z podręcznika PHP (moje podkreślenie):

Cechy są mechanizmem ponownego użycia kodu W językach o pojedynczym dziedziczeniu, takich jak PHP. ... Jest dodatkiem do tradycyjnych dziedziczenie i umożliwia horyzontalną kompozycję zachowania, czyli stosowanie członków klasy bez konieczności dziedziczenia.

Przykład:

trait myTrait {
    function foo() { return "Foo!"; }
    function bar() { return "Bar!"; }
}

Z powyższą cechą zdefiniowaną, mogę teraz zrobić co następuje:

class MyClass extends SomeBaseClass {
    use myTrait; // Inclusion of the trait myTrait
}

W tym momencie, kiedy tworzę instancję klasy MyClass, ma ona dwie metody, nazwane foo() i bar() - które pochodzą z myTrait. I-zauważ, że trait-zdefiniowane metody mają już ciało metody-które Interface - zdefiniowane metody nie mogę.

Dodatkowo-PHP, podobnie jak wiele innych języków, używa modelu pojedynczego dziedziczenia - co oznacza, że klasa może wywodzić się z wielu interfejsów, ale nie z wielu klas. Jednak Klasa PHP może mieć wiele inkluzji trait - co pozwala programistom na dołączanie elementów wielokrotnego użytku - tak jak mogłoby być, gdyby zawierała wiele klas bazowych.

Kilka rzeczy do zapamiętania:

                      -----------------------------------------------
                      |   Interface   |  Base Class   |    Trait    |
                      ===============================================
> 1 per class         |      Yes      |       No      |     Yes     |
---------------------------------------------------------------------
Define Method Body    |      No       |       Yes     |     Yes     |
---------------------------------------------------------------------
Polymorphism          |      Yes      |       Yes     |     No      |
---------------------------------------------------------------------

Polimorfizm:

We wcześniejszym przykład, gdzie MyClass extends SomeBaseClass, MyClass jest instancją SomeBaseClass. Innymi słowy, tablica taka jak {[18] } może zawierać instancje MyClass. Podobnie, Jeśli MyClass Rozszerzony IBaseInterface, tablica IBaseInterface[] bases może zawierać instancje MyClass. Nie ma takiej konstrukcji polimorficznej dostępnej z trait - Ponieważ {[4] } jest zasadniczo kodem, który jest kopiowany dla wygody programisty do każdej klasy, która go używa.

Pierwszeństwo:

Jako opis w instrukcji:

Dziedziczony element z klasy bazowej jest nadpisywany przez element wstawiony przez cechę. Porządek pierwszeństwa jest taki, że członkowie bieżącej klasy nadpisują metody Trait, które w zamian nadpisują metody dziedziczone.

Więc - rozważ następujący scenariusz:

class BaseClass {
    function SomeMethod() { /* Do stuff here */ }
}

interface IBase {
    function SomeMethod();
}

trait myTrait {
    function SomeMethod() { /* Do different stuff here */ }
}

class MyClass extends BaseClass implements IBase {
    use myTrait;

    function SomeMethod() { /* Do a third thing */ }
}

Podczas tworzenia instancji MyClass, powyżej, następuje:

  1. The Interface IBase wymaga funkcji bez parametru o nazwie SomeMethod(), aby pod warunkiem.
  2. klasa bazowa BaseClass zapewnia implementację tej metody-spełniającą tę potrzebę.
  3. The trait myTrait zawiera również funkcję bez parametru o nazwie SomeMethod(), , która ma pierwszeństwo nad BaseClass-wersja
  4. The class MyClass udostępnia własną wersję SomeMethod() - która ma pierwszeństwo nad wersją trait.

Wniosek

  1. An Interface nie może dostarczyć domyślnej implementacji ciała metody, podczas gdy trait może.
  2. An Interface jest polimorficznym, dziedziczenie construct-while a trait is not.
  3. wielokrotność Interfaces może być używana w tej samej klasie, a więc może być wielokrotność traits.
 57
Author: Troy Alford,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-05 00:09:20

Myślę, że traits są przydatne do tworzenia klas, które zawierają metody, które mogą być używane jako metody kilku różnych klas.

Na przykład:

trait ToolKit
{
    public $errors = array();

    public function error($msg)
    {
        $this->errors[] = $msg;
        return false;
    }
}

Możesz mieć i używać tej metody "error" w każdej klasie, która używa tej cechy.

class Something
{
    use Toolkit;

    public function do_something($zipcode)
    {
        if (preg_match('/^[0-9]{5}$/', $zipcode) !== 1)
            return $this->error('Invalid zipcode.');

        // do something here
    }
}

Podczas gdy za pomocą interfaces Można zadeklarować tylko podpis metody, ale nie kod jej funkcji. Ponadto, aby użyć interfejsu, musisz postępować zgodnie z hierarchią, używając implements. Tak nie jest w przypadku cech.

Jest zupełnie inaczej!

 22
Author: J. Bruni,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-15 12:11:01

Dla początkujących powyższa odpowiedź może być trudna, jest to najprostszy sposób, aby ją zrozumieć:

Cechy

trait SayWorld {
    public function sayHello() {
        echo 'World!';
    }
}

Więc jeśli chcesz mieć sayHello funkcję w innych klasach bez ponownego tworzenia całej funkcji możesz użyć cech,

class MyClass{
  use SayWorld;

}

$o = new MyClass();
$o->sayHello();

Cool right!

Nie tylko funkcje Można używać czegokolwiek w cechach (funkcja, zmienne, const..). możesz również użyć wielu cech: use SayWorld,AnotherTraits;

Interfejs

  interface SayWorld {
     public function sayHello();
  }

  class MyClass implements SayWorld { 
     public function sayHello() {
        echo 'World!';
     }
}

Tak więc interfejs różni się od cech: musisz odtworzyć wszystko w interfejsie w zaimplementowanej klasie. interfejs nie posiada implementacji. a interfejs może mieć tylko funkcje i const, nie może mieć zmiennych.

Mam nadzieję, że to pomoże!
 6
Author: Supun Praneeth,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-11 05:54:45

często używaną metaforą do opisania cech jest to, że cechy są interfejsami z implementacją.

Jest to dobry sposób myślenia o tym w większości sytuacji, ale istnieje wiele subtelnych różnic między tymi dwoma.

Na początek, operator instanceof nie będzie działał z cechami (tj. cecha nie jest prawdziwym obiektem), więc nie możesz nam sprawdzić, czy klasa ma określoną cechę(lub sprawdzić, czy dwie inne niepowiązane klasy mają tę cechę). To właśnie oni oznacza to, że jest to konstrukcja do ponownego wykorzystania kodu poziomego.

Istnieją Funkcje, które pozwolą ci uzyskać listę wszystkich cech używanych przez klasę, ale dziedziczenie cech oznacza, że będziesz musiał wykonać rekurencyjne kontrole, aby niezawodnie sprawdzić, czy klasa w pewnym momencie ma określoną cechę (przykładowy kod znajduje się na stronach PHP doco). Ale tak, to na pewno nie jest tak proste i czyste jak instanceof jest, a IMHO to funkcja, która uczyniłaby PHP lepszym.

Również klasy abstrakcyjne nadal są klasami, więc nie rozwiązują problemów związanych z wielokrotnym dziedziczeniem kodu. Pamiętaj, że możesz rozszerzyć tylko jedną klasę (rzeczywistą lub abstrakcyjną), ale zaimplementować wiele interfejsów.

Odkryłem, że cechy i interfejsy są naprawdę dobre do użycia ręka w rękę do tworzenia pseudo wielokrotnego dziedziczenia. Eg:

class SlidingDoor extends Door implements IKeyed  
{  
    use KeyedTrait;  
    [...] // Generally not a lot else goes here since it's all in the trait  
}

Oznacza to, że możesz użyć instanceof, aby określić, czy dany obiekt drzwi jest kluczowany, czy nie, wiesz, że otrzymasz spójny zestaw metod itp., a cały kod jest w jednym miejscu we wszystkich klasach, które używają KeyedTrait.

 3
Author: Jon Kloske,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-19 05:21:22

Cechy są po prostu dla code reuse .

Interfejs dostarcza tylko sygnaturę funkcji, które mają być zdefiniowane w klasie, gdzie mogą być używane w zależności od uznania programisty. W ten sposób otrzymujemy prototyp dla Grupa klas.

Dla odniesienia- http://www.php.net/manual/en/language.oop5.traits.php

 3
Author: Rajesh Paul,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-09-12 19:25:16

Cechę można uznać za zautomatyzowaną "kopiuj-wklej" kodu, w zasadzie.

Używanie cech jest niebezpieczne, ponieważ nie ma sposobu, aby wiedzieć, co robi przed wykonaniem.

Jednak cechy są bardziej elastyczne ze względu na brak ograniczeń, takich jak dziedziczenie.

Cechy mogą być przydatne do wprowadzenia metody, która sprawdza coś do klasy, np. istnienie innej metody lub atrybutu. ładny artykuł na ten temat (ale po francusku, przepraszam)

Dla Francuski-czytając ludzi, którzy mogą go zdobyć, GNU / Linux Magazine HS 54 ma artykuł na ten temat.

 2
Author: Benj,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-11-30 17:49:10

Inne odpowiedzi świetnie wyjaśniły różnice między interfejsami i cechami. Skupię się na użytecznym przykładzie świata rzeczywistego, w szczególności na tym, który pokazuje, że cechy mogą używać zmiennych instancji - pozwalając na dodanie zachowania do klasy z minimalnym kodem boilerplate.

Ponownie, jak wspominali inni, cechy dobrze łączą się z interfejsami, pozwalając interfejsowi określić kontrakt zachowania, a cechę spełnić implementację.

Dodawanie zdarzenia możliwość publikowania / subskrybowania klasy może być częstym scenariuszem w niektórych bazach kodu. Istnieją 3 wspólne rozwiązania:

  1. Zdefiniuj klasę bazową z kodem event pub/sub, a następnie klasy, które chcą oferować zdarzenia, mogą ją rozszerzyć, aby zyskać możliwości.
  2. definiuje klasę z kodem pub/sub zdarzenia, a następnie inne klasy, które chcą oferować zdarzenia, mogą używać go za pomocą kompozycji, definiując własne metody do zawijania złożonego obiektu, proxy wywołania metody do to.
  3. Zdefiniuj cechę za pomocą kodu event pub/sub, a inne klasy, które chcą oferować zdarzenia, mogą use cechę, aka zaimportować ją, aby zyskać możliwości.

Jak dobrze działa każdy z nich?

#1 nie działa dobrze. Byłoby, aż do dnia, w którym zdasz sobie sprawę, że nie możesz rozszerzyć klasy bazowej, ponieważ już rozszerzasz coś innego. Nie będę pokazywać tego przykładu, ponieważ powinno być oczywiste, jak ograniczające jest używanie dziedziczenia w ten sposób.

#2 & #3 oba działają dobrze. Pokażę przykład, który podkreśla pewne różnice.

Po Pierwsze, jakiś kod, który będzie taki sam między obydwoma przykładami:

Interfejs

interface Observable {
    function addEventListener($eventName, callable $listener);
    function removeEventListener($eventName, callable $listener);
    function removeAllEventListeners($eventName);
}

I jakiś kod do zademonstrowania użycia:

$auction = new Auction();

// Add a listener, so we know when we get a bid.
$auction->addEventListener('bid', function($bidderName, $bidAmount){
    echo "Got a bid of $bidAmount from $bidderName\n";
});

// Mock some bids.
foreach (['Moe', 'Curly', 'Larry'] as $name) {
    $auction->addBid($name, rand());
}

Ok, teraz pokażmy jak Implementacja klasy Auction będzie się różniła w przypadku używania cech.

Po pierwsze, oto jak #2 (używając kompozycji) wyglądałby:

class EventEmitter {
    private $eventListenersByName = [];

    function addEventListener($eventName, callable $listener) {
        $this->eventListenersByName[$eventName][] = $listener;
    }

    function removeEventListener($eventName, callable $listener) {
        $this->eventListenersByName[$eventName] = array_filter($this->eventListenersByName[$eventName], function($existingListener) use ($listener) {
            return $existingListener === $listener;
        });
    }

    function removeAllEventListeners($eventName) {
        $this->eventListenersByName[$eventName] = [];
    }

    function triggerEvent($eventName, array $eventArgs) {
        foreach ($this->eventListenersByName[$eventName] as $listener) {
            call_user_func_array($listener, $eventArgs);
        }
    }
}

class Auction implements Observable {
    private $eventEmitter;

    public function __construct() {
        $this->eventEmitter = new EventEmitter();
    }

    function addBid($bidderName, $bidAmount) {
        $this->eventEmitter->triggerEvent('bid', [$bidderName, $bidAmount]);
    }

    function addEventListener($eventName, callable $listener) {
        $this->eventEmitter->addEventListener($eventName, $listener);
    }

    function removeEventListener($eventName, callable $listener) {
        $this->eventEmitter->removeEventListener($eventName, $listener);
    }

    function removeAllEventListeners($eventName) {
        $this->eventEmitter->removeAllEventListeners($eventName);
    }
}

Oto jak wyglądałby #3 (cechy) like:

trait EventEmitterTrait {
    private $eventListenersByName = [];

    function addEventListener($eventName, callable $listener) {
        $this->eventListenersByName[$eventName][] = $listener;
    }

    function removeEventListener($eventName, callable $listener) {
        $this->eventListenersByName[$eventName] = array_filter($this->eventListenersByName[$eventName], function($existingListener) use ($listener) {
            return $existingListener === $listener;
        });
    }

    function removeAllEventListeners($eventName) {
        $this->eventListenersByName[$eventName] = [];
    }

    protected function triggerEvent($eventName, array $eventArgs) {
        foreach ($this->eventListenersByName[$eventName] as $listener) {
            call_user_func_array($listener, $eventArgs);
        }
    }
}

class Auction implements Observable {
    use EventEmitterTrait;

    function addBid($bidderName, $bidAmount) {
        $this->triggerEvent('bid', [$bidderName, $bidAmount]);
    }
}

Zauważ, że kod wewnątrz EventEmitterTrait jest dokładnie taki sam jak kod wewnątrz EventEmitter, z wyjątkiem tego, że trait deklaruje metodę triggerEvent() jako chronioną. Więc, jedyną różnicą, na którą musisz spojrzeć, jest implementacja Auction klasy .

A różnica jest duża. Korzystając z kompozycji, otrzymujemy świetne rozwiązanie, które pozwala nam ponownie wykorzystać naszą EventEmitter przez tyle klas, ile chcemy. Ale główną wadą jest to, że mamy dużo kodu kotła, który trzeba pisać i utrzymywać, ponieważ dla każdej metody zdefiniowanej w interfejsie Observable, musimy ją zaimplementować i napisać kod, który tylko przekazuje argumenty do odpowiedniej metody w naszym obiekcie EventEmitter. Użycie cechy w tym przykładzie pozwala nam tego uniknąć , pomagając nam zmniejszyć kod kotła i poprawić konserwację .

Jednak mogą się zdarzyć sytuacje, w których nie chcesz, aby twoja klasa Auction zaimplementowała pełny interfejs Observable - może chcesz ujawnić tylko 1 lub 2 metody, a może nawet żadnej, aby móc zdefiniować własne sygnatury metod. W takim przypadku nadal możesz preferować metodę kompozycji.

Ale ta cecha jest bardzo przekonująca w większości scenariuszy, zwłaszcza jeśli interfejs ma wiele metod, co powoduje, że piszesz wiele boilerplate.

* możesz właściwie zrobić jedno i drugie-zdefiniuj klasę EventEmitter na wypadek, gdybyś kiedykolwiek chciał jej użyć i zdefiniuj EventEmitterTrait trait również, używając implementacji klasy EventEmitter wewnątrz trait:)

 2
Author: goat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-04 05:22:51

Interfejs jest kontraktem, który mówi "Ten obiekt jest w stanie zrobić to coś", podczas gdy cecha daje obiektowi możliwość zrobienia tego.

Cecha jest zasadniczo sposobem "kopiowania i wklejania" kodu między klasami.

Spróbuj przeczytać ten artykuł

 1
Author: Hos Mercury,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-18 01:53:35

Jeśli znasz angielski i wiesz, co oznacza trait, dokładnie tak mówi nazwa. Jest to pakiet bezklasowych metod i właściwości, które można dołączyć do istniejących klas, wpisując use.

Zasadniczo można porównać go do jednej zmiennej. Funkcje zamknięcia mogą use tych zmiennych spoza zakresu i w ten sposób mają wartość wewnątrz. Są potężne i mogą być używane we wszystkim. To samo dzieje się z cechami, jeśli są używane.

 1
Author: Thielicious,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-13 11:37:52

Główna różnica polega na tym, że w przypadku interfejsów musisz zdefiniować rzeczywistą implementację każdej metody w ramach każdej klasy, która implementuje wspomniany interfejs, więc możesz mieć wiele klas implementujących ten sam interfejs, ale z innym zachowaniem, podczas gdy cechy są tylko kawałkami kodu wtryskiwanego do klasy; inną ważną różnicą jest to, że metody cech mogą być tylko metodami klasowymi lub metodami statycznymi, w przeciwieństwie do metod interfejsu, które mogą również (i zazwyczaj są) być metodami instancji.

 0
Author: Alessandro Martin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-09-16 05:40:39

Cecha jest taka sama jak klasa, której możemy użyć do wielu celów dziedziczenia, a także do wielokrotnego użycia kodu.

Możemy użyć trait wewnątrz klasy, a także możemy użyć wielu traitów w tej samej klasie za pomocą 'use keyword'.

Interfejs służy do ponownego użycia kodu tak samo jak cecha

Interfejs rozszerza wiele interfejsów, więc możemy rozwiązać problemy z wieloma dziedziczeniami, ale kiedy zaimplementujemy interfejs, powinniśmy stworzyć wszystkie metody wewnątrz klasy. Na więcej informacji kliknij poniżej link:

Http://php.net/manual/en/language.oop5.traits.php http://php.net/manual/en/language.oop5.interfaces.php

 0
Author: Chirag Prajapati,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-05 17:09:24