首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >汽车装潢师

汽车装潢师
EN

Code Review用户
提问于 2015-09-20 18:09:42
回答 2查看 1K关注 0票数 7

这是对装饰图案的一个很好的利用吗?若否,原因为何?例如,当我们有对象通过PHP或ASP与MVC通信时,这是一个好主意吗?通过模型将数据发送到扩展的装饰类;还可以使用该数据扩展到其他类。

例如,理论上可以创建一个数据库并将其与模型类联系--然后通过一个模型类创建这个模型类,但是使用扩展其基本构造函数的模型类来修饰它。具有使用构造函数注入从CarPinto对象获取方法的能力。这是一种通过装饰器来实现它的实用方法,还是仅仅是一种动态地装饰数据的方法?

程序类

代码语言:javascript
复制
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();
    }
}

这是装饰类

代码语言:javascript
复制
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派生出来的两个类

代码语言:javascript
复制
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 :{");
    }
}

下面是我们从装饰师继承的装饰器类

代码语言:javascript
复制
class CarLamborghini : Decorator
{
    public CarLamborghini(Decorator decorator) : base(decorator) {
        decorator.drive();
        decorator.stop();
        decorator.park();
    }
}

而另一个

代码语言:javascript
复制
class CarPinto : Decorator
{
    public CarPinto(Decorator decorator): base(decorator) {
        decorator.drive();
        decorator.stop();
        decorator.park();
    }
}

我的问题是,这是使用修饰器模式及其使用抽象类而不是接口实现的好方法。

EN

回答 2

Code Review用户

发布于 2015-09-20 20:59:58

组合与继承,接口与抽象类

你在这里混淆了概念--继承太多了,这个模式完全是关于合成的。我相信你以前见过这个:

偏爱组合而不是继承。

您有一个abstract基类,它包含一组abstract方法,或者一组具有“合理默认值”的virtual方法:

代码语言:javascript
复制
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类看起来更像这样:

代码语言:javascript
复制
public abstract class Car
{
    public abstract void Drive();
    public abstract void Stop();
    public abstract void Park();
}

抽象得很好。但是有一种更轻量级、更灵活的方法可以做到这一点:

代码语言:javascript
复制
public interface ICar
{
    void Drive();
    void Stop();
    void Park();
}

接口使我发现的模式更容易理解。如果我不得不用一句话来描述装饰模式:

装饰器实现与其修饰的类型相同的接口,每个方法调用修饰类型的成员,根据需要添加功能。

这使得:

代码语言:javascript
复制
class CarLamborghini : Decorator

...quite混淆了。

这似乎令人费解:

Car lambo =新CarLamborghini(新装饰器(新LuxuryCar();

,装饰师,

在您的设计中,Decorator类绝对没有任何用途。这是个禁止行动。很难过,因为这和我写的很接近:

代码语言:javascript
复制
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它:

代码语言:javascript
复制
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();
    }
}

测试驱动器

现在,如果你想要一个吱吱的兰博基尼,你可以有一个!因为你是在对抽象进行编码,打电话的人不会知道他们的豪华跑车有这样一个缺陷!

代码语言:javascript
复制
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,它甚至不会眨眼-这就是这个模式的美妙之处。

查看上面StopSqueakyBrakesDecorator方法:

public void Stop() { Console.WriteLine("Squeeeeeeak!"); \_car.Stop(); }

每一种方法都在调用_car.MethodWeAreIn:这就是装饰师的工作方式,也是他们的行为可以“堆积”的方式--通过装饰装饰器,您可以得到累积的操作,因为它们都相互调用。

因此,修饰器可以决定哪些代码在修饰代码之前或之后运行;它还可以决定哪些参数正在执行(从它接收到的参数派生出来),当涉及参数时。

车外的

:现实生活中的装饰师

在现实世界中,装饰模式非常酷--我发现Car示例不能很好地支持它。

例如,在使用IFileWriter写入文件系统的应用程序中,将日志记录作为关注事项:

代码语言:javascript
复制
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();}

不要在构造器中工作。它会咬你的。

票数 18
EN

Code Review用户

发布于 2015-09-20 22:36:55

您收到了一个很好的答案,说明了您的装饰器模式的实现,但是我看到了这段代码的总体设计中的一些问题。您的真正目标似乎是创建“标准”和“运动”car,这可以通过植入ICar或从抽象的Car基类继承共享功能来实现。问题是,真正的汽车不只是运动/标准版本。它们由不同的部分组成。让我们集中精力让车停下来。这意味着我们需要两个接口。

代码语言:javascript
复制
public interface IEngine
{
    void Start();
    void Stop();
    bool IsRunning { get; }
}

public interface IBrake
{
    void Apply();
}

那么Car就会像这样。

代码语言:javascript
复制
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!");
    }

这让我们可以自由地执行刹车和发动机,但我们认为适合,只要他们遵守合同。

代码语言:javascript
复制
 public class PerformanceBrakes : IBrake
 {
      public void Apply()
      {
           Console.WriteLine("Wow! We stopped fast!");
      }
  }

现在,我们可以混合和匹配部件来创建我们喜欢的任何类型的Car

代码语言:javascript
复制
var fastJallopy = new Car(new PerformanceEngine(), new SqueakyBrakes());
var sportsCar = new Car(new PerformanceEngine(), new PerformanceBrakes());

这个模型的真实世界的计数器部分更紧密,并相当灵活。

票数 10
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/105184

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档