Traits vs. interfaces

前端 未结 13 1140
傲寒
傲寒 2020-11-28 17:09

I\'ve been trying to study up on PHP lately, and I find myself getting hung up on traits. I understand the concept of horizontal code reuse and not wanting to necessarily in

相关标签:
13条回答
  • 2020-11-28 17:28

    A trait is essentially PHP's implementation of a mixin, and is effectively a set of extension methods which can be added to any class through the addition of the trait. The methods then become part of that class' implementation, but without using inheritance.

    From the PHP Manual (emphasis mine):

    Traits are a mechanism for code reuse in single inheritance languages such as PHP. ... It is an addition to traditional inheritance and enables horizontal composition of behavior; that is, the application of class members without requiring inheritance.

    An example:

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

    With the above trait defined, I can now do the following:

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

    At this point, when I create an instance of class MyClass, it has two methods, called foo() and bar() - which come from myTrait. And - notice that the trait-defined methods already have a method body - which an Interface-defined method can't.

    Additionally - PHP, like many other languages, uses a single inheritance model - meaning that a class can derive from multiple interfaces, but not multiple classes. However, a PHP class can have multiple trait inclusions - which allows the programmer to include reusable pieces - as they might if including multiple base classes.

    A few things to note:

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

    Polymorphism:

    In the earlier example, where MyClass extends SomeBaseClass, MyClass is an instance of SomeBaseClass. In other words, an array such as SomeBaseClass[] bases can contain instances of MyClass. Similarly, if MyClass extended IBaseInterface, an array of IBaseInterface[] bases could contain instances of MyClass. There is no such polymorphic construct available with a trait - because a trait is essentially just code which is copied for the programmer's convenience into each class which uses it.

    Precedence:

    As described in the Manual:

    An inherited member from a base class is overridden by a member inserted by a Trait. The precedence order is that members from the current class override Trait methods, which in return override inherited methods.

    So - consider the following scenario:

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

    When creating an instance of MyClass, above, the following occurs:

    1. The Interface IBase requires a parameterless function called SomeMethod() to be provided.
    2. The base class BaseClass provides an implementation of this method - satisfying the need.
    3. The trait myTrait provides a parameterless function called SomeMethod() as well, which takes precedence over the BaseClass-version
    4. The class MyClass provides its own version of SomeMethod() - which takes precedence over the trait-version.

    Conclusion

    1. An Interface can not provide a default implementation of a method body, while a trait can.
    2. An Interface is a polymorphic, inherited construct - while a trait is not.
    3. Multiple Interfaces can be used in the same class, and so can multiple traits.
    0 讨论(0)
  • 2020-11-28 17:29

    If you know English and know what trait means, it is exactly what the name says. It is a class-less pack of methods and properties you attach to existing classes by typing use.

    Basically, you could compare it to a single variable. Closures functions can use these variables from outside of the scope and that way they have the value inside. They are powerful and can be used in everything. Same happens to traits if they are being used.

    0 讨论(0)
  • 2020-11-28 17:30

    Traits are simply for code reuse.

    Interface just provides the signature of the functions that is to be defined in the class where it can be used depending on the programmer's discretion. Thus giving us a prototype for a group of classes.

    For reference- http://www.php.net/manual/en/language.oop5.traits.php

    0 讨论(0)
  • 2020-11-28 17:33

    I think traits are useful to create classes that contain methods that can be used as methods of several different classes.

    For example:

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

    You can have and use this "error" method in any class that uses this trait.

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

    While with interfaces you can only declare the method signature, but not its functions' code. Also, to use an interface you need to follow a hierarchy, using implements. This is not the case with traits.

    It is completely different!

    0 讨论(0)
  • 2020-11-28 17:34

    You can consider a trait as an automated "copy-paste" of code, basically.

    Using traits is dangerous since there is no mean to know what it does before execution.

    However, traits are more flexible because of their lack of limitations such as inheritance.

    Traits can be useful to inject a method which checks something into a class, for example, the existence of another method or attribute. A nice article on that (but in French, sorry).

    For French-reading people who can get it, the GNU/Linux Magazine HS 54 has an article on this subject.

    0 讨论(0)
  • 2020-11-28 17:35

    An often-used metaphor to describe Traits is Traits are interfaces with implementation.

    This is a good way of thinking about it in most circumstances, but there are a number of subtle differences between the two.

    For a start, the instanceof operator will not work with traits (ie, a trait is not a real object), therefore you can't use that to see if a class has a certain trait (or to see if two otherwise unrelated classes share a trait). That's what they mean by it being a construct for horizontal code re-use.

    There are functions now in PHP that will let you get a list of all the traits a class uses, but trait-inheritance means you'll need to do recursive checks to reliably check if a class at some point has a specific trait (there's example code on the PHP doco pages). But yeah, it's certainly not as simple and clean as instanceof is, and IMHO it's a feature that would make PHP better.

    Also, abstract classes are still classes, so they don't solve multiple-inheritance related code re-use problems. Remember you can only extend one class (real or abstract) but implement multiple interfaces.

    I've found traits and interfaces are really good to use hand in hand to create pseudo multiple inheritance. Eg:

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

    Doing this means you can use instanceof to determine if the particular Door object is Keyed or not, you know you'll get a consistent set of methods, etc, and all the code is in one place across all the classes that use the KeyedTrait.

    0 讨论(0)
提交回复
热议问题