Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Replace object in Inheritance

I have a class that contains both primitive and custom properties:

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 the future i want to extend the class with a int property plus a customization of one of my custom objects, in this way:

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

In Version_2 class the look of WeirdDad object has been replaced with its child WeirdChild so i want to substitute it. In this example, instead, i will have in the Version_2 class both WeirdDad and WeirdChild.

How would you implement this example?

like image 254
Max Bertoli Avatar asked Nov 19 '15 17:11

Max Bertoli


3 Answers

I believe what you want to do is called method hiding. Your Version_2 class should look like this:

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

When you want to access the 'Weird' property from Version_1, you will have to make a call to base.Weird

Make note however that this is not recommended and is a code smell.

like image 65
CodingMadeEasy Avatar answered Sep 19 '22 12:09

CodingMadeEasy


IMHO, Version_2 should not inherit Version_1 as it cannot behave totally like Version_1 :

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

Never forget that inheritance assumes that you are extending base class, not modifing it.

How about using template class and a base abstract class :

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; }
}

Then:

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; }
}
like image 24
Perfect28 Avatar answered Sep 21 '22 12:09

Perfect28


Think inheritance + polymorphism

Here's some code, I'll make points after ...

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 - a more general concept that defines basic stuff for all sub-types.
    • Sub-type will get "fixed" things like Name - inheritance
    • Sub-type can override default behavior (methods) - polymorphism
    • now we don't have funny looking stuff: public class WeirdDad : WeirdChild
  • Constructors
    • Version_1, Version_2 constructors force the client to provide the proper WeirdPerson sub-type.
    • Client must give us all required stuff.
    • We can validate / cross-validate incoming arguments.
  • We don't need no stinkin' interface
    • abstract class allows us to have default behavior (methods) and default state (properties)
    • public methods and properties of any class is an interface in the general sense. We do not need to have (C# keyword) interface in order to follow the principle code to interfaces not implementation
    • abstract methods force sub-class implementation. just. like. an. interface.
  • new
    • WARNING. Method hiding severs the inheritance chain at that point. If we then inherit from this class we are not inheriting the original base class' method implementation.
  • Liskov is happy.

Additional Refactoring

Make the Version heirarchy parallel the WeirdPerson heirarchy

Assuming this fits into your design. I think, a year from now you'll be glad you did.

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, ... ) {}
}

Edit - Motivated by comment discussion with DVK

The least knowledge principle says a client should not have to know internal details to use a class. Needing to know how to compose the correct Version and Weird is a violation, one could argue.

Let's pretend that a default constructor, for Visitor let's say, is necessary elsewhere in the overall design. Allowing this means a client can control the Version/Weird composition.

Abstraction - loose coupling - is in the abstract classes. Concrete classes must necessarily be correctly composed so "strong coupling" is inherent in creating concrete objects (the explicit-type constructor parameters), but the underlying loose coupling allows for desired flexibility.

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();
}
like image 44
radarbob Avatar answered Sep 20 '22 12:09

radarbob