所以我创造了这样的东西:
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的创建完全相同。
答案 0 :(得分:2)
首先;你发现了替代方案,这让我说你理解了这个概念。
您缺少的部分是继承不良的部分。嗯,那就是;如果你想复制界面而不是行为。
装饰器模式的关键点在于它是一个
alternative to inheritance
,具有在运行时改变和扩展行为的能力,并且没有绑定到具有特定版本或其他依赖关系的某个基类。
这就是为什么,您应该仅基于ScienceStudentDecorator
界面制作IStudent
并使用IStudent
代替Student
进行装饰(尽管.net的{{1}类往往会破坏这个规则。[我也倾向于创建一个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
接下来我们有一个我们想要绘制的矩形(请注意,这也可能是一个房子或墙,一个FileStream或一个MemoryStream,你认为它,你会命名它。)
但是,让我们保持一个矩形:
public interface IDrawable
{
//maybe with some additional properties as dimensions and position
void Draw();
}
没什么特别的。但我们假设我们想在矩形周围画一个边框。
让我们创建一个可绘制的边框:
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();
}
}
答案 1 :(得分: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}";
}
}