Considérez le problème suivant:
Vous avez une classe 'A »qui sert de classe de base pour beaucoup d'autres classes similaires, par exemple. une classe appelée b. p>
La classe A est utile de lui-même (et déjà utilisée partout) et n'est donc pas abstraite. P>
Le crux est que vous souhaitez maintenant Appliquer un nouveau protocole nécessitant toutes les classes héritantes d'une (y compris A elle-même) à mettre en œuvre une méthode 'Func ()'. Si une sous-classe oublie d'implémenter Func (), une erreur de compilateur devrait être une erreur de compilateur. P>
class A {
A func() { ret new A(...) }
}
class B : A {
A func() { ret new B(...) }
}
class C : A {
// Should give error : no func()
}
6 Réponses :
Il n'y a tout simplement aucun moyen de garder A code> un type de béton et de forcer une erreur de compilateur si les sous-types ne remplacent pas une méthode particulière. p>
+1 vrai. Si vous souhaitez une exécution de la compilation de la mise en œuvre de la méthode, vous devrez appliquer une interface à toutes les classes nécessitant la ou les méthodes.
On pourrait toujours refroidir la mise en œuvre pour créer un Abase code> équivalent à A code> mais a func code> comme abstrait protégé, puis faire "Code> scellé A: Abase code> avec une implémentation par défaut.
Si c'est ce que vous devez faire, alors oui. p>
À mon avis cependant, vous souhaiterez peut-être reconsidérer cette exigence. J'ai souvent mis en place des cadres similaires, où forçant les implémentations dans des sous-classes était une astuce intelligente. Ce que j'ai ensuite trouvé, c'est que comme un em> consommateur em> de ces cadres, je repousserais le code ou la défaillance de la mise en œuvre d'inune (comme la méthode vide). Ni l'un ni l'autre n'est idéal, car ils ajoutent de l'encombrement et du bruit et réduisent la maintenabilité. P>
En outre, si l'application de ce modèle est en tant qu'auto-usine (c'est-à-dire que votre exemple a des sous-classes des instances de retour d'eux-mêmes), vous voudrez peut-être essayer quelque chose de différent, comme un motif d'usine approprié. ER, et par "Essayez", je veux dire tirer parti d'une inversion du conteneur de contrôle, comme le château Windsor, Ninject, unité. P>
Merci pour vos réponses! Je suppose que je suis juste un peu paranoïaque :) Puisque je suis un ancien programmeur C ++ à partir de C # J'espérais quelque chose dans le sens d'une fonction de modèle qui briserait si instancié avec le mauvais type ... mais je se rendre compte que ce n'est pas très c # -ish
Je préférerais voir une sous-classe qui remplace la fonction abstraite validateStuff () avec une implémentation vide et un commentaire de code expliquant pourquoi il ne nécessite aucune validation. IMO, il facilite le maintien du code et plus utile à titre d'exemple à d'autres développeurs créant des sous-classes similaires. Comparez cela à une sous-classe qui ne remplace pas la fonction Virtual ValidAteStuff () et me laisse me demander pourquoi. N'était-ce pas nécessaire, ou le développeur a-t-il voulu juste oublier de le mettre en œuvre? Pour cette raison, je ne pense pas que ces fonctions vides ne soient toujours que "encombrement".
Ceci est peut-être même Messier, mais vous pourrez peut-être forcer B, C, D etc. pour mettre en œuvre une interface nécessitant des fonctions de fonctionnement (). Ensuite, assurez-vous qu'ils sont toujours construits via une méthode d'usine. Alors quelque chose de vaguement comme:
Mais l'objectif est de trouver chaque classe qui n'a pas été modifiée pour implémenter Func (). Cette méthode manquera chaque classe qui n'a pas été modifiée que vous avez besoin d'ifunc. Cela ne résout pas le problème; Cela le déplace simplement.
Vous ne pouvez pas instancier la classe A s'il est abstrait. Ainsi,
// In the A class definition.
protected virtual A func()
{
if (GetType() != typeof(A))
throw // ...
}
Fondamentalement, vous nous demandez de "faire cette pause, mais ne faites pas cette pause." Peut-être que vous voyez le conflit inhérent. P>
C'est l'une des raisons pour lesquelles les classes de base ne doivent être utilisées que comme des classes de base et non utilisées comme elles-mêmes. P>
Le principal problème de cette approche nécessite que toute la classe dérivée hérite maintenant de x plutôt que A. Vous venez de modifier le problème de l'application de la loi. à un autre. strong> p> une approche de nettoyage, à mon avis, est au refacteur Vous devez remplacer toutes les utilisations de FUNC code> et remplacer le FUNC code> à partir de A pour appeler cette méthode. Voici un exemple: A code> dans une classe de base abase code> et avoir Func code> être abstrait là-bas. Puis scellez A code> et nécessitent B code> et c code> pour hériter de Abase code> plutôt que A code> : p> A code> avec Abase code> - mais il y a d'excellents outils de refactoring dans Visual Studio (et outils tiers tels que Resharper) qui rendent cela substantiellement moins douloureux que celui autrefois. P> P>
Votre solution "désordonnée" semble être la bonne pour moi.