Decorator,Wrapper和Adapter模式之间有什么区别?

Zol*_*ási 9 c# design-patterns

我觉得我一直在使用这些模式系列很多次,但是,对我来说很难看出差异,因为它们的定义非常相似.基本上似乎所有这些都是关于包装另一个或多个对象来扩展或用额外的东西包装他们的行为.

对于快速示例,在存储库模式上实现缓存机制似乎就是这种情况.这是一个C#我可能会开始的快速示例代码.

public interface IRepository {
  IEnumerable<T> GetItems<T>();
}

public class EntityFrameworkRepository : IRepository {
  ...
}

public class CachedRepository : IRepository {

  private IRepository _repository;
  private ICacheProvider _cache;

  public CachedRepository(IRepository repository, ICacheProvider cache) {
    this._repository = repository;
    this._cache = cache;
  }

  public IEnumerable<T> GetItems<T>() {
    ...
  }
}
Run Code Online (Sandbox Code Playgroud)

例如,这些模式中的哪一种适用于这种情况?任何人都可以简要地澄清理论和实践上的差异吗?

Ken*_*eth 12

理论上它们是相同的,它将intent一种模式与另一种模式区分开来:

  • 装饰

允许通过使用具有相同接口的类包装对象来组合/添加功能

  • 适配器

允许您在没有已知接口实现的情况下包装对象,使其粘附到接口.关键是将一个界面"翻译"成另一个界面.

  • 包装纸

从来没有听说过这是一种设计模式,但我认为它只是上面的通用名称

比如你指定我会归类为一个装饰:本CacheRepository decoratesIRepository添加缓存功能.


Luk*_*nks 6

程序员可能会编写一个类 A 并专注于保存另一个类 B 的对象。类 A 将被称为类 B的包装器。为什么类 A 环绕类 B?装饰或调整它。装饰器和适配器是包装器。


想象一下,类 A 是这样编写的,它通过调用类 B 对象的方法来实现类 B 的接口。然后它可以用来代替 B 类。除了它让程序员有机会在调用 B 类对象的方法之前或之后添加一些代码之外,这没有任何意义。这个版本的 A 类将被称为B 类的装饰器装饰器在添加一些行为的同时保持界面不变。

interface ICatInterface {
  public void wakeUp();
}

class Cat implements ICatInterface {
  public void wakeUp() {
    System.out.println("I came. I saw. I napped.");
  }
}

class YogaCat implements ICatInterface {

  private ICatInterface cat;

  public YogaCat(ICatInterface cat) {
    this.cat = cat;
  }

  public void wakeUp() {
    System.out.println("[Stretch]"); // <- This is the decoration.
    cat.wakeUp();
  }
}

Run Code Online (Sandbox Code Playgroud)

请参阅此示例,了解使用此模式在运行时组合具有不同行为的对象的更复杂方法。


现在想象一下,类 A 是这样编写的,它实现了一些接口 C,但主要是通过调用它的类 B 对象的方法来实现的。这是一种将 B 类中可用的方法转换为接口 C 的方法。A 类的这个版本将被称为B 类的适配器。就像你想给手机充电一样。有从墙上或汽车电源到 USB 端口的适配器。适配器将接口更改为某个其他接口,但不一定添加任何行为。

interface TakeDirectionsInterface {
  public void turnLeft();
  public void turnRight();
  public void go();
  public void stop();
}

class Driver {
  public enum TurnDirection
  { 
    CLOCKWISE, COUNTERCLOCKWISE;
  }

  public enum FootPedal
  { 
    ACCELERATOR, BRAKE, CLUTCH;
  }

  public void turnSteeringWheel(TurnDirection direction) {
    System.out.println("Turning the steering wheel " + direction.toString() + ".");
  }

  public void pressPedal(FootPedal pedal) {
    System.out.println("Pressing the " + pedal.toString() + "pedal.");
  }
}

class DriverAdapter implements TakeDirectionsInterface {

  private Driver driver;

  public DriverAdapter(Driver driver) {
    this.driver = driver;
  }

  public void turnLeft(){
    driver.turnSteeringWheel(Driver.TurnDirection.COUNTERCLOCKWISE);
  }

  public void turnRight(){
    driver.turnSteeringWheel(Driver.TurnDirection.CLOCKWISE);
  }

  public void go(){
    driver.pressPedal(Driver.FootPedal.ACCELERATOR);
  }

  public void stop(){
    driver.pressPedal(Driver.FootPedal.BRAKE);
  }
}

Run Code Online (Sandbox Code Playgroud)