美文网首页Java设计模式设计模式
《设计模式》装饰者模式

《设计模式》装饰者模式

作者: 敏捷Studio | 来源:发表于2019-08-25 13:31 被阅读6次

基本介绍

定义

动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式相比生成子类更为灵活。

介绍

  • 装饰者模式属于结构型模式。
  • 装饰者模式在生活中应用实际上也非常广泛,一如一间房,放上厨具,它就是厨房;放上床,就是卧室。
  • 通常我们扩展类的功能是通过继承的方式来实现,但是装饰者模式是通过组合的方式来实现,这是继承的替代方案之一。

UML类图

装饰模式UML类图

角色说明:

  • Component(抽象组件):接口或者抽象类,被装饰的最原始的对象。具体组件与抽象装饰角色的父类。
  • ConcreteComponent(具体组件):实现抽象组件的接口。
  • Decorator(抽象装饰角色):一般是抽象类,抽象组件的子类,同时持有一个被装饰者的引用,用来调用被装饰者的方法;同时可以给被装饰者增加新的职责。
  • ConcreteDecorator(具体装饰类):抽象装饰角色的具体实现。

具体实现

就以装修房间为例子

1、创建抽象组件。这里是一个抽象房子类,定义一个装修的方法:

// 装修方法
public abstract class Room {
  public abstract void fitment();
}

2、创建具体组件。现在有一间新房子,已经装上了电:

// 继承Room
public class NewRoom extends Room {
  @Override
  public void fitment() {
    System.out.println("这是一间新房:装上电");
  }
}

3、创建抽象装饰角色。要为房子装修,定义抽象的房间装饰类:

// 继承Room,拥有父类相同的方法
public abstract class RoomDecorator extends Room {
  // 持有被装饰者的引用,这里是需要装修的房间
  private Room mRoom;

  public RoomDecorator(Room room) {
    this.mRoom = room;
  }

  @Override
  public void fitment() {
    // 调用被装饰者的方法
    mRoom.fitment();
  }
}

4、创建具体装饰类。我们要将房间装修成卧室和厨房,其具体实现是不同的:

// 卧室类,继承自RoomDecorator
public class Bedroom extends RoomDecorator {
  public Bedroom(Room room) {
    super(room);
  }

  @Override
  public void fitment() {
    super.fitment();
    addBedding();
  }

  private void addBedding() {
    System.out.println("装修成卧室:添加卧具");
  }
}

// 厨房类,继承自RoomDecorator
public class Kitchen extends RoomDecorator {
  public Kitchen(Room room) {
    super(room);
  }

  @Override
  public void fitment() {
    super.fitment();
    addKitchenware();
  }

  private void addKitchenware() {
    System.out.println("装修成厨房:添加厨具");
  }
}

5、客户端测试:

public void test() {
  // 有一间新房间
  Room newRoom = new NewRoom();
  RoomDecorator bedroom = new Bedroom(newRoom);
  // 装修成卧室
  bedroom.fitment();
  RoomDecorator kitchen = new Kitchen(newRoom);
  // 装修成厨房
  kitchen.fitment();
}

输出结果:

这是一间新房:装上电
装修成卧室:添加卧具
这是一间新房:装上电
装修成厨房:添加厨具

模式总结

应用场景

  • 需要扩展一个类的功能,或给一个类增加附加功能时
  • 需要动态的给一个对象增加功能,这些功能可以再动态的撤销
  • 当不能采用继承的方式对系统进行扩充或者采用继承不利于系统扩展和维护时。

优点

  • 采用组合的方式,可以动态的扩展功能,同时也可以在运行时选择不同的装饰器,来实现不同的功能。
  • 有效避免了使用继承的方式扩展对象功能而带来的灵活性差,子类无限制扩张的问题。
  • 被装饰者与装饰者解偶,被装饰者可以不知道装饰者的存在,同时新增功能时原有代码也无需改变,符合开放封闭原则。

缺点

  • 装饰层过多的话,维护起来比较困难。
  • 如果要修改抽象组件这个基类的话,后面的一些子类可能也需跟着修改,较容易出错。

Android中的源码分析

