2017-10-24 36 views
-1

Guten Abend, Ich versuche, SOLID-Prinzip mit DI zum ersten Mal in einem mittelgroßen Projekt zu implementieren. Die meiste Zeit verstehe ich, aber jetzt habe ich ein Problem. Lassen Sie mich ein armes Beispiel nehmen, aber es zeigt die Struktur der App. Ich habe Klassen mit verschiedenen Konstruktoren geerbt (Code unten angegeben). Wenn ich eine Instanz brauche, kenne ich die Klasse, die ich brauche. So, 2 Fragen zu diesem Code: 1/Muss ich in diesem Fall die Schnittstelle verwenden, um SOLID principe zu erreichen (oder einfach eine Instanz von ClassA wie ClassA exeplele = new ClassA ("text") in Program.cs) 2/Ist es gut gemacht? (Was ist gut/was ist schlecht, was zu tun/was zu vermeiden?)Schnittstellen und vererbte Klassen

class Program 
{ 
    static void Main(string[] args) 
    { 
     IFactory exemple = new Factory(); 
     //With Unity--> exemple=Resolve<IFactory>(); 
     exemple.GetTextClassA("test"); 
     exemple.GetTextClassB(1); 

     Console.ReadLine(); 
    } 
} 

public interface IFactory 
{ 
    ClassA GetTextClassA(string text); 
    ClassB GetTextClassB(int text); 
} 

public class Factory : IFactory 
{ 
    public ClassA GetTextClassA(string text) 
    { 
     return new ClassA(text); 
    } 
    public ClassB GetTextClassB(int text) 
    { 
     return new ClassB(text); 
    } 
} 

public abstract class MainClass 
{ 
    private string _text; 

    public MainClass(){} 

    protected abstract string GetText(); 
    protected virtual void Initialize() 
    { 
     _text = GetText(); 
     Console.WriteLine(_text); 
    } 
    public string TextToDisplay 
    { 
     get { return _text; } 
    } 
} 

public class ClassA : MainClass 
{ 
    string _textA; 
    public ClassA(string textParam) 
    { 
     _textA = textParam; 
     base.Initialize(); 
    } 
    protected override string GetText() 
    { 
     return "Text is :"+_textA; 
    } 
} 

public class ClassB : MainClass 
{ 
    int _numParam; 
    public ClassB(int numParam) 
    { 
     _numParam = numParam; 
     base.Initialize(); 
    } 
    protected override string GetText() 
    { 
     return "Text is :" + _numParam.ToString(); 
    } 
} 

Vielen Dank für Ihre Kommentare.

+0

Was ist Ihr konkretes Problem? Sie programmieren ein Modell nach einem Muster/Prinzip, wenn Sie ein bestimmtes Ziel erreichen wollen. Was ist dein Ziel? Wenn Sie diesen Weg nicht gehen, um dies zu wissen, sollten wir nicht über Probleme philosophieren, die derzeit nicht existieren. – Bagerfahrer

+0

Ich hatte Probleme bei der Pflege/Weiterentwicklung meines vorherigen Projekts. Ich beginne ein neues und ich beschloss, so nah wie möglich an guten Praktiken zu sein. Ich lese überall viel. In meinem Fall (speziell bei vererbten Klassen) möchte ich ein Maximum entkoppeln. Und ich weiß nicht, wie ich das erreichen soll. – Gadsweb

Antwort

2

Sie scheinen keine Abhängigkeiten zu injizieren.

Das Nötigste für DI erfordert zwei Dinge:

  1. Abhängigkeiten geschoben werden sollte, nicht gezogen.
  2. Das Abhängigkeitsdiagramm sollte an einem Ort beim Start an einer Stelle, die als composition root bekannt ist, zusammengesetzt sein.

Ein sehr einfaches Beispiel

interface IServiceA 
{ 
    void Foo(); 
} 

interface IServiceB 
{ 
    void Bar(); 
} 

class ServiceA : IServiceA 
{ 
    public void Foo() { do something ; } 
} 

class ServiceB : IServiceB 
{ 
    public void Bar() { do something else; } 
} 

interface IProgram 
{ 
    void Execute(); 
} 

class Program : IProgram 
{ 
    private readonly IServiceA _serviceA; 
    private readonly IServiceB _serviceB; 

    public Program(IServiceA serviceA, IServiceB serviceB) 
    { 
     _serviceA = serviceA; //Injected dependency 
     _serviceB = serviceB; //Injected dependency 
    } 

    public void Execute() 
    { 
     _serviceA.Foo(); 
     _serviceB.Bar(); 
    } 
} 

