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.