我们都知道ActivityServiceApplication等都是一个Context,这里面实际上就是通过装饰者模式来实现的。下面以startActivity()这个方法来简单分析一下。

1、Context
Context实际上是个抽象类,里面定义了大量的抽象方法,其中就包含了startActivity()方法:

// 抽象类
public abstract class Context {
  // 抽象方法
  public abstract void startActivity(@RequiresPermission Intent intent);
  // 其他代码略
}  

2、ContextImpl
Context类的具体实现实际上是在ContextImpl类,里面具体实现了startActivity()方法:

class ContextImpl extends Context {
  @Override
  public void startActivity(Intent intent) {
    warnIfCallingFromSystemProcess();
    startActivity(intent, null);
  }

  // 具体实现原理这里就不分析了
  @Override
  public void startActivity(Intent intent, Bundle options) {
    warnIfCallingFromSystemProcess();

    // Calling start activity from outside an activity without FLAG_ACTIVITY_NEW_TASK is
    // generally not allowed, except if the caller specifies the task id the activity should
    // be launched in.
    if ((intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) == 0 && options != null && ActivityOptions.fromBundle(options).getLaunchTaskId() == -1) {
      throw new AndroidRuntimeException("Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?");
    }
    mMainThread.getInstrumentation().execStartActivity(getOuterContext(), mMainThread.getApplicationThread(), null, (Activity) null, intent, -1, options);
  }

  // 其他代码略
}

3、ContextWrapper
通常我们在ActivityService里面调用startActivity()方法,实际上是调用他们的父类ContextWrapper里面的startActivity()方法,我们先来看下ActivityService的继承关系:

Activity继承关系 Service继承关系

可以看到ActivityService都是继承自ContextWrapper,再来看看ContextWrapper的代码:

// Context包装类
public class ContextWrapper extends Context {
  // 持有Context引用
  Context mBase;

  // 这里的base实际上就是ContextImpl      
  public ContextWrapper(Context base) {
    mBase = base;
  }

  @Override
  public void startActivity(Intent intent) {
    // 调用ContextImpl的startActivity()方法
    mBase.startActivity(intent);
  }

  //其他代码略
}

4、总结
Context类在这里就充当了抽象组件的角色,ContextImpl类则是具体的组件,而ContextWrapper就是具体的装饰角色,通过扩展ContextWrapper增加不同的功能,就形成了ActivityService等子类。最后,放一张总的UML类图帮助理解:

UML类图

相关文章

  • 设计模式

    设计模式 单例模式、装饰者模式、

  • 设计模式笔记汇总

    目录 设计原则 “依赖倒置”原则 未完待续... 设计模式 设计模式——策略模式 设计模式——装饰者模式 设计模式...

  • java IO 的知识总结

    装饰者模式 因为java的IO是基于装饰者模式设计的,所以要了解掌握IO 必须要先清楚什么事装饰者模式(装饰者模式...

  • 设计模式

    常用的设计模式有,单例设计模式、观察者设计模式、工厂设计模式、装饰设计模式、代理设计模式,模板设计模式等等。 单例...

  • JavaScript 设计模式核⼼原理与应⽤实践 之 结构型设计

    JavaScript 设计模式核⼼原理与应⽤实践 之 结构型设计模式 装饰器模式,又名装饰者模式。它的定义是“在不...

  • 8种设计模式:

    主要介绍 单例设计模式,代理设计模式,观察者设计模式,模板模式(Template), 适配器模式,装饰模式(Dec...

  • 装饰者模式

    JavaScript 设计模式 张容铭第十二章 房子装修--装饰者模式 (102页) 装饰者模式(Decorato...

  • Summary of February 2017

    READING Head First 设计模式:完成50%。内容:观察者模式、装饰者模式、工厂模式、单件模式、命令...

  • 装饰对象:装饰者模式

    装饰对象:装饰者模式   这是《Head First设计模式(中文版)》第三章的读书笔记。   装饰者模式,可以称...

  • 设计模式之装饰器模式

    也称装饰者模式、装饰器模式、Wrapper、Decorator。 装饰模式是一种结构型设计模式,允许你通过将对象放...

网友评论

    本文标题:《设计模式》装饰者模式

    本文链接:https://www.haomeiwen.com/subject/kmyhectx.html