当想要对已有的对象进行功能增强时,可以定义一个类,基于有的功能,并提供加强的功能。那么自定义的该类成为装饰类。
装饰类会通过构造函数接受被装饰的对象,并基于该装饰的对象的功能,提供更强的功能。装饰类最典型的例子是IO流中的BufferedWriter和BufferedReader。
它们的构造函数:
public BufferedReader(Reader in)
public BufferedWriter(Writer out)
其内部就是基于Reader和Writer对象的功能对它们的功能进行增强的。比如:不使用字符缓冲区读取字符时就只能读取单个字符,使用缓冲区,就可以一次读取一个文本行。
BufferedReader的内部实现原理其实就是基于Reader对象的功能实现的,请看下面的需求。
需求:自定义一个类,不使用继承,使该类具有BufferedReader类的readLine()功能。
import java.io.*;
public class MyBufferedReader
{
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("Demo.txt");
MyBufReadLine mr = new MyBufReadLine(fr);
String line = null;
while ((line=mr.myLine()) != null)
{
System.out.println(line);
}
mr.myClose();
}
}
//装饰类,构造函数接受一个FileReader对象
class MyBufReadLine
{
private FileReader f = null;
MyBufReadLine(FileReader f) {
this.f = f;
}
//基于FileReader对象的read方法,提供功能更强的myLine方法,一次读取一行
public String myLine() throws IOException {
StringBuilder sb = new StringBuilder();
int ch = 0;
while ((ch = f.read()) != -1)
{
if(ch == '\r')
continue;
if(ch == '\n')
return sb.toString();
else
sb.append((char)ch);
}
if (sb.length() != 0)
return sb.toString();
return null;
}
//基于FileReader的close方法自定义自己的close方法。
public void myClose() throws IOException {
f.close();
}
}
二,装饰设计模式与继承的不同
装饰的可以再一个类原有功能的基础上增加一些功能,这些功能是基于原有功能对原有的某些功能进行增强的。那么什么不直接使用继承来实现这些功能呢?
就拿下面这个示例代码举例:
class PersonDemo
{
public static void main(String[] args) {
Person p = new Person();
//p.eat();
SuperPerson sp = new SuperPerson(p);
sp.superEat();
}
}
class Person
{
public void eat() {
System.out.println("吃饭");
}
}
//后期装饰类,可以再不修改原代码的情况下,扩展原类的功能;
class SuperPerson
{
private Person p;
SuperPerson(Person p) {
rhis.p = p;
}
public void superEat() {
System.out.println("甜食");
System.out.println("喝酒");
p.eat();
System.out.println("来一杯");
}
}
使用继承,如果每次Person类需要增加一个功能的话,都新建一个Person的子类,然后在Person类中增加新的方法,就会很麻烦,每次都要创建子类,要对Person类的某个功能进行增强就需要重写Person类的某个方法。而使用装饰设计模式后,就可以不用每次创建Person类的子类,通过Person类的对象,就可以对原有方法进行功能增强,如果想要增加额外的功能,就不用每次都创建其子类,只需要在自定义的装饰类中增加就可以了,创建对象时,也只需要创建装饰类的对象。如果后期发现在定义类不合适,也可以注释掉自己的对象,使用原来的对象。所以装饰类比继承要灵活,避免了继承体系的臃肿,降低了类与类之间的关系。
三,观察示例一的代码我们发现,MyBufferedReader类的构造函数只能接受一个FileReader的对象,扩展性不是很好,如果后期还要对其他的Reader对象进行功能的增强,就要在定义一个MyBufferedReader类,为了提高扩展性,可以使用多态,让MyBufferedReader的构造函数接受一个Reader类型的对象,这样,即使是其他Reader子类想要增强某个功能也可以使用该类。
需求:提高示例一的扩展性,并且让MyBufferedReader类也是Reader类的子类,这样它就拥有了和BufferedReader类一样的功能。
分析:如果和让MyBufferedReader也成为Reader体系中的一员呢?只要继承Reader类就可以了。我们发现Reader类中有两个抽象方法close()和read()方法,只需要在装饰类中重写这两个方法就可以了。
import java.io.*;
public class MyReadLine
{
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("Demo.txt");
MyBufferedReader mr = new MyBufferedReader(fr);
String line = null;
while ((line=mr.myLine()) != null)
{
System.out.println(line);
}
mr.myClose();
}
}
class MyBufferedReader extends Reader
{
private Reader f = null;
MyBufferedReader(Reader f) {
this.f = f;
}
//定义一个临时容器。原BufferReader封装的是字符数组。
//为了演示方便。定义一个StringBuilder容器。因为最终还是要将数据变成字符串。
//BufferedReaer的底层也是读取单个字符,当遇到'\r\n'是会换行,此时返回读取到的一行的字符串
public String myLine() throws IOException {
StringBuilder sb = new StringBuilder();
int ch = 0;
while ((ch = f.read()) != -1)
{
if(ch == '\r')
continue;
if(ch == '\n')
return sb.toString();
else
sb.append((char)ch);
}
if (sb.length() != 0)
return sb.toString();
return null;
}
public void close()
throws IOException {
f.close();
}
public int read(char[] cbuf,
int off,
int len)
throws IOException {
return read(cbuf,off,len);//该抽象方法我们不知道如何去实现,所以有子类区实现,也就是我们传入的FileReader
//趋去实现。
}
public void myClose() throws IOException {
f.close();
}
}
总结:这个示例就是用到了多态,也是用到了装饰设计模式,多态体现在MyBufferedReader构造函数接受的参数。装饰设计模式体现在对Reader类的read方法进行了增强,可以读取一行文本。
四,装饰设计模式其他应用举例:
在字符读取流中还有一个典型的装饰设计模式的类就是:LineNumberReader类,该类也是一个装饰类。构造方法如下:
LineNumberReader(Reader in)
使用默认输入缓冲区的大小创建新的行编号 reader。
它也是对Reader的子类进行装饰,提供额外的功能。
他的基本使用方法是:
import java.io.*;
public class LineNumberReaderDemo
{
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("Demo.txt");
LineNumberReader lb = new LineNumberReader(fr);
String line = null;
lb.setLineNumber(100);//设置起始行号为100
while ((line=lb.readLine())!=null)
{
System.out.println(lb.getLineNumber() + ":" + line);
}
lb.close();
}
}
它的内部实现原理也使用到了装饰设计模式和多态以及继承等面向对象的原理。
需求:自定义一个获取行号的类,该类实现与 LineNumberReader 类相同的功能。同时使它也成为 Reader 的一个子类。然后使用自定义的该方法,实现与上述代码相同的功能。
import java.io.*;
public class MyLineNumberReaderDemo
{
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("Demo.txt");
MyLineNumberReader ml = new MyLineNumberReader(fr);
String line = null;
while ((line=ml.myReadLine())!=null)
{
System.out.println(ml.getNumber() + "::" + line);
}
}
}
class MyLineNumberReader
{
private Reader fr;
private int num=0;
MyLineNumberReader(Reader fr) {
this.fr = fr;
}
public void setNumber(int num) {
this.num = num;
}
public int getNumber() {
return num;
}
public void myClose() throws IOException {
fr.close();
}
public String myReadLine() throws IOException {
num++;
StringBuilder sb = new StringBuilder();
int ch = 0;
while ((ch=fr.read()) != -1)
{
if(ch == '\r')
continue;
if(ch == '\n')
return sb.toString();
else
sb.append((char)ch);
}
if(sb.length() != 0)
return sb.toString();
return null;
}
}
总结发现LineNumberReader类其实是BufferedReader的一个子类,在三只中我们模拟过BufferedReader类,所以为了提高程序的扩展性,只需要继承MyBufferedReader 类就可以减少代码量。
优化代码如下:
import java.io.*;
public class MyLineNumberReaderDemo
{
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("Demo.txt");
MyLineNumberReader ml = new MyLineNumberReader(fr);
String line = null;
while ((line=ml.myReadLine())!=null)
{
System.out.println(ml.getNumber() + "::" + line);
}
}
}
class MyLineNumberReader extends MyBufReadLine
{
private int num=0;
MyLineNumberReader(Reader fr) {
super(fr);//调用父类构造方法
}
public void setNumber(int num) {
this.num = num;
}
public int getNumber() {
return num;
}
public void myClose() throws IOException {
super.close();
}
//重写父类的myReadLine方法
public String myReadLine() throws IOException {
num++;
return super.myLine();
}
}