Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Decorator pattern and C#

I tried to run the following sample program in C# I get the output "You are getting a computer" instead of "You’re getting a computer and a disk and a monitor and a KeyBoard".

Why is this happens C# only not in Java. I java the same code I get the appropriate output.

If I debug I found created object hierarchy is correct but invoking computer.getComputer() always goes to Super class not in drived class, that is the problem.

Please help me to solve this issue.

namespace DecoratorTest1
{

    public class Computer
    {
        public Computer()
        {
        }

        public  String getComputer()
        {
            return "computer";
        }
    }


    public abstract class ComponentDecorator : Computer
    {
        public abstract String getComputer();
    }

    public class Disk : ComponentDecorator
    {
        Computer computer;
        public Disk(Computer c)
        {
            computer = c;
        }


        public override String getComputer()
        {
            return computer.getComputer() + " and a disk";
        }

    }

    public class Monitor : ComponentDecorator
    {
        Computer computer;
        public Monitor(Computer c)
       {
            computer = c;
        }
        public override String getComputer()
        {
            return computer.getComputer() + " and a Monitor";
        }

    }

    public class KeyBoard : ComponentDecorator
    {
        Computer computer;
        public KeyBoard(Computer c)
       {
            computer = c;
        }

        public  override String getComputer()
        {
            return computer.getComputer() + " and a KeyBoard";
        }

        public string call()
        {
            return "";
        }

    }



    class Program
    {
        static void Main(string[] args)
        {
            Computer computer = new Computer();
            computer = new Disk(computer);
            computer = new Monitor(computer);
            computer = new KeyBoard(computer);


            Console.WriteLine(" You are getting a " + computer.getComputer());
            Console.ReadKey();

        }
    }
}
like image 266
msaifurr Avatar asked Sep 04 '14 10:09

msaifurr


People also ask

Does C have design patterns?

All functions in C always use the same basic pattern (syntax). This syntax can be captured in a "Design Pattern".

What is decorator in C?

Decorator is a structural pattern that allows adding new behaviors to objects dynamically by placing them inside special wrapper objects, called decorators. Using decorators you can wrap objects countless number of times since both target objects and decorators follow the same interface.

What is C pattern design?

Design Patterns in the object-oriented world is a reusable solution to common software design problems that occur repeatedly in real-world application development. It is a template or description of how to solve problems that can be used in many situations. "A pattern is a recurring solution to a problem in a context."

What is the decorator pattern used for?

The Decorator Pattern allows class behavior to the decorated dynamically. It's a structural design pattern as it's used to form large object structures across many disparate objects. The concept of decorator is that it adds additional attributes to an object dynamically.


1 Answers

Decorator in C#

When using the decorator pattern, the idea is to have several classes implementing the same interface. One of them is the normal concrete implementation of the interface, Computer in your case. The others add something to the behavior of Computer. We can get rid of the ComponentDecorator. You can create an abstract decorator class that implements the IComputer interface, but you don't have to.

Getting started

We start by creating the interface and making your concrete Computer implement it:

public interface IComputer
{
    string getComputer();
}

public sealed class Computer : IComputer
{
    public string getComputer()
    {
        return "computer";
    }
}

Computer here is sealed. It doesn't have to be, but is done in this case to show that decorators exist next to your concrete class, instead of deriving from it.

Without abstract baseclass for decorators

The decorators implement IComputer instead of ComponentDecorator:

public class Disk : IComputer
{
    IComputer _computer;
    public Disk(IComputer computer)
    {
        _computer = computer;
    }

    public String getComputer()
    {
        return _computer.getComputer() + " and a disk";
    }
}

public class Monitor : IComputer
{
    IComputer _computer;
    public Monitor(IComputer computer)
    {
        _computer = computer;
    }

    public String getComputer()
    {
        return _computer.getComputer() + " and a Monitor";
    }
}

public class KeyBoard : IComputer
{
    IComputer _computer;
    public KeyBoard(IComputer computer)
    {
        _computer = computer;
    }

    public  String getComputer()
    {
        return _computer.getComputer() + " and a KeyBoard";
    }
}

With abstract baseclass for decorators

If you do choose to use an abstract class to implement decorators, have it take an IComputer as dependency in the constructor. Furthermore, you should then use base.getComputer() instead of computer.getComputer(), like so:

public abstract class ComputerDecorator : IComputer
{
    private IComputer _computer;
    public ComputerDecorator(IComputer computer)
    {
        _computer = computer;
    }

    public virtual string getComputer()
    {
        return _computer.getComputer();
    }
}

public class Disk : ComputerDecorator
{
    public Disk(IComputer computer) : base(computer)
    {
    }

    public override String getComputer()
    {
        return base.getComputer() + " and a disk";
    }
}

public class Monitor : ComputerDecorator
{
    public Monitor(IComputer computer) : base(computer)
    {
    }

    public override String getComputer()
    {
        return base.getComputer() + " and a Monitor";
    }
}

public class KeyBoard : ComputerDecorator
{
    public KeyBoard(IComputer computer) : base(computer)
    {
    }

    public override String getComputer()
    {
        return base.getComputer() + " and a KeyBoard";
    }
}

In both cases, we can wrap it all up in the same way:

class Program
{
    public static void Main(string[] args)
    {
        IComputer computer = new KeyBoard(new Monitor(new Disk(new Computer())));

        Console.WriteLine(" You are getting a " + computer.getComputer());
    }
}

See it work with and without abstract decorator.

What if we can't change the base class

User InBetween suggested that it might not be possible to change the base class. If the base class already implements an interface, that's not an issue. So let's assume that it doesn't, like in your code.

To implement decorator in this case, we first need to create an adapter for our base class and implement our decorator alongside it.

So let's assume the base class is Computer and that we can't change it:

public sealed class Computer
{
    public string getComputer()
    {
        return "computer";
    }
}

To create an adapter, we create the IComputer interface like before, and a class that wraps Computer:

public sealed class ComputerAdapter : IComputer
{
    private Computer _computer;
    public ComputerAdapter(Computer computer)
    {
        _computer = computer;
    }

    public string getComputer()
    {
        return _computer.getComputer();
    }
}

The decorators remain unchanged from the previous example, since they already implement IComputer. Wrapping it up changes a little, since we now have to pass a Computer to our ComputerAdapter instance:

class Program
{
    public static void Main(string[] args)
    {
        Computer sealedComputer = new Computer();
        IComputer computer = new KeyBoard(new Monitor(new Disk(new ComputerAdapter(sealedComputer))));

        Console.WriteLine(" You are getting a " + computer.getComputer());
    }
}

But the result is the same, as can be seen here.

Why does your code work in Java, but not in C#?

While it doesn't actually implement decorator, your code would work if Computer.getComputer() was virtual. In your code, in Main, computer is of type Computer. Since getComputer() is not virtual, Computer.getComputer() is called instead of the intended KeyBoard.getComputer(). Since in Java every method is always virtual, that problem doesn't happen.

Your C# compiler should be giving you a warning that getComputer() from subclasses is hiding the original implementation. Warnings indicate that what you're doing will compile, but might not do what you expect, which is the case here.

like image 101
Bart van Nierop Avatar answered Sep 17 '22 22:09

Bart van Nierop