La surcharge est-elle un exemple du principe ouvert / fermé?

12

Wikipédia dit

"les entités logicielles (classes, modules, fonctions, etc.) devraient être ouvertes pour l'extension, mais fermées pour la modification"

Le mot fonctions a attiré mon attention, et je me demande maintenant si nous pouvons supposer que la création d'une surcharge pour une méthode peut être considérée comme un exemple du principe ouvert / fermé ou non?

Permettez-moi d'expliquer un exemple. Considérez que vous avez une méthode dans votre couche de service, qui est utilisée dans près de 1000 emplacements. La méthode obtient userId et détermine si l'utilisateur est administrateur ou non:

bool IsAdmin(userId)

Considérez maintenant que quelque part, il est nécessaire de déterminer si l'utilisateur est administrateur ou non, en fonction du nom d'utilisateur et non de l'ID utilisateur. Si nous changeons la signature de la méthode mentionnée ci-dessus, alors nous avons cassé le code à 1000 endroits (les fonctions devraient être fermées à la modification). Ainsi, nous pouvons créer une surcharge pour obtenir le nom d'utilisateur, trouver l'ID utilisateur basé sur le nom d'utilisateur et la méthode d'origine:

public bool IsAdmin(string username)
{
    int userId = UserManager.GetUser(username).Id;
    return IsAdmin(userId);
}

