1

AKTUALISIERT: So ziemlich jeder hier hat mir gesagt, dass ich nur neu anfangen muss auf, wie ich meine Klassen entwarf (danke Leute dafür Ihre ausgezeichneten Antworten übrigens!). Den Hinweis nehmend, fing ich an, umfangreiche Lesung auf dem strategy pattern zu machen. Ich möchte Verhaltensklassen (oder Strategieklassen) erstellen, die von einer abstrakten Basisklasse oder -klassen erben. Die Kandidatenklasse würde dann Eigenschaften mit den verschiedenen abstrakten Basisklassen/Klassen als Type für die Verhaltensweisen oder Strategien haben. vielleicht etwas wie das:Wie man geteiltes Verhalten zwischen Klassen (ohne mehrfache Vererbung des Kurses) in C#

public abstract class SalaryStrategy { 
    public abstract decimal Salary { get; set; } 
    public abstract decimal Min { get; set; } 
    public abstract decimal Mid { get; set; } 
    public decimal CompaRatio { 
     get { 
      if (this.Mid == 0) { return 0; } 
      else { return this.Salary/this.Mid; } 
     } 
    } 
} 

public class InternalCurrentSalaryStrategy { 
    public override decimal Salary { get; set; } 
    public override decimal Min { 
     get { return this.Salary * .25m; } 
     set { } 
    } 
    public override decimal Mid { get; set; } 
} 

public class Candidate { 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public SalaryStrategy CurrentSalaryStrategy { get; set; } 
} 

public static void Main(string[] args) { 
    var internal = new Candidate(); 
    internal.CurrentSalaryStrategy = new InternalCurrentSalaryStrategy(); 
    var internalElp = new Candidate(); 
    internalElp.CurrentSalaryStrategy = new InternalCurrentSalaryStrategy(); 
    var elp = new Candidate(); 
    // elp.CurrentSalaryStrategy can stay null cause it's not used for elps 
} 

Kommentare oder Anregungen?


ORIGINAL Frage:

ich versuche zu lernen und mehr beherrschen zu Design Patterns und Prinzipien werden. Ich arbeite gerade an einem Design für wenige Klassen, das mich überhäuft hat. Hier ist eine sehr verkürzte Version des Codes:

public class Candidate { 
    public int Id { get; set; } 
    public string Comments { get; set; } 
    // lots more properties and behaviors... 
} 

