2017-09-08 2 views
1

Je suis un développeur PHP, et je travaille avec Symfony2 pour le moment.Quel motif de conception dois-je utiliser pour appliquer une méthode commune aux entités?

Je voudrais présenter mon problème comme suit:

  • J'ai 4 entités: l'utilisateur, compte, clients, commerçants. Tous ont un statut.
  • Je veux créer une méthode commune nommée 'isValid' pour eux, mais je ne veux pas modifier leur code.
  • La logique de la méthode est très simple

    <!-- language: php --> 
    public function isValid() 
    { 
        return self::STATUS_ACTIVE == $this->status; 
    } 
    
  • En les séparer et appliquer une HAS-Une relation entre celle-ci avec des entités, je pense que ce sera plus souple et maintenable. Je n'ai pas besoin de dupliquer mon code à aucune entité, même dans le futur.

Si vous avez de l'expérience. Pourriez-vous s'il vous plaît m'aider à choisir un modèle approprié pour cette situation?

+0

Ceci n'est pas une duplication qui devrait vous inquiéter. – svgrafov

Répondre

0

Il n'est pas judicieux de jouer avec cela, l'entité a une valeur de statut, et les setters/getters devraient être dans la déclaration de fichier d'entité. Je comprends le principe DRY mais le faire à ce niveau ... ce n'est pas une bonne idée à mon humble avis.

Mais si vous êtes sûr que tous ont le statut, puis utilisez un trait dans un fichier séparé:

trait CheckStatusTrait{ 
    return $this->status; 
} 

Et vous ajoutez juste le trait à vos classes:

class User { 
    use CheckStatusTrait 
} 
1

Création un has-une relation entre ces entités n'a aucun sens, car ils ne sont pas liés.

Cependant, la duplication de code n'est presque jamais justifiable. Je le résoudrais en créant une interface commune (User est-une entité validable, Customer est-une entité validable) et fais un trait pour encapsuler le comportement commun.

Créer l'interface commune:

interface Validatable 
{ 

    public function isValid(): bool; 

} 

Créer un trait à mettre en œuvre le comportement commun:

trait HasStatus 
{ 

    /** 
    * @var int 
    * @ORM\Column(type="integer") 
    */ 
    private $status; 

    public function isValid(): bool 
    { 
     return $this->status === EntityStatus::STATUS_ACTIVE; 
    } 

} 

Faites vos entités à mettre en œuvre la nouvelle interface et de mettre le trait à utiliser pour éviter les doubles emplois:

class User implements Validatable { 
    use HasStatus; 

} 

Et l'utiliser:

/** @var Validatable[] $validatables */ 
$validatables = [new User(), new Merchant(), new Customer()]; 

foreach ($validatables as $validatable) { 
    var_dump($validatable->isValid()); 
} 

Pourquoi avons-nous besoin de l'interface? Bien techniquement nous n'en avons pas besoin, mais j'aime l'inclure car il permet de se référer à Utilisateur, Client, Marchand avec un typehint "Validatable" commun et il exprime votre intention dans le code.

+0

Peut-être préférable de mettre la constante dans le trait? – COil

+0

@COil qui serait considéré comme un anti modèle [[1] (https://fr.m.wikipedia.org/wiki/Constant_interface)] par certains – Pete

+0

Ok. Mais dans cet exemple, je pense que c'est une surutilisation d'utiliser un tel trait, je ne l'utiliserais que pour une fonction plus complexe. – COil

0

Pour ce type de code simple, il serait bon de le laisser dupliqué dans chaque entité, vous pouvez ajouter une interface avec "isValid" pour les traiter de la même manière de l'extérieur si nécessaire.Peut-être que votre code pour vérifier la validation deviendrait plus complexe et il est logique d'avoir une seule classe qui est responsable. Ensuite, vous pouvez créer une classe StatusValidator qui vérifie si le statut d'un objet donné est valide. Il serait logique d'ajouter une interface à ce genre d'objets, qui a une méthode "getStatus", disons "getStatusInterface". Afterwords vous pouvez injecter le StatusValidator et l'utiliser dans votre méthode isValid. Parce que vos objets sont des entités, vous devez utiliser l'événement postLoad de doctrine pour injecter le StatusValidator.

Vous pouvez également faire la même chose un peu moins complexe en n'injectant pas le StatusValidator mais demandez à l'StatusValidator si l'obejct avec getStatusInterface est valide.

0

Merci à tous pour les supports. J'utilise également le trait pour résoudre la duplication de code. Cependant, je vois que ce trait ne semble pas parfait. J'ai dû faire des inconvénients ci-dessous:

  • Masquer le champ dans un trait rend l'entité énigmatique. En réalité, non seulement le champ status sera disponible sur beaucoup d'entités mais aussi updatedDate, createdDate, type ... Il a aussi l'air désordonné quand une classe utilise beaucoup de traits.
  • Après une période de développement, le trait recueille de plus en plus de méthode, par ex. StatusTrait peut avoir isValid, hasStatus, setStatusDefault. Mais toutes les entités ne les utilisent pas. Je veux dire que certaines entités utilisent isValid, d'autres utilisent setStatusDefault .., donc nous aurons plus de méthodes mixtes dans les traits. Dans ce numéro, je pense que si je peux implémenter une classe de relation HAS-A, je peux facilement les configurer au moment de l'exécution lorsqu'une entité en a besoin.
  • En utilisant trait, la classe d'entité est modifiée, pas l'extension. Ce n'est pas une bonne pratique dans la conception de POO. La classe d'entité n'est pas cohérente avec la table de base de données. Honnêtement, je préfère construire une méthode logique en dehors de la classe d'entité.

Above est ma vision. J'essaie seulement de trouver une meilleure façon de mettre en œuvre ce problème. J'espère que c'est aussi utile pour tout le monde. J'avais entendu parler de ValueObject mais je ne le comprenais pas vraiment. Je vais essayer de comprendre!