装饰者模式
缘起:增强一个类的功能,有两种方式:一、继承,二、装饰者模式。
增强一个类的功能的时候我们可以选择使用继承
通过继承去实现增强一个类的功能的优点:代码结构清晰,通俗易懂
缺点:使用不灵活,会导致继承的体系过于庞大 ---问题出现
装饰者模式实现增强模式:
优点:内部可以通过多态技术对多个需要增强的类进行增强,可以是这些装饰类达到互相装饰的目的,而实现特别的灵活。
缺点:需要内部通过多态技术维护需要被增强的类,今儿是的代码稍微复杂。
装饰着设计模式:增强一个类的功能,而且可以让这些装饰类互相装饰。
凡是模式步骤都是固定的,之前讲过一个单例模式。
装饰者模式的步骤:
1、在装饰类的内部,维护一个被装饰类的功能。
2、让装饰类有一个共同的父类或者是父接口
来直接看下面的例子,增强BufferedReader的功能,
需求1: 编写一个类拓展BufferedReader的功能, 增强readLine方法返回 的字符串带有行号。
需求2:编写一个类拓展BufferedReader的功能, 增强readLine方法返回 的字符串带有分号。
需求3: 编写一个类拓展BufferedReader的功能, 增强readLine方法返回 的字符串带有双引号。
需求4: 编写一个类拓展BufferedReader的功能, 增强readLine方法返回 的字符串带有行号+ 分号。
需求5: 编写一个类拓展BufferedReader的功能, 增强readLine方法返回 的字符串带有分号+ 双引号。
需求6: 编写一个类拓展BufferedReader的功能, 增强readLine方法返回 的字符串带有双引号+ 行号。
需求7: 编写一个类拓展BufferedReader的功能, 增强readLine方法返回 的字符串带有行号+ 分号+双引号。
其中需求7使我们的终极需求,直接认真看下面的代码:
import java.io.IOException; //带行号的缓冲输入字符流 class BufferedLineNum2 extends BufferedReader{ //在内部维护一个被装饰类的引用。 BufferedReader bufferedReader; int count = 1; public BufferedLineNum2(BufferedReader bufferedReader){ super(bufferedReader);// 注意: 该语句没有任何的作用,只不过是为了让代码不报错。 this.bufferedReader = bufferedReader; } public String readLine() throws IOException{ String line = bufferedReader.readLine(); if(line==null){ return null; } line = count+" "+line; count++; return line; } } //带分号缓冲输入字符流 class BufferedSemi2 extends BufferedReader{ //为什么要继承? 是为了让这些装饰类的对象可以作为参数进行传递,达到互相装饰 的效果。 //在内部维护一个被装饰类的引用。 BufferedReader bufferedReader; public BufferedSemi2(BufferedReader bufferedReader){ // new BuffereLineNum(); super(bufferedReader);// 注意: 该语句没有任何的作用,只不过是为了让代码不报错。 this.bufferedReader = bufferedReader; } public String readLine() throws IOException{ String line = bufferedReader.readLine(); //如果这里的ReadLine方法是调用了buffereLineNum的readLine方法,问题马上解决。 if(line==null){ return null; } line = line +";"; return line; } } //缓冲类带双引号 class BufferedQuto2 extends BufferedReader{ //在内部维护一个被装饰的类 BufferedReader bufferedReader; public BufferedQuto2(BufferedReader bufferedReader){ //new BufferedSemi2(); super(bufferedReader) ; //只是为了让代码不报错.. this.bufferedReader = bufferedReader; } public String readLine() throws IOException{ String line = bufferedReader.readLine(); //如果这里的ReadLine方法是调用了buffereLineNum的readLine方法,问题马上解决。 if(line==null){ return null; } line = """+line +"""; return line; } } public class Demo2 { public static void main(String[] args) throws IOException { File file = new File("F:\Demo1.java"); FileReader fileReader = new FileReader(file); //建立缓冲输入字符流 BufferedReader bufferedReader = new BufferedReader(fileReader); //建立带行号的缓冲输入字符流 BufferedLineNum2 bufferedLineNum = new BufferedLineNum2(bufferedReader); //在行号的基础上,再加上分号的缓冲输入字符流 BufferedSemi2 bufferedSemi2 = new BufferedSemi2(bufferedLineNum); //在行号分号的基础上,再加上双引号的缓冲输入字符流 BufferedQuto2 bufferedQuto2 = new BufferedQuto2(bufferedSemi2); String line = null; while((line = bufferedQuto2.readLine())!=null){ System.out.println(line); } } }
注意上面的例子,在子类的构造函数中总是有这么一句:super(bufferedReader),之所以这样做,是因为,父类BufferedReader默认没有无参的构造函数,如果父类没有午餐构造函数那么子类也一定没有无参构造函数。默认情况下,如果这是给子类写一个无参的构造函数时,就会调用父类的无参的构造函数,但是父类没有午餐的构造函数,就会出错。这是需要给子类定义一个构造函数传入的参数应当和父类构造函数需要的参数相同。