public class InternalCandidate : Candidate { 
    public decimal CurrentMid { get; set; } 
    public decimal CurrentMax { 
     get { return this.CurrentMin * 1.3m; 
    } 
    // lots more properties and behaviors... 
} 

public class EntryLevelCandidate : Candidate { 
    public string Gpa { get; set; } 
    // lots more properties and behaviors... 
} 

public class InternalEntryLevelCandidate /* what do I inherit here??? */ { 
    // needs all of the properties and behaviors of 
    // EntryLevelCandidate but also needs the CurrentMin and 
    // CurrentMax (and possibly more) in InternalCandidate 
} 

Die InternalEntryLevelCandidate Klasse in erster Linie ein EntryLevelCandidate ist aber braucht einige der Implementierungen von InternalCandidate zu teilen. Ich sage Implementierungen, weil ich nicht möchte, dass sich die Implementierungen unterscheiden oder wiederholen, sonst würde ich eine Schnittstelle für allgemeine Verträge verwenden und konkrete Implementierungen in jeder Klasse haben. Einige Implementierungen der Eigenschaften und Verhaltensweisen von InternalCandidate müssen gemeinsam oder gemeinsam verwendet werden. Ich habe über C++ - und Ruby-Mixins gelesen, die etwas ähnlich zu dem zu sein scheinen, was ich machen möchte. Ich lese auch diesen interessanten Blogbeitrag, in dem eine Idee für einen Verhaltenstyp diskutiert wird, bei dem eine Klasse mehrere Verhaltensweisen erben kann, während eine einzelne Beziehung beibehalten wird: http://www.deftflux.net/blog/post/A-good-design-for-multiple-implementation-inheritance.aspx. Dies scheint zu vermitteln, was ich will. Kann mir jemand eine Anleitung geben, wie ich dies mit guten Designpraktiken erreichen kann?

+0

Ich glaube, Sie durch ein Beispiel denken müssen, die eindeutig Mehrfachvererbung muss, hart sein sein Gehen wirklich –

+0

@ samob99: Erstens, thx für alle Ihre Vorschläge und Gedanken! Ich versuche nicht speziell, Mehrfachvererbung zu verwenden.Ich habe mich vielleicht in ein Szenario verschlüsselt, in dem ich zweideutig mehrfache Vererbung benötige, aber mein Problem ist, dass ich nicht weiß, wie ich es wieder programmieren soll. Wie sollte ich diese Klassen entworfen haben, damit ich nicht mehrfache Vererbung benötige? Ein Codebeispiel, das mit einem Entwurfsprinzip verknüpft ist, wäre am hilfreichsten. – gabe

Antwort

5

Unveränderbare Datenwertklassen. Wenn Eigenschaften in Ihren verschiedenen Candidate-Unterklassen eine Art von sinnvollen Datenwert darstellen, erstellen Sie eine unveränderliche Klasse dafür mit den von Ihnen benötigten Verhaltensweisen. Jede Ihrer verschiedenen Candidate-Unterklassen kann dann den Datentyp verwenden, aber Ihr Code ist immer noch in den Datenklassen eingekapselt.

Erweiterungsmethoden. Diese könnten überladen werden, um mit beliebigen Klassen zu arbeiten.

Ich würde das Dekoratormuster vermeiden und mit kompilierter/reflektierbarer Funktionalität bleiben.

Zusammensetzung. Entwickeln Sie sofort die einzigartigen Verhaltensweisen in separaten Klassen und bauen Sie Ihre Kandidaten-Klassen um sie herum, anstatt einzigartige Verhaltensweisen in Ihren Kandidaten-Klassen zu schreiben und später ihre Funktionalität für verwandte Klassen herauszuziehen.

Je nachdem, wie Sie die Klassen verwenden, können Sie auch explizite und implizite Konvertierungsoperatoren in einen verwandten Typ implementieren und implementieren, anstatt die Schnittstellen neu zu implementieren (die Sie vermeiden wollten) die Art/Implementierung, die Sie für welchen Zweck auch immer benötigen. Eine andere Sache, die ich gerade dachte, bezogen sich auf den letzten Absatz, ist ein Leasing-System, wo Ihre Klasse spawns und Objekt des entsprechenden Typs, erlaubt es manipuliert werden, dann konsumiert es später, um die aktualisierten Informationen zu assimilieren .

1

Ich stimme zu, dass Vererbung hier nicht das Richtige zu sein scheint. Ich bin mir nicht sicher, ob ich die perfekte Antwort kenne, aber vielleicht ist die Decorator pattern angemessen.

Eine andere, eher esoterische Idee ist es, über aspektorientierte Programmierung nachzudenken. Sie können einige ziemlich erstaunliche Dinge mit Aspekten tun, aber es ist ein sehr fortgeschrittenes Thema, das ich noch nicht gemeistert habe. Die Art von Leuten, die haben, sind wie Rikard Oberg und seine Qi4J Kohorten.

+0

Ich habe auch über Decorator nachgedacht. Die Instantiierung von Kandidaten wird jedoch ein bisschen schwierig sein und braucht einige Überlegungen. Vielleicht eine Fabrik, die Kandidaten erstellt und sie entsprechend der Art des Kandidaten dekoriert, was auch bedeutet, dass Sie eine Art Kandidatenstatus oder -typ definieren müssen. Es muss einen besseren Weg geben. –

+0

Ich habe über das Decorator-Muster auf http://www.dofactory.com/Patterns/PatternDecorator.aspx gelesen, aber ich hatte Schwierigkeiten zu verstehen, wie ich es für meinen Code implementiere. Könnten Sie einen Blick auf die Implementierung mit meinem Code werfen, wenn Sie mit dem Muster vertrauter sind? Jede Hilfe wäre willkommen. – gabe

+0

Keine Zeit im Moment Gabe, aber ich werde versuchen, es bald zu bekommen. – duffymo

2

Haftungsausschluss:

Meiner Erfahrung nach Mehrfachvererbung benötigen ist eher die Ausnahme als die Regel, kann eine sorgfältige Gestaltung von Klassenhierarchien in der Regel vermeiden Sie diese Funktion benötigen. Ich stimme JP zu, dass diese Anforderung in Ihrer Stichprobe vermieden werden könnte.

Zurück zur Frage, gibt es keine saubere Lösung, aber Sie ein paar Optionen:

