La tâche consiste à configurer un élément matériel au sein de l'appareil, selon certaines spécifications d'entrée. Ceci devrait être réalisé comme suit:
1) Collectez les informations de configuration. Cela peut se produire à différents moments et à différents endroits. Par exemple, le module A et le module B peuvent tous deux demander (à des moments différents) des ressources à mon module. Ces «ressources» sont en fait ce qu'est la configuration.
2) Une fois qu'il est clair qu'aucune autre requête ne sera réalisée, une commande de démarrage, donnant un résumé des ressources demandées, doit être envoyée au matériel.
3) Ce n'est qu'après cela que la configuration détaillée desdites ressources peut (et doit) être effectuée.
4) Aussi, seulement après 2), le routage des ressources sélectionnées vers les appelants déclarés peut (et doit) être effectué.
Une cause fréquente de bugs, même pour moi, qui ai écrit la chose, est de confondre cet ordre. Quelles conventions de dénomination, conceptions ou mécanismes puis-je utiliser pour rendre l'interface utilisable par quelqu'un qui voit le code pour la première fois?
la source
discovery
ouhandshake
?Réponses:
C'est une refonte, mais vous pouvez empêcher l'utilisation abusive de nombreuses API mais ne pas avoir de méthode à ne pas appeler.
Par exemple, au lieu de
first you init, then you start, then you stop
Votre constructeur
init
est un objet qui peut être démarré etstart
crée une session qui peut être arrêtée.Bien sûr, si vous avez une restriction à une session à la fois, vous devez gérer le cas où quelqu'un essaie d'en créer une avec une déjà active.
Appliquez maintenant cette technique à votre propre cas.
la source
zlib
etjpeglib
sont deux exemples qui suivent ce modèle pour l'initialisation. Pourtant, de nombreuses documentations sont nécessaires pour enseigner le concept aux développeurs.Vous pouvez demander à la méthode de démarrage de renvoyer un objet qui est un paramètre obligatoire à la configuration:
Même si votre
MySession
est juste une structure vide, cela renforcera par la sécurité de type qu'aucuneConfigure()
méthode ne peut être appelée avant le démarrage.la source
module->GetResource()->Configure(nullptr)
?a, b, c, d
, je peux commencera
et l'utiliserMySession
pour essayer de l'utiliserb
comme un objet déjà commencé, alors qu'en réalité ce n'est pas le cas.S'appuyant sur la réponse de Cashcow - pourquoi devez-vous présenter un nouvel objet à l'appelant, alors que vous pouvez simplement présenter une nouvelle interface? Rebrand-Pattern:
Vous pouvez également laisser ITerminateable implémenter IRunnable, si une session peut être exécutée plusieurs fois.
Votre objet:
De cette façon, vous ne pouvez appeler que les bonnes méthodes, car vous ne disposez que de l'interface IStartable au début et la méthode run () ne sera accessible que lorsque vous aurez appelé start (); De l'extérieur, il ressemble à un modèle avec plusieurs classes et objets, mais la classe sous-jacente reste une classe, qui est toujours référencée.
la source
Il existe de nombreuses approches valides pour résoudre votre problème. Basile Starynkevitch a proposé une approche «zéro bureaucratie» qui vous laisse avec une interface simple et repose sur le programmeur utilisant correctement l'interface. Bien que j'apprécie cette approche, je vais en présenter une autre qui a plus d'ingénierie mais permet au compilateur de détecter certaines erreurs.
Identifier les différents états de votre appareil peut être, comme
Uninitialised
,Started
,Configured
et ainsi de suite. La liste doit être finie.¹Pour chaque état, définissez une
struct
réserve contenant les informations supplémentaires nécessaires concernant cet état, par exempleDeviceUninitialised
,DeviceStarted
etc.Emballez tous les traitements dans un seul objet
DeviceStrategy
où les méthodes utilisent des structures définies en 2. comme entrées et sorties. Ainsi, vous pouvez avoir uneDeviceStarted DeviceStrategy::start (DeviceUninitalised dev)
méthode (ou quel que soit l'équivalent selon les conventions de votre projet).Avec cette approche, un programme valide doit appeler certaines méthodes dans la séquence imposée par les prototypes de méthodes.
Les différents états sont des objets indépendants, c'est à cause du principe de substitution. S'il vous est utile que ces structures partagent un ancêtre commun, rappelez-vous que le modèle de visiteur peut être utilisé pour récupérer le type concret de l'instance d'une classe abstraite.
Alors que j'ai décrit dans 3. une
DeviceStrategy
classe unique , il y a des situations où vous voudrez peut-être diviser la fonctionnalité qu'il fournit en plusieurs classes.Pour les résumer, les points clés de la conception que j'ai décrite sont:
En raison du principe de substitution, les objets représentant des états de périphérique doivent être distincts et ne pas avoir de relations d'héritage spéciales.
Emballez les traitements de périphérique dans des objets de startegy plutôt que dans les objets représentant des périphériques eux-mêmes, de sorte que chaque périphérique ou état de périphérique ne se voit que lui-même, et la stratégie les voit tous et exprime les transitions possibles entre eux.
Je jurerais avoir vu une fois une description d'une implémentation de client Telnet suivant ces lignes, mais je n'ai pas pu la retrouver. Cela aurait été une référence très utile!
¹: Pour cela, suivez votre intuition ou trouvez les classes d'équivalence des méthodes dans votre implémentation réelle pour la relation «méthode₁ ~ méthode₂ ssi. il est valable de les utiliser sur le même objet »- en supposant que vous avez un gros objet encapsulant tous les traitements sur votre appareil. Les deux méthodes de listage des états donnent des résultats fantastiques.
la source
Utilisez un modèle de générateur.
Avoir un objet qui a des méthodes pour toutes les opérations que vous avez mentionnées ci-dessus. Cependant, il n'effectue pas ces opérations tout de suite. Il se souvient simplement de chaque opération pour plus tard. Étant donné que les opérations ne sont pas exécutées immédiatement, l'ordre dans lequel vous les transmettez au générateur n'a pas d'importance.
Après avoir défini toutes les opérations sur le générateur, vous appelez une
execute
méthode -met. Lorsque cette méthode est appelée, elle exécute toutes les étapes répertoriées ci-dessus dans le bon ordre avec les opérations que vous avez enregistrées ci-dessus. Cette méthode est également un bon endroit pour effectuer des contrôles d'intégrité couvrant plusieurs opérations (comme essayer de configurer une ressource qui n'a pas encore été configurée) avant de les écrire sur le matériel. Cela pourrait vous éviter d'endommager le matériel avec une configuration absurde (au cas où votre matériel serait sensible à cela).la source
Il vous suffit de documenter correctement la façon dont l'interface est utilisée et de donner un exemple de didacticiel.
Vous pouvez également avoir une variante de bibliothèque de débogage qui effectue des vérifications d'exécution.
Peut-être définir et documenter correctement certaines conventions de nommage (par exemple
preconfigure*
,startup*
,postconfigure*
,run*
....)BTW, beaucoup d'interfaces existantes suivent un modèle similaire (par exemple les kits d'outils X11).
la source
Il s'agit en effet d'une erreur courante et insidieuse, car les compilateurs ne peuvent appliquer que des conditions de syntaxe, alors que vous avez besoin que vos programmes clients soient "grammaticalement" corrects.
Malheureusement, les conventions de dénomination sont presque entièrement inefficaces contre ce type d'erreur. Si vous voulez vraiment encourager les gens à ne pas faire de choses non grammaticales, vous devez distribuer un objet de commande d'une sorte qui doit être initialisé avec des valeurs pour les conditions préalables, afin qu'ils ne puissent pas effectuer les étapes dans le désordre.
la source
En utilisant ce modèle, vous êtes sûr que tout implémenteur s'exécutera dans cet ordre exact. Vous pouvez aller plus loin et créer un ExecutorFactory qui construira des exécuteurs avec des chemins d'exécution personnalisés.
la source
step1(); step2(); step3();
. Le but du générateur d'étapes est de fournir une API qui expose certaines étapes et d'appliquer la séquence dans laquelle elles sont appelées. Cela ne devrait pas empêcher un programmeur de faire d'autres choses entre les étapes.