装饰者模式在JDK和Mybatis中是怎么应用的? java io包
https://mp.weixin.qq.com/s/-bj71dBylRHRqiPorOpVyg
原创: 李立敏 Java识堂 3月10日
有一个卖煎饼的店铺找上了你,希望你能给她们的店铺开发一个收银系统,已知一个煎饼的价格是8元,一个鸡蛋的价格是1元,一根香肠的价格是2元。
你二话不说写出了如下代码
// 煎饼类
public class Battercake {
protected String getDesc() {
return "煎饼";
}
protected int cost() {
return 8;
}
}
// 加鸡蛋的煎饼
public class BattercakeWithEgg extends Battercake{
@Override
public String getDesc() {
return super.getDesc() + " 加一个鸡蛋";
}
@Override
public int cost() {
return super.cost() + 1;
}
}
// 加鸡蛋和香肠的煎饼
public class BattercakeWithEggSausage extends BattercakeWithEgg {
@Override
public String getDesc() {
return super.getDesc() + " 加一根香肠";
}
@Override
public int cost() {
return super.cost() + 2;
}
}
测试一下,正常工作
public class Test {
public static void main(String[] args) {
Battercake battercake = new Battercake();
// 煎饼 销售价格:8
System.out.println(battercake.getDesc() + " 销售价格:" + battercake.cost());
Battercake battercakeWithEgg = new BattercakeWithEgg();
// 煎饼 加一个鸡蛋 销售价格:9
System.out.println(battercakeWithEgg.getDesc() + " 销售价格:" + battercakeWithEgg.cost());
Battercake battercakeWithEggSausage = new BattercakeWithEggSausage();
// 煎饼 加一个鸡蛋 加一根香肠 销售价格:11
System.out.println(battercakeWithEggSausage.getDesc() + " 销售价格:" + battercakeWithEggSausage.cost());
}
}
但是这样会造成一个问题,煎饼的搭配种类很多。比如,加1根香肠的煎饼,加2个鸡蛋的煎饼,加2个鸡蛋和1根香肠的煎饼,如果对每一种可能都写一个实现,会造成类爆炸。
这个时候你就应该想到用装饰者模式了。来看看如何改造上面的代码
// 组件类
public abstract class ABattercake {
protected abstract String getDesc();
protected abstract int cost();
}
// 具体组件实现类
public class Battercake extends ABattercake {
protected String getDesc() {
return "煎饼";
}
protected int cost() {
return 8;
}
}
// 抽象装饰器类
public class AbstractDecorator extends ABattercake {
private ABattercake aBattercake;
public AbstractDecorator(ABattercake aBattercake) {
this.aBattercake = aBattercake;
}
protected String getDesc() {
return this.aBattercake.getDesc();
}
protected int cost() {
return this.aBattercake.cost();
}
}
// 具体的装饰器实现类
public class EggDecorator extends AbstractDecorator {
public EggDecorator(ABattercake aBattercake) {
super(aBattercake);
}
@Override
protected String getDesc() {
return super.getDesc() + " 加一个鸡蛋";
}
@Override
protected int cost() {
return super.cost() + 1;
}
}
// 具体的装饰器实现类
public class SausageDecorator extends AbstractDecorator {
public SausageDecorator(ABattercake aBattercake) {
super(aBattercake);
}
@Override
protected String getDesc() {
return super.getDesc() + " 加一根香肠";
}
@Override
protected int cost() {
return super.cost() + 2;
}
}
如果有人想买加2个鸡蛋和1根香肠的煎饼,实现方式如下
public class Test {
public static void main(String[] args) {
ABattercake aBattercake = new Battercake();
aBattercake = new EggDecorator(aBattercake);
aBattercake = new EggDecorator(aBattercake);
aBattercake = new SausageDecorator(aBattercake);
// 煎饼 加一个鸡蛋 加一个鸡蛋 加一根香肠 销售价格为: 12
System.out.println(aBattercake.getDesc() + " 销售价格为: " + aBattercake.cost());
}
}
可以看到当要添加新的功能时,我们可以使用继承,在子类中添加新能的扩展实现。但有时候继承是不可行的,因为有些类是被final修饰的。而且待添加的新功能存在多种组合,使用继承的方式会导致大量子类的的出现。
而装饰者模式则是通过组合的方式来替代继承,为对象添加功能
看一下上述代码的UML图

