Traits and code inclusion
How do traits work?
Traits resemble classes in that they group together code elements under a common name, and with the following syntax:
1 2 3 |
trait TraitName { // The trait's code } |
In the example given below, a trait with the name of Price has a method changePriceByDollars() that calculates the new price from the old price and the price change in dollars.
1 2 3 4 5 6 |
trait Price { public function changePriceByDollars($price, $change) { return $price + $change; } } |
Once we create a trait, we can use it in other classes with the use keyword. In the example given below, both the classes Bmw and Mercedes use the Price trait.
1 2 3 4 5 6 |
class Bmw { use Price; } class Mercedes { use Price; } |
In order to see our code in action, let’s create objects from the classes and then, let’s make use of the changePriceByDollars() method that they got from the trait.
1 2 3 4 5 6 |
$bmw1 = new Bmw(); // Add 3000$ echo $bmw1 -> changePriceByDollars(45000, +3000); $mercedes1 = new Mercedes(); // Subtract 2100$ echo $mercedes1 -> changePriceByDollars(42000, -2100); |
Result:
48000
39900
From this code example, we can understand that we can use the code from the traits inside the classes in pretty much the same way that we can include a block of code into each of the classes.
Is it possible for a class to use more than one trait?
A class can use more than one trait, after all, this is what traits were invented for.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
// The first trait trait Price { public function changePriceByDollars($price, $change) { return $price + $change; } } // The second trait trait SpecialSell { public function annonunceSpecialSell () { return __CLASS__ . " on special sell"; } } // The Mercedes class uses both traits class Mercedes { use Price; use SpecialSell; } $mercedes1 = new Mercedes(); // Subtract 2100$ echo $mercedes1 -> changePriceByDollars(42000, -2100); echo $mercedes1 -> annonunceSpecialSell(); |
Let’s present to our code another trait with the name of SpecialSell that has a method with the name of annonunceSpecialSell, and make the Mercedes class use both traits.
Result:
39900
Mercedes on special sell
How is a trait different from inheritance?
Traits use a special form of inheritance that enables them to include the code from the traits in the classes.
- Traits use a form of inheritance that is known as horizontal inheritance in which the code from the trait is included in the classes in which it is used. It is pretty much like using ‘require’ or ‘include’ in the classes to include code from the outside, albeit not hackish.
- In a trait it is possible to put concrete (real) methods, abstract methods, properties and even constants.
- While the same class can use more than one trait, it can only inherit from one class.
- Traits do not respect the visibility scope, thus allowing a trait’s methods to access private properties and methods in the class that uses them.
In the example given below, the trait’s method changePriceByDollars() is allowed to interact with the private $price property.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
trait Price { // The method needs the $price property. public function changePriceByDollars($change) { return $this -> price += $change; } } class Mercedes { use Price; // The $price is private private $price; public function __construct($price) { $this -> price = $price; } public function getPrice() { return $this -> price; } } $mercedes1 = new Mercedes(42000); echo $mercedes1 -> getPrice(); $mercedes1 -> changePriceByDollars(-2100); echo $mercedes1 -> getPrice(); |
Result:
42000
39900
We can see that the trait’s methods has access to private methods within the classes that use them.
What are the advantages of using traits?
Traits allow us to use code from more than one resource in a class and, by doing so, we are able to circumvent the limitation of single class inheritance.
What are the disadvantages of using traits?
When using a trait, we should be on the lookout for code duplication and for naming conflicts that are the result of calling the methods in different traits with the same name.
Traits may be easily misused, resulting in a code that looks like a patchwork, instead of consisting of well-designed and well-thought of class hierarchies through the use of inheritance.
In which scenarios is it preferable to use traits?
In some cases, the use of traits can save the day and prove to be preferable to using inheritance. Think of a scenario in which number of classes implement the same interface and so share its code, but only a few of the classes (and not all of them) need a certain piece of that code. It is reasonable to use inheritance for those methods that are shared by all of the classes, while leaving it better of to traits to hold the methods that are needed in only some of the classes. For example, if we have three classes that inherit the Car interface (Mercedes, Bmw, and Audi) and we need the method that handles special sell to work in only two of these classes (Mercedes and Bmw), we will use a trait so that the code will be shared only by the classes that need it.
The message to take home is that traits are a tool that allows code inclusion from different resources and so could be quite handy when used appropriately]]>