您的位置:首页 > 其它

装饰者模式

2016-09-20 18:27 141 查看
原文出处:http://blog.csdn.net/yanbober/article/details/45395747

概述

装饰模式就是使用被装饰类的一个子类的实例,在客户端将这个子类的实例交给装饰类;是继承的替代方案,装饰模式应该为所装饰的对象增强功能

装饰模式可以在不改变一个对象本身功能的基础上给对象增加额外的新行为。装饰模式是一种用于替代继承的技术,它通过一种无须定义子类的方式来给对象动态增加职责,使用对象之间的关联关系取代类之间的继承关系。在装饰模式中引入了装饰类,在装饰类中既可以调用待装饰的原有类的方法,还可以增加新的方法,以扩充原有类的功能。

核心思想

概念: 动态地给一个对象增加一些额外的职责,就增加对象功能来说,装饰模式比生成子类实现更为灵活。装饰模式是一种对象结构型模式。

重点: 装饰者模式结构重要核心模块:

Component(抽象构件)

它是具体构件和抽象装饰类的共同父类,声明了在具体构件中实现的业务方法,它的引入可以使客户端以一致的方式处理未被装饰的对象以及装饰之后的对象,实现客户端的透明操作。

ConcreteComponent(具体构件)

它是抽象构件类的子类,用于定义具体的构件对象,实现了在抽象构件中声明的方法,装饰器可以给它增加额外的职责(方法)。

Decorator(抽象装饰类)

它也是抽象构件类的子类,用于给具体构件增加职责,但是具体职责在其子类中实现。它维护一个指向抽象构件对象的引用,通过该引用可以调用装饰之前构件对象的方法,并通过其子类扩展该方法,以达到装饰的目的。

ConcreteDecorator(具体装饰类)

它是抽象装饰类的子类,负责向构件添加新的职责。每一个具体装饰类都定义了一些新的行为,它可以调用在抽象装饰类中定义的方法,并可以增加新的方法用以扩充对象的行为。

注意事项:装饰者模式使用时需要注意以下几个问题:

尽量保持装饰类的接口与被装饰类的接口相同,这样,对于客户端而言,无论是装饰之前的对象还是装饰之后的对象都可以一致对待。这也就是说,在可能的情况下,我们应该尽量使用透明装饰模式。

尽量保持具体装饰类是一个“轻”类,也就是说不要把太多的行为放在具体构件类中,我们可以通过装饰类对其进行扩展。

如果只有一个具体构件类,那么抽象装饰类可以作为该具体构件类的直接子类。

使用场景

在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。

当不能采用继承的方式对系统进行扩展或者采用继承不利于系统扩展和维护时可以使用装饰模式。

不能采用继承的情况主要有系统中存在大量独立的扩展,为支持每一种扩展或者扩展之间的组合将产生大量的子类,使得子类数目呈爆炸性增长;类已定义为不能被继承(譬如final类)

InputStream is = System.in;
//把字节流装饰成字符流
InputStreamReader isr = new InputStreamReader(is);
//把字符流装饰成高效流
BufferedReader br = new BufferedReader(isr);
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
Scanner scanner = new Scanner(System.in);


ListView动画,通过装饰者模式扩充adapter

Recyclerview添加头布局,脚布局,通过装饰者模式扩充adapter

Context

程序猿实例

这里还是以苦逼的程序猿为例来说明装饰者模式。假设有两个程序员他们原来只会各自的技能,现在需要让Android程序猿具备设计模式技能,那就给他装饰一下。如下实现:

package yanbober.github.io;

//Component(抽象构件)
interface ProgramMonkey {
void skills();
}
//ConcreteComponent(具体构件)
class AndroidProgramMonkey implements ProgramMonkey {
@Override
public void skills() {
System.out.println("会写Android代码!");
}
}
//ConcreteComponent(具体构件)
class PHPProgramMonkey implements ProgramMonkey {
@Override
public void skills() {
System.out.println("会写PHP代码!");
}
}
//Decorator(抽象装饰类)
class ProgramMonkeyDecorator implements ProgramMonkey {
protected ProgramMonkey mProgramMonkey;

public ProgramMonkeyDecorator(ProgramMonkey mProgramMonkey) {
this.mProgramMonkey = mProgramMonkey;
}

public void skills() {
mProgramMonkey.skills();
}
}
//ConcreteDecorator(具体装饰类)
class PatternDecorator extends ProgramMonkeyDecorator {
public PatternDecorator(ProgramMonkey mProgramMonkey) {
super(mProgramMonkey);
}

@Override
public void skills() {
super.skills();
System.out.println("会设计模式!");
}
}

