2015-11-19 9 views
5

Ich habe eine Klasse, die beide primitive und benutzerdefinierte Eigenschaften enthält:Ersetzen Objekt in Inheritance

public class Version_1 
{ 
    public string Name { get; set; } 
    public int Age { get; set; } 
    public WeirdDad Weird { get; set; } 
    public MysteriousDad Mysterious { get; set; } 
} 

In Zukunft i die Klasse mit einem int-Eigenschaft erweitern möchten sowie eine Anpassung von einer meiner benutzerdefinierten Objekte, in auf diese Weise:

public class Version_2 : Version_1 
{ 
    public string IdentityCode { get; set; } 
    public WeirdChild Weird { get; set; } 
} 

In Version_2 Klasse das Aussehen WeirdDad Objekt mit seinem Kind ersetzt WeirdChild so will ich es ersetzen. In diesem Beispiel habe ich stattdessen in der Version_2 Klasse WeirdDad und WeirdChild.

Wie würden Sie dieses Beispiel implementieren?

+1

Warum abstrahieren Sie seltsame und mysteriöse Vatereigenschaften, um die abstrakteste Basis, die mit Version 1 möglich ist, zurückzugeben? Die Änderung, die Sie mit v 2 vornehmen möchten, würde den Clientcode beschädigen. Kind und Vater sind beide "Personen", also, haben sie eine Rückkehr Person, machen sie es virtuell, und dann können Sie Ihren seltsamen Getter außer Kraft setzen, ohne Kunden zu bremsen, weil das verrückte Kind auch eine Person ist –

+1

Was Sie suchen, heißt "Verstecken" erhalten wahrscheinlich eine Compilerwarnung, wenn Sie den obigen Code kompilieren, aber Sie verstecken sie explizit, indem Sie die Eigenschaft "new" deklarieren, zB: 'public new WeirdChild Weird {get; einstellen; } 'Beachten Sie jedoch, dass dies bei der Verwendung des' Version_2' -Objekts, das als 'Version_1' deklariert ist, wie' Version_1 untergeordnet = neue Version_2(); verwirrend sein kann. ',' Child.Wierd' wird auf der Basisklasse ausgeführt, nicht auf der abgeleiteten Klasse ein. –

+0

Es tut mir leid, aber Ihre Frage ist nicht klar. Sie möchten nicht die Eigenschaft 'Weird' von der Basisklasse für die Instanz des 'Version_2'-Objekts sehen und diese ausblenden? –

Antwort

2

Hier einige Code, ich werde Punkte Denken Erbe + Polymorphismus genannt werden, nachdem machen ..

.
public abstract class WeirdPerson() { 
    public virtual void DoThis() { 
     // base/default implementation as desired 
    } 

    public virtual void DoThat() { // ditto } 
    public abstract void GoWildAndCrazy; 
} 

public class WeirdChild : WeirdPerson { 
    public override void DoThis() { // weird child behavior} 
    // etc. 
} 

public class WeirdDad : WeirdPerson { 
    // override and add methods as needed. 
} 

public class Version_1 
{ 
    public string Name { get; protected set; } 
    public int Age { get; protected set; } 
    public WeirdPerson WeirdFamilyMember { get; protected set; } 
    public MysteriousDad Mysterious { get; protected set; } 

    public Version_1 (WeirdChild child, string name, int age, MysteriousDad Dad) { 

    } 
} 

public class Version_2 : Version_1 { 
    public Version_2 (WeirdDad dad, string name, int age, MysteriousDad grandfather) : base (dad, name, age, grandfather) {} 
} 

