-2

CannibalGhost : Ghost (CannibalGhost dervies from Ghost)

CannibalGhost can eat any sort of Ghosts and get their Height added to his Height. However Ghosts should be able to be as big as they want, while CannibalGhosts should only be at a maximum Height of 50. It can't surpass under any circumstances. Not via. set-accessor and not via constructor.

I have tried so far:

  • override Constructor:

    Can't override constructor

  • implementing a if(height >= 50) in Set-Accessor

    does not work if I create an object with a constructor in the Main-Program

  • implementing a barrier in Main-Code so height never gets over 50

    does not work if I want new objects of CannibalGhost later (and is a mess)

  • writing it so CannibalGhost does not derive from Ghost

    So I am able to write a new constructor and set-Accessor with a ceiling of 50. It worked but I want CannibalGhosts also to be a Ghost (derive from Ghost)

Summary: How can I set the property Height of CannibalGhost to max 50, but not in the base class Ghost?

Community
  • 1
  • 1
WiseWars
  • 3
  • 2
  • Maybe you create the *Ghosts in a Factory, which does the Height check: https://en.wikipedia.org/wiki/Factory_method_pattern I do not like an approach where the constructor throws an exception if height > 50 – Markus Meyer Oct 12 '20 at 11:29
  • Please show some code. – Fildor Oct 12 '20 at 11:53

2 Answers2

4

You could build Ghost in such a way that it knows that some validation is required in derived class(es) without implementing the logic itself in the base class

public class Ghost
{
    private int height;

    protected virtual bool IsValidHeight(int newHeight)
    {
        return true;
    }

    public int Height
    {
       get{ return height; }
       set
       {
           if(!IsValidHeight(value) throw new InvalidHeightException(value);
           height = value;
       }
    }
}

public class CannibalGhost 
{
    protected override bool IsValidHeight(int newHeight
    {
        return newHeight <= 50;
    }
}

perhaps instead of throwing an exception you want to limit the height you could make that virtual method

protected virtual int LimitHeight(int newHeight)
{
     return newHeight;
}

and

protected override int LimitHeight(int newHeight)
{
    return (int)Math.Min(newHeight,50);
}

then the setter would just be

set
{
    height = LimitHeight(value);  
}

This would have no effect on Ghost but would limit the derived CannibalGhost to 50.


In truth, this is not a great solution. You can't know when designing a base class all the places you might want this sort of validation down the line - and its the reason inheritance breaks down for more complex solutions than this simple example.

Does CannibalGhost really have an is-a relationship with Ghost? Or is it actually composed of multiple Ghost instances (a has-a-collection-of relationship). In this case it's height could merely be a sum of all its composed Ghost instances with a limit of 50. No inheritance required. Perhaps all ghosts implement IGhost for shared behaviours - something to consider!

Jamiec
  • 133,658
  • 13
  • 134
  • 193
0

By making the Height property virtual in the base class you can have derived classes implement more specific behaviour by overriding it.

Example:

public class Ghost
{
    public virtual int Height { get; set; }
}

public class CannibalGhost : Ghost
{
    private int _height;
    public override int Height
    {
        get
        {
            return _height;
        }
        set
        {
            if (value > 50)
                throw new InvalidOperationException($"A {nameof(CannibalGhost)} cannot have a height over 50");

            _height = value;
        }
    }
}
Richiban
  • 5,569
  • 3
  • 30
  • 42
  • A problem to watch out for with this approach comes if you want to initialise `Height` in the base class constructor. In that event, you'd be [making a virtual member call from a constructor](https://stackoverflow.com/questions/119506/virtual-member-call-in-a-constructor) which is bad. – Matthew Watson Oct 12 '20 at 11:35
  • @MatthewWatson Correct. If anyone finds themselves in this situation then I would move the `_height` field into the base class and make it protected. Then you can set it directly from the constructor without worry. – Richiban Oct 12 '20 at 11:45