J'ai besoin de créer des surcharges pour des fonctions sur une interface existante sans affecter les composants qui implémentent ou utilisent actuellement l'interface (idéalement).
i Figure J'ai quelques options: p>
simplifié
Interface d'origine: EM> P> public interface IServerComponent2 : IServerComponent
{
bool Add(int a, int b, int c);
}
6 Réponses :
Si les nouvelles méthodes peuvent être exprimées en termes d'anciennes méthodes, vous pouvez utiliser des méthodes d'extension: si les méthodes ne peuvent pas être exprimées comme ça (c'est-à-dire qu'ils ont vraiment besoin Pour être mis en œuvre par les composants eux-mêmes), alors je vous recommanderais de définir une nouvelle interface. Cette approche a une compatibilité maximale en arrière. P> p>
Les méthodes d'extension fonctionnent, mais auront également pour effet de diffuser la fonctionnalité de la classe d'exécution entre d'autres classes réduisant la cohésion. Je vois cela comme une solution temporaire au mieux. Dans le cas où vous ne pouvez pas étendre la classe, car vous n'avez pas accès à la source, les méthodes d'extension ont un sens. Je pourrais le faire de cette façon pour des mises à jour mineures à une base de code existante, mais je l'ai certainement placé sur ma liste de refactorings pour la prochaine version majeure.
Cela dépend totalement du design. Si vous ajoutez Nouvelle fonctionnalité i>, alors je serais d'accord; Il appartient à une interface. Si vous ajoutez surcharges i> (par exemple, une fonctionnalité presque identique, toujours expansable en termes d'autres fonctionnalités), alors je suis en désaccord. Dans ce cas, les méthodes d'extension permettent orthogonalité i>, réduisant la duplication de code. Joe Duffy a un bon article de blog sur le sujet.
@Stephen - très intéressant! Cependant, les problèmes d'expédition virtuelle me donnent les Willies. Je ne pense pas que c # soutient cela assez bien pour utiliser dans tous les projets mais les projets les plus simples, mais il suffit de promouvoir trop l'entropie de code (et de menacer de réduire la cohésion, bien que définitivement pas dans des cas simples tels que celui que nous envisageons.)
@Jeff: La post de Joe Duffy prend plus loin que c # vraiment soutient (j'ai Un poteau de blog qui est allongé dans cela aussi bien ). C ++ prend en charge un "polymorphisme statique" complet car il a des modèles; C # manque encore à cet égard. Cependant, orthogonalité i> est toujours possible et important. Est-ce que cela réduit vraiment la cohésion? Je dis "nay". API modernes (LINQ, TPL, RX) Tout définissez une "interface minimale" à mettre en œuvre et fournir la puissance réelle de la bibliothèque en tant que méthodes d'extension. Nous réintroduisons lentement la conception orthogonale de la STL C ++
@Stephen - Il existe des cas où l'ajout de fonctionnalités via des méthodes d'extension augmente la compréhensibilité (voir les extensions HTMLHelper en MVC, par exemple), mais dans le cas général, je préférerais ne pas avoir à naviguer vers d'autres classes pour comprendre une méthode sur la classe I 'm en travaillant avec. J'ai voté la réponse précisément en raison du cas d'utilisation spécifique que vous référence et de votre commentaire sur de nouvelles fonctionnalités, mais je refacteur toujours dans une version majeure pour mettre tout le code de la nouvelle classe au même endroit.
@tvanfosson: Cela dépend de l'arrière-plan du programmeur. Je suis un utilisateur lourde C ++ STL de Way Retour, donc pour moi des méthodes d'extension ne sont que naturelles. Si C # avait un polymorphisme statique complet (ou une autre solution pour le problème de l'envoi virtuel), je dirais alors des méthodes d'extension seraient la meilleure solution. Je vois où tu viens, cependant; La STL a provoqué une bonne quantité de controverse car ce n'était pas classique (basé sur l'héritage) oop. Les programmeurs classiques de l'OOP s'efforceraient de tout placer sur l'interface. La STL utilise des concepts OOP, juste de différentes manières; C'est une évolution du design.
Si votre interface IserverComponent n'a pas encore été expédiée ou une interface interne implémentée par vos propres classes et que les nouveaux membres d'interface ont du sens pour toutes les classes existantes implémentant l'interface, modifiez l'interface existante.
sinon sinon , Créez une interface IserverComponent2 qui étend IserverComponce. IIRC C'est ce que les directives de conception-cadre recommandent. Un exemple de ceci peut être trouvé dans la framework .NET sous forme de x509Certificate2 classe. p>
Toutefois, si les nouveaux membres peuvent être mis en œuvre en termes de membres d'origine, vous pouvez également utiliser Méthodes d'extension : P>
public interface IServerComponent
{
bool Add(int a, int b);
}
public static class ServerComponentExtensions
{
public static bool Add(this IServerComponent isc, int a, int b, int c)
{
return isc.Add(a, b) && isc.Add(b, c) && isc.Add(c, a);
}
}
Cela dépend du contexte - s'il existe de nombreuses classes qui implémentent l'interface d'origine, ou si elle est publiée, le seul moyen pratique est d'introduire le nouveau. D'autre part, avoir une seule interface est beaucoup plus propre à long terme, je dirais donc le refacteur de l'existant si vous pouvez vous le permettre. P>
Si vous n'avez pas besoin ou que vous souhaitez modifier les classes (maintenant ou à l'avenir) qui implémentent déjà l'ancienne interface, vous devez simplement créer une deuxième interface. Cependant, cela ressemble plus à un piratage et peut vous donner un code moins intuitif. p>
Plus, reportez-vous à un refactoring n'est jamais une bonne idée de mon expérience. Donc, si vous soupçonnez que vous devrez mettre en œuvre l'interface plus tard, vous pourriez également simplement modifier l'existant et épargner-vous de maux de tête. C'est vraiment le moyen plus propre de le faire. P>
Je pense que le maintien d'une seule interface est plus facile, mais la deuxième approche avec 2 interface est meilleure car toutes les classes doivent mettre en œuvre bool ajouter (int A, int b, int c); Code> P >
Je réalise que votre exemple est conçu mais je veux vous donner une réponse artificielle différente de ce que vous exprimez afin que vous puissiez y penser d'une manière différente. Au lieu d'avoir votre méthode d'interface fonctionner sur quelque chose de concret, cela pourrait être logique de travailler sur quelque chose de résumé.
Par exemple P> si cela est vraiment un Pensez que cela rend beaucoup plus de sens, mais si cela ressemble vraiment à un calcul (), vous voudriez alors déplacer une partie ou la totalité de cette opération de méthodes vers les objets IADDABLETHINGS. P> public interface IAddableThings
{
int a;
int b;
bool CalculateMe();
}