msa*_*urr 5 c# design-patterns decorator
我试图运行在C#中下面的示例程序,我得到的输出"你要一部电脑",而不是"你得到一台电脑和一盘和一个显示器和键盘".
为什么这只发生在C#中,而不是在Java中.我java相同的代码我得到了适当的输出.
如果我调试我发现创建的对象层次结构是正确的但是调用computer.getComputer()总是去超级类而不是驱动类,这就是问题所在.
请帮我解决这个问题.
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();
}
}
}
Run Code Online (Sandbox Code Playgroud)
Bar*_*rop 11
使用装饰器模式时,想法是让几个类实现相同的接口.其中一个是接口的正常具体实现,Computer在您的情况下.其他人添加了一些东西的行为Computer.我们可以摆脱它ComponentDecorator.您可以创建一个实现IComputer接口的抽象装饰器类,但您不必这样做.
我们首先创建界面并使其具体Computer实现:
public interface IComputer
{
string getComputer();
}
public sealed class Computer : IComputer
{
public string getComputer()
{
return "computer";
}
}
Run Code Online (Sandbox Code Playgroud)
Computer这是sealed.它不一定是,但在这种情况下完成,以显示装饰器存在于您的具体类旁边,而不是从它派生.
装饰器实现IComputer而不是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";
}
}
Run Code Online (Sandbox Code Playgroud)
如果确实选择使用抽象类来实现装饰器,那么IComputer在构造函数中使用as依赖项.此外,你应该使用base.getComputer()而不是computer.getComputer()像这样:
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";
}
}
Run Code Online (Sandbox Code Playgroud)
在这两种情况下,我们都可以用同样的方式将它们包装起来:
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());
}
}
Run Code Online (Sandbox Code Playgroud)
用户InBetween建议可能无法更改基类.如果基类已经实现了接口,那不是问题.所以我们假设它不像你的代码那样.
要在这种情况下实现装饰器,我们首先需要为我们的基类创建一个适配器,并在它旁边实现我们的装饰器.
所以我们假设基类是Computer,我们不能改变它:
public sealed class Computer
{
public string getComputer()
{
return "computer";
}
}
Run Code Online (Sandbox Code Playgroud)
要创建适配器,我们IComputer像以前一样创建接口,并创建一个包装的类Computer:
public sealed class ComputerAdapter : IComputer
{
private Computer _computer;
public ComputerAdapter(Computer computer)
{
_computer = computer;
}
public string getComputer()
{
return _computer.getComputer();
}
}
Run Code Online (Sandbox Code Playgroud)
装饰器与前面的示例保持不变,因为它们已经实现了IComputer.包装起来有点变化,因为我们现在必须将a传递Computer给我们的ComputerAdapter实例:
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());
}
}
Run Code Online (Sandbox Code Playgroud)
但结果是一样的,如这里所见.
虽然它实际上并没有实现装饰器,但如果Computer.getComputer()是的话,你的代码就可以了virtual.在您的代码中Main,computer是,类型Computer.既然getComputer()不是virtual,Computer.getComputer()则调用而不是预期的KeyBoard.getComputer().因为在Java中,每个方法都是virtual,所以这个问题不会发生.
您的C#编译器应该给出一个警告,指出getComputer()子类隐藏了原始实现.警告表明您正在进行的操作将编译,但可能无法按预期执行,这就是这种情况.
computer.getComputer()在下一行中
Console.WriteLine(" You are getting a " + computer.getComputer());
Run Code Online (Sandbox Code Playgroud)
调用计算机的版本,getComputer因为它是编译时类型(因为方法不是虚拟的)。
如果需要多态行为,则需要getComputer在计算机类中标记为virtual。然后,您可以完全删除该类,ComponentDecorator而不会添加任何内容。
为什么这种情况只发生在C#中而不发生在Java中?
因为默认情况下,java中所有方法都是虚拟的(可以被重写)。在 C# 中则不然。您需要明确标记它virtual。
所以你的完整实现就变成了
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 "";
}
}
Run Code Online (Sandbox Code Playgroud)