Une meilleure architecture alors si (quelque chose) DoIt () else Dont ()

J'essaye de créer un mécanisme qui permettra à l'application de décider (en exécution) s'il faut exécuter certaines fonctionnalités.

"Some Functionality" peut être n'importe quoi, il peut s'agir de c # code qui est contenu dans plusieurs classs dans plusieurs dll, il peut s'agir d'une interface user, il peut s'agir d'une requête de database, etc.

Plus important encore, il devrait s'intégrer dans l'infrastructure actuelle que j'ai, que je ne peux pas re-concevoir et build à partir de zéro.

Plus je pense à cela, il me semble que la seule solution que je puisse utiliser serait de tenir une table qui sera le "référentiel de fonctionnalités" et elle dira (par key unique) si une fonctionnalité est activée / désactivée.

Ensuite, dans le code, je devrai placer dans chaque spot qui gère une telle fonctionnalité une instruction if else.

Par exemple

If(functionalityEnabled)? DoFunctionality() Else DoTheUsusal() 

Y a-t-il un meilleur moyen ou un meilleur design pour l'implémenter? Je voudrais garder la solution aussi simple que possible, mais d'un autre côté, cette solution est vraiment moche et finira par faire que mon code ressemble à du code spaghetti.

Vos pensées seront appréciées, j'utilise c # avec sql server, web api pour les services web.

Modifier:

Je tiens à dire que j'apprécie le time et les efforts de chacun pour répondre à ma question, il y a eu des idées vraiment intéressantes que vous avez soulevées. J'ai finalement marqué la réponse de @dasblinkenlight car elle convenait le mieux aux besoins, bien que d'autres réponses ici soient vraiment bonnes et puissent être utiles aux autres.

Je vous remercie.

Un quip autrefois populaire parmi les programmeurs OO a été que chaque condition du code indique une opportunité manquée de sous-classr. Bien que cette règle soit loin d'être universelle, et qu'elle ne soit pas à la hauteur de la composition, il y a un grain de vérité, surtout quand vous voyez la même condition surgir en plusieurs if différentes methods de la même class.

Une façon courante de gérer cette situation est d'utiliser une combinaison d'inheritance et de composition, et de déplacer la décision vers un seul endroit où votre object est créé.

La manière d'inheritance ressemble à ceci:

 interface Doer { void doSomething(); } class BasicDoer implements Doer { public void doSomething() { ... } } class EnhancedDoer extends BasicDoer { public void doSomething() { base.doSomething(); ... } } // At construction time: Doer doer; if (someCondition) doer = new BasicDoer(); else doer = new EnhancedDoer(); 

La composition ressemble à ceci:

 interface Doer { void doSomething(); } // Create several implementations of Activity, then... // At construction time: List<Doer> doers = new ArrayList<>(); if (someCondition1) doers.add(new SomeKindOfDoer()); if (someCondition2) doers.add(new AnotherKindOfDoer()); if (someCondition3) doers.add(new YetAnotherKindOfDoer()); 

Maintenant, au lieu d'un if vous faites ceci:

 for (Doer d : doers) { d.doSomething(); } 

