【问题标题】:Simplified decorator pattern. Is that correct?简化的装饰器模式。那是对的吗?
【发布时间】:2018-04-03 09:49:46
【问题描述】:

所以我创建了这样的东西:

interface IStudent
{
    string DisplayInformation();
}

public class Student : IStudent
{
    public string Name { get; set; }

    public string Grade { get; set; }

    public int Age { get; set; }

    public virtual string DisplayInformation()
    {
        return $"{Name} - {Age} years old is in {Grade} grade";
    }
}

public class StudentDecorator : Student
{
    private Student _student;

    public StudentDecorator(Student student)
    {
        _student = student;
    }

    public override string DisplayInformation()
    {
        return _student.DisplayInformation();
    }
}

public class ScienceStudentDecorator : StudentDecorator
{
    public string Labs { get; set; }

    public ScienceStudentDecorator(Student student) : base(student)
    {
    }

    public override string DisplayInformation()
    {
        var info =  base.DisplayInformation();
        return $"{info}. Labse are {Labs}";
    }
}

我正在这样装饰学生:

        var student = new Student
        {
            Age = 15,
            Grade = "Fourth",
            Name = "John"
        };

        var scienceStudent = new ScienceStudentDecorator(student)
        {
            Labs = "Biology, History, Physics"
        };

        Console.WriteLine(scienceStudent.DisplayInformation());

        Console.Read();

让我想知道的是,如果我将 ScienceStudentDecorator 更改为继承并持有 Student,它的工作原理完全相同。我还在装饰学生。我只是跳过与学生装饰者的仪式。我的问题是我误解了这个概念吗?

更改版本:

public class ScienceStudentDecorator : Student
{
    private Student _student;

    public string Labs { get; set; }

    public ScienceStudentDecorator(Student student)
    {
        _student = student;
    }

    public override string DisplayInformation()
    {
        var info =  _student.DisplayInformation();
        return $"{info}. Labse are {Labs}";
    }
}

Student 和 ScienceDecorator 的创建完全一样。

【问题讨论】:

  • 您似乎缺少的一点是装饰器模式适用于具有行为的服务。它最适用于某事的服务,而不是您的Student 类,后者看起来更像一个数据传输对象 (DTO)。

标签: c# .net design-patterns decorator


【解决方案1】:

首先;你发现了替代方案,这让我说你理解了这个概念。

您缺少的部分是继承不好的部分。嗯,就是这样;如果你想复制界面而不是行为。

装饰器模式的关键在于它是一个alternative to inheritance,具有在运行时更改和扩展行为的相同能力,并且不依赖于具有特定版本或其他依赖项的某些基类。

这就是为什么,你应该让你的ScienceStudentDecorator 只基于IStudent 接口,并用IStudent 而不是Student 进行装饰(尽管.net 的Stream 类倾向于打破这个规则。[我也倾向于创建一个abstract NullStudent 只是为了保持简单的装饰])

我要指出的另一件事是,decorator 下方的对象不是必需的。请注意,以下代码也足够了;非行为继承:

public interface IStudent //this would rather be called an IInformationDisplayer
{
     string DisplayInformation();
}

public class Student : IStudent
{
    public string Name, Grade, Age, etc... { get; set; }
    private IStudent _student = null;

    public Student() { }
    public Student(IStudent student) {  _student = student; }

    public string DisplayInformation()
    {
        return $"{_student?.DisplayInformation()}" + 
               $"{Name} - {Age} years old is in {Grade} grade";
    }
}

public class ScienceStudent : IStudent //it's still a decorator
{
    public string Labs { get; set; }
    private IStudent _student;
    public ScienceStudentDecorator(IStudent student)
    {
        _student = student;
    }

    public string DisplayInformation()
    {
        var info =  _student?.DisplayInformation();
        return $"{info}. Labse are {Labs}";
    }
}

更新 为了更清楚,让我们研究以下案例:

假设你有一个IDrawable 类型:

public interface IDrawable
{ 
    //maybe with some additional properties as dimensions and position
    void Draw();
} 

接下来我们要绘制一个矩形(注意这也可以是房子或墙壁、FileStream 或 MemoryStream,你想它,你会命名它。)

但是,让我们用一个矩形来保留它:

public class Rectangle : IDrawable
{
    private IDrawable _drawable;
    public class Rectangle(IDrawable drawable)
    {
      _drawable = drawable; //just to make it uniform
    }

    private InernalDraw() { ... }

    public void Draw()
    {
        //do the drawing magic
        InernalDraw(); //some drawing code
        //and do something with the decorated item
        _drawable?.Draw();
    }
}

还没有什么特别的。但是让我们假设我们想在矩形周围画一个边框。

让我们创建一个可绘制的边框:

public class Border : IDrawable
{
    private IDrawable _drawable;
    public class Border(IDrawable drawable)
    {
      _drawable = drawable;
    }

    private InternalDrawWithSomeSpecialLogicAndStuff() { ... }

    public void Draw()
    {
        //draw the decorated item:
        _drawable?.Draw();

        //and work out some magic to draw a border around it,
        //note that properties as dimensions would be helpful.
        InternalDrawWithSomeSpecialLogicAndStuff();
    }
}

现在,让我们考虑以下代码:

public static void Main()
{
    IDrawable borderlessRectangle = new Rectangle(null);
    IDrawable borderedRectangle = new Border(borderlessRectangle);

    borderlessRectangle.Draw();//gives a rectangle without a border
    borderedRectangle.Draw();//gives a rectangle with a border
}

【讨论】:

  • 实际上,松耦合只是意味着我们有一个抽象,而不是一个具体的实现。抽象是接口还是抽象类没有区别。但由于本例中的Student 不是抽象的,所以接口更有意义。
  • @NightOwl888:嗯.. 是的,结果不对。我稍微改写了一下,跳过了“松耦合”这个词。
  • 哇,你的重构真的让我明白了这一点。谢谢! :)
  • @Alieniasty:因为这个例子缺少一点真实的场景,我添加了一个替代例子。
【解决方案2】:

装饰器模式的概念是从继承和松耦合中获得替代。 你了解了装饰器的基本概念。我的建议是为基础装饰器添加一层抽象,作为基础,为我们提供位置,默认接口所需的任何装饰器行为,而具体的装饰器实现将只关注装饰。

public interface IStudent
{
    string Name {get;}

    string DisplayInformation();
}

public class Student : IStudent
{
    public string Name { get; set; }

    public string DisplayInformation()
    {
        return $"{Name} - {Age} years old is in {Grade} grade";
    }
}



/// Base for decorators which stores our base in constructor
/// Gives default behaviour to all props and methods required by Interface
public abstract class StudentDecorator : IStudent
{
    protected IStudent BaseStudent {get;}

    protected StudentDecorator(IStudent student)
    {
        BaseStudent = student;
    }

    public virtual string Name { get { return BaseStudent.Name;} }

    public virtual string DisplayInformation()
    {
        return BaseStudent.DisplayInformation();
    }

/// Concrete decorator, we don't need to override Name property
public class ScienceStudentDecorator : StudentDecorator
{
    public string Labs { get; set; }

    public ScienceStudentDecorator(IStudent student) : base (student)
    {
        ///Behaviour here done by abstract constructor
    }

    public override string DisplayInformation()
    {
        var info =  BaseStudent?.DisplayInformation();
        return $"{info}. Labse are {Labs}";
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-12-28
    • 2011-09-08
    • 1970-01-01
    • 2023-03-29
    • 2019-02-23
    • 1970-01-01
    • 2014-02-20
    • 2013-02-17
    相关资源
    最近更新 更多