J'ai un objet d'usine Quand j'appelle Idéalement, je voudrais garder le code de la construction d'objets à l'intérieur de la classe dérivée elle-même. Tout le code relatif à cet objet est co-localisé. Exemple: P> standallememeager code> pour générer des instances d'un objet code> code> objet pour un jeu que je bâtiment. Il y a beaucoup de défis. Les constructeurs pour chaque code> La dérivation de la classe code> sont différents, mais il existe une interface commune entre eux, définie dans la classe de base. manager.Createchallenge () Code> , il renvoie une instance de Challenge Code>, qui est l'un des types dérivés. P> class Challenge {}
class ChallengeA : Challenge {
public static Challenge MakeChallenge() {
return new ChallengeA();
}
}
class ChallengeB : Challenge {
public static Challenge MakeChallenge() {
return new ChallengeB();
}
}
3 Réponses :
Vous êtes "décentralisant" l'usine, de sorte que chaque sous-classe soit responsable de la création elle-même. P>
Plus couramment, vous auriez une usine centrale qui saurait sur les sous-types possibles et comment les construire (assez souvent, simplement en créant une nouvelle instance et en revenant cette instance saisie comme une interface commune ou une classe de base commune). Cette approche évite la question que vous avez actuellement. Je ne vois pas non plus d'avantages à votre approche actuelle. Vous ne gagnez actuellement aucune encapsulation ou de réutilisation de code sur la mise en œuvre la plus typique d'une usine. P>
Pour une référence supplémentaire, consultez P>
YA, c'est une variation du scénario classique, principalement de contenir les détails de la construction à la classe dérivée pour la lisibilité.
J'aime vraiment le modèle que vous décrivez et utilisez-le souvent. La façon dont j'aime le faire est: Ce modèle a de nombreuses propriétés agréables. Personne ne peut faire un nouveau Lorsque le client veut un défi code> car il est abstrait. Personne ne peut faire une classe dérivée car Challenge Code> STR par défaut est privé. Personne ne peut obtenir à ChallestA CODE> ou HISTLINTB code> car ils sont privés. Vous définissez l'interface à Challenge code> et c'est la seule interface dont le client doit comprendre. P> A code>, ils demandent défi code> pour un, et ils l'obtiennent. Ils n'ont pas besoin de s'inquiéter du fait que, derrière les scènes, A code> est implémenté par de défi code>. Ils viennent d'obtenir un défi code> qu'ils peuvent utiliser. P> p>
J'aime l'idée de mettre en œuvre l'usine dans la classe de base abstraite elle-même.
Il y a un couple rédigé à cette approche, cependant. Étant donné que ChallestA CODE> et HISTERTB code> sont privés, vous ne pouvez pas les tester. Puisque vous n'utilisez pas abstrait usine code>, vous ne pouvez pas vous moquer de l'usine dans des tests d'unité. Multiples méthodes d'usine pour le même type signifie que vous mettez la décision de la classe à créer dans le code d'appel. La beauté d'une usine en ce qui me concerne est que vous mettez la décision de la mise en œuvre concrète de créer en une seule place. Ceci est fondamentalement le code hérité par conception.
Les défis et les défis @Bstenzel sont destinés à être utilisés par Interface Challenge. Donc, si vous obtenez un objet de contestear en appelant Challenge.Makea (), identique aux défis, vous pouvez tester les deux.
L'ajout de la déclaration partielle à la classe peut vous permettre de relever les défis et les défis dans des fichiers séparés dans la même assemblée. Une considération intéressante si A & B sont des classes non triviales.
Est-ce une mauvaise pratique de faire l'une de ces classes public afin que je puisse exposer des méthodes les autres n'auraient pas dû?
@Alexanderderck: Certaines personnes envisagent une classe nichée publique comme une mauvaise pratique; Les classes imbriquées se sentent un peu bizarre parce que les gens ne sont pas habitués à penser à une classe comme un membre de quelque chose d'autre (autre qu'un espace de noms). Je serais donc prudent d'adapter ce modèle de cette façon; Certains pourraient le trouver étrange.
@ERICLIPPERT Dans votre exemple, y a-t-il un moyen de déplacer chacune des classes privées contenues (par exemple, de ChamplillageA, Hatletb) à leurs propres fichiers .CS pourtant les avoir encore des implémentations partielles du défi de la classe abstraite contenant? En d'autres termes, je peux beaucoup de ces classes privées et pour aider à l'organisation du code, je souhaite peut-être les mettre chaque fichier dans leur propre dossier dans Visual Studio. Est-ce possible? Merci d'avance et merci pour tout ce que vous faites pour la communauté C #.
@ User1886710: Mais ce ne sont pas des implémentations partielles pour commencer. Ce sont des sous-classes. Si ce que vous demandez, c'est que vous puissiez dire Partial abstrait Class Challenge {Classe privée ...} Code> Dans un fichier et Défi de classe abstrait partielle {Classe privée Hontb ... Code> Dans un autre, assurez-vous de pouvoir le faire. En fait, je crois que nous faisons cela dans les sources de Roslyn.
@ Ericlippertt génial. Obtenu ça marche. Merci de clarifier. :)
Par design, le motif d'usine devrait permettre différentes implémentations de l'usine et, dans votre cas, vous avez fusionné les deux côtés en un, ce qui a fait le principe de découplage inexistant. En outre, le retourner de nouveaux défis (); code> Les déclarations sont toutes dans la classe abstraite elle-même, ce qui implique que la classe abstraite connaît tout sur ses descendants. Je ne pense pas que ce soit une bonne idée.
Pas nécessairement la réponse que vous recherchez mais ... Vous pouvez utiliser la mise en œuvre suivante si vous pouvez vous éloigner de la méthode statique par classe.
using System;
public class Test
{
public static void Main()
{
var c1 = ChallengeManager.CreateChallenge();
var c2 = ChallengeManager.CreateChallenge();
//var c = ChallengeManager.CreateChallenge<Challenage>(); // This statement won't compile
}
}
public class ChallengeManager
{
public static Challenage CreateChallenge()
{
// identify which challenge to instantiate. e.g. Challenage1
var c = CreateChallenge<Challenage1>();
return c;
}
private static Challenage CreateChallenge<T>() where T: Challenage, new()
{
return new T();
}
}
public abstract class Challenage{}
public class Challenage1: Challenage{}
public class Challenage2: Challenage{}
Belle approche pour utiliser une méthode paramétrée.
Avec cette approche, vous enfreignez le principe d'inversion de dépendance en apportant le code d'appel dépend des mises en œuvre concrètes. Vous ne gagnerez rien sur Nouveau Challenge1 () Code> Lorsque vous appelez Createchallenge
Je suis d'accord, celui-ci n'a pas vraiment mis en œuvre le motif d'usine. Ma réponse (mise à jour) a été largement centrée autour de cette déclaration en question: Maintenant, mon appel à Saclallemanager.createchallenge n'a besoin que de décider de la classe à appeler Makecallenge () sur. La mise en œuvre de la construction est contenue par la classe elle-même. I> de cette façon, il élimine la nécessité de Makechallenge code> méthode.
Coller fortement à la conception que vous suggérez: Comment allez-vous identifier quelle classe à instancier dans
sletherManager.createchaallenge () code>? et; Si vous connaissez déjà la classe à instancier, pourquoi ne pouvez-vous pas simplement l'installer dansCreatechalhange code> méthode?Ce qui se passera-t-il est une méthode de création () de création () de création () dans ce cas et non aussi gérable que de rompre la construction aux classes concernées.
Votre conception est le meilleur ici design-sage. Je préfère ne pas mettre plusieurs méthodes d'usine statiques dans les classes de béton, cependant. Utilisez des constructeurs réels pour tout ce qui est la responsabilité des classes et la prise de décision et la construction de dépendances dans
Createchallenge Code>. SiCreatEchallenge code> devient trop longtemps à votre goût, extrayez les méthodes pour rendre l'intention plus claire. Si c'est vraiment long, extrayez-le dans sa propre classe. Ymmv