146 votes

des traits en php – n’importe quel monde réel exemples/meilleures pratiques ?

Des traits ont été un des ajouts plus importants pour PHP 5.4. Je sais que la synatax et de comprendre l’idée derrière les traits, comme re-utilisation de code horizontale pour des trucs communs comme l’exploitation forestière, sécurité, etc. mise en cache.

Cependant, je ne sais toujours pas encore comment je ferais utilisent des traits dans mes projets.

Y a-t-il des projets open source qui utilisent déjà des traits ? Tout bon articles/lecture matériel sur la façon d’architectures de structure à l’aide de caractères ?

197voto

Gordon Points 156415

Je pense qu'on serait à rechercher dans les langues qui ont des Traits pour un certain temps maintenant d'apprendre les Bonnes/Meilleures pratiques. Mon opinion sur le Trait est que vous ne devriez les utiliser pour le code que vous devez dupliquer dans d'autres classes que de partager la même fonctionnalité.

Exemple pour un Enregistreur de trait:

interface Logger
{
    public function log($message, $level);    
}

class DemoLogger implements Logger
{
    public function log($message, $level)
    {
        echo "Logged message: $message with level $level", PHP_EOL; 
    }
}

trait Loggable // implements Logger
{
    protected $logger;
    public function setLogger(Logger $logger)
    {
        $this->logger = $logger;
    }
    public function log($message, $level)
    {
        $this->logger->log($message, $level);
    }
}

class Foo implements Logger
{
    use Loggable;
}

Et puis vous n' (démo)

$foo = new Foo;
$foo->setLogger(new DemoLogger);
$foo->log('It works', 1);

Je suppose que la chose importante à considérer lors de l'utilisation de traits, c'est qu'ils sont vraiment juste des morceaux de code qui sont copiés dans la classe. Cela peut facilement conduire à des conflits, par exemple, lorsque vous essayez de modifier la visibilité des méthodes, par exemple

trait T {
    protected function foo() {}
}
class A { 
    public function foo() {}
}
class B extends A
{
    use T;
}

Ci-dessus entraînera une erreur (de démonstration). De même, toutes les méthodes déclarées dans le trait qui le sont déjà déclarés dans l'utilisation de la classe ne sera pas copiée dans la classe, par exemple

trait T {
    public function foo() {
    return 1;
}
}
class A { 
    use T;
    public function foo() {
    return 2;
}
}

$a = new A;
echo $a->foo();

imprime 2 (démo). Ce sont des choses que vous voulez éviter, car ils font des erreurs difficiles à trouver. Vous voulez aussi éviter de mettre des choses en traits qui opèrent sur des propriétés et des méthodes de la classe qui l'utilise, par exemple

class A
{
    use T;
    protected $prop = 1;
    protected function getProp() {
        return $this->prop;
    }
}

trait T
{
    public function foo()
    {
        return $this->getProp();
    }
}

$a = new A;
echo $a->foo();

œuvres (démo) mais maintenant, le trait est intimement liée à l'Un et à l'ensemble de l'idée à l'horizontale réutilisation est perdu.

Lorsque vous suivez la Ségrégation Interface Principe , vous aurez de nombreuses petites classes et d'interfaces. Qui rend les Traits d'un candidat idéal pour les choses que vous avez mentionnées, par exemple, des préoccupations intersectorielles, mais pas de composer des objets (dans un structurelle sens). Dans notre Enregistreur exemple ci-dessus, le trait est complètement isolé. Il n'a pas de dépendances sur les classes de béton.

Nous pourrions utiliser l'agrégation/composition (comme on l'a montré ailleurs sur cette page) pour atteindre la même classe, mais l'inconvénient de l'utilisation de l'agrégation/composition, c'est que nous devrons ajouter le proxy/délégataire méthodes manuellement à chaque classe alors que ce devrait être en mesure de se connecter. Les Traits de résoudre ce bien en me permettant de garder le standard en un seul endroit et de manière sélective l'appliquer en cas de besoin.

Remarque: étant donné que les traits sont un nouveau concept en PHP, toute opinion exprimée ci-dessus est sujette à changement. J'ai pas eu beaucoup de temps pour évaluer le concept de moi-même encore. Mais j'espère qu'il est assez bon pour vous donner quelque chose à penser.

87voto

NikiC Points 47270

Mon opinion personnelle est qu'il y a en fait très peu de demande pour des caractères lors de l'écriture de code propre.

Au lieu d'utiliser des traits de hack code dans une classe, il est mieux de passer dans les dépendances via le constructeur ou par l'intermédiaire de setters:

class ClassName {
    protected $logger;

    public function __construct(LoggerInterface $logger) {
        $this->logger = $logger;
    }
    // or
    public function setLogger(LoggerInterface $logger) {
        $this->logger = $logger;
    }
}

La principale raison pour laquelle je trouve que c'est mieux que l'aide de traits, c'est que votre code est beaucoup plus souple en retrait de la dur de couplage à un trait. Par exemple, vous pouvez tout simplement passer à un autre enregistreur de classe. Cela rend votre code réutilisable et testable.

19voto

D. Marti Points 77

:) Je n'aime pas à théoriser et de débat sur ce qui doit être fait avec quelque chose. Dans ce cas, les traits. Je vais vous montrer ce que je trouve traits utiles et vous pouvez apprendre de lui, ou de l'ignorer.

Traits - ils sont parfaits pour appliquer des stratégies. Stratégie de modèles de conception, en bref, sont utiles lorsque vous souhaitez les mêmes données à manipuler (de filtrer, trier, etc) de façon différente.

