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

Asked

Viewed 4,104 times

30

From PHP 5.4 we have the Trait, that "are mechanisms that help (and greatly) the reuse of 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();
}

Then some confusion came to mind:

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

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

  • Why do they say it is not advisable to use a trait without implementing interface?

  • I must implement an interface classe abstrata, since the abstract class itself can already "force" the creation of a method for the class that will extend the abstract class?

  • Complicated this... +1

  • Cool question, I’m too lazy to give a long answer. I don’t know if the problem is having several questions in one, even if related. I think I’d answer if it weren’t so wide.

  • Come on, @bigown. It’s lunchtime, bro!

  • You noticed the downside of trait? http://php.net/manual/en/language.oop5.traits.php#107965

  • Vixi, @Guilhermelautert. Once again a language complication !

  • 2

    I’ve had colleagues who said "Trait is another PHP gambiara, this time to say you have multiple inheritance", I can’t say anything I never needed that kind of inheritance.

Show 2 more comments

2 answers

39


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

It is recommended that you continue using interfaces to define contracts (as a project documentation) and abstract classes to implement the code groundwork of these contracts, leaving some more specific methods for their child classes to implement. Finally, if the hierarchy of classes for your project is important, keep doing it this way.

And as for the traits?

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

For example: imagine you have the following base classes:

  • Gambler
  • Vehicle
  • Gun
  • Enemy
  • Scenario

And the following daughter classes:

  • Rafael extends Gambler
  • Car extends Vehicle
  • Motorbike extends Vehicle
  • Magnum extends Gun
  • AR-15 extends Gun
  • Knife extends Gun
  • Soldier extends Enemy
  • Monster extends Enemy
  • Water extends Scenario
  • Fire extends Scenario
  • ...
  • Tanquedeguerra extends Weapon or Vehicle?? What if it can be controlled? It’s a Player too? Look! It can also take damage!

In a case like this, traits would be useful to define 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, man, but this I can do with abstract class!"

Good, let’s take it easy there. You can only extend a single abstract class. That is, it clearly defines a parent-child relationship. If you want to force the hierarchy of classes, 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, i.e.: it has priority over a method of a parent class with the same name.

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

In this case, PHP offers the following syntax to solve them:

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

Finally, if you want to remember what each structure is intended to be, you can observe the semantics of the commands you write:

Interface you implements:

She’s a contract. She can’t implement anything, just define, but what she defines you has to follow.

Abstract class you stretches: (inherits)

He is a father. He will help you in whatever you need to build your foundation, but some things you will have to do yourself.

Trait you uses:

It is a tool. You can use it for good or for evil. "With great powers come great responsibilities." (Ben, Tio)

  • 6

    +1 Great example and explanation, I will remember this example with certainty.

9

I’m not going to go in for documentation because every case is more than documented, I’m just going to try to pass on my experience in my answer.

Use depends on implementation and what you want to achieve.

One classe abstract I use a lot when I want to define a pattern or a mecanismo where I can reuse code. The class that stretching has the basis for its implementation the associated code. Ie: Define a contract with some implicit logic.

An interface I use when I want to define a contract that a given mechanism has to use for it to work and especially to identify it as the type of interface that implements. It is logical that the option happens when I do not need to define a contract with associated logic with respect to code, with the exception of constants.

It is common, but again depends a lot on the intended, use a classe abstract with a interface at the same time. However this is implementation decision being that and for example with the instanceof may know of both.

As to the traits and not wanting to use definitions:

Traits are a Mechanism for code reuse in single inheritance Languages such as PHP. A 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 more oriented to a new metadology that tries to insert the term in PHP.

In the use of traits I’ve gotten some conflicts because when we use traits and if one of the methods has the same name on both, you will get error which obliges me to a very precise naming declaration and in large projects it is usually common as well as a problem.

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

  • But, still, we can access static methods of traits (which ends up creating some confusion between classes and traits for those who see the code, without seeing the declaration of the class or trait)

  • And you quoted an important point: "the conflict of names between the methods of traits". That is, there is no point in having an interface for the trait, since you would have to rename a method because of a conflict :\

  • @Wallacemaxters yes any of the mechanisms have their questions, the experience is that it defines us... they are there is to use what suits best... I do not say no to anything especially when I impose certain uses... but to traits I have said "no" whenever I can. perhaps by habit.

  • @Wallacemaxters what do you mean "I’d really like a broader point of view"...

Browser other questions tagged

You are not signed in. Login or sign up in order to post.