void Main() 
{ 
    //Composition root 
    var container = new UnityContainer(); 
    container.RegisterType<IServiceA, ServiceA>(); 
    container.RegisterType<IServiceB, ServiceB>(); 
    container.RegisterType<IProgram, Program>(); 

    //The one and only one entry point for Program 
    container.Resolve<IProgram>().Execute(); 
} 

Was ist hier los?

Program ist Ihre Hauptklasse, die alles tut. Aber es tut auch nichts. Unter SRP werden die interessanten Verantwortlichkeiten auf andere Klassen ausgelagert (ServiceA und ServiceB). Nur weiß es nicht, was die Klassen sind - nur die Schnittstellen, die es braucht. Und es weiß nicht, woher sie kommen oder wie sie erschaffen werden. Sie sind nur da, weil sie gedrückt (injiziert) werden, wenn Program erstellt wird.

Der Unity-Container ist mit Registrierungen für alle Schnittstellen ausgestattet, die Program benötigt. Es injiziert die Abhängigkeiten in Program über Konstruktorargumente (Unity tut dies automatisch für Sie). Das Programm kann dann die Dienste aufrufen, um die Aufgabe zu erledigen, wiederum ohne zu wissen, woher sie kommen.

Warum machen wir das so? Ein paar Gründe,

  1. Ein Testprogramm können andere Klassen (Stubs) erstellen, die IServiceA und IServiceB implementieren. Durch die Injektion dieser Stubbed-Abhängigkeiten kann sich der Test ausschließlich auf die Logik in Program konzentrieren.

  2. Durch die spezifischen Schnittstellen als Konstruktorargumente wird sofort ersichtlich, was genau von Program abhängt. Wenn Sie die falsche Schnittstelle erhalten, wird das Programm nicht einmal kompilieren. Das ist viel besser, als auf die Laufzeit zu warten, um herauszufinden, dass ein benötigter Dienst nicht verfügbar ist (deshalb injizieren wir nicht den Unity-Container selbst - Sie würden nicht erkennen können, welche Schnittstellen Program versuchen wird, ohne zu ziehen Lesen Sie den gesamten Code. Gleiches mit Service Locator pattern oder einfache statische Fabriken).

  3. Wenn Abhängigkeiten mithilfe des Containers eingefügt werden, können Abhängigkeiten auch Abhängigkeiten aufweisen (z. B. wenn ServiceA eine Abhängigkeit von ServiceC aufweist). Der Unity-Container wird auch in die injizierten Klassen injizieren, k und injizieren in die injizierten injizierten Klassen und so weiter.

  4. Sie verwalten die Lebensdauer (pro Anforderung, pro Thread oder pro Prozess) im Kompositionswurzel, da dort die Verantwortung liegt. Sie möchten nicht, dass die Objektlebensdauerlogik in der Codebasis verstreut ist, da dies es sehr schwierig macht, das gleiche Objekt beispielsweise in einer Anwendungsdomäne mit Threads pro Benutzer im Vergleich zu einer Dienst- oder Windows-Anwendung auszuführen, die unterschiedliche Lebenszeitregeln hat.

+0

DI ist nicht der Punkt für mich hier. Ich verwende Unity in anderen Fällen mit einer Konfigurationsdatei. Ich denke (aber nicht sicher :-)), dass ich diesen Punkt verstanden habe. Die Frage ist mehr über die Verwendung von Schnittstellen mit einer abstrakten Klasse. Soweit ich weiß, müssen wir eine Schnittstelle zur Entkopplung bereitstellen. Aber wie man Interfaces mit vererbten Klassen und verschiedenen Konstruktoren verfügbar macht. Das ist der Hauptpunkt für mich in meinem Beispiel. – Gadsweb

+0

Ich bin verwirrt, Ihre Frage enthält "Ich versuche SOLID-Prinzip mit DI zu implementieren". Wenn Sie versuchen, SOLID zu sein, dann "Wenn ich eine Instanz brauche, weiß ich, dass die Klasse, die ich brauche" ein Problem ist - Ihr Code sollte sich auf Schnittstellen stützen und nicht wissen oder sich darum kümmern, was die Klasse ist. Wenn Sie diese Denkweise nicht haben, dann ist es nicht DI oder SOLID. Fragen Sie nach einem Workaround? –

+0

Ja ja, für mich ist auch nicht immer alles klar. Um genau zu sein, zu diesem Zeitpunkt, wenn ich ClassA (seine Methoden) brauche, mache ich ClassA test = new ClassA() und nach test.Method(). Dies ist der Standardweg für mich. Aber ich möchte eine Schnittstelle verwenden und da es eine geerbte Klasse ist, weiß ich nicht, wie ich sie implementieren soll. Für mich macht die Verwendung einer Schnittstelle mit geerbten Klassen keinen Sinn ... – Gadsweb

Verwandte Themen