J'ai des doutes sur la mise en œuvre suivante du modèle d'état: p>
J'ai un objet de commande. Pour la simplicité, supposons qu'il ait une quantité, des produits producteurs, des prix et des fournisseurs. En outre, il existe un ensemble d'états connus dans lesquels la commande peut transition: p>
ordre.isvalid () changements entre les états. C'est-à-dire qu'en état, certaines opérations ne peuvent pas être effectuées. Alors, ils ressemblent à: orderprocessor.processor.Process (commande) est un objet qui vérifie l'ordre.isvalid, transition de la commande à certains états, l'enregistre à la base de données et effectuer des actions personnalisées (dans certains états, admin est notifié, dans d'autres la client, etc.). J'en ai un pour chaque état. Une autre option consiste à déplacer toute la logique de validation dans chaque processeur de chaque état, mais je dois maintenant suivre quand la quantité d'une commande a été modifiée pour voir si cette opération est valide dans l'état actuel.
qui laisse l'ordre type d'anémie pour moi. em> p>
li>
ol>
Que pensez-vous les gars? Pouvez-vous me donner des conseils pour mieux concevoir cette chose? P>
Void Séquantitude (int Q) {
si (_state.canchangequantité ())), cela.Quanity = q;
sinon jeter une exception.
}
Est-ce bon, ou devrais-je obtenir chaque État pour mettre en œuvre une opération de séduction? Dans ce cas, où sera stocké la valeur? Dans l'ordre, ou l'état? Dans ce dernier cas, je devrai copier les données dans chaque transition de l'état? Em> p>
li>
Dans StateeaderProcessor, la personne qui vérifie la commande est notifiée par courrier électronique et la commande est transiguée à l'état b.
Maintenant, cela enfonce les transitions de l'état en dehors de la classe d'ordre. Cela signifie que l'ordre a une méthode de «instate», chaque processeur peut donc le changer. Cette chose à changer l'état de l'extérieur ne semble pas bien. Est-ce correct? Em> p>
li>
5 Réponses :
Avez-vous plusieurs classes différentes, une par état.
BaseOrder {
// common getters
// persistence capabilities
}
NewOrder extends BaseOrder {
// setters
CheckingOrder placeOrder();
}
CheckingOrder extends BaseOrder {
CancelledOrder cancel();
PricingOrder assignSupplier();
}
Donc, vous n'utiliseriez pas le modèle d'état? Comment allez-vous accomplir certaines des exigences d'échantillon décrites? Je ne peux pas voir la photo. Merci.
L'essence du modèle d'état (au moins comme je lisais en.wikipedia.org/wiki/state_pattern) Est-ce que les mêmes capacités sont disponibles dans chaque état et l'utilisation de l'héritage pour les différents états rend un sens polymorphe parfait. Vous voulez également des comportements supplémentaires spécifiques à chaque état. Il est logique de les ajouter à ces sous-classes. Je ne vois aucune violation du concept d'état dans l'approche que je décris.
@DJNA: Comme vous le décrivez, vous semblez manquer de l'objet contextuel. Le point du modèle d'état est-il vous permet de transmettre un objet en direct (le contexte) d'un état à un autre sans copier toutes les données à un nouvel objet. Pour ce faire, vous mettez les données dans la classe de contexte, puis de déléguer les opérations à une classe d'État. Cela vous permet de changer l'état après la création d'objets. Sans la classe de contexte, vous avez juste une vieille hiérarchie de héritage unie plutôt que celle de l'état.
@Martin Brown Oui, tu as raison, merci. J'étais occupé à vous concentrer sur la vue du client, où je pense que l'utilisation de classes d'états distinctes est raisonnable. Le pseudo code comme je le présente perd le contexte, ce qui évite de copier des données aux transitions de l'état.
La modification de l'objet d'état actuel peut être effectuée directement à partir d'un objet d'état, de la commande et même d'une source externe (processeur), bien que inhabituelle. P>
Selon le modèle d'état, l'objet de la commande déléguette toutes les demandes à l'objet ordinaire actuel. Si la séquantitation () est une opération spécifique à l'état (c'est dans votre exemple), chaque objet orderstate doit le mettre en œuvre. P>
Donc, si la séparation est une opération spécifique à l'état, la quantité sera enregistrée dans l'état actuel. Lorsque la commande se déplace vers un nouvel état, je pourrai non seulement besoin du contexte (commander) mais le vieil état aussi, pour obtenir la quantité. Est-ce correct? Y a-t-il d'autres façons de faire cela? Merci.
Je dirais que la quantité est un attribut de la commande elle-même. Les classes d'ordinateurs encapsulent un comportement spécifique à l'État. Vous aurez les classes suivantes: Ordre, Ordinerstatea, Orgererstateb, Ordererstatec, ordonnée. Dans votre A. et C. États, la quantité de commande peut être modifiée dans les autres États ne peut pas. Vous pouvez lancer une exception de la méthode de la séduction () dans ces cas.
C'est là que ma question va. Si dans chaque ordettate, j'ai une méthode de la séparation, je vais stocker des données dans chaque état de droite. Maintenant, je pourrais stocker la quantité dans chaque état (données) ou je pourrais vérifier si je peux définir la quantité dans l'ordre (de l'état, le comportement) et soulever une exception. Quelle est la bonne façon d'aller? (désolé si mon commentaire n'était pas clair)
Je stockais la quantité dans la commande et soulever une exception si l'appelant veut changer la quantité dans un état lorsqu'il n'est pas autorisé à.
Pour que le modèle d'état fonctionne, l'objet contextuel doit exposer une interface que les classes d'état peuvent utiliser. Au minimum, cela devra inclure une méthode (1) Avoir une méthode code> Canchangequantitity Code> est probablement mieux que d'avoir tous les états implémenter un (2) La méthode (3) Le point sur la validation soulève la question de la validation. Dans certains cas, il est judicieux de permettre au client de définir des propriétés sur des valeurs non valides et de les valider que lorsque vous effectuez un certain traitement. Dans ce cas, chaque état ayant une méthode «isvalid ()» qui valide tout le contexte a du sens. Dans d'autres cas, vous souhaitez une erreur plus immédiate auquel cas je créerais un changestate (état) code>. Cela a peur que ce soit juste une des limitations du motif et constitue une raison possible pourquoi il n'est pas toujours utile. Le secret de l'utilisation du modèle d'état consiste à maintenir l'interface requise par les états aussi petite que possible et limité à une portée serrée. P>
Séquatisation code>. Si certains États font quelque chose de plus complexe que de jeter une exception, ce conseil peut ne pas suivre. P>
SetState code> est inévitable. Cependant, il devrait être conservé aussi bien que possible que possible. En Java, cela constituerait probablement une portée de paquet, dans .net, ce serait une portée de l'assemblage (interne). P>
isquantaticityvalid (Qté) code> et ispricevalid (prix) code> qui serait appelé par les méthodes définies avant de changer le Valeurs, s'ils retournent Faux lancent une exception. J'ai toujours appelé ces deux deux validations tôt et tardives et il n'est pas facile de dire que vous avez besoin sans en savoir plus sur ce que vous faites. P>
Je stockerais des informations dans la classe d'ordre et transmettrais un pointeur à l'instance de commande à l'état. Quelque chose comme ceci:
Ceci est un scénario idéal pour le modèle d'état.
Dans le modèle d'état, vos classes d'État devraient être responsables de l'état de transition, non seulement en vérifiant la validité de la transition. En outre, la poussée des transitions de l'état en dehors de la classe de commande n'est pas une bonne idée et va à l'encontre du motif, mais vous pouvez toujours travailler avec une classe de chargement de commande. P>
Vous devez obtenir chaque classe d'état pour mettre en œuvre l'opération de séquantitation . La classe d'État devrait mettre en œuvre toutes les méthodes pouvant être valables dans certains États mais pas dans d'autres, que cela implique ou non un changement d'état. p>
Il n'y a pas besoin de méthodes telles que Canchangequantitity () et ISVALID () - Les classes d'état garantissent que vos instances de commande sont toujours dans un état valide, car toute opération qui n'est pas valide pour l'état actuel jettera Si vous l'essayez. p>
Les propriétés de votre classe de commande sont stockées avec la commande, pas l'état. En .NET, vous feriez ce travail en imbriquant vos classes d'État à l'intérieur de la classe d'ordre et en fournissant une référence à la commande lors de la prise d'appels - la classe d'État aura ensuite accès aux membres privés de la commande. Si vous ne travaillez pas dans .net, vous devez trouver un mécanisme similaire pour votre langue - par exemple, des classes d'amis en C ++. P>
Quelques commentaires sur vos états et transitions: P> < ul>
indique une note que la commande est nouvelle, la quantité est> 0 et il a un identifiant de produit. Pour moi, cela signifie que vous fournissez les deux valeurs du constructeur (pour vous assurer que votre instance démarre dans un état valide, mais vous n'auriez pas besoin d'une méthode de séparation), ou si vous avez besoin d'un état initial qui a un assignation (Int32 quantité, int32 productid) méthode qui passera de l'état initial à l'état A. P> li>
De même, vous voudrez peut-être envisager un état de fin de transition à partir de l'état C, après que le fournisseur a rempli le prix. P> li>
Si la transition de votre état nécessite l'affectation de deux propriétés, vous pouvez envisager d'utiliser une seule méthode qui accepte les deux propriétés par paramètre (et non de la séparation suivie d'un ensemble SETProductDID) pour rendre la transition explicite. p> li>
Je suggérerais également plus de noms d'état descriptifs - par exemple, au lieu d'être déclaré, appelez-le annulé. p> li> ul>
Voici un exemple de la manière dont j'étais implémenter ce modèle en C #, sans ajouter de nouveaux états: p> Vous pouvez travailler avec vos classes de processeur de commande , mais ils travaillent avec les méthodes publiques sur la classe d'ordonnance et laissent les cours d'état de la commande garder toute responsabilité pour l'état de transition. Si vous devez savoir quel état vous êtes actuellement (pour permettre au processeur de commande de déterminer quoi faire), vous pouvez ajouter une propriété d'état de chaîne sur la classe de commande et sur la base de base et que chaque classe d'état en béton renvoie son nom. p> p>
Cela semble être une approche judicieuse pour moi. Le seul problème est que les classes d'État étant l'accès par défaut permettent d'écrire des tests d'unité pour eux. Comment approchez-vous cela?
Tests unitaires: Configuration de l'état initial, exécutez API publique pour vérifier le comportement, confirmez que l'état résultant est comme prévu. (Ignorer les classes dont le rôle principal est de faciliter la collaboration ici.) Utilisation de cet exemple, je considère que la classe de commande doit faire partie de l'unité que je teste - le comportement fourni par les classes d'État fait partie intégrante de la commande. Donc, je les teste comme une seule unité.
La classe d'État elle-même extraite essentiellement un comportement de la classe d'ordre qui ne peut être considérée comme isolée de la classe d'ordre - la classe d'état est un comportement de l'ordre, c'est ce qui obtiendra la commande de son état initial à la personne résultante. état suivant une opération.