从上图就可以画出装饰者模式的UML图如下

Component(组件):组件接口或抽象类定义了全部组件实现类以及所有装饰器实现的行为。
ConcreteComponent(具体组件实现类):具体组件实现类实现了Component接口或抽象类。通常情况下,具体组件实现类就是被装饰器装饰的原始对象,该类提供了Component接口中定义的最基本的功能,其他高级功能或后序添加的新功能,都是通过装饰器的方式添加到该类的对象之上的。
Decorator(抽象装饰器):所有装饰器的父类,它是一个实现了Component接口的类,并在其中封装了一个Component对象,也就是被装饰的对象。而这个被装饰的对象只要是Component类型即可,这就实现了装饰器的组合和复用
ConcreteDecorator(具体的装饰器):该实现类要向被装饰对象添加某些功能
java io包

从上图可以看出,InputStream是组件,FileInputStream,ByteArrayInputStream是具体组件实现类,FilterInputStream是抽象装饰器,LineInputStream是具体的装饰器。
InputStream和OutputStream,Reader和Writer体系都用到了装饰者模式,不再概述。
举个例子,我们进行IO操作时,经常写如下代码,你是否意识到这个用到了装饰者模式呢?
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(new File("D:/test.txt")));
当我们意识到这个用到装饰器模式时,想增加新功能时,就直接查找是否有相应的具体装饰器即可,或者自己实现一个装饰器,而不是陷入迷茫。
举个例子,我们想把从文件中读入的内容都转为小写时,只要自己继承FilterInputStream,实现相应的功能即可
public class LowerCaseInputStream extends FilterInputStream {
/*
* 自己的装饰类,将大写字母转为小写字母
*/
protected LowerCaseInputStream(InputStream in) {
super(in);
}
@Override
public int read() throws IOException {
int c = super.read();
return (c == -1 ? -1 : Character.toLowerCase((char)c));
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
int result = super.read(b, off, len);
for (int i=off; i<=off+result; i++) {
b[i] = (byte)Character.toLowerCase((char)b[i]);
}
return result;
}
}
D:/test.txt的文件内容如下
THIS is JUST for TEST
测试类
public class InputTest {
public static void main(String[] args) {
int c;
try {
InputStream in = new LowerCaseInputStream(new BufferedInputStream(new FileInputStream("D:/test.txt")));
while ((c = in.read()) >= 0) {
//this is just for test
System.out.print((char)c);
}
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Mybatis缓存模块
Mybatis的缓存模块中,使用了装饰器模式的变体,其中将Decorator接口和Componet接口合并为一个Component接口,类间结构如下

Mybatis的Cache接口就是上图中的Component
public interface Cache {
// 省略一部分方法
String getId();
void putObject(Object key, Object value);
Object getObject(Object key);
Object removeObject(Object key);
}
看一下Cache接口的实现类

仔细看包名,由包名就可以看到PerpetualCache扮演着ConcreteComponent(具体组件实现类)的角色,其余的都是装饰类,为什么要弄这么多装饰类呢?
举个例子,我们可以在二级缓存中配置缓存回收策略。
可配置的选项有
LRU:最近最少使用,移除最长时间不被使用的对象
FIFO:先进先出,按对象进入缓存的顺序来移除它们
SOFT:软引用,移除基于垃圾回收器状态和软引用规则的对象
WEAK:弱引用,更积极的移除基于垃圾收集器状态和弱引用规则的对象
再看上面的装饰类和这个配置选项的名字是不是很类似,Mybatis根据你配置的缓存回收策略来选择相应的装饰类,完成扩展功能。
装饰者模式在JDK和Mybatis中是怎么应用的? java io包的更多相关文章
- java.io包中的字节流—— FilterInputStream和FilterOutputStream
接着上篇文章,本篇继续说java.io包中的字节流.按照前篇文章所说,java.io包中的字节流中的类关系有用到GoF<设计模式>中的装饰者模式,而这正体现在FilterInputStre ...
- Java中的阻塞和非阻塞IO包各自的优劣思考(经典)
Java中的阻塞和非阻塞IO包各自的优劣思考 NIO 设计背后的基石:反应器模式,用于事件多路分离和分派的体系结构模式. 反应器(Reactor):用于事件多路分离和分派的体系结构模式 通常的,对一个 ...
- 1.java.io包中定义了多个流类型来实现输入和输出功能,
1.java.io包中定义了多个流类型来实现输入和输出功能,可以从不同的角度对其进行分 类,按功能分为:(C),如果为读取的内容进行处理后再输出,需要使用下列哪种流?(G) A.输入流和输出流 B ...
- Hadoop与HBase中遇到的问题(续)java.io.IOException: Non-increasing Bloom keys异常
在使用Bulkload向HBase导入数据中, 自己编写Map与使用KeyValueSortReducer生成HFile时, 出现了以下的异常: java.io.IOException: Non-in ...
- [19/03/29-星期五] IO技术_File(文件)类(可操作文件,不能操作其里边内容,位于Java.io 包中)&递归遍历
一.概念 java.io.File类:代表文件和目录. 在开发中,读取文件.生成文件.删除文件.修改文件的属性时经常会用到本类. 以pathname为路径创建File对象,如果pathname是相对路 ...
- 在mybatis中使用存储过程报错java.sql.SQLException: ORA-06550: 第 1 行, 第 7 列: PLS-00905: 对象 USER1.HELLO_TEST 无效 ORA-06550: 第 1 行, 第 7 列:
hello_test是我的存储过程的名字,在mapper.xml文件中是这么写的 <select id="getPageByProcedure" statementType= ...
- windows 中使用hbase 异常:java.io.IOException: Could not locate executable null\bin\winutils.exe in the Hadoop binaries.
平时一般是在windows环境下进行开发,在windows 环境下操作hbase可能会出现异常(java.io.IOException: Could not locate executable nul ...
- java.io包中的四个抽象类
IO所谓的四大抽象类就是: InputStream.OutputStream.Reader.Writer
- 从装饰者模式的理解说JAVA的IO包
1. 装饰者模式的详解 装饰者模式动态地将责任附加到对象上.若要扩展功能,装饰者提供了比继承更有弹性 的替代方案. 装饰者模式设计类之间的关系: 其 中Component是一个超类,ConcreteC ...
随机推荐
- linux清除缓存
linux清除缓存:需要root权限$ sync$ echo 3 >/proc/sys/vm/drop_caches 上面的echo 3 是清理所有缓存 echo 0 是不释放缓存 echo 1 ...
- 1154:LETTERS
题目链接http://bailian.openjudge.cn/practice/1154/ 总时间限制: 1000ms 内存限制: 65536kB 描述 A single-player game i ...
- 【PHP】PHP 7.4 新特性
PHP 7.4 预计在 2019 年年末就会正式发布了,本文先来看看一下 PHP 7.4 的新特性. 1.预加载 预加载的实现理论上是可以为 PHP 带来很大的性能提升的.比如说:现在传统的 PHP ...
- 词向量可视化--[tensorflow , python]
#!/usr/bin/env python # -*- coding: utf-8 -*- """ ---------------------------------- ...
- MMU内存管理单元
arm-linux学习-(MMU内存管理单元) 什么是MMU MMU(Memory Management Unit)主要用来管理虚拟存储器.物理存储器的控制线路,同时也负责虚拟地址映射为物理地址,以及 ...
- Unity应用架构设计(5)——ViewModel之间如何共享数据
对于客户端应用程序而言,单页应用程序(Single Page Application)是最常见的表现形式.有经验的开发人员往往会把一个View分解多个SubView.那么,如何在多个SubView之间 ...
- Mobile 抓包,代理
Mobile代理,抓包工具 Fiddler 下载链接, 适用于Win平台.免费: Charles, 下载链接, 使用与MAC平台,收费,有30天的免费使用期,重新下载安装可以再次获得30天的免费使用时 ...
- 【Spark深入学习 -16】官网学习SparkSQL
----本节内容-------1.概览 1.1 Spark SQL 1.2 DatSets和DataFrame2.动手干活 2.1 契入点:SparkSess ...
- SQL Server 2016新特性:数据库级别配置
新的 ALTER DATABASE SCOPED CONFIGURATION (Transact-SQL) 用来配置数据库级别配置. 这个语句可以配置每个数据库的配置: 清理过程cache 设置MA ...
- VMware Workstation 14.1.1 精简特别版
VMware Workstation 精简特别版,由卡饭网友のcuiplay精简制作,集成许可证密钥安装即永久激活,该特别版最大特色可安装MAC OS X客户操作系统,此外添加了DELL SLIC 2 ...