J'ai donc voulu hériter d'un sealed class
dans csharp et j'ai été brûlé. Il n'y a tout simplement aucun moyen de le desceller à moins d'avoir accès à la source.
Ensuite, cela m'a fait penser "pourquoi sealed
existe-t-il même?". Il ya 4 mois. Je n'ai pas pu le comprendre, malgré la lecture de beaucoup de choses à ce sujet, telles que:
- Souhaits de Jon Skeet "les classes ont été scellées par défaut dans .NET. "
- Vous préférez la composition à l'héritage?
- "Vous ne devez pas sceller toutes les classes (...)"
- Comment vous moquez-vous d'une classe scellée?
J'ai essayé de digérer tout cela depuis, mais c'est trop pour moi. Finalement, hier, je l'ai essayé à nouveau. Je les ai à nouveau analysés, et quelques autres:
- Pourquoi une classe devrait-elle être autre que "abstraite" ou "finale / scellée"?
- En plus de 15 ans de programmation, la première fois que j'ai entendu parler de SOLID , à partir d'une réponse à une question déjà liée et je n'ai évidemment pas tout lu il y a 4 mois
Enfin, après beaucoup de réflexion, j'ai décidé de modifier fortement la question d'origine en fonction du nouveau titre.
La vieille question était trop large et subjective. Il s'agissait essentiellement de demander:
- Dans l'ancien titre: une bonne raison d'utiliser scellé
- Dans le corps: comment modifier correctement une classe scellée? Oubliez l'héritage? Utiliser la composition?
Mais maintenant, en comprenant (ce que je n'ai pas fait hier) que tout sealed
fait empêche l'héritage , et nous pouvons et devons en effet utiliser la composition plutôt que l'héritage , j'ai réalisé que j'avais besoin d' exemples pratiques .
Je suppose que ma question ici est (et en fait a toujours été) exactement ce que M. Mindor m'a suggéré dans une conversation : comment la conception pour l'héritage peut-elle entraîner des coûts supplémentaires?
la source
Réponses:
Ce n'est pas si difficile à comprendre.
sealed
a été créé SPÉCIFIQUEMENT pour Microsoft afin de leur faciliter la vie, d'économiser des tonnes d'argent et d'améliorer leur réputation. Puisqu'il s'agit d'une fonction de langue, tout le monde peut également l'utiliser, mais VOUS n'aurez probablement jamais besoin d'utiliser scellé.La plupart des gens se plaignent de ne pas pouvoir étendre une classe et s'ils le font, ils disent que tout le monde sait que c'est la responsabilité des développeurs de la faire fonctionner correctement. C'est vrai, SAUF que ces mêmes personnes n'ont aucune idée de l'histoire de Windows et l'un des problèmes
sealed
essaie de résoudre.Supposons qu'un développeur ait étendu une classe de base .Net (car scellé n'existait pas) et l'a fait fonctionner parfaitement. Oui, pour le développeur. Le développeur livre le produit au client. L'application du développeur fonctionne très bien. Le client est content. La vie est belle.
Désormais, Microsoft publie un nouveau système d'exploitation, qui comprend la correction des bogues dans cette classe de base .Net particulière. Le client souhaite suivre son temps et choisit d'installer le nouveau système d'exploitation. Du coup, l'application que le client aime tant ne fonctionne plus, car elle ne tenait pas compte des bugs corrigés dans la classe de base .Net.
Qui en est responsable?
Ceux qui connaissent l'histoire de Microsoft savent que le nouveau système d'exploitation de Microsoft sera blâmé et non le logiciel d'application qui a mal utilisé les bibliothèques Windows. Il revient donc à Microsoft de résoudre le problème au lieu de la société d'application qui a créé le problème. C'est l'une des raisons pour lesquelles le code Windows s'est gonflé. D'après ce que j'ai lu, le code du système d'exploitation Windows est jonché de if-then spécifique pour vérifier si une application et une version spécifiques sont en cours d'exécution et si oui, effectuez un traitement spécial pour permettre au programme de fonctionner. C'est beaucoup d'argent dépensé par Microsoft pour corriger l'incompétence d'une autre entreprise.
L'utilisation
sealed
n'élimine pas complètement le scénario ci-dessus, mais cela aide.la source
sealed
par défaut, sauf si elles étaient spécifiquement scellées, simplement parce que si peu de classes sont réellement conçues pour être héritées. Il est beaucoup plus probable que votre classe n'ait pas été conçue pour la prendre en charge, et vous devez vous assurer que vous avez passé ce temps de développement si vous vous efforcez de la marquer comme non scellée.sealed
. S'il s'agissait en fait de l'option par défaut, cela signifierait que si quelqu'un héritait d'un type, il saurait qu'il est conçu pour le prendre en charge.String
puis le muter après le contrôle de sécurité mais avant les E / S? Je crois que ce type de scénario est la raison pour laquelle JavaString
est marquéfinal
(similaire àsealed
) et je parie que la même logique sous-tend certaines décisions C #.sealed
est utilisé pour indiquer que l'auteur de la classe ne l'a pas conçu pour être hérité. Rien de moins, rien de plus.Concevoir correctement une interface est déjà assez difficile pour de nombreux programmeurs. Concevoir correctement une classe qui peut être potentiellement héritée ajoute de la difficulté et des lignes de code. Plus de lignes de code écrites signifient plus de code à maintenir. Pour éviter cette difficulté croissante,
sealed
peut montrer que la classe n'a jamais été destinée à être héritée, et dans ce contexte, sceller une classe est une solution parfaitement valide à un problème .Imaginez le cas d'où
CustomBoeing787
dérive la classeAirliner
. CelaCustomBoeing787
remplace certaines méthodes protégées deAirliner
, mais pas toutes. Si le développeur a suffisamment de temps et que cela en vaut la peine, il peut tester la classe à l'unité pour s'assurer que tout fonctionne comme prévu, même dans un contexte où des méthodes non remplacées sont appelées (par exemple, des setters protégés qui changent l'état de l'objet). Si le même développeur (1) n'a pas le temps, (2) ne veut pas dépenser des centaines ou des milliers de dollars supplémentaires de son patron / client, ou (3) ne veut pas écrire de code supplémentaire, il ne le fait pas besoin en ce moment (YAGNI), alors il peut:soit libérer le code dans la nature tel quel, étant donné que c'est la préoccupation des programmeurs qui maintiendront ce projet plus tard pour se soucier des bugs potentiels,
ou marquez la classe
sealed
pour montrer explicitement aux futurs programmeurs que cette classe n'est pas destinée à être héritée, et que la descellage et son utilisation en tant que parent doivent se faire à vos propres risques.Est-ce une bonne idée de sceller autant de classes que possible, ou aussi peu que possible? D'après mon expérience, il n'y a pas de réponse définitive. Certains développeurs ont tendance à en utiliser
sealed
trop; d'autres ne se soucient pas de la façon dont la classe serait héritée et du problème qu'elle pourrait causer. Face à une telle utilisation, le problème est similaire à celui qui consiste à déterminer si les programmeurs doivent utiliser deux ou quatre espaces pour l'indentation: il n'y a pas de bonne réponse.la source
tl;dr
ici. C'est soit "Non, il n'y a pas une seule bonne raison" , soit "Je pense qu'il n'y a pas de bonne raison, mais je ne sais pas aussi bien". . Pour moi, "pas de réponse définitive" est l'un ou l'autre.sealed
est utilisé pour indiquer que l'auteur de la classe ne l'a pas conçu pour être hérité. Voilà votre seule bonne raison.String
puis le muter après le contrôle de sécurité mais avant les E / S? Je crois que ce type de scénario est la raison pour laquelle JavaString
est marquéfinal
(similaire àsealed
) et je parie que la même logique sous-tend certaines décisions C #.Lorsqu'une classe est scellée, elle permet au code utilisant ce type de savoir exactement à quoi elles ont affaire.
Maintenant , en C #
string
estsealed
, vous ne pouvez pas en hériter, mais supposons une seconde que cela n'a pas été le cas. Si vous l'avez fait, alors quelqu'un pourrait écrire un cours comme celui-ci:Ce serait maintenant une classe de chaînes qui viole toutes sortes de propriétés que les concepteurs
string
savaient être vraies. Ils savaient que laEquals
méthode serait transitive, ce n'est pas le cas. Heck, cette méthode égale n'est même pas symétrique, car une chaîne pourrait être égale à elle, mais elle ne serait pas égale à cette chaîne. Je suis sûr qu'il y a toutes sortes de programmeurs qui ont écrit du code en supposant que laToString
méthode destring
ne lèvera pas d'exception pour les valeurs non nulles. Vous pourrez désormais violer cette hypothèse.Il existe un certain nombre d'autres classes pour lesquelles nous pourrions le faire, et toutes sortes d'autres hypothèses que nous pourrions violer. Si vous supprimez la fonction de langue pour
sealed
les concepteurs de langage doivent maintenant commencer à tenir compte de ce genre de choses dans tout le code de leur bibliothèque. Ils doivent effectuer toutes sortes de vérifications pour s'assurer que les types étendus des types qu'ils souhaitent utiliser seront écrits "correctement", ce qui peut être une opération très coûteuse (à la fois en temps de développement et au moment de l'exécution). En étant en mesure de sceller une classe, ils peuvent garantir que cela n'est pas possible et que toutes les affirmations qu'ils font sur les détails d'implémentation de leurs propres types ne peuvent pas être violées, à l'exception des types qui sont intentionnellement conçus pour être étendus. Pour ces types conçus pour être étendus, vous trouverez que le code de langue doit être beaucoup plus défensif sur la façon dont il les traite.la source
sealed
fonctionnalités intégrées qui ne sont pas exposées pour nous. Il n'explique toujours pas pourquoi l'utiliser en dehors d'une portée aussi étroite et, étant aussi étroit que le code du compilateur , n'explique même pas l'sealed
existence.string
type n'est pas du code de compilation. Cela fait partie du code de langue. Complètement différent. En tout cas, je viens de choisir un exemple. Vous pouvez choisir la plupart des classes scellées de la BCL entière et l'utiliser comme exemple.Ah non? Pas souvent. Je n'utilisé scellé quand j'ai un type immuable (ou une autre limitation) qui vraiment vraiment doit rester immuable et je ne peux faire confiance à furfill que héritières exigence sans introduire des bugs subtils, catastropic dans le système.
Nous utilisons l'héritage pour les choses qui ne sont pas par défaut. Même si la composition est préférée à l'héritage (et c'est le cas), cela ne signifie pas que l'héritage doit être rejeté. Cela signifie que l'héritage a certains problèmes intrinsèques qu'il introduit dans la conception et la maintenabilité du code et que la composition fait la même chose avec (en général) moins de problèmes. Et cela signifie que même si la composition est privilégiée, l'hérédité est bonne pour certains sous-ensembles de problèmes.
Je ne veux pas être trop méchant, mais si vous venez d'entendre parler de SOLID et des tests unitaires, peut-être devriez-vous sortir de votre sous-pierre et écrire du code. Les choses ne sont pas claires, et rarement "toujours faire X" ou "ne jamais utiliser Y" ou "Z est le meilleur" soit la bonne façon (comme il semble que vous gravitez vers en fonction des vues de votre question). Lorsque vous écrivez du code, vous pouvez voir des cas où cela
sealed
pourrait être bon et des cas où cela vous cause du chagrin - comme tout autre compromis.la source
sealed
, s'il vous plaît?Tout d'abord, vous devriez lire le post d'Eric Lippert sur les raisons pour lesquelles Microsoft scelle ses classes.
http://blogs.msdn.com/b/ericlippert/archive/2004/01/22/61803.aspx
Deuxièmement, le mot clé scellé existe pour faire exactement ce qu'il fait - empêcher l'héritage. Il existe de nombreuses situations où vous souhaitez empêcher l'héritage. Considérez une classe dont vous avez une collection. Si votre code dépend du fait que cette classe fonctionne d'une manière particulière, vous ne voulez pas que quelqu'un remplace une des méthodes ou propriétés de cette classe et provoque l'échec de votre application.
Troisièmement, l'utilisation de scellés encourage la composition plutôt que l'héritage, ce qui est une bonne habitude. L'utilisation de la composition sur l'héritage signifie que vous ne pouvez pas briser le principe de substitution de Liskov et que vous suivez le principe Open / Closed.
sealed
est donc un mot-clé qui vous aide à suivre deux des cinq principes les plus importants de la programmation oo. Je dirais que cela en fait une fonctionnalité très précieuse.la source
Je pense que cette question n'a pas de réponse objective et que tout dépend de votre point de vue.
D'un côté, certaines personnes veulent que tout soit «ouvert» et le fardeau de s'assurer que tout fonctionne correctement incombe à la personne qui prolonge la classe. C'est pourquoi les classes sont ouvertes à l'héritage par défaut. Et pourquoi les méthodes Java sont toutes virtuelles par défaut.
De l'autre côté, certains veulent que tout soit fermé par défaut et offrent des options pour l'ouvrir. Dans ce cas, c'est l'auteur de la classe de base qui doit s'assurer que tout ce qu'il "ouvre" est sûr à utiliser de n'importe quelle manière imaginable. C'est pourquoi en C # toutes les méthodes sont non virtuelles par défaut et doivent être déclarées virtuelles pour être remplacées. C'est aussi pour ces personnes, qui doivent être un moyen de contourner les défauts «ouverts». Comme rendre la classe scellée / finale, car ils voient quelqu'un dérivant de la classe un gros problème pour la conception de leur propre classe.
la source
sealed
ne peut pas. Comment en hériter? Tout ce que je lis, c'est que nous ne pouvons pas. Déjà. Je ne sais pas. Je fais des allers-retours sur ce sujet depuis 4 mois maintenant, depuis que j'ai entendu parler de scellé pour la première fois. Et je ne sais toujours pas comment le faire correctement quand j'ai besoin de modifier quoi que ce soit sur une classe scellée. Donc, je ne vois aucune "option pour l'ouvrir"!le problème avec scellé en C # est que c'est plutôt final. En 7 ans de développement commercial C #, le seul endroit où je l'ai vu utilisé est sur les classes Microsoft .NET où je pense que j'avais une raison légitime d'étendre une classe de framework pour implémenter un comportement ajusté, et parce que la classe était scellée, je ne pouvais pas .
Il est impossible d'écrire une classe en pensant à toutes les façons possibles de l'utiliser et en décidant qu'aucune d'entre elles n'est légitime. De même, si la sécurité du framework dépend de l'héritage de la classe, vous avez un défaut de conception de sécurité.
Donc, en conclusion, je suis fermement d'avis que le scellé ne sert à rien, et rien de ce que j'ai lu jusqu'ici n'a changé ce point de vue.
Je peux voir qu'il y a eu jusqu'à présent trois arguments qui justifient la mise sous scellés jusqu'à présent.
L'argument 1 est qu'il élimine une source de bogues lorsque les gens héritent des classes et ont ensuite un accès accru aux internes de cette classe sans vraiment comprendre correctement les internes.
L'argument 2 est que si vous étendez une classe microsoft, puis que microsoft implémente un correctif de bogue dans cette classe mais que votre extension reposait sur ce bogue, votre code est maintenant cassé, microsoft obtient le blâme et scellé empêche cela
L'argument 3 est qu'en tant que développeur de la classe, c'est mon choix de vous autoriser ou non à l'étendre, dans le cadre de ma conception de la classe.
L'argument 1 semble être un argument selon lequel vous, le programmeur final, ne savez pas comment utiliser mon code. C'est peut-être le cas, mais si vous me permettez toujours d'accéder à l'interface des objets, il est toujours vrai que j'utilise votre objet et que je l'appelle sans comprendre comment l'utiliser, et peut donc faire autant de dégâts de cette façon . Il s'agit d'une faible justification de la nécessité de scellé.
Je comprends d'où vient la base factuelle de l'argument 2. En particulier, lorsque Microsoft a effectué des vérifications supplémentaires du système d'exploitation sur les appels d'API Win32 pour identifier les appels mal formés, qui ont amélioré la stabilité générale de Windows XP par rapport à Windows NT, mais au coût à court terme, de nombreuses applications ont été cassées lors de la sortie de Windows XP. Microsoft a résolu ce problème en ajoutant des «règles» pour ces vérifications, à savoir ignorer lorsque l'application X enfreint cette règle, c'est un bogue connu
L'argument 2 est un argument médiocre, car scellé au mieux ne fait pratiquement aucune différence, car s'il y a un bogue dans la bibliothèque .NET, vous n'avez pas d'autre choix que de trouver un moyen de contourner le problème et lorsque le correctif Microsoft sortira, il est fort probable signifie que votre travail qui dépend du comportement rompu est maintenant rompu. Que vous ayez contourné cela en utilisant l'héritage ou par un autre code wrapper supplémentaire, lorsque le code est corrigé, votre travail sera interrompu.
L'argument 3 est le seul argument qui ait une substance pour se justifier. Mais c'est encore faible. Cela va à l'encontre de la réutilisation du code.
la source
It is impossible to write a class thinking about every possible way it could be used
- C'est précisément la raison pour laquelle de nombreuses classes Framework sont scellées ... Cela élimine la nécessité de réfléchir à toutes les manières possibles d'étendre la classe.J'utilise uniquement des
sealed
mots clés sur les classes qui ne contiennent que des méthodes statiques.la source
static
place?Comme le montre ma question, je ne suis pas un expert ici. Mais je ne vois aucune raison
sealed
d'exister.Il semble être fait pour aider les architectes de code à concevoir une interface. Si vous voulez écrire une classe qui sortira dans la nature et que beaucoup de gens en hériteront, modifier quoi que ce soit dans cette classe peut la casser pour trop de gens. Nous pouvons donc le sceller et personne ne peut hériter. "Problème résolu".
Eh bien, cela ressemble à "couvrir un endroit et en découvrir un autre". Et ce n'est même pas la résolution d'un problème - c'est simplement l' élimination d'un outil : l' héritage. Comme tout outil, il a de nombreux usages et hériter d'une classe instable est un risque que vous prenez. Celui qui a pris ce risque devrait mieux le savoir.
Peut-être qu'il pourrait y avoir un mot-clé
stable
, afin que les gens sachent qu'il est plus sûr d'en hériter.la source
sealed
n'est pas un outil utile pour les concepteurs de framework. Les classes dans un cadre doivent être des boîtes noires; vous ne devriez pas avoir à connaître les internes d'une classe pour pouvoir l'utiliser correctement. Pourtant, c'est exactement ce que demande l'héritage.