简体   繁体   中英

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();

        }
    }
}

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.

computer.getComputer() in following line

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

calls Computer's version of getComputer because it is the compile time type(as method is not virtual).

If you need polymorphic behavior, you need to mark getComputer in computer class as virtual . You could then completely remove that ComponentDecorator class which adds nothing.

Why is this happens C# only not in Java?

Because by default all methods are virtual(can be overridden) in java. In c# it is not. You need to explicitly mark it virtual .

So your complete implementation becomes

public class Computer
{
    public Computer()
    {
    }

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

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


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

}

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

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

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

    public string call()
    {
        return "";
    }
}

The Computer.getComputer method is not marked as virtual , and the ComponentDecorator.getComputer method is not marked as override . In C# you can create a method in a derived class with the same signature as a method in the base class without getting a compiler error (although you will get a warning). The effect of this is is that the method in the derived class "hides" the method in the base class rather than overriding it, so if you call the method through a reference typed as the derived class you get the derived class's implementation, but if you call the method through a reference typed as the base class you get the base class implementation. For example:

void Main()
{
    DerivedHide d1 = new DerivedHide();
    Console.WriteLine(d1.GetName()); // "DerivedHide"
    Base b = d1;
    Console.WriteLine(b.GetName());  // "Base"

    DerivedOverride d2 = new DerivedOverride();
    Console.WriteLine(d2.GetName());// "DerivedOverride"
    b = d2;
    Console.WriteLine(b.GetName()); // "DerivedOverride"
}

public class Base
{
    public virtual string GetName(){ return "Base"; }
}

public class DerivedHide : Base
{
    public string GetName() { return "DerivedHide"; } // causes compiler warning
}

public class DerivedOverride : Base
{
    public override string GetName() { return "DerivedOverride"; }
}

If you add virtual to Computer.getComputer and override to ComponentDecorator.getComputer your code will work as expected.

(BTW, the convention in C# (unlike Java) is to write method names in PascalCase rather than camelCase, so Computer.GetComputer would be preferred to Computer.getComputer .)

In order to use the Decorator Pattern as in your example you need Computer.GetComputer() to be virtual. In Java I think all methods are virtual by default. That is not the case in C# where you explicitly need to define the method as virtual through the virtual keyword. That is why the code works in java but not in C#.

Still that is not the only issue in your code. Even if you make Computer.GetComputer() virtual, the output remains the same. The other issue is that you hare effectively hiding the base class Computer.GetComputer() method in your ComponentDecorator (the C# compiler allows you to elide the new keyword although it will give you a warning). To keep the method virtual, you need to define the method as public abstract override String getComputer(); . Although it may seem strange, abstract override is perfectly valid in C#: What is the use of 'abstract override' in C#? Again this works in Java because ComponentDecorator.GetComputer is also virtual by default.

With those two changes your code will work well enough although I agree with other answers in that you are better off just subclassing Computer directly instead of using the DecoratorComponent . If Computer is not in your codebase and the method GetComputer is not virtual then you will have to use a different pattern.

I would have gone with something like this, if I were to implement the decorator pattern -

public interface IComponent
{
    String getComputer();
}
public class Computer : IComponent
{
    public Computer()
    {
    }

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

public interface IComponentDecorator : IComponent
{
}

public class Disk : IComponentDecorator
{
    IComponent computer;
    public Disk(IComponent c)
    {
        computer = c;
    }


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

}

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

}

public class KeyBoard : IComponentDecorator
{
    IComponent computer;
    public KeyBoard(IComponent c)
    {
        computer = c;
    }

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

    public string call()
    {
        return "";
    }

}



class Program
{
    static void Main(string[] args)
    {
        IComponent 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();

    }
}

The output? -

You are getting a computer and a disk and a Monitor and a KeyBoard

在此输入图像描述

And here is some more samples - Decorator Pattern

None of the code examples here actually implement the decorator pattern (well, they didn't when I wrote this...). It defeats the point if the concrete classes and decorators are part of the same inheritance tree. In that case there would be no point to actually store a reference to a concrete object, because you could simply call base .

In the decorator pattern, your concrete classes and decorators should implement a common interface. It doesn't rely on inheritance nor polymorphism.

public interface IComponent
{
    String getComputer();
}

public class Computer : IComponent
{
    public String getComputer()
    {
        return "computer";
    }
}

public abstract class ComponentDecorator
{
    protected ComponentDecorator(IComponent component)
    {
        this.Component = component;
    }

    protected IComponent Component { get; private set; }
}

public class Disk : ComponentDecorator, IComponent
{
    public Disk(IComponent c) : base(c)
    {
    }

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

public class Monitor : ComponentDecorator, IComponent
{
    public Monitor(IComponent c)
        : base(c)
    {
    }

    public String getComputer()
    {
        return this.Component.getComputer() + " and a monitor";
    }
}

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

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

I extracted an interface. You could use an abstract class all the same, though. The abstract decorator is simply to re-use storing the component reference, but nothing more.

The point is that the decorators shouldn't inherit from the concrete class(es), because that would defeat the point of the decorator pattern. As you can see, I didn't even make the base decorator implement the interface to demonstrate that you need absolutely no late binding.

BTW: getComputer() violates the C# conventions. It should be a property in C# and start with a capital letter. Apart from language-specific conventions, the name of the method is lying about its intention.

The real solution to my problem will be

public abstract override String getComputer() 

in ComponentDecorator which is suggested by InBetween; Because I want to decorate Computer, so it should be unchanged, even in my case (real application I am working with now) it is not possible to change anything in Computer class.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM