Interfaces and Traits

Unit: 8 of 18

In previous lessons, we gave a brief introduction to interfaces and traits, along with abstract classes. The reason why these three concepts are connected lies precisely in the fact that all three represent a new level, that is, a higher level of abstraction within object-oriented programming. In each of the three cases, the class remains at the usual level of abstraction, while abstract classes, interfaces, and traits sit above it and help define exactly what the class will use. In most cases, how it will be used for definition and implementation (polymorphism) is left to the class itself.

Since we have already familiarized ourselves in detail with abstract classes, which we said represent a template for creating templates, it is time to learn more about interfaces and traits, which also influence how a template is created (class).


INTERFACE

When in object-oriented programming we talk about interfaces, we are actually talking about a concept that allows us to define which methods a class that uses that interface must implement.

In fact, I said the same thing about abstract classes: in them we define some abstract methods that the class that inherits the abstract class must implement. However, there are some differences between interfaces and abstract classes in using and working with them, so we’ll mention that first.

The first and main difference between interfaces and abstract classes is that a class can only inherit from one abstract class, while at the same time, a class can use multiple interfaces simultaneously. In fact, it makes interfaces reusable and allows us to divide them into specific segments and functionalities. Say, if we had a User class, in separate interfaces we could define which methods will be used to authenticate and register users in one interface, while in another interface we could define which methods we would use to get users, editing users etc. When we later add an Administrator class, for example, then we could reuse the same interfaces that we used for the User class.

Let’s look at an example, where we have both the abstract class and the interfaces,  and notice how easy it actually is for us  to separate the functionality by segments, or wholes, and then insert them all into the class itself.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
<?php
interface iUser1{
    public function login();
    public function register();
}
interface iUser2{
    public function get();
    public function set();
    public function edit();
}
class UserTemplate implements iUser1, iUser2{
    public $type;
    public function login(){
        return $this->type . " is logged in! <br>";
    }
    public function register(){
        return $this->type . " is registered! <br>";
    }
    public function get(){
        return $this->type . " data is here! <br>";
    }
    public function set(){
        return $this->type . " data is saved! <br>";
    }
    public function edit(){
        return $this->type . " is edited! <br>";
    }
}
class User extends UserTemplate{
    public function __construct(){
        $this->type = "User";
    }
}
class Admin extends UserTemplate{
    public function __construct(){
        $this->type = "Admin";
    }
}
$user = new User();
echo $user->login();
echo $user->register();
echo $user->set();
echo $user->get();
echo $user->edit();
echo "<br><br>";
$admin = new Admin();
echo $admin->login();
echo $admin->register();
echo $admin->set();
echo $admin->get();
echo $admin->edit();

We see that interfaces are created with the keyword interface , where after that keyword we just specify the name of the interface, the braces inside which we will list the methods that the class will have to use later, i.e. implement them. As we can see, methods can immediately specify which input parameters will be required during implementation, which the class must respect and use. It is important to note once again that the class does not get to choose which methods it implements and which it does not, but must implement all the methods specified in the interface.

We also see that when it comes to interfaces, only using the interface within the class is done using the implements keyword , as opposed to inheritance when we have the extends keyword . We also see that by specifying a comma after implements and the name of the first interface, we can specify a virtually unlimited number of interfaces that the class can use.

Here we see that method implementation is nothing more than defining an identical method inside a class with an identical number of parameters (if any) and then specifying the logic that will do what the method does (this is the actual implementation, i.e. the part that doesn’t had within the interface).

Here it is important to mention some more of the differences between abstract classes and interfaces to fully explain this difference and further clarify what interfaces are actually for.

  • Interfaces cannot have properties or fields, whereas abstract classes can.
  • All methods listed in the interface must be public, while methods in abstract classes can be public or protected.
  • All methods listed in the interface are automatically abstract, so we don’t need to specify the abstract keyword  as we do in abstract classes and their methods.
  • All methods listed in the interface cannot have an implementation inside them (remember that in abstract classes we could have implemented non-abstract methods, but regular methods).
  • A class can implement one or more interfaces at the same time as it inherits from another class (this means we can do both inheritance and interface implementation in a single line of code, which can be practical).

