23 Stimmen

Entwurfsmuster, das anstelle der Mehrfachvererbung zu verwenden ist

Ich komme aus einem C++-Hintergrund und bin an Mehrfachvererbung gewöhnt. Ich mag das Gefühl einer Schrotflinte, die direkt auf meinen Fuß gerichtet ist. Heutzutage arbeite ich mehr in C# und Java, wo man nur eine Basisklasse erben, aber eine beliebige Anzahl von Schnittstellen implementieren kann (habe ich die Terminologie richtig verstanden?).

Nehmen wir zum Beispiel zwei Klassen, die eine gemeinsame Schnittstelle implementieren, aber unterschiedliche (aber erforderliche) Basisklassen:

public class TypeA : CustomButtonUserControl, IMagician
{
    public void DoMagic()
    {
        // ...
    }
}

public class TypeB : CustomTextUserControl, IMagician
{
    public void DoMagic()
    {
        // ...
    }
}

Beide Klassen sind UserControl s, so dass ich die Basisklasse nicht ersetzen kann. Beide müssen die DoMagic Funktion. Mein Problem ist nun, dass beide Implementierungen der Funktion identisch sind. Und ich hasse Copy-and-Paste-Code.

Die (möglichen) Lösungen:

  1. Ich möchte natürlich TypeA y TypeB eine gemeinsame Basisklasse zu teilen, in der ich die identische Funktionsdefinition nur einmal schreiben kann. Aufgrund der Beschränkung auf nur eine Basisklasse kann ich jedoch keinen Platz in der Hierarchie finden, an den sie passt.
  2. Man könnte auch versuchen, eine Art von zusammengesetztes Muster . Das Einsetzen der DoMagic Funktion in einer separaten Hilfsklasse, aber die Funktion hier benötigt (und ändert) eine ganze Reihe von internen Variablen/Feldern. Sie alle als (Referenz-)Parameter zu senden, würde einfach schlecht aussehen.
  3. Mein Gefühl sagt mir, dass die Adapterschema könnte hier einen Platz haben, eine Klasse, die bei Bedarf zwischen den beiden konvertiert. Aber es fühlt sich auch hakelig an.

Ich habe dies als sprachenunabhängig gekennzeichnet, da es für alle Sprachen gilt, die diesen Ansatz "eine Basisklasse - viele Schnittstellen" verwenden.

Bitte weisen Sie mich auch darauf hin, wenn ich eines der von mir genannten Muster missverstanden habe.

In C++ würde ich einfach eine Klasse mit den privaten Feldern und der Funktionsimplementierung erstellen und sie in die Vererbungsliste aufnehmen. Was ist der richtige Ansatz in C#/Java und dergleichen?

10voto

Dinah Punkte 50664

Sie können das Strategiemuster oder etwas Ähnliches verwenden, um die hat eine (Zusammensetzung) anstelle von ist eine (Vererbung):

public class TypeA : CustomButtonUserControl, IMagician {
    IMagician magicObj = new Magical();
    public void DoMagic() {
        magicObj.DoMagic();
    }
}

public class TypeB : CustomButtonUserControl, IMagician {
    IMagician magicObj = new Magical();
    public void DoMagic() {
        magicObj.DoMagic();
    }
}

public class Magical : IMagician {
    public void DoMagic() {
        // shared magic
    }
}

Es gibt noch andere Möglichkeiten, Ihre privaten IMagician-Mitglieder zu instanziieren (z. B. indem Sie sie als Parameter per Konstruktor übergeben), aber die obige Vorgehensweise sollte Ihnen den Einstieg erleichtern.

9voto

Patrick Karcher Punkte 22087
  • In .Net können Sie Erweiterungsmethoden auf Schnittstellen anwenden. Es ist wirklich toll, wenn es möglich ist, und für Sie anwendbar, weil es eine seltene Möglichkeit ist, eine gemeinsame Umsetzung zu einer Schnittstelle. Sicherlich in Betracht ziehen, aber es könnte nicht für Sie arbeiten, da Sie sagen, dass DoMagic arbeitet mit vielen Privatmitgliedern zusammen. Können Sie diese privaten Variablen verpacken internal möglicherweise? Auf diese Weise könnte die Erweiterungsmethode auf sie zugreifen.
  • Die gemeinsame Funktionalität in einer anderen Klasse haben. Wenn es einen logischen Platz für diese gemeinsame Funktionalität gibt, übergeben Sie Ihre Objekte an diese Methode der anderen Klasse (vielleicht handelt es sich um eine UI-Funktionalität, und Sie haben bereits einen UI-Helper ). Auch hier gilt: Können Sie die privaten Daten mit einer internen/öffentlichen Eigenschaft offenlegen? (Sicherheit/Einkapselung ist bei all dem natürlich ein Thema. Ich weiß nicht, ob Ihre Klassen nur für den internen Gebrauch bestimmt sind oder ob sie öffentlich zugänglich gemacht werden sollen).
  • Andernfalls übergeben Sie der schnittstellendefinierten Methode eine eigene Funktionalitätsklasse (oder einen spezifischen Funktionszeiger). Sie müssten zwar ein wenig doppelten Code verwenden, um Ihre privaten Variablen an diesen externen Funktionsverweis zu übergeben, aber das wäre nicht viel, und Ihre Implementierung befände sich an einer Stelle.
  • Vielleicht machen wir es zu kompliziert. Sie werden sich heute Abend vor dem Einschlafen nicht ganz objektorientiert fühlen, aber könnten Sie irgendwo in Ihrer Bibliothek eine statische Routine haben, die alle IMagician-Implementierer aufrufen?
  • Am Ende könnte Adapter tatsächlich das sein, wonach Sie suchen. Weniger wahrscheinlich, aber dennoch eine Überlegung wert, ist die Dekoratives Muster .

Wenn Ihnen nichts besonders gut erscheint, wählen Sie das, was Ihnen am besten gefällt, es benutzen ein paar Mal, und morgen neu anordnen :)

3voto

Roman Punkte 61632

Ersetzen Sie Vererbung durch Komposition.

Verschieben Sie Ihre "gemeinsame" Funktion in eine separate Klasse, erstellen Sie eine Instanz dieser Klasse und fügen Sie sie in die Objekte TypA und TypB ein.

3voto

Justin Niessner Punkte 235353

Ihr Bauchgefühl ist in diesem Fall richtig. Das Adapter-Muster ist das, wonach Sie suchen.

DoFactory hat gute .NET-Beispiele (die auch ihren Java-Pendants ziemlich nahe kommen sollten):

Adapter-Entwurfsmuster in C# und VB.NET

1voto

Björn Pollex Punkte 72424

Das zusammengesetzte Muster ist für komplexe Objekte gedacht, d. h. der Schwerpunkt liegt auf einem Objekt, das aus anderen Objekten besteht. Das Strategie-Muster kann als ein Spezialfall davon betrachtet werden, aber eine Strategie muss kein Objekt sein. Ich denke, dass dies eher auf Ihren Fall zutrifft. Andererseits hängt dies stark von der Art des Gegenstands ab. DoMagic() tut.

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