装饰者模式

引入:

我们知道使用BufferedReader的readLine()方法可以从输入流一次性读入一行数据。假如,我们现在有以下几个需求:
1.每次读入时在行首加入读入的行号;
2.读入时在行末加上分号;
3.读入时给每行加上双引号;
4.同时满足1和2的要求;
5.同时满足1,2和3的要求;
简单一点,我们可以使用继承,分别编写满足1,2,3,4,5要求的类,让他们继承BufferedReader,然后重写readLine()方法。如下(以5为例):

package cn.edu.xidian_decorator;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;

public class ExtendBufferedReader extends BufferedReader {

	int count = 1;
	String line = null;
	
	public ExtendBufferedReader(Reader in) {
		super(in);
	}

	@Override
	public String readLine() throws IOException {
		if((line = super.readLine()) == null)
			return null;
		return (count++)+"\""+line+";"+"\"";			//对功能进行扩展,满足5的要求
	}
	
	public static void main(String[] args) throws Exception {
		String newLine = null;
		ExtendBufferedReader ebr = new ExtendBufferedReader(new FileReader
											("./src/cn/edu/xidian_decorator/ExtendBufferedReader.java"));
		while((newLine = ebr.readLine()) != null)
			System.out.println(newLine);
	}
}
显然,编写5个不同的类可以分别实现题中的5个要求,但这样做太麻烦。我们不难发现,题中的第4和第5这两个要求都是在1,2,3的基础上进行扩展的。怎样才能够编写更少的类实现更多的功能呢?这里就需要用到装饰者模式。

一、装饰者模式的作用

装饰者模式主要是用来增强类的功能,即在一个类原有方法的基础上做出扩展,以增强类的功能。理论上来讲,我们可以通过继承的方式来增强类的功能(重写类方法),这种方法简单明了,容易理解。但是当类的扩展功能较多时就会使得继承体系复杂,使用不太灵活。装饰者的优势在于可以让装饰者互相装饰。以题中的要求为例,如果让满足1,2,3要求的类互相装饰,则可以实现更多的功能。

二、如何使用装饰者

1.在装饰者的内部维护一个被装饰类的引用;
2.让装饰者类有一个共同的父类或者父接口;
在装饰者的内部维护一个被装饰类的引用是为了让装饰者类可以调用被装饰者类的方法,并在此基础上进行扩展。让装饰者拥有一个共同的父类或者父接口是为了利用多态达到装饰者类可以互装饰的效果。以上题为例,当我们想要实现要求1时可以新建一个类BufferedReaderLineNum并且在这个类的内部维护一个BufferedReader类实例的引用,这样我们就能够使用BufferedReader类的readLine方法。除此之外,我们还需要让当前类继承BufferReader。这样,在实现4的要求时,我们可以将一个实现1要求的类作为满足2要求的类BufferedReaderSemicolon内部维护的被装饰类BufferedReader( 这其实是一个多态,传参时我们会传入一个BufferedReader的子类BufferedReaderLinNum的实例)。当使用类BufferedReaderSemicolon的readLine()方法时,由于其内部维护的时一个BufferedReaderLinNumr类的实例,因此会在BufferedReaderLinNum的readLine()方法进行扩充,BufferedReaderLinNum的readLine()方法已经在行首加入了行号,因此,调用BufferedReaderSemicolon的readLine()方法会先在行首加入行号再在行末加上分号。如下所示(实现要求5):
package cn.edu.xidian_decorator;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

/**
 * 
 * 该类主要满足要求1,即读入时在行首加入行号;
 * @author DW
 *
 */
class BufferedReaderLineNum extends BufferedReader {
	
	BufferedReader br = null;
	int count = 1;
	String line = null;
	
	public BufferedReaderLineNum(BufferedReader in) {
		super(in);		//如果不好理解,可以忽略这行代码,它的作用只是调用父类构造方法,让代码不报错
		this.br = in;
	}

	@Override
	public String readLine() throws IOException {
		if((line = br.readLine()) == null)
			return null;
		return (count++)+line;		//对功能进行扩展,满足1的要求
	}
}

/**
 * 
 * 该类主要满足要求2,即在行末加上分号
 * @author DW
 *
 */
class BufferedReaderSemicolon extends BufferedReader {
	
	BufferedReader br = null;
	String line = null;
	
	public BufferedReaderSemicolon(BufferedReader in) {
		super(in);		//如果不好理解,可以忽略这行代码,它的作用只是调用父类构造方法,让代码不报错
		this.br = in;
	}

	@Override
	public String readLine() throws IOException {
		if((line = br.readLine()) == null)
			return null;
		return line+";";		//对功能进行扩展,满足2的要求
	}
}

/**
 * 
 * 该类主要满足要求3,即在每行加上双引号
 * @author DW
 *
 */
class BufferedReaderMark extends BufferedReader {
	
	BufferedReader br = null;
	String line = null;
	
	public BufferedReaderMark(BufferedReader in) {
		super(in);		//如果不好理解,可以忽略这行代码,它的作用只是调用父类构造方法,让代码不报错
		this.br = in;
	}

	@Override
	public String readLine() throws IOException {
		if((line = br.readLine()) == null)
			return null;
		return "\""+line+"\"";		//对功能进行扩展,满足3的要求
	}
}

/**
 * 测试类
 * @author DW
 *
 */
public class Main {

	public static void main(String[] args) throws Exception {
		String newLine = null;
		BufferedReaderLineNum brl = new BufferedReaderLineNum(new BufferedReader(new FileReader(
										"./src/cn/edu/xidian_decorator/Main.java")));
		BufferedReaderSemicolon brs = new BufferedReaderSemicolon(brl);
		BufferedReaderMark brm = new BufferedReaderMark(brs);
		while((newLine = brm.readLine()) != null)
			System.out.println(newLine);
	}
}


你可能感兴趣的:(java,装饰者模式,java,se,设计模式)