书中通过小菜要去约会,引出了穿什么衣服的问题。小菜被要求写一个可以搭配不同服饰穿衣服的系统。
在未使用设计模式时,代码如下:
public class Person {
private String name;
public Person(String name) {
super();
this.name = name;
}
public void wearTShirts(){
System.out.println("大T恤");
}
public void wearBigTrouser(){
System.out.println("垮裤");
}
public void wearSneaters(){
System.out.println("破球鞋");
}
public void wearSuit(){
System.out.println("西装");
}
public void wearTie(){
System.out.println("领带");
}
public void wearLeatherShoes(){
System.out.println("皮鞋");
}
public void show(){
System.out.println("装扮" +name);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
主方法:
public class Main {
public static void main(String[] args) {
Person xc = new Person("小菜");
xc.wearTShirts();
xc.wearBigTrouser();
xc.wearSneaters();
xc.show();
}
}
但是如果需要增加妆扮,则需要修改Person类,这违反了开放封闭原则,于是引出了设计模式。
装饰模式(Decorator):
装饰模式UML图:
装饰模式是利用setComponent来封装的。这样每个装饰对象的实现就和如何使用这个对象分开了,每个装饰对象只关心自己的功能,不需要关心如何被添加到关系链当中。
装饰模式代码:
public class Person {
private String name;
public Person() {
super();
}
public Person(String name) {
super();
this.name = name;
}
public void show(){
System.out.println("裝飾"+name);
}
}public abstract class Finery extends Person{//服饰抽象类 private Person component;public Finery( Person component) {this.component = component;}public void show(){if(null!= component){component.show();}}}
public class Tshirts extends Finery{
public Tshirts(Person component) {
super(component);
}
@Override
public void show() {
System.out.println("大T恤");
super.show();
}
}
public class BigTrouser extends Finery{
public BigTrouser(Person component) {
super(component);
// TODO Auto-generated constructor stub
}
@Override
public void show() {
System.out.println("破球鞋");
super.show();
}
}
客户端代码:
public class Main {
public static void main(String[] args) {
Person xc = new Person("小菜");
Tshirts ts = new Tshirts(xc);
BigTrouser bt = new BigTrouser(ts);
bt.show();
}
}
因为题中只有一个ConcreteCompenont 而没有Compentont 接口,那么decorator类可以是ConcreteCompenont的一个子类。同样道理,如果只有一个
ConcreteCompenont类,那么就没有必要建立一个decorator类,而可以把decorator和ConcreteCompenont合并成一个类。
装饰模式总结
当系统需要新功能的时候,是像旧的勒种添加新的代码。这些新家的代码通常装饰了原有的类的核心职责或主要行为,但这种做法的问题在于,他们在
主类中加入了新的字段,新的方法和新的逻辑,从而增加了主类的复杂程度,而新加入的东西仅仅是为了满足某种特定情况下才会执行的特殊行为的需
求。而装饰模式却提供了一个非常好的解决方案,它把每个要装饰的功能放在单独的类中,并让这个类包装它所要装饰的对象,因此,当执行特殊方式
时,客户代码就可以在运行时根据需要有选择第,按顺序第使用装饰功能包装对象。
这样做就是有效的把类的核心职责和装饰功能区分开了。而且可以去除相关类中重复的装饰逻辑。
--------------------------------------------------分割线-----------------------------------------
IO中的装饰模式
一开始,看到装饰模式,我感觉和Spring AOP 好像,在百度查了一下,发现并不一样。AOP使用的是代理模式,而最常见的使用装饰模式的就是JDK 中的IO流。
下面的代码IO流代码充分显示了装饰模式的作用:
package IO;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.io.PushbackInputStream;
import java.io.PushbackReader;
public class IOTest {
/* test.txt内容:
* hello world!
*/
public static void main(String[] args)
throws IOException, ClassNotFoundException {
//文件路径可自行更换
final String filePath = "F:/test.txt";
//InputStream相当于被装饰的接口或者抽象类,FileInputStream相当于原始的待装饰的对象,FileInputStream无法装饰InputStream
//另外FileInputStream是以只读方式打开了一个文件,并打开了一个文件的句柄存放在FileDescriptor对象的handle属性
//所以下面有关回退和重新标记等操作,都是在堆中建立缓冲区所造成的假象,并不是真正的文件流在回退或者重新标记
InputStream inputStream = new FileInputStream(filePath);
final int len = inputStream.available();//记录一下流的长度
System.out.println("FileInputStream不支持mark和reset:" + inputStream.markSupported());
System.out.println("---------------------------------------------------------------------------------");
/* 下面分别展示三种装饰器的作用BufferedInputStream,DataInputStream,PushbackInputStream,LZ下面做了三个装饰器的功能演示 */
//首先装饰成BufferedInputStream,它提供我们mark,reset的功能
BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);//装饰成 BufferedInputStream
System.out.println("BufferedInputStream支持mark和reset:" + bufferedInputStream.markSupported());
bufferedInputStream.mark(0);//标记一下
char c = (char) bufferedInputStream.read();
System.out.println("LZ文件的第一个字符:" + c);
bufferedInputStream.reset();//重置
c = (char) bufferedInputStream.read();//再读
System.out.println("重置以后再读一个字符,依然会是第一个字符:" + c);
bufferedInputStream.reset();
System.out.println("---------------------------------------------------------------------------------");
//装饰成 DataInputStream,我们为了又使用DataInputStream,又使用BufferedInputStream的mark reset功能,所以我们再进行一层包装
//注意,这里如果不使用BufferedInputStream,而使用原始的InputStream,read方法返回的结果会是-1,即已经读取结束
//因为BufferedInputStream已经将文本的内容读取完毕,并缓冲到堆上,默认的初始缓冲区大小是8192B
DataInputStream dataInputStream = new DataInputStream(bufferedInputStream);
dataInputStream.reset();//这是BufferedInputStream提供的功能,如果不在这个基础上包装会出错
System.out.println("DataInputStream现在具有readInt,readChar,readUTF等功能");
int value = dataInputStream.readInt();//读出来一个int,包含四个字节
//我们转换成字符依次显示出来,可以看到LZ文件的前四个字符
String binary = Integer.toBinaryString(value);
int first = binary.length() % 8;
System.out.print("使用readInt读取的前四个字符:");
for (int i = 0; i < 4; i++) {
if (i == 0) {
System.out.print(((char)Integer.valueOf(binary.substring(0, first), 2).intValue()));
}else {
System.out.print(((char)Integer.valueOf(binary.substring(( i - 1 ) * 8 + first, i * 8 + first), 2).intValue()));
}
}
System.out.println();
System.out.println("---------------------------------------------------------------------------------");
//PushbackInputStream无法包装BufferedInputStream支持mark reset,因为它覆盖了reset和mark方法
//因为流已经被读取到末尾,所以我们必须重新打开一个文件的句柄,即FileInputStream
inputStream = new FileInputStream(filePath);
PushbackInputStream pushbackInputStream = new PushbackInputStream(inputStream,len);//装饰成 PushbackInputStream
System.out.println("PushbackInputStream装饰以后支持退回操作unread");
byte[] bytes = new byte[len];
pushbackInputStream.read(bytes);//读完了整个流
System.out.println("unread回退前的内容:" + new String(bytes));
pushbackInputStream.unread(bytes);//再退回去
bytes = new byte[len];//清空byte数组
pushbackInputStream.read(bytes);//再读
System.out.println("unread回退后的内容:" + new String(bytes));
System.out.println("---------------------------------------------------------------------------------");
/* 以上有两个一层装饰和一个两层装饰,下面我们先装饰成Reader,再进行其它装饰 */
//由于之前被PushbackInputStream将流读取到末尾,我们需要再次重新打开文件句柄
inputStream = new FileInputStream(filePath);
InputStreamReader inputStreamReader = new InputStreamReader(inputStream,"utf-8");//先装饰成InputStreamReader
System.out.println("InputStreamReader有reader的功能,比如转码:" + inputStreamReader.getEncoding());
System.out.println("---------------------------------------------------------------------------------");
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);//我们进一步在reader的基础上装饰成BufferedReader
System.out.println("BufferedReader有readLine等功能:" + bufferedReader.readLine());
System.out.println("---------------------------------------------------------------------------------");
LineNumberReader lineNumberReader = new LineNumberReader(inputStreamReader);//我们进一步在reader的基础上装饰成LineNumberReader
System.out.println("LineNumberReader有设置行号,获取行号等功能(行号从0开始),当前行号:" + lineNumberReader.getLineNumber());
System.out.println("---------------------------------------------------------------------------------");
//此处由于刚才被readLine方法将流读取到末尾,所以我们再次重新打开文件句柄,并需要将inputstream再次包装成reader
inputStreamReader = new InputStreamReader(new FileInputStream(filePath));
PushbackReader pushbackReader = new PushbackReader(inputStreamReader,len);//我们进一步在reader的基础上装饰成PushbackReader
System.out.println("PushbackReader是拥有退回操作的reader对象");
char[] chars = new char[len];
pushbackReader.read(chars);
System.out.println("unread回退前的内容:" + new String(chars));
pushbackReader.unread(chars);//再退回去
chars = new char[len];//清空char数组
pushbackReader.read(chars);//再读
System.out.println("unread回退后的内容:" + new String(chars));
}
}
本文通过一个具体案例,介绍了装饰模式的概念及其实现方式,并探讨了其在Java IO流中的应用。
 装饰模式&spm=1001.2101.3001.5002&articleId=69332489&d=1&t=3&u=c8673760eb0d4a93a4958382fc579201)
1064

被折叠的 条评论
为什么被折叠?



