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 the int property plus setting up one of my custom objects, this way:

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

In the Version_2 class, the appearance of the WeirdDad object has been replaced by its child WeirdChild, so I want to replace it. In this example, I will instead have both WeirdDad and WeirdChild in the Version_2 class.

How would you implement this example?

+5
source share
5 answers

Thinking inheritance + polymorphism

Here is the code, I will 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 is a more general concept that defines the basic material for all subtypes.
    • The subtype will receive "fixed" things, such as Name - inheritance
    • Subtype may override default behavior (methods) - polymorphism
    • now we have no fun things: public class WeirdDad : WeirdChild
  • Constructors
    • Constructors
    • Version_1 , Version_2 force the client to provide the corresponding subtype of WeirdPerson .
    • The client must provide us with all the necessary materials.
    • We can check / cross-check the incoming arguments.
  • We don't need any stinkin ' interface
    • abstract class allows us to have default behavior (methods) and default state (properties)
    • public methods and properties of any class are an interface in a general sense. We do not need to have (C # keyword) interface in order to follow the main code for non-executable interfaces
    • abstract methods enforce the implementation of a subclass. just. as. up interface .
  • new
    • A WARNING. The hide method at this point breaks the inheritance chain. If we then inherit this class, we do not inherit the implementation of the method of the original base class.
  • Liskov is happy.

Additional refactoring

Make Version hierarchy parallel to WeirdPerson hierarchy

Assuming this matches your design. I think in a year you will 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 discussion of comment with DVK

The principle of least knowledge states that the client does not need to know the internal details to use the class. The need to know how to properly compile Version and Weird is a violation, it can be argued.

Assume that the default constructor, for Visitor let's say, is needed elsewhere in the overall design. This means that the client can manage the Version / Weird composition.

Abstraction - loose communication - is in the abstract classes. Concrete classes must necessarily be correctly composed, so that a "strong connection" is inherent in the creation of specific objects (parameters of the constructor of the explicit type), but the basic free communication provides the 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(); } 
+2
source

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

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

If you want to access the "Weird" property from Version_1, you need to make a call to base.Weird

Please note that this is not recommended and is a code smell.

+3
source

IMHO, Version_2 should not inherit Version_1, since it cannot behave completely like Version_1:

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

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

How about using the template class and the 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; } } 
+2
source

You need to rethink what you are trying to do a little, because what you offer violates the principle of Liskov substitution.

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

Substitution is a principle in object-oriented programming. It says that in a computer program, if S is a subtype of T, then objects of type T can be replaced by objects of type S (i.e. objects of type S can replace objects of type T) without changing any desired property of this program (correctness task being performed, etc.).

If you want Version_2 to contain WeirdChild, and not some class that implements WeirdDad, you can do this, but you need to create a level backup and start thinking in terms of generics, interfaces and / or abstract classes.

For instance:

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

Finally, you can define your classes as such:

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

The problem with the Ksv3n example is that you REPRESENT that the developer assigns a subclass of WeirdChild WeirdDad to the Weird property, when in fact it can be any type of WeirdDad or WeirdDad itself. This is a recipe for runtime exceptions.

+1
source

I agree with @CodingMadeEasy that using the new is smelling code.

Therefore, I suggest using an implementation of an explicit interface.

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

Thus, in the client, depending on the type of interface, the correct property will be called

0
source

Source: https://habr.com/ru/post/1236356/


All Articles