Si vous avez deux classs implémentant la même interface, votre application peut appeler la fonctionnalité (methods, propriétés) de la class sans savoir exactement si elle appelle la fonctionnalité de base ou la fonctionnalité alternative:

 IFunctionalityX { DoIt(); } class BasicFunctionalityX: IFunctionalityX { public DoIt() { // Default behaviour goes here } } class PluginFunctionalityX: IFunctionalityX { public DoIt() { // Alternative functionality. } } 

Si PluginFunctionalityX partage des parties de son implémentation avec BasicFunctionalityX, vous pouvez en hériter de l'autre, mais que ce soit ou non n'a pas vraiment d'importance. Tant que vous utilisez l'interface, c'est ce qui count, et vous pouvez utiliser cette méthode indépendamment du fait que les classs soient liées ou non.

Dans l'initialisation de votre programme, vous pouvez prendre la décision une fois et créer une instance de la bonne class. Vous pouvez stocker cette class dans un conteneur contenant toutes vos fonctionnalités. FunctionalityX est une propriété de l'interface IFunctionalityX, et vous pouvez créer d'autres interfaces (et propriétés) pour d'autres fonctionnalités.

 if (functionalityXEnabled) { FunctionalityContainer.FunctionalityX = new PluginFunctionality(); } else { FunctionalityContainer.FunctionalityX = new BasicFunctionality(); } 

Ensuite, dans le rest de votre application, vous pouvez appeler votre fonctionnalité via:

 FunctionalityContainer.FunctionalityX.DoIt(); 

Au lieu de l'implémenter de toutes pièces, vous pouvez utiliser une bibliothèque d' dependency injections , comme Unity . Cela vous permet également d'get plus facilement une instance de la bonne fonctionnalité au moment où vous en avez besoin sans avoir à tout créer au début de votre programme, et sans écrire de code constructor complexe pour toutes les fonctionnalités.

Vous souhaitez répartir votre code différemment au moment de l'exécution en fonction d'un paramètre de configuration. Les conditions et le polymorphism sont deux façons de le faire.

Conditionnels

Lors de l'exécution, vérifiez les valeurs à l'aide de if , switch ou d'autres methods de search. Vous faites déjà cela.

 if (configFile.cloudAccount == null) { saveFileToDisk(); } else saveFileToCloud(); 

Avantages

  • Ils sont conditionnels, vous ne pouvez pas vraiment éviter d'en faire un à un moment quelconque dans un projet de développement non sortingvial

Désavantages

  • Les faire à chaque moment de votre application serait douloureux, cependant. Donc, ils sont mieux combinés avec d'autres stratégies pour minimiser leur utilisation

Polymorphisme

Lorsque vous chargez votre application, lisez le file de configuration et construisez les composants de votre application en conséquence:

 interface IFileSaver { /* Used to save files in your application */ } class DiskSaver : IFileSaver { /* The default file saving class */ } class CloudSaver : IFileSaver { /* If they've configured a cloud account */ } // EXAMPLE USE int Main (...) { // Setup your application, load a config file. // You'll need to check the config with a conditional // here (uh oh) but other components of your application // will just use the IFileSaver interface if (configFile.cloudAccount != null) { YourApplication.FileSaver = new CloudSaver(configFile.cloudAccount); } else { YourApplication.FileSaver = new DiskSaver(); } } // Somewhere else in your application void SaveCurrentDocument() { // No if's needed, it was front loaded when initialising // the application YourApplication.FileSaver.Save(); } 

Avantages

  • Convient parfaitement à la design orientée object
  • Toutes vos vérifications de configuration sont à chargement frontal. Après le chargement dans les classs correctes le rest de votre programme les utilisera, inconscients de leur implémentation réelle. Pour cette raison, vous n'avez pas besoin de faire des vérifications dans votre code.
  • Le compilateur sera capable de vérifier statiquement les erreurs de type dans votre approche

Désavantages

  • Seulement aussi flexible que l'interface de votre class. Peut-être que vous voulez des étapes supplémentaires et des vérifications à effectuer avec un CloudSaver , ils feraient mieux de s'intégrer dans l'interface pré-existante; sinon, ils n'arriveront pas.

Les scénarios courts vous permettent d'exécuter les vérifications de manière explicite chaque fois que vous en avez besoin. En principe, vous bénéficiez d'une grande souplesse de procédure. Par exemple, la routine SaveAs doit peut-être save les files légèrement différemment de la routine Save . Cependant, comme vous l'avez identifié, cela conduit à un code répétitif long. Dans ces cas, structurer votre code pour utiliser le polymorphism pourrait aider.

De toute façon, vous aurez certainement besoin d'avoir un certain nombre de controls conditionnels partout où il y a de la flexibilité dans votre application.

Note: Il y a beaucoup d' autres façons de réaliser des vérifications de configuration à l'exécution, je ne fais que souligner les plus courantes (et généralement simples)

Si c'est juste une seule condition, alors vous n'avez pas d'autre choix que d'utiliser if else et est parfait pour des conditions uniques.

Si vous avez plus de 1 condition, vous pouvez penser à utiliser l'instruction Switch.

Dans la mesure où vous craignez que votre code ne soit compliqué avec l'instruction if else, placez votre code dans les fonctions,

 if(condition) { DoThis(); } else { DoSomethingElse(); } 

Peut-être que quelque chose de similaire au model de design de stratégie (encapsulation de comportement) le rendra plus maniable si la fonctionnalité n'exige pas beaucoup d'interaction avec datatables d'object (bien que l'interaction soit possible). Avantages: code extensible lisible, contre: beaucoup de code.

 namespace SomethingLikeStrategy { public interface Behaviour { void doThis(); void changeM(ref int m); void doThat(); } public class BehaviourOriginal : Behaviour { public void doThis() { Console.WriteLine("foo"); } public void changeM(ref int m) { m = 20; } public void doThat() { throw new Exception("not implemented"); } } public class BehaviourSpecial : Behaviour { public void doThis() { Console.WriteLine("bar"); } public void changeM(ref int m) { m = 10; } public void doThat() { throw new Exception("not implemented"); } } public class MyClass { Behaviour mBehaviour; int mM = 0; public MyClass() { mBehaviour = new BehaviourOriginal(); } public void setSpecialBehaviour(bool special) { if (special) { mBehaviour = new BehaviourSpecial(); } else { mBehaviour = new BehaviourOriginal(); } } public void doThis() { mBehaviour.doThis(); } public void doThat() { mBehaviour.doThat(); } public void changeM() { mBehaviour.changeM(ref mM); } public void printM() { Console.WriteLine(mM); } } class Program { public static void Main(ssortingng[] args) { MyClass myClass = new MyClass(); myClass.doThis(); myClass.setSpecialBehaviour(true); myClass.doThis(); myClass.setSpecialBehaviour(false); myClass.printM(); myClass.changeM(); myClass.printM(); myClass.setSpecialBehaviour(true); myClass.changeM(); myClass.printM(); Console.Write("Press any key to continue . . . "); Console.ReadKey(true); } } }