  • WeirdPerson - ein allgemeineres Konzept, das grundlegende Funktionen für alle Untertypen definiert.
    • Untertypen erhalten „fixed“ Dinge wie Name-Erbe
    • Untertypen können override Standardverhalten (Methoden) - Polymorphismus
    • jetzt haben wir nicht lustig aussehenden Sachen: public class WeirdDad : WeirdChild
  • Konstrukteurs
    • Version_1, Version_2 Konstruktoren erzwingen, dass der Client den richtigen WeirdPerson Untertyp bereitstellt.
    • Der Kunde muss uns alle erforderlichen Sachen geben.
    • Wir können eingehende Argumente validieren/gegenprüfen.
  • Wir brauchen keine keine stinkin interface
    • abstract Klasse uns Standardverhalten (Methoden) und Standardzustand (Eigenschaften)
    • public Methoden und Eigenschaften jeder Klasse haben können ein Schnittstelle im allgemeinen Sinne. Wir brauchen nicht (C# -Schlüsselwort) interface, um dem Prinzip Code zu den Schnittstellen nicht Implementierung
    • abstract Methoden zu folgen zwingen Unterklassenimplementierung. gerade. mögen. ein.interface.
  • new
    • WARNUNG. Das Methodenverbergen trennt die Vererbungskette an diesem Punkt. Wenn wir dann von dieser Klasse erben, erben wir nicht die Methodenimplementierung der ursprünglichen Basisklasse.
  • Liskov ist glücklich.

Zusätzliche Refactoring

Machen Sie den Version Hierarchie parallel die WeirdPerson Hierarchie

Angenommen, dies in Ihrem Design passt. Ich denke, in einem Jahr wirst du froh sein, dass du es getan hast.

public abstract class Version_X { 
    // all the original properties here. 

    // virtual and abstract methods as needed 

    // LOOK! standard-issue constructor! 
    protected Version_X (WeirdPerson person, ...) { // common validation, etc. } 
} 

public class Version_1 : Version_X { 
    public Version_1(WeirdChild child, ...) : base (child, ...) {} 
} 

public class Version_2 : Version_X { 
    public Version_2 (WeirdDad dad, ...) {} 
} 

Bearbeiten - Motiviert von comment discussion with DVK

Das Mindeste Wissen Prinzip sagt ein Kunde sollte keine internen Details kennen müssen, um eine Klasse zu verwenden. Zu wissen, wie man die richtigen Version und Weird verfasst, ist eine Verletzung, könnte man argumentieren.

Lassen Sie uns so tun, dass ein Standardkonstruktor, für Visitor sagen wir, ist anderswo im Gesamtentwurf notwendig. Dies bedeutet, dass ein Client die Version/Weird Zusammensetzung steuern kann.

Abstraktion - lose Kopplung - ist in den abstract Klassen. Konkrete Klassen müssen notwendigerweise korrekt zusammengesetzt sein, so dass "starke Kopplung" dem Erzeugen konkreter Objekte (den Konstruktorparametern des expliziten Typs) inhärent ist, aber die zugrundeliegende lose Kopplung ermöglicht die gewünschte Flexibilität.

public enum WeirdFamily { Child, Dad, Mother, TheThing } 

public static class AdamsFamilyFactory() { 
    public static Version_X Create (WeirdFamily familyMember) { 
     switch (familyMember) { 
      case Dad: 
       return BuildAdamsDad(); 
     // . . . 
     } 
    } 
} 

public static class MunstersFactory() { // Munsters implementation } 

// client code 

List<Version_X> AdamsFamily = new List<Version_X>(); 
Version_X Pugsly = AdamsFamilyFactory.Create(WeirdFamily.Child); 
AdamsFamily.Add(Pugsly); 

List<Version_X> Munsters= new List<Version_X>(); 
Version_X Eddie= MunstersFactory.Create(WeirdFamily.Child); 
Munsters.Add(Eddie); 

DoTheMonsterMash(Munsters); 
DoTheMonsterMash(AdamsFamily); 

public void DoTheMonsterMash(List<Version_X> someWeirdFamily { 
    foreach (var member in someWeirdFamily) 
     member.GoWildAndCrazy(); 
} 
3

Ich glaube, was Sie tun wollen, heißt Methode Verstecken. Ihre Version_2 Klasse sollte wie folgt aussehen:

public class Version_2 : Version_1 
{ 
    public string IdentityCode { get; set; } 
    public new WeirdChild Weird { get; set; } 
} 

Wenn Sie die ‚Weird‘ Eigenschaft von Version_1 zugreifen möchten, werden Sie einen Anruf an base.Weird

Notieren Sie machen müssen jedoch, dass dies nicht zu empfehlen und ist ein Code-Geruch.

+1

Sollte da nicht irgendwo ein 'neues' drin sein? – CompuChip

+0

@CompuChip Ups. Ich habe es hinzugefügt xD – CodingMadeEasy

+1

Gute Antwort. Dies ist so ähnlich wie bei einem Schleudersitz. Ja, es ist verfügbar, sollte aber nur verwendet werden, wenn Sie absolut keine anderen Optionen haben. – DVK

2

IMHO, Version_2 sollte nicht erben Version_1, da es nicht ganz wie Version_1 verhalten kann:

Version_2 version_2Instance = new Version_2(); 
version_2Instance.Weird = new WeirdDad(); 

nie, dass Vererbung vergessen wird vorausgesetzt, dass die Basisklasse erweitern, modifing es nicht.

Wie wäre es Template-Klasse und eine Basis abstrakte Klasse mit:

public abstract class VersionBase<T> 
{ 
    public string Name { get; set; } 
    public int Age { get; set; } 
    public abstract T Weird { get; set; } 
    public MysteriousDad Mysterious { get; set; } 
} 

Dann:

public class Version_1 : VersionBase<WeirdDad> 
{ 
    public override WeirdDad Weird { get; set; } 
} 

public class Version_2 : VersionBase<WeirdChild> 
{ 
    public string IdentityCode { get; set; } 
    public override WeirdChild Weird { get; set; } 
} 
+0

Dies sollte gut sein, wenn nur ein T-Objekt vorhanden ist (seltsam). Aber ich habe mehrere dieser benutzerdefinierten Objekte. –

1

Sie müssen überdenken, was Sie versuchen, ein wenig zu tun, weil das, was Sie vorschlagen, bricht Liskov Ersatzprinzip.

Von: https://en.wikipedia.org/wiki/Liskov_substitution_principle

Ersetzbarkeit ist ein Prinzip in der objektorientierten Programmierung. Es besagt, dass in einem Computerprogramm, wenn S ein Subtyp von T ist, Objekte vom Typ T durch Objekte vom Typ S ersetzt werden können (dh Objekte vom Typ S können Objekte vom Typ T ersetzen), ohne irgendeinen der wünschenswerten zu verändern Eigenschaften dieses Programms (Richtigkeit, ausgeführte Aufgabe usw.).

Wenn Sie nur Version_2 wollen eine WeirdChild enthalten, und nicht jede Art von Klasse Umsetzung WeirdDad, es kann getan werden, aber Sie müssen eine Ebene zurück und starten Sie in Bezug auf Generika, Schnittstellen zu denken, und/oder abstrakte Klassen.

Zum Beispiel:

public interface IWeirdFamilyMember 
{ 
    // put some properties here 
} 

public interface IMyClass<T> where T: IWeirdFamilyMember 
{ 
    string Name { get; set; } 
    int Age { get; set; } 
    T Weird { get; set; } 
} 

Schließlich Sie Ihre Klassen als solche definieren könnte:

public class Version_1 : IMyClass<WeirdDad> 
{ 
    string Name { get; set; } 
    int Age { get; set; } 
    WeirdDad Weird { get; set; } 
} 

public class Version_2 : IMyClass<WeirdChild> 
{ 
    string Name { get; set; } 
    int Age { get; set; } 
    WeirdChild Weird { get; set; } 
} 

Das Problem mit Ksv3n dem Beispiel ist, dass Sie die Entwickler davon aus wird die WeirdChild Unterklasse von WeirdDad zuweisen zu dem Weird-Eigentum, wenn es in Wirklichkeit ein beliebiger WeirdDad oder ein WeirdDad selbst sein könnte. Dies ist ein Rezept für Laufzeitausnahmen.

+0

* Sie ÜBERNEHMEN, dass der Entwickler das WeirdChild * zuweist - das können wir annehmen, wenn es einen 'Version_2'-Konstruktor gibt, der den gewünschten Unterklasse-Typ verlangt.Diese Situation ist eine gute Lektion zum Thema Verkapselung: Lassen Sie Clients keine Eigenschaften (im Allgemeinen Zustand) direkt festlegen. – radarbob

+0

@radarbob Ja, Sie könnten den Standardkonstruktor als privat definieren, die Weird-Eigenschaft zu einer schreibgeschützten Eigenschaft machen und einen Konstruktor erstellen, der den korrekten abgeleiteten Objekttyp annimmt, aber dann andere Komplexitäten einführt und vorhandenen Code brechen, der auf a basiert parameterloser Konstruktor. Insgesamt enden Sie damit, eine ganze Menge Code zu koppeln. – DVK

+0

** 1. ** Wenn der Client einen Standardkonstruktor erhält, müssen wir dem Client erlauben, die Eigenschaften zu setzen - und das ist das Problem. ** 2. ** Wenn Verbindungsverlust bedeutet, dass die falschen (Sub-) Typen zusammengesetzt werden können, dann ist natürlich eine engere Kopplung erforderlich. ** 3. ** Wie es ist, ist die engere Kopplung bereits da - es ist impliziert, weil das falsche 'Weird' Objekt den Code brechen wird. Also zwingt der Kunde, das Richtige zu tun * pro Design * macht die Kopplung nicht enger, macht den Code weniger fehlerhaft ** 4. ** * und hält sich an das Prinzip des geringsten Wissens. * – radarbob

0

Ich stimme @CodingMadeEasy, dass die Verwendung von neuen ist ein Riechcode.

Daher schlage ich vor, Explizite Schnittstellenimplementierung zu verwenden.

interface IVersion_1 
{ 
    WeirdDad Weird { get; set; } 
} 


interface IVersion_2 
{ 
    WeirdChild Weird { get; set; } 
} 

class Version_1 : IVersion_1 
{ 
    public string Name { get; set; } 
    public int Age { get; set; } 
    public WeirdDad Weird { get; set; } 
    public MysteriousDad Mysterious { get; set; } 
} 

class Version_2 : Version_1, IVersion_2 
{ 
    public string IdentityCode { get; set; } 
    WeirdChild IVersion_2.Weird { get; set; } 
} 

So in dem Client, in Abhängigkeit von der Art der Schnittstelle wird richtig Eigenschaft