装饰模式
动态的给一个对象添加一些额外的职责,就增加功能来说,装饰者到相比子类更加灵活
装饰着的特点:装饰器和被装饰器都实现同一个接口
, 主要目的装饰器和被装饰器都实现同一个接口, 主要目的是为了扩展之后依旧保留 OOP 关系(同宗同源)
应用场景:IO 流包装、 数据源包装、 简历包装
为什么说是动态的将责任附加到对象身上,因为装饰者模式有了装饰角色,就可以根据需要动态的装饰不同的具体构件角色和具体装饰角色,这个具体构件和装饰角色是可以动态的切换的
在spring中的引用: Spring 的 ApplicationContext 中配置所有的 DataSource
。 这些 DataSource 可能是各种不同类型的, 比如不同的数据库: Oracle、 SQL Server、 MySQL 等, 也可能是不同的数据源: 比如Apache 提 供 的 org.apache.commons.dbcp.BasicDataSource 、 Spring 提 供 的org.springframework.jndi.JndiObjectFactoryBean 等。 然后 SessionFactory 根据客户的每次请求, 将 DataSource 属性设置成不同的数据源, 以到达切换数据源的目的。
在spring的命名体现:Spring 中用到的包装器模式在类名上有两种表现: 一种是类名中含有 Wrapper
, 另一种是类名中含有Decorator
。 基本上都是动态地给一个对象添加一些额外的职责
装饰者模式的结构
装饰者模式以对客户透明的方式动态地给一个对象附加上更多的责任。换言之,客户端并不会觉得对象在装饰前和装饰后有什么不同。装饰者模式可以在不使用创造更多子类的情况下,将对象的功能加以扩展。
装饰者模式的类图如下
/**
* @Project: spring
* @description: 装饰角色 拓展了发送短信的功能
* @author: sunkang
* @create: 2018-09-05 21:41
* @ModificationHistory who when What
**/
public interface ISiginForThirdService extends ISiginSerevice {
/**
* 原有登录的方法
* @param username
* @param password
* @return
*/
ResultMsg login(String username, String password);
/**
* 发送短信
* @param msg
* @return
*/
ResultMsg sendShortMessage(String msg);
}
在装饰模式中的角色有:
-
抽象构件(Component)角色
:给出一个抽象接口,以规范准备接收附加责任的对象。 -
具体构件(ConcreteComponent)角色
:定义一个将要接收附加责任的类。 -
装饰(Decorator)角色
:持有一个构件(Component)对象的实例,并定义一个与抽象构件接口一致的接口。 -
具体装饰(ConcreteDecorator)角色
:负责给构件对象“贴上”附加的责任。
装饰者模式的实例演示
- 需求
在原有的登录的接口的情况下,动态的增加了发送短信的功能
- 抽象构件角色 定义登录的接口
/**
* @Project: spring
* @description: 抽象构件角色 登录的接口业务
* @author: sunkang
* @create: 2018-09-05 20:51
* @ModificationHistory who when What
**/
public interface ISiginSerevice {
ResultMsg login(String username, String password);
}
- 具体构件角色 ,登录的具体实现
/***
* @Description:
* @Param: 具体构件角色 登录的具体实现
* @return:
* @Author: sunkang
* @Date: 2018/9/5
*/
public class SiginService implements ISiginSerevice {
/**
* 登录的方法
* @param username
* @param password
* @return
*/
public ResultMsg login(String username,String password){
return new ResultMsg("200","登录成功",new Object());
}
}
- 装饰角色,拓展了发送短信的功能
/**
* @Project: spring
* @description: 装饰角色 拓展了发送短信的功能
* @author: sunkang
* @create: 2018-09-05 21:41
* @ModificationHistory who when What
**/
public interface ISiginForThirdService extends ISiginSerevice {
/**
* 原有登录的方法
* @param username
* @param password
* @return
*/
ResultMsg login(String username, String password);
/**
* 发送短信
* @param msg
* @return
*/
ResultMsg sendShortMessage(String msg);
}
- 具体的装饰角色,原有的登录功能动态增加了发送短信的功能
/**
* @Project: spring
* @description: 具体的装饰角色 原有的登录功能增加了发送短信的功能
* @author: sunkang
* @create: 2018-09-06 09:14
* @ModificationHistory who when What
**/
public class SiginForThirdService implements ISiginForThirdService {
private ISiginSerevice siginSerevice ;
public SiginForThirdService(ISiginSerevice siginSerevice) {
this.siginSerevice = siginSerevice;
}
@Override
public ResultMsg login(String username, String password) {
ResultMsg msg = siginSerevice.login(username,password);
//注册成功发送短信的功能
if(msg.getCode().equals("200")){
System.out.println("用户登录成功");
msg = sendShortMessage(username);
}
return msg;
}
/**
* 发送短信的功能 这个是装饰器 增加的额外的功能,在登录成功之后发送短信通知
* @param username
* @return
*/
@Override
public ResultMsg sendShortMessage(String username) {
System.out.println("恭喜用户:"+username+"发送短信成功");
return new ResultMsg("200","发送短信成功",new Object());
}
}
- 测试案例
/**
* @Project: spring
* @description: 装饰者测试
* @author: sunkang
* @create: 2018-09-06 09:23
* @ModificationHistory who when What
**/
public class SiginTest {
public static void main(String[] args) {
ISiginSerevice siginSerevice = new SiginService();
ISiginSerevice siginForThirdService = new SiginForThirdService(siginSerevice);
siginForThirdService.login("sunkang","4324");
}
}
装饰者模式在Java IO体系中的应用
java 的IO体系很好地应用了装饰者模式,下边我们可以先看一下Java IO流体系。
从图中可以看出,InputStream就是装饰者模式中的超类(Component),ByteArrayInputStream,FileInputStream相当于被装饰者(ConcreteComponent),这些类都提供了最基本的字节读取功能。
而另外一个和这两个类是同一级的类FilterInputStream即是装饰者(Decorator),BufferedInputStream,DataInputStream,PushbackInputStream(都继承了FilterInputStream类)…这些都是被装饰者装饰后形成的成品。
根据装饰者模式的特点,我们可以总结出这些IO流的使用方法:
File file = new File ("hello.txt");
FileInputStream in=new FileInputStream(file);
BufferedInputStream inBuffered=new BufferedInputStream (in);
这里BufferedInputStream主要是提供了缓存机制,先读入一个byte[],等count到达缓存Byte[]的大小的时候,再一次读入。 当然你也可以写成:
BufferedInputStream inBuffered =
new BufferedInputStream (new FileInputStream(new File ("hello.txt")));
从使用的角度来看装饰者模式,可以看出它的一个缺点:装饰者模式的实现对于使用者是透明的,当使用者不熟悉你的实现的时,就很难理解。 同理你可以学习一下另外一个结构outputStream 。
适配器模式比较简单就不多讲了,主要是解决了java无法多继承的问题,下面大概讲一下IO包中是怎么用这个模式的,用它来做什么? InputStreamReader和InputStream的功能的不同点在于InputStream是以二进制输入 / 输出, I/O 速度快且效率高,由于读到的是字节,也就不存在乱码问题,平台移植性好。但是它的 read ()方法读到的是一个字节,很不利于人们阅读。InputStreamReader类将字节转换为字符。 你可以在构造器中指定编码的方式,如果不指定的话将采用底层操作系统的默认编码方式。
在字符流中:
File file = new File ("hello.txt");
FileInputStream in=new FileInputStream(file);
InputStreamReader inReader=new InputStreamReader(in);
BufferedReader bufReader=new BufferedReader(inReader);
可以看出步骤2到3使用的是适配器模式,而3到4使用的是装饰者模式 。