Vous cherchez des conseils de conception OO

12

Je développe une application qui sera utilisée pour ouvrir et fermer les vannes dans un environnement industriel, et je pensais à quelque chose de simple comme ceci: -

public static void ValveController
{
    public static void OpenValve(string valveName)
    {
        // Implementation to open the valve
    }

    public static void CloseValve(string valveName)
    {
        // Implementation to close the valve
    }
}

(L'implémentation écrirait quelques octets de données sur le port série pour contrôler la vanne - une "adresse" dérivée du nom de la vanne, et soit "1" ou "0" pour ouvrir ou fermer la vanne).

Un autre développeur a demandé si nous devrions plutôt créer une classe distincte pour chaque vanne physique, dont il existe des dizaines. Je suis d'accord qu'il serait plus agréable d'écrire du code PlasmaValve.Open()plutôt que ValveController.OpenValve("plasma"), mais est-ce exagéré?

De plus, je me demandais comment aborder au mieux la conception avec quelques exigences futures hypothétiques à l'esprit: -

  1. On nous demande de prendre en charge un nouveau type de vanne nécessitant différentes valeurs pour l'ouvrir et la fermer (pas 0 et 1).
  2. On nous demande de supporter une valve qui peut être réglée dans n'importe quelle position de 0 à 100, plutôt que simplement "ouverte" ou "fermée".

Normalement, j'utiliserais l'héritage pour ce genre de choses, mais j'ai récemment commencé à me concentrer sur "la composition plutôt que l'héritage" et je me demande s'il y a une solution plus lisse à utiliser en utilisant la composition?

Andrew Stephens
la source
2
Je créerais une classe de valve générique qui a un identifiant pour la valve spécifique (pas une chaîne, peut-être une énumération) et toutes les informations nécessaires pour contrôler le flux à l'intérieur des méthodes OpenValve / CloseValve. Alternativement, vous pouvez rendre la classe valv abstraite et effectuer des implémentations distinctes pour chacune, où la vanne d'ouverture / fermeture appelle simplement la logique à l'intérieur de la classe de vanne donnée pour le cas où différentes vannes ont des mécanismes d'ouverture / fermeture différents. Le mécanisme commun serait défini dans la classe de base.
Jimmy Hoffa du
2
Ne vous inquiétez pas des exigences futures hypothétiques. YAGNI.
pdr
3
@pdr YAGNI est une lame à double tranchant, je conviens que cela vaut la peine de suivre dans le général, mais poussé à l'extrême, on pourrait dire que faire quoi que ce soit pour aider la maintenabilité future ou la lisibilité viole YAGNI, à cause de cela, je trouve la portée de YAGNI trop ambiguë pour beaucoup. Cela dit, beaucoup de gens savent où utiliser YAGNI et où le jeter, car rendre compte de l'avenir vous évitera de sérieuses douleurs. Je pense juste que l'on devrait être prudent en suggérant aux gens de suivre YAGNI lorsque vous ne savez pas où ils vont atterrir sur ce spectre.
Jimmy Hoffa du
2
L'homme, la «composition sur l'hérédité» est surfaite. Je créerais une classe / interface abstraite Valve, puis les sous-classerais dans PlasmaValve. Et puis je m'assurerais que mon ValveController fonctionnerait avec les vannes, sans se soucier de quelle sous-classe elles sont exactement.
MrFox
2
@suslik: Absolument. J'ai également vu un excellent code appelé spaghetti par des gens qui ne comprennent pas les principes SOLID. Nous pourrions continuer indéfiniment avec cela. Mon point est que j'ai vu plus de problèmes causés par le rejet des principes établis (nés d'une expérience de plusieurs années) d'emblée que je n'en ai vu par une sur-adhésion. Mais je conviens que les deux extrêmes sont dangereux.
pdr

Réponses:

12

Si chaque instance de l'objet valve exécutait le même code que ce ValveController, il semble que plusieurs instances d'une même classe seraient la bonne solution. Dans ce cas, il suffit de configurer la valve qu'il contrôle (et comment) dans le constructeur de l'objet valve.

Cependant, si chaque contrôle de vanne a besoin d'un code différent pour fonctionner, et que le ValveController actuel exécute une instruction de commutateur géant qui fait des choses différentes selon le type de vanne, vous avez mal réimplémenté le polymorphisme. Dans ce cas, réécrivez-le dans plusieurs classes avec une base commune (si cela a du sens) et laissez le principe de responsabilité unique être votre guide de conception.

stonemetal
la source
1
+1 pour avoir mentionné les instructions de commutation basées sur le type comme odeur de code. Je vois fréquemment ce genre de déclarations de changement où le développeur prétend qu'il suivait juste KISS. Exemple parfait de la façon dont les principes de conception peuvent être pervertis heh
Jimmy Hoffa
2
Plusieurs instances peuvent également faciliter la liaison des vannes dans une séquence, vous permettant de modéliser la tuyauterie réelle de l'usine sous forme de graphique dirigé dans votre code. Vous pouvez également ajouter une logique métier aux classes, au cas où vous auriez besoin de faire quelque chose comme ouvrir une vanne quand une autre se ferme pour éviter une montée en pression, ou fermer toutes les vannes en aval pour ne pas obtenir un effet de "coup de bélier". lorsque la vanne est rouverte.
TMN
1

Mon principal reproche est d'utiliser des cordes pour le paramètre identifiant la valve.

Créez au moins une Valveclasse qui a getAddresssous la forme les besoins d'implémentation sous-jacents et transmettez-les à la ValveControlleret assurez-vous que vous ne pouvez pas créer de vannes inexistantes. De cette façon, vous n'aurez pas à gérer les chaînes incorrectes dans chacune des méthodes d'ouverture et de fermeture.

Que vous créiez des méthodes pratiques qui appellent l'ouverture et la fermeture ValveControllervous appartient, mais pour être honnête, je garderais toutes les communications vers le port série (y compris l'encodage) dans une seule classe que d'autres classes appelleront en cas de besoin. Cela signifie que lorsque vous devez migrer vers un nouveau contrôleur, vous devez uniquement modifier une classe.

Si vous aimez les tests, vous devez également créer ValveControllerun singleton pour pouvoir vous en moquer (ou créer une machine de formation pour les opérateurs).

monstre à cliquet
la source
Je n'ai jamais vu quelqu'un recommander un singleton à des fins de test auparavant - généralement, il va dans l'autre sens.
Kazark
Honnêtement, le singleton est plus pour éviter la statique et donc la communication peut être synchronisée
Ratchet Freak