12 Stimmen

Welche Rolle spielen Delegierte bei der Dependency Injection?

In den meisten Beispielen für Dependency Injection sehe ich einfache Objekte eingespeist wird, wie in dem folgenden Beispiel SecurityManager wird injiziert in Hauptanwendung .

Es wäre jedoch naheliegend, die Delegierte wie in dem folgenden Beispiel LogHandler wird injiziert in Hauptanwendung .

Werden Delegierte im Allgemeinen nicht in der Dependency Injection verwendet? Welche Gründe sprechen für und gegen ihre Verwendung?

using System;
using System.Windows;
using System.Windows.Controls;

namespace TestSimpleDelegate82343
{
    public partial class Window1 : Window
    {
        public delegate void LogHandler(string message);

        public Window1()
        {
            InitializeComponent();
        }

        private void Button_Gui_Lax_Click(object sender, RoutedEventArgs e)
        {
            MainApplication app = new MainApplication(new LogHandler(GuiLogHandler), new LaxSecurityManager());
        }

        private void Button_Console_Lax_Click(object sender, RoutedEventArgs e)
        {
            MainApplication app = new MainApplication(new LogHandler(ConsoleLogHandler), new LaxSecurityManager());
        }

        private void Button_Gui_Tough_Click(object sender, RoutedEventArgs e)
        {
            MainApplication app = new MainApplication(new LogHandler(GuiLogHandler), new ToughSecurityManager());
        }

        private void Button_Console_Tough_Click(object sender, RoutedEventArgs e)
        {
            MainApplication app = new MainApplication(new LogHandler(ConsoleLogHandler), new ToughSecurityManager());
        }

        public void GuiLogHandler(string message)
        {
            TextBlock tb = new TextBlock();
            tb.Text = "logging: " + message;
            TheContent.Children.Add(tb);
        }

        public void ConsoleLogHandler(string message)
        {
            Console.WriteLine("logging: " + message);
        }
    }

    public interface ISecurityManager
    {
        bool UserIsEntitled();
    }

    public class LaxSecurityManager : ISecurityManager
    {
        public bool UserIsEntitled()
        {
            return true;
        }
    }

    public class ToughSecurityManager : ISecurityManager
    {
        public bool UserIsEntitled()
        {
            return false;
        }
    }

    public class MainApplication
    {
        public MainApplication(Window1.LogHandler logHandler, ISecurityManager securityManager)
        {
            logHandler("test1");
            logHandler("test2");
            logHandler("test3");
            if (securityManager.UserIsEntitled())
            {
                logHandler("secret");
            }
        }
    }

}

6voto

Mark Seemann Punkte 216836

Ich verwende gelegentlich Delegierte als Anonyme Schnittstellen - auch für DI.

Ein Problem bei diesem Ansatz ist jedoch, dass es etwas schwieriger wird, zu testen, ob die richtige Abhängigkeit injiziert und in einer Klasse verwendet wurde, da eine Delegateninstanz kein Typ ist, und man manchmal einfach nur überprüfen möchte, ob eine Klasse den richtigen Typ von Strategie/Abhängigkeit verwendet.

5voto

mo. Punkte 4039

Um auf die objektorientierten Grundsätze zurückzukommen: Eines der Hauptmerkmale eines Objekts ist, dass es ein Verhalten hat und Staat . Ich könnte mir ein Szenario vorstellen, in dem ein Log-Handler eine Art von Status (Logfilename, Datenbankverbindung usw.) beibehalten muss, aber es könnte auch ein Argument für einen Log-Handler sein, der sich nicht um den Status kümmern muss.

Wenn Ihre Abhängigkeit einen eigenen Zustand verwalten muss, verwenden Sie ein eigenes Objekt (bzw. eine Schnittstelle).

Wenn Ihre Abhängigkeit nur Verhalten und nicht Zustand hat, dann könnte ein Delegat geeignet sein, obwohl einige Leute vielleicht bequemer mit einem echten Objekt (Schnittstelle) sowieso, da es einfacher sein könnte, um Zustand-Management zu ihm später hinzufügen, wenn nötig.

Ein Vorteil von Delegaten ist, dass sie VERRÜCKT einfach mit Lambda-Ausdrücken zu spotten sind :) (auch wenn Schnittstellen auch ziemlich einfach zu überlisten sind)

Natürlich kann jeder Delegat immer noch eine normale Methode eines normalen Objekts sein, und diese Methode kann durchaus ein Verhalten haben, das den Zustand des Objekts beeinflusst, und es gibt sicherlich triftige Gründe, das zu tun, aber Sie nähern sich dem Punkt, an dem es sinnvoller sein könnte, eine Abhängigkeit vom gesamten Objekt zu nehmen, anstatt nur von einer seiner Methoden.

Weiter unten auf diesem Weg kann die Injektion von Delegierten auch eine Möglichkeit sein, die Prinzip der Schnittstellentrennung So können Sie sicherstellen, dass Ihr System nicht von Dingen abhängig ist, die es nicht braucht.

Eine weitere Anmerkung zu den Delegierten...

Es gibt fast nie einen guten Grund, einen eigenen Delegatentyp zu definieren. Die meisten Anwendungsfälle passen in die Func<> y Action<> C#-Typen (und Ereignisse, aber das ist ein anderes Thema). In Ihrem Fall, Ihre MainApplication Konstruktor sollte nicht mit einem Window1.LogHandler als Parameter, sondern stattdessen nur ein Action<string> . Dann würden Sie es einfach mit anrufen:

MainApplication app = new MainApplication(ConsoleLogHandler, new ToughSecurityManager());

oder ähnlich, da die ConsoleLogHandler Methode passt bereits in die Action<string> Unterschrift.

Und in Ihrem Test würden Sie es einfach mit instanziieren:

MainApplication app = new MainApplication(x => { /*Do nothing*/ }, new MySecurityManagerStub());

oder noch besser:

int timesCalled;
MainApplication app = new MainApplication(x => { timesCalled++ }, new MySecurityManagerStub());

Dann können Sie überprüfen, ob MainApplication die Methode genau so oft aufgerufen hat, wie Sie beabsichtigt haben.

2voto

Julien Lebosquain Punkte 39679

Ich weiß, dass MEF ermöglicht zum Beispiel die Injektion von Delegierten. Sie können jedoch auch eine ILog-Schnittstelle erstellen, die eine Log-Methode mit der gleichen Signatur wie Ihr Delegat hat. Ich denke, es wird viel klarer zu verstehen, dass die Absicht war, eine Implementierung eines Objekts in der Lage, die Protokollierung und nicht eine einzelne Log-Funktion zu injizieren.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X