Par exemple, vous avez une liste de produits que vous souhaitez filtrer en fonction de certains critères (les marques, les spécifications, peu importe), ou triés par différents moyens (prix, étiquette, peu importe). Vous pouvez créer un tri trait de caractère qui contient des fonctions différentes pour les différents types de tri (numérique, chaîne, date, etc). Vous pouvez ensuite utiliser ce trait non seulement dans votre classe de produit (comme dans l'exemple), mais également dans d'autres classes qui ont besoin de stratégies similaires (pour appliquer un tri numérique à certaines données, etc).

Essayez-le:

<?php
trait SortStrategy {
    private $sort_field = null;
    private function string_asc($item1, $item2) {
        return strnatcmp($item1[$this->sort_field], $item2[$this->sort_field]);
    }
    private function string_desc($item1, $item2) {
        return strnatcmp($item2[$this->sort_field], $item1[$this->sort_field]);
    }
    private function num_asc($item1, $item2) {
        if ($item1[$this->sort_field] == $item2[$this->sort_field]) return 0;
        return ($item1[$this->sort_field] < $item2[$this->sort_field] ? -1 : 1 );
    }
    private function num_desc($item1, $item2) {
        if ($item1[$this->sort_field] == $item2[$this->sort_field]) return 0;
        return ($item1[$this->sort_field] > $item2[$this->sort_field] ? -1 : 1 );
    }
    private function date_asc($item1, $item2) {
        $date1 = intval(str_replace('-', '', $item1[$this->sort_field]));
        $date2 = intval(str_replace('-', '', $item2[$this->sort_field]));
        if ($date1 == $date2) return 0;
        return ($date1 < $date2 ? -1 : 1 );
    }
    private function date_desc($item1, $item2) {
        $date1 = intval(str_replace('-', '', $item1[$this->sort_field]));
        $date2 = intval(str_replace('-', '', $item2[$this->sort_field]));
        if ($date1 == $date2) return 0;
        return ($date1 > $date2 ? -1 : 1 );
    }
}

class Product {
    public $data = array();

    use SortStrategy;

    public function get() {
        // do something to get the data, for this ex. I just included an array
        $this->data = array(
            101222 => array('label' => 'Awesome product', 'price' => 10.50, 'date_added' => '2012-02-01'),
            101232 => array('label' => 'Not so awesome product', 'price' => 5.20, 'date_added' => '2012-03-20'),
            101241 => array('label' => 'Pretty neat product', 'price' => 9.65, 'date_added' => '2012-04-15'),
            101256 => array('label' => 'Freakishly cool product', 'price' => 12.55, 'date_added' => '2012-01-11'),
            101219 => array('label' => 'Meh product', 'price' => 3.69, 'date_added' => '2012-06-11'),
        );
    }

    public function sort_by($by = 'price', $type = 'asc') {
        if (!preg_match('/^(asc|desc)$/', $type)) $type = 'asc';
        switch ($by) {
            case 'name':
                $this->sort_field = 'label';
                uasort($this->data, array('Product', 'string_'.$type));
            break;
            case 'date':
                $this->sort_field = 'date_added';
                uasort($this->data, array('Product', 'date_'.$type));
            break;
            default:
                $this->sort_field = 'price';
                uasort($this->data, array('Product', 'num_'.$type));
        }
    }
}

$product = new Product();
$product->get();
$product->sort_by('name');
echo '<pre>'.print_r($product->data, true).'</pre>';
?>

En conclusion, je pense à propos de traits comme des accessoires (ce que je peux utiliser pour modifier mes données). Les mêmes méthodes et des propriétés qui peut se couper de mes cours et les mettre en un seul endroit, pour un entretien facile, plus courte et plus propre code.

4voto

thaddeusmt Points 8012

Je suis très heureux pour les Traits, car il permet de résoudre un problème commun lors de l'élaboration des extensions de la plateforme de commerce électronique Magento. Le problème se produit lorsque les extensions d'ajouter des fonctionnalités à une classe de base (comme par exemple le modèle de l'Utilisateur) en l'élargissant. Ceci est fait en pointant le Zend autochargeur (via un fichier de configuration XML) pour utiliser le modèle de l'Utilisateur de l'extension, et ont de ce nouveau modèle d'étendre le modèle de base. (exemple) Mais si deux extensions de remplacer le même modèle? Vous obtenez une "race condition" et que l'on est chargé.

La solution actuelle est de modifier les extensions si on étend l'autre modèle de remplacer classe dans une chaîne, puis définissez la configuration d'extension pour les charger dans le bon ordre, donc l'héritage de la chaîne d'œuvres.

Ce système provoque souvent des erreurs, et lors de l'installation de nouvelles extensions, il est nécessaire de vérifier les conflits et modifier les extensions. C'est une douleur, et rompt le processus de mise à niveau.

Je pense à l'aide de Traits serait un bon moyen de faire la même chose sans cette fâcheuse modèle de remplacer la "race condition". Accordée, il pourrait encore y avoir des conflits si plusieurs Traits de mettre en œuvre des méthodes avec le même nom, mais j'imagine quelque chose comme un simple espace de noms de la convention pourrait régler ce, pour la plupart.

TL;DR je pense que les Traits pourraient être utiles pour la création d'extensions/modules/plugins pour les grandes PHP progiciels comme Magento.

0voto

Nico Points 802

Vous pourriez avoir un trait pour l’objet en lecture seule comme ceci :

Vous pouvez détecter si ce trait est utilisé et déterminer wheter ou pas, vous devriez écrire cet objet dans un fichier de base de données, etc..

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X