If we summarize everything we have said here, we can see that interfaces actually serve as our starting point when creating an object-oriented application. With them and their methods we make a kind of blueprint of what classes and objects will look like, what kind of interaction they will have, and we group functionalities according to their similarities and differences.

Of course, we could do the same with abstract classes, but here we don’t have this freedom of grouping functionality by similarities and differences into several separate interfaces, that is, the ability to use one or more interfaces in different classes (emphasis on multiple interfaces simultaneous). Because of this, interfaces look more attractive in those initial stages of an object-oriented application and lay the foundation and a good foundation for later work.

Traits

Now that we’ve covered abstract classes and interfaces, it’s time to deal with traits in PHP. I said about traits that they solve one of the problems that I didn’t even see existed, but that started to appear at some point during the work, which is that a class can only inherit from one class. This problem is partially solved by using interfaces, but the problem with interfaces is precisely that we don’t have to have methods defined and implemented.

Unlike this, a class can use multiple traits without any problems , and  within them can have methods defined and implemented,  as well as properties, fields, along with properties and static methods of those abstract methods . Basically, a lot of what we know and understand about classes in object-oriented programming can be applied to traits.

Let’s first look at an example of how we can create a trait and use it in a class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?php
trait user_function{
    public function login(){
        return $this->type . " is logged in!";
    }
    public function register(){
        return $this->type . " is registered!";
    }
}
class User{
    public $type;
    use user_function;
    public function __construct($type){
        $this->type = $type;
    }
}
$user1 = new User("User");
echo $user1->login();
echo "<br><br>";
$user2 = new User("Admin");
echo $user2->login();

We see that the trait itself is created using the trait keyword and then specifying the trait name,  we use it in the class using the use keyword  Basically, if we compare this to class inheritance, the word trait has replaced the word class, while the keyword use has replaced the keyword extends. The rest is pretty self-explanatory and is part of what we’ve already learned before: creating fields, creating methods, creating a class, calling fields and methods in the object created by the class.

To really understand the true power of traits here, it’s important to look at an example that will involve creating multiple traits, using multiple traits in the class, instantiating the class, and calling everything we’ve defined in life through several objects.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
<?php
trait user_function{
    public function login(){
        return $this->type . " is logged in! <br>";
    }
    public function register(){
        return $this->type . " is registered! <br>";
    }
}
trait user_function2{
    public function get(){
        return $this->type . " data is here! <br>";
    }
    public function set(){
        return $this->type . " data is saved! <br>";
    }
    public function edit(){
        return $this->type . " is edited! <br>";
    }
}
class User{
    public $type;
    use user_function;
    use user_function2;
    public function __construct($type){
        $this->type = $type;
    }
}
$user1 = new User("User");
echo $user1->login();
echo $user1->get();
echo "<br><br>";
$user2 = new User("Admin");
echo $user2->login();
echo $user2->get();

Here it is interesting to note that, unlike inheritance, when two objects have static members and both have access to the same value, in the static properties obtained through traits, each of the objects has a separate value in the static properties, which means basically that if we change the static property in one object, that change will not reflect on another object (which would happen when we have inheritance), which we can also see in the example itself.

Finally, we’ll mention here one more interesting definition of why we need traits and where to use them: if we copy code that is identical from one class inside another class, then we have a great candidate for a thread that will ensure the same code in both classes and is only written in one place.

Of course, you can also read about traits in the official PHP documentation:

https://www.php.net/manual/en/language.oop5.traits.php


Exercise 08.01

Modify the exercise we had in the previous lesson so that instead of an abstract class, it has an interface where we will define an info method that will print all the information from the classes that will use this interface.

In real life define everything that every laptop has generically: manufacturer, model, price.

After that, create two concrete classes (let’s say the MicrosoftLaptop and AppleLaptop classes).

In those classes, you inherit the generic laptop class and define some specifications for these two classes.

For example, Microsoft Surface laptops have touchscreens, while Apple laptops have touchbars.

Instantiate these classes and print all the information we have about them.

Of course, you use the constructor as well as the print method.

 

Note:

You can see in the repository of this course an example solution for this exercise or at the following link:

https://github.com/amarbeslija/php-development

 

What problem do traits solve?

Leave a Reply

Your email address will not be published. Required fields are marked *