When should I use Inheritance, Abstract Class, Interface or a Trait?

28

Since PHP 5.4 we have Trait , which "are mechanisms that help (and much) reuse code, and serve perfectly to solve the problem of lack of multiple inheritance."

Example of Abastrata Class

abstract class AbstractUser
{
    abstract public function getLanguage();
}


class PtUser extends AbstractUser
{
    public function getLanguage()
    {
        return 'pt_br';
    }
}

Example with Traits and Interfaces

class PtUser implements UserLang
{
    use TraitUserLang;
}

interface UserLang
{
    public function getLanguage();
}

trait TraitUserLang
{
    public function getLanguage()
    {
        return 'pt_br';
    }

}

For both cases, I could do something like:

$user = new PtUser;

// caso 1
if ($user instanceof AbstractUser) {
    $user->getLanguage();
}
// caso 2
if ($user instanceof UserLang) {
      $user->getLanguage();
}

There came some confusion in my mind:

  • When should I use an abstract class, or a simple inheritance?

  • When should I use trait, followed by an interface implemented in the class that will use it (according to some recommendations I read the internet out)?

  • Why do you say not recommending to use trait without implementing interface ?

asked by anonymous 08.07.2015 / 18:07

2 answers

32

Traits can be seen almost as an automation of Ctrl+C and Ctrl+V . This definition may seem coarse, but in fact traits can be quite useful if used carefully in specific situations (more on this below).

It is recommended that you continue using interfaces to define contracts (such as project documentation) and abstract classes to implement the base tag of these contracts, leaving some more specific for their daughter classes to implement. Anyway, if the class hierarchy for your project is important, keep doing it that way.

What about traits?

They definitely do not serve to define contracts. Traits completely ignore the class hierarchy. Use them to define very specific behaviors that can be reused by different types of objects. Among other things, they are very useful for classes that implement contracts that are repeated several times.

For example, imagine that you have the following base classes:

  • Player
  • Vehicle
  • Weapon
  • Enemy
  • Scenario

And the following child classes:

  • Rafael extends Player
  • Car% with% Vehicle
  • Motor% of% Vehicle
  • Magnum% with% Weapon
  • AR-15% with% Weapon
  • Knife% with% Weapon
  • Soldier extends Enemy
  • Monster extends Enemy
  • Water extends Scenario
  • Fire extends Scenario
  • ...
  • Water Tank% with% Weapon or Vehicle ?? What if he can be controlled? Are you a Player too? Look! It can also take damage!

In such a case, traits would be useful for defining very specific methods and properties that can be used by several objects of different types. For example:

//Pode ser type-hinted
Interface RecebeDano {
    protected $vida;
    public function foiAcertado($dano);
}

Interface CausaDano {
    protected $dano;
    public function acertou(RecebeDano $alvo);
}

//Pode ser usado pelas classes Jogador (com as mãos),
//Arma (todas), TanqueDeGuerra e Fogo (item do cenário)
Trait ImplementsCausaDano {
    protected $dano = 10; //Pode ser sobrescrito pela classe
    public function acertou(RecebeDano $alvo) {
        $alvo->foiAcertado($this->dano);
    }
}
  

"Ah, dude, but this I can do with abstract class!"

Well, let's take it easy. You can only extend a single abstract class. That is, it clearly defines a parent-child relationship. If you want to force class hierarchy, beauty. However, if you need a more flexible structure (like the tank example above), traits are the best way.

Important:

The methods of traits are evaluated as if they had been defined in the class that uses them, that is: it has priority over a method of a parent class with the same name.

The only entity that can override a method of a trait is the class itself that uses it, or another trait that has the same method. If the class that uses traits does not explicitly define which method prevails, this will cause a conflict.

In this case, PHP offers the following syntax for solving them:

class Classe {
    use TraitA, TraitB {
        TraitA::metodoDuplicado insteadof TraitB;
    }
}

Finally, if you want to remember what each structure is meant to be, you can look at the semantics of the commands you write:

Interface you implements:

  

It is a contract. She can not implement anything, just define, but what she   define you have to follow.

Abstract class you extends: (inherits)

  

It's a father. It will help you in whatever you need to constitute your base, however some things you will have to do yourself.

Trait you uses:

  

It is a tool. You can use it for better or for worse. "With big powers come big responsabilities." (Ben, Uncle)

    
04.09.2015 / 06:08
9

I will not go in to do documentation because each case is more than documented, I will just try to pass on my experience in my answer.

Use depends on implementation and what you want to achieve.

A classe abstract I use a lot when I want to set a default or a mecanismo where I can have code reuse. The class that extends is the basis for implementing the associated code. That is: Defining a contract with some implicit logic.

An interface I use when I want to define a contract that a particular mechanism must use to work the same, and especially to identify it as the type of interface it implements. Of course the option happens when I do not need to define a contract with associated logic in regards to code, with the exception of constants.

It's common, but again it depends a lot on what you want, to use a classe abstract with a interface at the same time. However this is an implementation decision, and for example with instanceof you can know about both.

As for traits and not wanting to use settings I transcribe:

  

Traits are a mechanism for code reuse in single inheritance languages   such as PHP. Trait is intended to reduce some limitations of single   inheritance by enabling a developer to reuse sets of methods freely in   several independent classes living in different class hierarchies.

That is, it is another mechanism by which we can define a contract with associated code and that is oriented towards a new metadology that is trying to enter term in PHP.

When using traits I have got some conflicts because when we use traits different and if one of the methods has the same name in both, you will get error which forces me to a declaration of very precise names and in large projects is usually common as well as a problem.

It is also important to say that you can not directly instantiate any of the mechanisms without implementing them in classes that extend or implement them.

    
08.07.2015 / 18:57