这是对装饰图案的一个很好的利用吗?若否,原因为何?例如,当我们有对象通过PHP或ASP与MVC通信时,这是一个好主意吗?通过模型将数据发送到扩展的装饰类;还可以使用该数据扩展到其他类。
例如,理论上可以创建一个数据库并将其与模型类联系--然后通过一个模型类创建这个模型类,但是使用扩展其基本构造函数的模型类来修饰它。具有使用构造函数注入从CarPinto对象获取方法的能力。这是一种通过装饰器来实现它的实用方法,还是仅仅是一种动态地装饰数据的方法?
程序类
class Program
{
static void Main(string[] args)
{
Car lambo = new CarLamborghini(new Decorator(new LuxuryCar()));
Car cheapyCar = new CarPinto(new Decorator(new BummyCar()));
Console.ReadLine();
}
}这是装饰类
class Decorator : Car
{
Car car;
public Decorator(Car _car) {
this.car = _car;
}
public override void drive()
{
car.drive();
}
public override void stop()
{
car.stop();
}
public override void park()
{
car.park();
}
}以下是从Car派生出来的两个类
class LuxuryCar : Car
{
public override void drive()
{
Console.WriteLine("I'm driving a fast car");
}
public override void stop()
{
Console.WriteLine("I have come to a complete stop quick");
}
public override void park()
{
Console.WriteLine("I am in park in a close range");
}
}
class BummyCar : Car
{
public override void drive()
{
Console.WriteLine("I am a slow driver");
}
public override void stop()
{
Console.WriteLine("I have stopped abrubtly");
}
public override void park()
{
Console.WriteLine("I am in park :{");
}
}下面是我们从装饰师继承的装饰器类
class CarLamborghini : Decorator
{
public CarLamborghini(Decorator decorator) : base(decorator) {
decorator.drive();
decorator.stop();
decorator.park();
}
}而另一个
class CarPinto : Decorator
{
public CarPinto(Decorator decorator): base(decorator) {
decorator.drive();
decorator.stop();
decorator.park();
}
}我的问题是,这是使用修饰器模式及其使用抽象类而不是接口实现的好方法。
发布于 2015-09-20 20:59:58
你在这里混淆了概念--继承太多了,这个模式完全是关于合成的。我相信你以前见过这个:
偏爱组合而不是继承。
您有一个abstract基类,它包含一组abstract方法,或者一组具有“合理默认值”的virtual方法:
public abstract class Car
{
public virtual void Drive()
{
Console.WriteLine("♪On the road again♪");
}
public virtual void Stop()
{
Console.WriteLine("BRAAAAAAAAAKE!");
}
public virtual void Park()
{
Console.WriteLine("Your destination is on the right.")
}
}如果在您展示的所有实现中覆盖所有这些方法,那么我将猜测Car类看起来更像这样:
public abstract class Car
{
public abstract void Drive();
public abstract void Stop();
public abstract void Park();
}抽象得很好。但是有一种更轻量级、更灵活的方法可以做到这一点:
public interface ICar
{
void Drive();
void Stop();
void Park();
}接口使我发现的模式更容易理解。如果我不得不用一句话来描述装饰模式:
装饰器实现与其修饰的类型相同的接口,每个方法调用修饰类型的成员,根据需要添加功能。
这使得:
class CarLamborghini : Decorator...quite混淆了。
这似乎令人费解:
Car lambo =新CarLamborghini(新装饰器(新LuxuryCar();
在您的设计中,Decorator类绝对没有任何用途。这是个禁止行动。很难过,因为这和我写的很接近:
class Decorator : ICar
{
private readonly ICar _car;
public Decorator(ICar car) {
_car = car;
}
public void Drive()
{
_car.Drive();
}
public void Stop()
{
_car.Stop();
}
public void Park()
{
_car.Park();
}
}注意:
PascalCase成员名private readonly _camelCase域camelCase参数但这是个禁止行动.有这样一个装潢师是没有意义的!一个装潢师应该有一个目的,一个告诉我们这个目的是什么的名字。拿一辆能让车开走的车吧!当您Stop它:
class SqueakyBrakesDecorator : ICar
{
private readonly ICar _car;
public SqueakyBrakesDecorator(ICar car) {
_car = car;
}
public void Drive()
{
_car.Drive();
}
public void Stop()
{
Console.WriteLine("Squeeeeeeak!");
_car.Stop();
}
public void Park()
{
_car.Park();
}
}现在,如果你想要一个吱吱的兰博基尼,你可以有一个!因为你是在对抽象进行编码,打电话的人不会知道他们的豪华跑车有这样一个缺陷!
public static class CarTester
{
public static void TestDrive(ICar car)
{
car.Drive();
car.Stop();
car.Park();
}
}请注意,这段代码是如何完全忽略任何类似装饰者的东西的?据它所知,它正在获得一个ICar:如何实际实现每个方法并不是它关心的问题,重要的是具体类型实现了ICar接口。
这正是装饰师所做的:它实现了它正在装饰的类型的接口:
类SqueakyBrakesDecorator : ICar {私有只读ICar _car;public SqueakyBrakesDecorator(ICar car) { _car = car;}
所以现在我们可以运行CarTester.TestDrive并给它一个SqueakyBrakesDecorator,它甚至不会眨眼-这就是这个模式的美妙之处。
查看上面Stop的SqueakyBrakesDecorator方法:
public void Stop() { Console.WriteLine("Squeeeeeeak!"); \_car.Stop(); }
每一种方法都在调用_car.MethodWeAreIn:这就是装饰师的工作方式,也是他们的行为可以“堆积”的方式--通过装饰装饰器,您可以得到累积的操作,因为它们都相互调用。
因此,修饰器可以决定哪些代码在修饰代码之前或之后运行;它还可以决定哪些参数正在执行(从它接收到的参数派生出来),当涉及参数时。
车外的
在现实世界中,装饰模式非常酷--我发现Car示例不能很好地支持它。
例如,在使用IFileWriter写入文件系统的应用程序中,将日志记录作为关注事项:
public class LoggingFileWriter : IFileWriter
{
private readonly IFileWriter _writer;
private readonly ILogger _logger;
public LoggingFileWriter(IFileWriter writer, ILogger logger)
{
_writer = writer;
_logger = logger;
}
public void Write(string text)
{
try
{
_writer.Write(text);
_logger.Info("File written: " + _writer.FileName);
_logger.Debug(text);
}
catch (IOException exception)
{
_logger.Error(exception);
}
}
}这种模式显然有助于坚持SRP --单一责任原则。它负责注销某些FileWriter类,并将其传递给这个LoggingFileWriter类,后者不关心文件实际上是如何编写的实现细节。
通过调用修饰对象自己的方法,您可以启用装饰器的“累积”:您可以有一个LoggingFileWriter,它接收一个TweetingFileWriter,当文件包含一个<Tweet>Up to 140 characters</Tweet>标记时,它会发送一个Tweet,这会接收一个.好吧,你有个想法:它们都是同一个界面。他们每个人都有自己的特定功能,为了增加更多的功能,您只需编写另一个装饰器,并在您已经编写的功能的基础上构建该功能:这符合开放/封闭原则,该原则规定您的类应该对扩展开放,但不接受修改--类不应该有20个被修改的理由;decorator模式只是实现这一目标的工具。
构造函数:
公共LoggingFileWriter(IFileWriter撰稿人,ILogger记录器)
记录类型取决于抽象,而不是具体类型。这就是依赖注入原则的精神:我们没有使用new来构建特定的ILogger实现,而是只注入了一个构造函数。
构造函数注入是最有用和最常见的依赖注入类型。
注意构造函数的作用:
公共LoggingFileWriter(IFileWriter编写器,ILogger记录器){ _writer =记录器;_logger =记录器;}
它分配private readonly字段。没别的了。与之相比:
公共CarLamborghini(装饰装饰):基(装饰){ decorator.drive();decorator.stop();decorator.park();}
不要在构造器中工作。它会咬你的。
发布于 2015-09-20 22:36:55
您收到了一个很好的答案,说明了您的装饰器模式的实现,但是我看到了这段代码的总体设计中的一些问题。您的真正目标似乎是创建“标准”和“运动”car,这可以通过植入ICar或从抽象的Car基类继承共享功能来实现。问题是,真正的汽车不只是运动/标准版本。它们由不同的部分组成。让我们集中精力让车停下来。这意味着我们需要两个接口。
public interface IEngine
{
void Start();
void Stop();
bool IsRunning { get; }
}
public interface IBrake
{
void Apply();
}那么Car就会像这样。
public class Car
{
public Car(IEngine engine, IBrake brakes)
{
_engine = engine;
_brakes = brakes;
}
public void Start()
{
if (!_engine.IsRunning)
{
_engine.Start();
}
}
public void Stop()
{
if (_engine.IsRunning)
{
_brakes.Apply();
_engine.Stop();
return;
}
Console.WriteLine("You're already stopped!");
}这让我们可以自由地执行刹车和发动机,但我们认为适合,只要他们遵守合同。
public class PerformanceBrakes : IBrake
{
public void Apply()
{
Console.WriteLine("Wow! We stopped fast!");
}
}现在,我们可以混合和匹配部件来创建我们喜欢的任何类型的Car。
var fastJallopy = new Car(new PerformanceEngine(), new SqueakyBrakes());
var sportsCar = new Car(new PerformanceEngine(), new PerformanceBrakes());这个模型的真实世界的计数器部分更紧密,并相当灵活。
https://codereview.stackexchange.com/questions/105184
复制相似问题