装饰模式(Decorator Pattern)也称为包装模式(Wrapper Pattern),结构型设计模式之一,其使用一种对客户端透明的方式来动态扩展对象的功能,同时它也是继承关系的一种替代方案之一。(组合方案也是继承关系的一种替代方案)。
装饰模式的定义
动态给一个对象添加一些额外的职责。就增加功能来说,装饰模式生成子类更加灵活。
装饰模式的使用场景
需要透明且动态地扩展类的功能时。
装饰模式的UML类图
角色介绍
Component:抽象组件
可以是一个接口或者抽象类,其充当被装饰的原始对象。
ConcreteComponent:组件具体实现类
该类是Component类基本实现,也是我们装饰的具体对象。
Decorator:抽象装饰者
顾名思义,其承担的职责就是为了装饰我们的组件对象,其内部一定要有一个指向组件对象的引用。在大多数情况下,该类为抽象类,需要根据不同的装饰逻辑实现不同的子类。当然,如果装饰逻辑单一,只有一个的情况下我们可以省略该类直接作为具体的装饰者。
ConcreteDecoratorA:装饰者具体实现类
只是对抽象装饰者作出具体的实现。
ConcreteDecoratorB:同上。
同上。
Client:客户类。
D:\Users\user\ProjectThree\DecoratorPatternDemo\app\src\main\java\decoratorpattern\gome\com\decoratorpatterndemo\Component.java
public abstract class Component {
/**
* 抽象方法
* 可以添加更多抽象方法
*/
public abstract void operate();
}
D:\Users\user\ProjectThree\DecoratorPatternDemo\app\src\main\java\decoratorpattern\gome\com\decoratorpatterndemo\ConcreteComponent.java
public class ConcreteComponent extends Component {
@Override
public void operate() {
//实现具体逻辑
}
}
D:\Users\user\ProjectThree\DecoratorPatternDemo\app\src\main\java\decoratorpattern\gome\com\decoratorpatterndemo\Decorator.java
public abstract class Decorator extends Component {
private Component component;//持有一个Component对象的引用
/**
*必要的构造方法 需要一个Component类型的对象
* @param component
*/
public Decorator(Component component) {
super();
this.component = component;
}
@Override
public void operate() {
component.operate();
}
}
D:\Users\user\ProjectThree\DecoratorPatternDemo\app\src\main\java\decoratorpattern\gome\com\decoratorpatterndemo\ConcreteDecoratorA.java
public class ConcreteDecoratorA extends Decorator {
/**
* 必要的构造方法 需要一个Component类型的对象
*
* @param component
*/
public ConcreteDecoratorA(Component component) {
super(component);
}
@Override
public void operate() {
//装饰方法A和B既可以在父类方法前调用也可在之后调用
operateA();
super.operate();
operateB();
}
/**
* 自定义的装饰方法 A
*/
public void operateA() {
//装饰方法逻辑......
}
/**
* 自定义的装饰方法B
*/
public void operateB() {
//装饰方法逻辑......
}
}
D:\Users\user\ProjectThree\DecoratorPatternDemo\app\src\main\java\decoratorpattern\gome\com\decoratorpatterndemo\MainActivity.java
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//构造被装饰的组件对象
Component component = new ConcreteComponent();
//根据组件对象构造装饰者对象A并调用,此时相当于给组件对象增加装饰者A的功能方法
Decorator decorator = new ConcreteDecoratorA(component);
decorator.operate();
}
}
装饰模式简单Demo
D:\Users\user\ProjectThree\DecoratorPatternDemo\app\src\main\java\decoratorpattern\gome\com\decoratorpatterndemo\Person.java
//我们将人定义为一个抽象类,将其穿衣服的行为定义为一个抽象方法
//该类就是上面我们所提到的抽象组件类,也就是我们需要装饰的原始对象
public abstract class Person {
/**
* Person 下有一个穿着的抽象方法
*/
public abstract void dressed();
}
D:\Users\user\ProjectThree\DecoratorPatternDemo\app\src\main\java\decoratorpattern\gome\com\decoratorpatterndemo\Boy.java
//Boy类继承于Person类,该类仅对Person中的dressed方法做了具体的实现,
//而Boy类则是我们所要装饰的具体对象,现在需要一个装饰者类装饰我们这个Boy对象。
public class Boy extends Person {
@Override
public void dressed() {
//Boy类下的dressed方法的基本逻辑
Log.v(MainActivity.DecoratorTAG, "穿衣服");
}
}
//PersonCloth类来表示人所穿的衣服
//在PersonCloth类中我们保持了一个对Person类的引用,可以方便地调用具体被装饰对象中的方法,
//这便是在不破坏原类层次结构的情况下增加一些功能
//我们只需要在被装饰对象的相应方法前或后增加相应的功能
//在装饰物只有一个的情况下,可不必声明一个抽象类作为装饰者抽象的提取,仅需要定义一个普通类表示装饰者即可。
public abstract class PersonCloth extends Person {
protected Person mPerson;//保持一个Person类的引用
public PersonCloth(Person person) {
this.mPerson = person;
}
@Override
public void dressed() {
//调用Person类中的dressed方法
mPerson.dressed();
}
}
D:\Users\user\ProjectThree\DecoratorPatternDemo\app\src\main\java\decoratorpattern\gome\com\decoratorpatterndemo\CheapCloth.java
public class CheapCloth extends PersonCloth {
public CheapCloth(Person person) {
super(person);
}
public void dressShorts() {
Log.v(MainActivity.DecoratorTAG, "穿短裤");
}
@Override
public void dressed() {
super.dressed();
dressShorts();
}
}
D:\Users\user\ProjectThree\DecoratorPatternDemo\app\src\main\java\decoratorpattern\gome\com\decoratorpatterndemo\ExpensiveCloth.java
public class ExpensiveCloth extends PersonCloth {
public ExpensiveCloth(Person person) {
super(person);
}
private void dressShirt() {
Log.v(MainActivity.DecoratorTAG, "传短袖");
}
private void dressLeather() {
Log.v(MainActivity.DecoratorTAG, "穿件皮衣");
}
private void dressJean() {
Log.v(MainActivity.DecoratorTAG, "穿件牛仔裤");
}
@Override
public void dressed() {
super.dressed();
dressShirt();
dressLeather();
dressJean();
}
}
CheapCloth 与 ExpensiveCloth 都为原本Boy类中的dressed方法提供了功能扩展,不过这种扩展并非直接修改原有的方法逻辑或结构,更恰当的说,仅仅是在另一个类中将原本方法和新逻辑进行封装整合。
D:\Users\user\ProjectThree\DecoratorPatternDemo\app\src\main\java\decoratorpattern\gome\com\decoratorpatterndemo\MainActivity.java
public class MainActivity extends AppCompatActivity {
public static final String DecoratorTAG = "decoratortag";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Person person = new Boy();
PersonCloth clothCheap = new CheapCloth(person);
clothCheap.dressed();
PersonCloth clothExpensive = new ExpensiveCloth(person);
clothExpensive.dressed();
}
}
Log分析:
V/decoratortag: 穿衣服
V/decoratortag: 穿短裤
V/decoratortag: 穿衣服
V/decoratortag: 传短袖
V/decoratortag: 穿件皮衣
V/decoratortag: 穿件牛仔裤
Android源码中的装饰模式
Context类在android中经常使用,它本质上是一个抽象类,其在我们的装饰模式中相当于抽象组件,在其内部定义了大量的抽象方法。
startActivity方法
/**(有关应用程序环境的全局信息的接口。 这是一个抽象类,其实现由Android系统提供。)
* Interface to global information about an application environment. This is
* an abstract class whose implementation is provided by
* the Android system. It
* allows access to application-specific resources and classes, as well as
* up-calls for application-level operations such as launching activities,
* broadcasting and receiving intents, etc.
*/(它允许访问特定于应用程序的资源和类,以及对应用程序级操作的上调,例如启动活动,广播和接收意图等。)
public abstract class Context {
}
/**
* Launch a new activity. You will not receive any information about when
* the activity exits.
*
* <p>Note that if this method is being called from outside of an
* {@link android.app.Activity} Context, then the Intent must include
* the {@link Intent#FLAG_ACTIVITY_NEW_TASK} launch flag. This is because,
* without being started from an existing Activity, there is no existing
* task in which to place the new activity and thus it needs to be placed
* in its own separate task.
*
* <p>This method throws {@link ActivityNotFoundException}
* if there was no Activity found to run the given Intent.
*
* @param intent The description of the activity to start.
* @param options Additional options for how the Activity should be started.
* May be null if there are no options. See {@link android.app.ActivityOptions}
* for how to build the Bundle supplied here; there are no supported definitions
* for building it manually.
*
* @throws ActivityNotFoundException
*
* @see #startActivity(Intent)
* @see PackageManager#resolveActivity
*/
public abstract void startActivity(@RequiresPermission Intent intent,
@Nullable Bundle options);
/**
* Same as {@link #startActivity(Intent, Bundle)} with no options
* specified.
*
* @param intent The description of the activity to start.
*
* @throws ActivityNotFoundException
*`
* @see #startActivity(Intent, Bundle)
* @see PackageManager#resolveActivity
*/
public abstract void startActivity(@RequiresPermission Intent intent);
正真的实现是在ContextImpl中完成的,ContextImpl继承自Context抽象类,并实现了Context中的抽象方法。
/**
* Common implementation of Context API, which provides the base
* context object for Activity and other application components.
*(Context API的通用实现,它为Activity和其他应用程序组件提供基本上下文对象。)
* @hide
*/
public 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);
}
... ...
}
Activity从类层次结构上来说本质上是一个Context,如果大家留意过代码会发现,Activity并非直接继承自Context,而是继承于ContextThemeWrapper。
public class Activity extends ContextThemeWrapper
implements LayoutInflater.Factory2,
Window.Callback, KeyEvent.Callback,
OnCreateContextMenuListener, ComponentCallbacks2,
Window.OnWindowDismissedCallback, WindowControllerCallback,
AutofillManager.AutofillClient {
}
而这个ContextThemeWrapper又继承于ContextWrapper。
/**
* A context wrapper that allows you to modify or replace the theme of the
* wrapped context.
*/
public class ContextThemeWrapper extends ContextWrapper {
}
/**
* Proxying implementation of Context that simply delegates all of its calls to
* another Context. Can be subclassed to modify behavior without changing
* the original Context.
*/
public class ContextWrapper extends Context {
Context mBase;
public ContextWrapper(Context base) {
mBase = base;
}
}
最终的这个ContextWrapper才继承于Context,ContextWrappper就是我们要找的装饰者,在ContextWrapper中有一个Context的引用。
#ContextWrapper
{
... ...
@Override
public void startActivity(Intent intent) {
mBase.startActivity(intent);
}
@Override
public void startActivity(Intent intent, Bundle options) {
mBase.startActivity(intent, options);
}
... ...
}
这里可以看到ContextWrapper中的startActivity方法也仅仅是简单地调用了具体组件实现类ContextImpl中对应的方法而已,实质上ContextWrapper中所有方法都仅仅是调用了ContextImpl中对应的方法而已。
装饰模式的套路都是很相似的,对于具体的方法的包装扩展则由ContextWrapper的具体子类完成,比如我们的Activity、Service和Application。
参考《Android源码设计模式》