Search code examples
c#oopconstructordecoratorsuperclass

Why in the concrete decorator's constructor I have to repeat code of the superclass constructor?


was trying to implement the Decorator pattern - for simplicity in this example we only have 1 concrete decorator named ConcreteDecorator1 and I got it to work, the example in functional. However my question is around OOP and more specifically I don't understand why I was getting a "component is null" exception on this line of the ConcreteDecorator1 : myField = this.myField + component.Operation(); BEFORE adding this.component = component in the constructor of the same class? I mean this.component = component happens in the c-tor of the super class (Decorator) so why wasn't that enough?

class Program
{
    static void Main(string[] args)
    {

        
        var cc = new ConcreteComponent();
        cc.Operation();
        Console.WriteLine(cc.myField);

        var cd1 = new ConcreteDecorator1(cc);
        cd1.Operation();
        Console.WriteLine(cd1.myField);

        var cd2 = new ConcreteDecorator1(cd1);
        cd2.Operation();
        Console.WriteLine(cd2.myField);

        Console.ReadLine();
    }
}



abstract class Component
{

    public int myField;
    public virtual int Operation()
    {
        return myField;
    }
}

class ConcreteComponent : Component
{
    public override int Operation()
    {
        myField = 22;
        return myField;
    }
}

class Decorator : Component
{
    private Component component;
    public Decorator(Component component)
    {
        this.component = component;
    }
}

class ConcreteDecorator1 : Decorator
{
    private Component component;
    public ConcreteDecorator1(Component component) : base(component)
    {
        this.component = component; // WHY IS THIS LINE NECESSARY HERE? Don't we have it in the constructor of the base class, Decorator?
    }
    public override int Operation()
    {
        myField = 100;
        myField = this.myField + component.Operation(); // error on this line: component was null ...  UNLESS WE ADD this.component = component; in the c-tor WHy was this necessary? Doesn't the super class c-tor take care of it?
        return myField;
    }
}

So bottom line: why in the concrete decorator's constructor I have to repeat code of the superclass constructor? I thought I could leave it as this:

public ConcreteDecorator1(Component component) : base(component)
{
}

and potentially add in the body something extra, like additional logic, if needed (not needed here but just saying).

Later edit - Another working variant (Variant2):

class Program
    {
        static void Main(string[] args)
        {

            var cc = new ConcreteComponent();
            cc.Operation();
            Console.WriteLine(cc.myField);

            var cd1 = new ConcreteDecorator1(cc);
            cd1.Operation();
            Console.WriteLine(cd1.myField);

            var cd2 = new ConcreteDecorator1(cd1);
            cd2.Operation();
            Console.WriteLine(cd2.myField);

        }
    }

    abstract class Component
    {

        public int myField;
        public virtual int Operation()
        {
            return myField;
        }
    }

    class ConcreteComponent : Component
    {
        public override int Operation()
        {
            myField = 22;
            return myField;
        }
    }

    class Decorator : Component
    {
        public Component Component { get; set; }
        
    }

    class ConcreteDecorator1 : Decorator
    {
        public ConcreteDecorator1(Component component) 
        {
            this.Component = component; 
        }
        public override int Operation()
        {
            myField = 100;
            myField = this.myField + this.Component.Operation(); 
            return myField;
        }
    }

Still not great, because I would rather define the Operation in the Decorator and in the concrete decorator I would just set the myField value...

Variant 3 (later update, also works):

abstract class Component
{
    protected int myField;
    public virtual int Operation()
    {
        return this.myField;
    }
}

class ConcreteComponent : Component
{
    public ConcreteComponent(int myField)
    {
        this.myField = myField;
    }
}

class Decorator : Component
{
    protected Component component;
    public Decorator(Component component)
    {
        this.component = component;
    }

    public override int Operation()
    {
        this.myField = this.myField + component.Operation();
        return myField;
    }
}

class ConcreteDecorator1 : Decorator
{
    public ConcreteDecorator1(Component component) : base(component)
    {
        this.myField = 100;
    }

}

class ConcreteDecorator2 : Decorator
{
    public ConcreteDecorator2(Component component) : base(component)
    {
        this.myField = 1000;
    }

}

class Program
{
    static void Main(string[] args)
    {

        var cc = new ConcreteComponent(22);
        Console.WriteLine(cc.Operation());

        //var cd1 = new ConcreteDecorator(cc, 100);
        //cd1.Operation();
        //Console.WriteLine(cd1.myField);

        var cd2 = new ConcreteDecorator1(cc);
        Console.WriteLine(cd2.Operation());

        var cd3 = new ConcreteDecorator2(cd2);
        Console.WriteLine(cd3.Operation());

        Console.ReadLine();
    }
}

Solution

  • You do not have to redefine a common object private Component component;. Change the double definition of component as follows

    class Decorator : Component
    {
       public Component component;
       public Decorator(Component component)
       {
          this.component = component;
       }
    }
    
    class ConcreteDecorator1 : Decorator
    {
       public ConcreteDecorator1(Component component) : base(component)
       {
               
       }
       public override int Operation()
       {
          myField = 100;
          myField = this.myField + component.Operation(); // error on this line: component was null ...  UNLESS WE ADD this.component = component; in the c-tor WHy was this necessary? Doesn't the super class c-tor take care of it?
          return myField;
       }
    }
    

    With this code you only set the component of the parent class, not the component of this class. And when this code is executed myField = this.myField + component.Operation (); The program gives a null error.

    public ConcreteDecorator1(Component component) : base(component)
    {
        //This is wrong because the component is never set      
    }
    

    Suppose you have two variables called x. You define one globally and the other locally within a function. You must use this keyword when you want to access the global variable.

    see this example:

        static void Main(string[] args)
        {
            math m = new math();
            m.cc();
            m.dd();
        }
        class math
        {
            private int x = 100;
            public int pp(out int disX)
            {
                int x = 200;
                disX = this.x;
                return x;
            }
    
            public void cc()
            {
                int x = 50;
                Console.WriteLine(x);      //answer: 50
                Console.WriteLine(this.x); //answer: 100
            }
    
            public void dd()
            {
                int t;
                Console.WriteLine(pp(out t));    // answer: 200
                Console.WriteLine(t);            //answer: 100
            }
        }