  • Verwenden Erweiterungsmethoden, haben den Nachteil, dass richtigen Resolve klickt nicht funktioniert, auch wirklich diese einige Leute nicht mögen Welpen.

  • Erstellen Sie ein Aggregatobjekt, das die Instanzen der einzelnen Klassen enthält, die Sie zusammensetzen möchten, und implementieren Sie Stubmethoden, die delegieren.

  • Definieren Sie eine Schnittstelle für jedes Verhalten und lassen Sie die Methoden in der Basis überprüfen, ob this is IInterface, bevor Sie das Verhalten ausführen. (Ermöglicht es Ihnen, Verhaltensdefinitionen auf die Basis zu ziehen)

In der Nähe Duplikat: Multiple inheritance in C#

4

Hier ist eine wissenschaftliche Arbeit über das Thema, das ich denke, ist ziemlich interessant (PDF link).

Aber ich denke, Sie versuchen Geschäftslogik in Ihren Verallgemeinerungen auferlegen. Sie wissen zufällig, dass ein InternalCandidate seinen GPA nie sehen lassen wird. Aber ein InternalCandidate hat sicherlich einen GPA. Also, du hast diesen seltsamen Typen namens InternalEntryLevelCandidate geknackt, weil du zufällig weißt, dass du dir den GPA dieses Mannes ansehen willst. Architektonisch denke ich, dass das EntryLevelCandidate falsch ist. Ich würde einem Kandidaten ein "Level" -Konzept hinzufügen und ihm ein GPA geben. Es liegt an der Geschäftslogik zu entscheiden, ob sie auf die GPA schauen oder nicht.

Edit: Auch Scott Meyers macht eine gute Arbeit der Zerlegung dieses Problems in seinen Büchern.

+0

@JP: Thx für die Antwort, JP! Meine Anforderungen in diesem Fall erfordern jedoch nicht die GPA überhaupt in InternalCandidate verwendet werden. Das war nur ein Beispielcode. Die Gpa-Eigenschaft stellt nur eine Eigenschaft dar, die für Candidate oder abgeleitete Klassen mit Ausnahme von EntryLevelCandidate und den daraus abgeleiteten Klassen nicht benötigt/verwendet/gespeichert wird. – gabe

+0

Verstehen. Sie denken, dass die allgemeine Antwort darin besteht, die Beziehung zu überdenken und zu überdenken, um den Diamanten zu vermeiden. :) –

+0

Würdest du mir ein Codebeispiel von dem geben können, was du vorschlägst? Dieser Designraum ist etwas, mit dem ich seit einiger Zeit zu kämpfen habe. Eine Implementierung zu sehen würde mir sehr helfen. – gabe

0

Ich würde nur die Delegation pattern verwenden. Letztendlich würde ich eine Schnittstelle für jede einzelne Funktionalität verwenden und dann eine konkrete Klasse als Delegat für jede Schnittstelle haben. Dann verwenden Ihre letzten Klassen nur die Delegaten, die sie benötigen, und können von mehreren Schnittstellen erben.

public class InternalEntryLevelCandidate : EntryLevelCandidate { 
    private InternalCandidate internalCandidateDelegate 
     = new InternalCandidate(); 

    public decimal CurrentMid { 
     get { return internalCandidateDelegate.CurrentMid; } 
     set { internalCandidateDelegate.CurrentMid = value; } 
    } 

    public decimal CurrentMax { 
     get { return internalCandidateDelegate.CurrentMax } 
    } 
}