Drawing decorator and C #

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

Why is this happening in C #, but not in Java. I java the same code, I get the corresponding output.

If I am debugging, I find that the created object hierarchy is correct, but the call to computer.getComputer () always belongs to the Super class, not in the drived class, this is a problem.

Please help me solve this problem.

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(); } } } 
+5
source share
7 answers

Decorator in C #

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

Beginning of work

Let's start by creating an interface and creating your specific Computer its implementation:

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

Computer here sealed . This is not necessary, but in this case, it is done to show that decorators exist next to your specific class, instead of extracting from it.

No abstract base layer for decorators

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 an abstract base layer for decorators

If you decide to use an abstract class to implement decorators, keep the IComputer construct in mind as a dependency. In addition, you should use base.getComputer() instead of computer.getComputer() , for example:

 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 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 how an abstract decorator works with and without .

What if we cannot change the base class

User InBetween suggested that changing the base class is not possible. If the base class already implements the interface, this is not a problem. Therefore, suppose this is not the case as in your code.

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

So, let's say that the base class is Computer and that we cannot change it:

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

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

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

Decorators remain unchanged from the previous example, since they already implement IComputer . Its completion changes a bit, since now we need to pass 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 is your code working in Java, but not in C #?

Until it implements a decorator, your code will work if computer.getComputer() was virtual . In your code in Main , Computer is of type Computer . Since getComputer() not virtual , KeyBoard.getComputer() is called instead of the expected KeyBoard.getComputer() computer.getComputer() . Since in Java every method is always virtual , this problem does not occur.

Your C # compiler should provide you with a warning that getComputer() from subclasses hides the original implementation. The warnings show that what you do will be compiled, but may not do what you expect to have here.

+9
source

computer.getComputer() in the next line

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

calls the computer version of getComputer because it is a compile-time type (since the method is not virtual).

If you require polymorphic behavior, you should mark getComputer in the computer class as virtual . Then you can completely remove the ComponentDecorator class, which does not add anything.

Why is this happening in C # but not in Java?

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

So your full 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 ""; } } 
+3
source

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 the method in the base class without receiving a compiler error (although you will get a warning). The effect of this is that the method in the derived class β€œhides” the method in the base class, and does not override it, so if you call the method through a link entered as a derived class, you get an implementation of the derived class, but if you call the method through the link entered as the base class, you get the implementation of the base class. For instance:

 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 and not in camelCase, so Computer.getComputer would be preferable to Computer.getComputer .)

0
source

To use the Decorator pattern, as in your example, you need Computer.GetComputer() be virtual. In Java, I think that all methods are virtual by default. This does not apply to C #, where you clearly need to define the method as virtual using the virtual . This is why the code works in java, but not in C #.

However, this is not the only problem in your code. Even if you create a virtual Computer.GetComputer() , the output will remain the same. Another problem is that you hare effectively hiding the base class method Computer.GetComputer() in your ComponentDecorator (the C # compiler allows you to eliminate the new keyword, although it will give you a warning). To support a virtual method, you need to define the method as public abstract override String getComputer(); . Although this may seem strange, abstract override works great in C #: What is "abstract overriding" in C #? Again this works in Java, because ComponentDecorator.GetComputer is also virtual by default.

With these two changes, your code will work quite well, although I agree with the other answers that you better just subclass Computer directly and not use DecoratorComponent . If Computer not in your code base and the GetComputer method is not virtual, you will have to use a different template.

0
source

I would go with something like this if I were to implement a 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(); } } 

Exit? -

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

enter image description here

And here are some more examples - Decorator Pattern

0
source

None of the code examples actually implement the decorator pattern (well, they did not do this when I wrote this ...). This defeats the point if specific classes and decorators are part of the same inheritance tree. In this case, it would not make sense to actually refer to a specific object, because you could just call base .

In the decorator template, your specific classes and decorators must implement a common interface. He does not rely on inheritance or 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 the interface. However, you can use an abstract class. An abstract decorator is simply reusing a link to a component, but nothing more.

The thing is, decorators should not inherit from a particular class (es), because this will defeat the point of the decorator template. As you can see, I did not even have the base decorator implement the interface to demonstrate that you do not need absolutely any late binding.

BTW: getComputer() violates C # rules. This should be a property in C # and start with a capital letter. In addition to language-specific conventions, the name of the method lies in relation to its intent.

0
source

The real solution to my problem would be

 public abstract override String getComputer() 

in ComponentDecorator , which is offered by InBetween; Because I want to decorate a computer, so it should not change, even in my case (the real application I'm working with now) cannot change anything in the class of computers.

0
source

Source: https://habr.com/ru/post/1201804/


All Articles