Ich habe eine .NET-Assembly, die ich erstelle und ich möchte nur ein paar Klassen und Methoden für den Endbenutzer sichtbar sein. Ich erstelle die Assembly mit einem Bibliotheksklasse-Projekt in Framework 4.5 mit Visual Studio 2015. Die Assembly stellt eine Steuerkonsole der Art bereit, um einige zusammenhängende Back-End-Dienste und -Anwendungen zu verwalten. Die meiste Arbeit wird innerhalb der Baugruppe ausgeführt und ist für den Endbenutzer nicht sichtbar. Die meisten Klassen im Namespace der Assembly sind intern und für den Endbenutzer nicht sichtbar. Die Assembly stellt jedoch einige öffentliche Klassen und Methoden bereit, mit denen der Endbenutzer interagieren kann. So wie ich es mir vorstelle, stellt die Assembly eine öffentliche statische Klasse bereit, in der der Endbenutzer die von der Assembly benötigten Parameter eingibt und dann ein Objekt zurückgibt, das der Endbenutzer verwenden kann. Hier ist zum Beispiel eine Pseudocode-Vorlage, wie diese öffentliche statische Klasse konzeptionell aussehen könnte.So implementieren Sie Setter Dependency Injection auf interne Access Modifiers
public static class ControlClientStarter
{
public static ISrvcManagerConsole GetSrvcManagerConsole(/*. all the parameters . */)
{
// Some code that parses the parameters . . .
ISrvc_Controller accountController = new AccountSrvc_Controller(/*. . . */);
ISrvc_Controller contractController = new ContractSrvc_Controller(/*. . . */);
ISrvc_Controller imageController = new ImageSrvc_Controller(/*. . . */);
ICatalogApp_Controller calatlogController = new CatalogApp_Controller(/*. . . */);
IPortal_Controller portalController = new PortalSrvc_Controller(/*. . . */);
ISrvcManagerConsole srvcMngrConsole = new SrvceManagerConsole(
accountController,
contractController,
imageController,
calatlogController,
portalController);
return srvcMngrConsole;
}
}
Der Code zeigt, oben, dass die Baugruppe eine DLL-public static Klasse stellt ControlClientStarter genannt, die eine öffentliche statische Methode namens GetSrvcManagerConsole() (...). Diese statische Methode akzeptiert mehrere Parameter und erstellt dann basierend auf diesen Parametern und verschiedenen Logikobjekten Objekte, die die ISrvc_Controller-Schnittstelle implementieren, sowie andere Objekte, die andere Schnittstellen implementieren. Diese Objekte werden in den Konstruktor des srvcMngrConsole-Objekts eingefügt und an den Endbenutzer zurückgegeben.
Der Endbenutzer würde die Steuerkonsole mit etwas konzeptionell ähnlich wie diese zuzugreifen,
ISrvcManagerConsole clientMngrConsole = ControlClientStarter.GetSrvcManagerConsole( /*. . . */);
clientMngrConsole.DoThis( /*. . . */);
clientMngrConsole.StopThis( /*. . . */);
clientMngrConsole.GetThat( /*. . . */);
clientMngrConsole.PublishThat( /*. . . */);
Das Innere der Steuerkonsole etwas abstrakt wie diese Pseudo-Code aussehen könnte, wenn die Argumente in die SrvcManagerConsole bekommen weitergegeben gespeichert Schnittstelle Umsetzung von Feldern im srvcMngrConsole Objekt,
internal class SrvcManagerConsole : ISrvcManagerConsole
{
ISrvc_Controller accountController ;
ISrvc_Controller contractController ;
ISrvc_Controller imageController ;
ICatalogApp_Controller calatlogController;
IPortal_Controller portalController ;
internal SrvcManagerConsole(
ISrvc_Controller accountController,
ISrvc_Controller contractController,
ISrvc_Controller imageController,
ICatalogApp_Controller calatlogController,
IPortal_Controller portalController)
{
this.accountController = accountController;
this.contractController = contractController;
this.imageController = imageController;
this.calatlogController = calatlogController;
this.portalController = portalController;
}
public void DoThis(/*. . . */)
{ /*. . . */ }
public void StopThis(/*. . . */)
{ /*. . . */ }
public void GetThat(/*. . . */)
{ /*. . . */ }
public void PublishThat(/*. . . */)
{ /*. . . */ }
// private and-or internal methods and properties . . .
}
Wie Sie sehen können, bin ich einen Konstruktor Dependency Injection mit tight-Kopplung zu vermeiden; Aber es gibt ein Problem damit. Was, wenn ich in Zukunft einen MapPublishApp_Controller, einen VideoSrvc_Controller usw. hinzufügen möchte?
Wenn ich sie nach dem ersten Release zum Konstruktor hinzufüge, muss sich auch anderer Code ändern, der diese Assembly bereits verwendet. Wenn ich einen weiteren SrvceManagerConsole-Konstruktor mit zusätzlichen Parametern hinzufüge, die die neuen Controller übernehmen, dann breche ich den vorherigen Code nicht, aber die Konstruktorparameter werden zu zahlreich. Abhängig von den Parametern, die der Endbenutzer an die GetSrvcManagerConsole() -Methode der ControlClientStarter-Klasse sendet, möchte ich möglicherweise nicht alle Controller verwenden. Es wäre viel besser, wenn ich Methoden haben könnte, die die Controller den Feldern hinzufügen. Die Herausforderung besteht darin, dass ich nicht möchte, dass der Endbenutzer auf diese Felder zugreifen kann. Ich möchte nur die Assembly die Controller-Klassen instanziieren und die versteckten Felder zuweisen. Wenn ich diese Zuweisungsmethoden in den internen Zugriffsmodifizierern der SrvceManagerConsole-Klasse angibt, kann die GetSrvcManagerConsole() -Methode diese nicht anzeigen, da sie öffentlich ist.
Beachten Sie die Zugriffsmodifizierer auf den Schnittstellen. Der einzige, der öffentlich ist, ist derjenige, der von der SrvcManagerConsole-Klasse implementiert wird. Der Rest ist intern, da sie für den Endbenutzer nicht zugänglich sein sollte.
internal interface ISrvc_Controller { /*. . . */ }
internal interface ICatalogApp_Controller { /*. . . */ }
internal interface IPortal_Controller { /*. . . */ }
public interface ISrvcManagerConsole
{
void DoThis(/*. . . */);
void StopThis(/*. . . */);
void GetThat(/*. . . */);
void PublishThat(/*. . . */);
}
Diese Methoden oben wie PublishThat() werden die Felder verwenden, die in den Konstruktor injiziert, um die Objekte festgelegt wurde.Der Endbenutzerprogrammierer, der meine Assembly verwendet, wird diese Schnittstellenimplementierungsinstanzen niemals sehen. Das clientMngrConsole-Objekt, das vom Endbenutzerprogrammierer erstellt wird, verwendet diese internen Objekte, ohne dass der Programmierer weiß, wie sie verwendet werden.
Die Klassen, die Schnittstellen mit internen Zugriffsmodifikatoren implementieren wären in etwa so abstrakt sein,
class AccountSrvc_Controller : ISrvc_Controller { /*. . . */ }
class ContractSrvc_Controller : ISrvc_Controller { /*. . . */ }
class ImageSrvc_Controller : ISrvc_Controller { /*. . . */ }
class CatalogApp_Controller : ICatalogApp_Controller { /*. . . */ }
class PortalSrvc_Controller : IPortal_Controller { /*. . . */ }
Es könnte mehr Controller-Klassen in der Zukunft sein, vielleicht auch nicht. Ich konnte nicht herausfinden, wie die Controller - Klassen den Backend - Feldern zugewiesen werden, so dass diese Felder für den Endbenutzer nicht zugänglich sind, während ein gewisses Maß an loser Kopplung beibehalten wird Zukunft. Ich habe jedoch festgestellt, dass, wenn die anderen Methoden und Eigenschaften in der ISrvceManagerConsole-Schnittstelle angegeben sind und diese Schnittstelle mit einem öffentlichen Modifikator gesetzt ist, diese Methoden und Eigenschaften in der SrvceManagerConsole-Klasse, die diese Schnittstelle implementiert, auch als public und festgelegt werden können erfolgreich zugegriffen, obwohl die SrvceManagerConsole-Klasse selbst als "internal" festgelegt wurde. Ich versuchte Property Setter Injektion, aber C# (weise) unterstützt keine Eigenschaft mit einem Interface-Backend (weil das keinen Sinn machen würde). Eine weitere Möglichkeit besteht darin, dass der SrvceManagerConsole-Konstruktor einen Parameter übernimmt, der möglicherweise alle anderen Schnittstelleninstanzen in ein Dictionary einbindet. Das habe ich noch nicht ausprobiert. Ich kann nicht die erste Person sein, die damit konfrontiert wurde. Es muss eine gemeinsame Lösung geben, die ich nicht finden konnte. Ich weiß, dass der Industriestandard ist, IoC-Container für das zu verwenden, was ich oben mache, aber das wird hier nicht gemacht. Ich habe noch nie zuvor eine solche Architektur ausprobiert. Ich weiß, dass dies wahrscheinlich nicht die beste Architektur ist; aber im Moment muss es nicht unbedingt einer übermäßig komplexen Architektur entsprechen. Ich suche ein adäquates Design, da ich mehr darüber lerne.
Edit # 1, warum das Builder-Muster hier nicht
nehme ich nicht funktioniert versuchen, mithilfe eines Builder-Muster wie der Pseudo-Code unter dem Konstruktor anti-Muster zu vermeiden.
Stellen Sie sich vor, dass die SrvcManagerConsole sieht wie folgt aus:
internal class SrvcManagerConsole : ISrvcManagerConsole
{
internal ISrvc_Controller accountController;
internal SrvcManagerConsole() { }
internal void AddAccountController(ISrvc_Controller accountController)
{
this.accountController = accountController;
}
}
Stellen ich die ControlClientStarter zu so etwas wie dies zu ändern:
public static class ControlClientStarter
{
public static ISrvcManagerConsole GetSrvcManagerConsole(/*. all the parameters . */)
{
Federated_ControlBuilder fedBuilder = new Federated_ControlBuilder();
ISrvcManagerConsole srvcMngrConsole = fedBuilder.GetSrvcManagerConsole();
return srvcMngrConsole;
}
}
Und dann realisiere ich das Federated_ControlBuilder wie folgt aus:
internal class Federated_ControlBuilder : IControl_Builder
{
internal ISrvcManagerConsole srvcMngrConsole;
internal Federated_ControlBuilder()
{
srvcMngrConsole = new SrvcManagerConsole();
}
public void InjectAccountController(ISrvc_Controller accountController)
{
// The srvcMngrConsole object can not see the InjectAccountController method.
// srvcMngrConsole.
}
internal ISrvcManagerConsole GetSrvcManagerConsole()
{
return srvcMngrConsole;
}
}
Die InjectAccountController (...) Methode der Federated_Contr Die olBuilder-Klasse kann die AddAccountController (...) -Methode der SrvcManagerConsole-Klasse nicht sehen, da sie intern ist.
Der Knackpunkt meiner Frage ist nicht, wie man ein Konstruktor-Antimuster an und für sich vermeidet; vielmehr besteht die Herausforderung darin, das Anti-Pattern zu vermeiden, während bestimmte Klassen und Methoden in einer Baugruppe für den Endbenutzer nicht zugänglich sind. Daher wird das Wort "internal" der Frage "Wie setze ich Setter Dependency ein? Injection on interne Access Modifiers."
Wenn es nur darum ging, ein Konstruktor-Anti-Pattern zu vermeiden, hätte ich die Felder der SrvcManagerConsole öffentlich machen und ihnen direkt über die GetSrvcManagerConsole (...) -Methode der ControlClientStarter-Klasse hinzufügen können.
Vielen Dank für Ihre Empfehlung. Es wird geschätzt. Ich habe eine Antwort in meinem ursprünglichen Beitrag nach dem Abschnitt "Bearbeiten # 1..." – Beebok