public class Main {
public static void main(String[] args) {
//有一个Android程序猿只会写Android代码
ProgramMonkey programMonkey = new AndroidProgramMonkey();
programMonkey.skills();
//装饰一下他,装逼的技能,他竟然除了写Android还懂设计模式
programMonkey = new PatternDecorator(programMonkey);
programMonkey.skills();

programMonkey = new PHPProgramMonkey();
programMonkey.skills();
}
}


装饰模式优点

对于扩展一个对象的功能,装饰模式比继承更加灵活性,不会导致类的个数急剧增加。

可以通过一种动态的方式在运行时选择不同的具体装饰类,从而实现不同的行为。

可以对一个对象进行多次装饰,通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合,得到功能更为强大的对象。

具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,原有类库代码无须改变,符合“开闭原则”

使用装饰模式,可以提供比继承更灵活的扩展对象的功能,它可以动态的添加对象的功能,并且可以随意的组合这些功能

装饰模式缺点

使用装饰模式进行系统设计时将产生很多小对象,这些对象的区别在于它们之间相互连接的方式有所不同,而不是它们的类或者属性值有所不同,大量小对象的产生势必会占用更多的系统资源,在一定程序上影响程序的性能。

装饰模式提供了一种比继承更加灵活机动的解决方案,但同时也意味着比继承更加易于出错,排错也很困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为繁琐。

正因为可以随意组合,所以就可能出现一些不合理的逻辑

对象增强的手段

继承

被增强的对象固定的

增强的内容也是固定的

装饰者模式

被增强的对象是可以切换的

增强的内容是固定的

动态代理(AOP)

被增强的对象可以切换:Service

增强的内容也可以切换:事务处理

继承

缺点:

增强的内容是死的,不能动!

被增强的对象也是死的!

使用继承会使类增多!!!

class 咖啡类 {}

class 有糖咖啡 extends 咖啡类 {
}

class 加奶咖啡 extends 咖啡类 {
}

class 加盐咖啡 extends 咖啡类 {
}

class 加糖加奶 extends 加奶 {
}


装饰者模式

增强的内容是不能修改的!

被增强的对象可以是任意的!

class 咖啡类 {}

class 有糖咖啡 extends 咖啡类 {
}

class 加奶咖啡 extends 咖啡类 {
}

class 加盐咖啡 extends 咖啡类 {
}

咖啡 a = new 加糖();
咖啡 b = new 加盐(a);//对a进行装饰,就是给a加盐
咖啡 c = new 加奶(b);


IO流!

四大家:

1. 字节:InputStream、OutputStream

2. 字符:Reader、Writer

InputStream

FileInputSteram:它是节点流!就是和一个资源绑定在一起的!文件!

BufferedInputStream:它是装饰流!创建我是一定要给我一个底层对象,然后我不管你给我的是什么流,我都会给它添加缓冲区!

new BufferedInputStream(任意的InputStream)

FileInputStream in = new FileInputStream("F:/a.jpg");
BufferedInputStream b = new BufferedInputStream(in);
ObjectInputStream o = new ObjectInputStream(b);


Connection

装饰:不知道被增强对象的具体类型时,可以使用!

是你还有你,一切拜托你!

is a

has a

use a

class MyConnection implements Connection {//是你
// 还有你
private Connection con;//底层对象,被增强对象

public MyConnection(Connection con){//通过构造器传递底层对象!
this.con = con;
}

// 一切拜托你
public Statement createStatement() {
return con.createStatement();
}

// 增强点
public void close(){
把当前连接归还给池!
}
}

Connection con = 通过四大参数创建连接对象!是由mysql提供的!
Connection con1 = new MyConnection(con);

con1.createStatement()
con.createStatement();

con1.close();
con.close();
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: