装饰者模式和C#

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

C#中的装饰者

使用装饰器模式时,想法是让几个类实现相同的接口.其中一个是接口的正常具体实现,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)

但结果是一样的,如这里所见.

为什么你的代码在Java中工作,而在C#中不工作?

虽然它实际上并没有实现装饰器,但如果Computer.getComputer()是的话,你的代码就可以了virtual.在您的代码中Main,computer是,类型Computer.既然getComputer()不是virtual,Computer.getComputer()则调用而不是预期的KeyBoard.getComputer().因为在Java中,每个方法都是virtual,所以这个问题不会发生.

您的C#编译器应该给出一个警告,指出getComputer()子类隐藏了原始实现.警告表明您正在进行的操作将编译,但可能无法按预期执行,这就是这种情况.


Sri*_*vel 3

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)

  • 关键是引入一个接口“IComputer”。它定义了方法“getComputer()”。“Computer”可以实现接口,而“getComputer()”不必是虚拟的,装饰器也可以。 (2认同)
  • 显然他可以修改“Computer”,因为他写了它,因此“Computer”可以实现“IComputer”。如果您无法修改它,则也无法将 `getComputer()` 设为虚拟。 (2认同)