De cette façon, nous avons étendu notre fonction en créant une surcharge pour elle (les fonctions doivent être ouvertes à l'extension).

Est-ce un exemple de principe ouvert / fermé?

Saeed Neamati
la source

Réponses:

5

J'interpréterais personnellement la déclaration wiki ainsi:

  • pour une classe: n'hésitez pas à hériter de la classe et à remplacer ou étendre ses fonctionnalités, mais ne piratez pas la classe d'origine, modifiant ainsi ce qu'elle fait.
  • pour un module (peut-être une bibliothèque par exemple): n'hésitez pas à écrire un nouveau module / bibliothèque qui encapsule l'original, fusionne les fonctions dans des versions plus faciles à utiliser ou étend l'original avec des fonctionnalités supplémentaires, mais ne changez pas le module d'origine.
  • pour une fonction (c'est-à-dire une fonction statique, pas une méthode de classe): votre exemple est raisonnable pour moi; réutilisez la fonction IsAdmin (int) d'origine à l'intérieur de la nouvelle chaîne IsAdmin (chaîne). l'original ne change pas, le nouveau func étend ses fonctionnalités.

cependant, si votre code utilise la classe 'cUserInfo' dans laquelle réside IsAdmin (int), vous enfreignez essentiellement la règle et changez la classe. malheureusement, la règle ne sera conservée que si vous créez une nouvelle classe cUserWithNameInfo: public cUserInfo et que vous y mettez votre substitution IsAdmin (chaîne). Si je possédais la base de code, je n'obéirais jamais à la règle. Je dirais des conneries et je ferais simplement le changement que vous proposez.

Sassafras_wot
la source
3

Tout d'abord, votre exemple est malheureusement artificiel. Vous ne feriez jamais cela dans le monde réel, car cela provoque un double-get inutile. Ou pire, car l'ID utilisateur et le nom d'utilisateur peuvent devenir le même type à un moment donné. Plutôt que

public bool IsAdmin(int userid)
{
    User user = UserManager.GetUser(userid);
    return user.IsAdmin();
}

public bool IsAdmin(string username)
{
    int userId = UserManager.GetUser(username).Id;
    return IsAdmin(userId);
}

Vous devriez vraiment étendre cette classe.

public bool IsAdmin(int userid)
{
    User user = UserManager.GetUserById(userid);
    return user.IsAdmin();
}

public bool IsUsernameAdmin(string username)
{
    User userId = UserManager.GetUserByName(username);
    return user.IsAdmin();
}

Vous pouvez refactoriser davantage et modifier le nom de la première méthode en IsUserIdAdmin, pour des raisons de cohérence, mais ce n'est pas nécessaire. Le fait est qu'en ajoutant la nouvelle méthode et en garantissant qu'aucun code d'appel n'est cassé, vous avez trouvé votre classe extensible. Ou en d'autres termes, ouvert à l'extension .

Et voici la chose avec le principe ouvert-fermé. Vous ne savez jamais vraiment à quel point votre code est conforme jusqu'à ce que vous essayiez d'étendre une partie de celui-ci et que vous vous trouviez obligé de le modifier à la place (avec le risque accru qui en découle). Mais, avec l'expérience, vous apprenez à prédire.

Comme le dit l'oncle Bob (c'est moi qui souligne):

La fermeture ne pouvant être complète, elle doit être stratégique. C'est-à-dire que le concepteur doit choisir les types de changements contre lesquels fermer son dessin. Cela prend une certaine quantité de prescience dérivée de l'expérience . Le concepteur expérimenté connaît suffisamment les utilisateurs et l'industrie pour juger de la probabilité de différents types de changements. Il s'assure ensuite que le principe ouvert-fermé est invoqué pour les changements les plus probables.

pdr
la source
Merci pour votre bonne explication. Mais avoir 1000 aller-retour ou un aller-retour n'est pas le point principal ici. Oublions donc les performances pour le moment. Cependant, ce que j'ai fait a également été d'étendre la classe, car je lui ai ajouté une autre méthode, bien qu'avec le même nom, mais avec des paramètres d'entrée différents. Mais j'ai vraiment apprécié votre citation de l' oncle Bob . +1. ;)
Saeed Neamati
@SaeedNeamati: Le point que je faisais est que cela n'a pas d'importance si vous ajoutez une méthode qui utilise une méthode existante ou ajoutez une méthode qui est entièrement indépendante, de toute façon votre classe était ouverte à l'extension. Mais, si vous deviez modifier l'interface de méthode existante pour y parvenir, vous n'êtes pas fermé à la modification.
pdr
2

Les modules conformes au principe ouvert-fermé ont deux attributs principaux.

  1. Ils sont «ouverts pour l'extension». Cela signifie que le comportement du module peut être étendu. Que nous pouvons faire en sorte que le module se comporte de manières nouvelles et différentes à mesure que les exigences de l'application changent ou pour répondre aux besoins de nouvelles applications.
  2. Ils sont «fermés pour modi fi cation». Le code source d'un tel module est inviolable. Personne n'est autorisé à y apporter des modifications de code source.
  1. En surchargeant votre méthode, vous étendez les fonctionnalités du module existant, répondant ainsi aux nouveaux besoins de votre application

  2. Vous n'apportez aucune modification à une méthode existante, vous ne violez donc pas la deuxième règle.

Je serais intéressé d'entendre ce que les autres ont à dire concernant l'interdiction de changer la méthode d'origine. Oui, vous pouvez mettre des modificateurs d'accès appropriés et appliquer l'encapsulation, mais que faire d'autre?

Réf: http://www.objectmentor.com/resources/articles/ocp.pdf

CodeART
la source
1

Le principe ouvert-fermé est un objectif, un cas idéal, pas toujours une réalité. En particulier lorsque vous cherchez à refactoriser l'ancien code, la première étape est souvent une modification lourde afin de rendre l'OCP possible. La racine du principe est que le code de travail fonctionne déjà et que la modification introduit éventuellement des bogues. Par conséquent, le meilleur scénario n'est pas de modifier le code existant, mais simplement d'ajouter un nouveau code.

Mais disons que vous aviez une fonction appelée BigContrivedMethod(int1, int2, string1). BigContrivedMethodfait trois choses: chose1, chose2 et chose3. À ce stade, la réutilisation de BCM est probablement difficile, car elle en fait trop. Le refactoriser (si possible) en ContrivedFunction1(int), ContrivedFunction2(int)et ContrivedFunction3(string)vous donne trois méthodes plus petites et plus ciblées que vous pouvez combiner plus facilement.

Et c'est la clé de l'OCP en ce qui concerne les méthodes / fonctions: la composition. Vous "étendez" les fonctions en les appelant à partir d'autres fonctions.

N'oubliez pas que l'OCP fait partie de 5 autres principes, les directives SOLID. Cette première est la clé, la responsabilité unique. Si tout dans votre base de code ne faisait que la seule chose spécifique qu'il devait faire, vous n'auriez jamais besoin de modifier le code. Il vous suffirait d'ajouter du nouveau code ou de combiner l'ancien code ensemble de nouvelles façons. Étant donné que le vrai code respecte rarement cette directive, vous devez souvent le modifier pour obtenir SRP avant de pouvoir obtenir OCP.

CodexArcanum
la source
Je pense que vous parlez ici du principal responsable unique, et non de l'OCP.
Saeed Neamati
1
Je dis que vous ne pouvez pas avoir d'OCP sans SRP. Le code qui en fait trop ne peut pas être étendu, il ne peut pas être réutilisé. SOLID est presque écrit dans l'ordre d'importance, chaque principe reposant sur ceux qui l'ont précédé pour être réalisable.
CodexArcanum
Cette réponse devrait être davantage votée.
Ashish Gupta
0

Vous suivez le principe ouvert-fermé si vous modifiez le comportement de votre programme en écrivant du nouveau code plutôt qu'en modifiant l'ancien code.

Comme il est quasiment impossible d'écrire du code ouvert à tous les changements possibles (et vous ne le souhaitez pas car vous entrez dans la paralysie de l'analyse), vous écrivez du code qui répond à tous les comportements différents sur lesquels vous travaillez actuellement par extension plutôt que par modification.

Lorsque vous rencontrez quelque chose que vous devez changer, au lieu de simplement modifier quelque chose pour autoriser le nouveau comportement, vous déterminez quel est le point de changement, restructurez votre programme sans modifier son comportement afin de pouvoir ensuite modifier ce comportement en écrivant un nouveau code .

Alors, comment cela s'applique à votre cas:

Si vous ajoutez de nouvelles fonctions à votre classe, votre classe n'est pas ouverte / fermée, mais les clients de la fonction que vous surchargez le sont.

Si vous ajoutez simplement de nouvelles fonctions qui fonctionnent SUR votre classe, alors elle et les clients de votre fonction sont ouverts / fermés.

Edward Strange
la source