设计模式--结构模式--装饰模式

一、基本概念

      有一次面试时,面试官问了一个问题,怎么做类增强,当时心里想,这是什么鬼问题,通过实现接口,继承类不就可以了吗,当时不知道面试官的想法或者要解决什么问题。后续带着这个问题做项目,碰到了装饰模式,才明白,面试官可能是让我理解一下设计模式的应用。

     Java中三个基本特性:继承、接口、反射是实现扩展功能基础手段。其他模式的本质也无非是这些。

     装饰模式(Decorator Pattern) ,也称为包装模式(wapper Pattern):动态地给一个对象增加一些额外的职责。它最明显的区别就是,不是继承一个类,而是通过组合的方式,增强了功能。

    普通共识:装饰模式比生成子类实现更为灵活,装饰模式以对客户透明的方式动态地给一个对象附加上更多的责任,可以在不需要创建更多子类的情况下,让对象的功能得以扩展。是一种用于替代继承的技术,它通过一种无须定义子类的方式给对象动态增加职责,使用对象之间的关联关系取代类之间的继承关系。

    如果从方便认知上可以这样理解:继承,认为是爷爷->父亲->儿子,辈分不一样;而装饰模式呢,可以认为是辈分是一致,只做增强和特性化需求。

 

二、简单理解

  这个小段,用一个小例子说明一下装饰模式(Decorator Pattern) /包装模式(wapper Pattern)。

   1、定义枪的接口,枪可以装刺刀和射击

package comm.pattern.struct.decorator.wapper;

import java.security.PublicKey;

/**
 * 
 * @Title: IGun.java
 * @Package: comm.pattern.struct.decorator.wapper
 * @Description: 定义枪的类型
 * @author yangzhancheng
 * @2022年5月25日:下午11:02:52
 *
 */
public interface IGun {
	
	/**
	 * 装刺刀,尽量不这么定义,一个类定义一个方法
	 */
	public void bayonet();
	
	/**
	 * 射击
	 */
	public void shut();
	
}

  2、定义步枪(非全自动步枪,只能一发一射)

package comm.pattern.struct.decorator.wapper;

/**
 * 
 * @Title: Rifle.java
 * @Package: comm.pattern.struct.decorator.wapper
 * @Description: 步枪
 * @author yangzhancheng
 * @2022年5月25日:下午11:04:58
 *
 */
public class Rifle implements IGun {

	/**
	 * 装刺刀
	 */
	public void bayonet() {
		System.out.println("步枪装刺刀....");
	}

	/**
	 * 射击
	 */
	@Override
	public void shut() {
		System.out.println("步枪射击,点射");
	}

}

  3、定义自动步枪(在步枪的基础上增强,或者说是包装一下)

package comm.pattern.struct.decorator.wapper;

/**
 * 
 * @Title: AutoRifleMapper.java
 * @Package: comm.pattern.struct.decorator.wapper
 * @Description: 自动步枪
 * @author yangzhancheng
 * @2022年5月25日:下午11:08:14
 *
 */
public class AutoRifleMapper implements IGun{

	private IGun iGun;
	
	public AutoRifleMapper(IGun iGun){
		this.iGun  = iGun;
	}
	
	/**
	 * 装刺刀
	 */
	@Override
	public void bayonet(){
		iGun.bayonet();
	}
	
	/**
	 * 射击
	 */
	@Override
	public void shut(){
		iGun.shut();
		System.out.println("自动步枪射击,连续射击");
	}
	
}

  4、测试类

package comm.pattern.struct.decorator.wapper;

/**
 * 
 * @Title: Client.java
 * @Package: comm.pattern.struct.decorator.wapper
 * @Description: 客户端演示
 * @author yangzhancheng
 * @2022年5月25日:下午11:18:50
 *
 */
public class Client {

	public static void main(String[] args) {

		IGun  rifle = new Rifle();
		rifle.bayonet();
		rifle.shut();
		
		System.out.println("\n---------------------------------");
		AutoRifleMapper autoRifleMapper = new AutoRifleMapper(rifle);
		autoRifleMapper.bayonet();
		autoRifleMapper.shut();
	}

}

  测试结果

步枪装刺刀....
步枪射击,点射

---------------------------------
步枪装刺刀....
步枪射击,点射
自动步枪射击,连续射击

  

三、按设计模式思路理解

  (一)基本概念

    1、装饰模式指的是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。

    2、装饰对象包含一个真实对象的引用,非常关键。

    3、角色:

         A:抽象构件角色(Component):定义一个对象接口或抽象类,可以给这些对象动态地添加职责。

         B:具体构件角色(ConcreteComponent):实际被动态地添加职责的对象。

         C:抽象装饰者角色(Decorator):实现了Component接口,用来扩展Component类的功能,但对于Component来说,是无需知道Decorator的存在的。

         D:具体装饰者角色(ConcreteDecorator):动态地添加职责的对象。

         E:客户端(Client)

  (二)例子(为简单实现,把C省略了)

    1:抽象构件角色(Component):

package comm.pattern.struct.decorator;

/**
 * 
 * @Title: Component.java
 * @Package: comm.pattern.struct.decorator
 * @Description: 抽象构建,模拟杨过
 * @author yangzhancheng
 * @2020年3月16日:上午12:00:38
 *
 */
public interface Component {
	public void operation();
}

    2:具体构件角色(ConcreteComponent):

package comm.pattern.struct.decorator;

public class ConcreteComponent implements Component {

	public void operation() {
		System.out.println("小张说:'杨过,名过,字改之,是金庸武侠小说《神雕侠侣》的主人公';");
	}

}

    3:抽象装饰者角色(Decorator):

            省略。

    4:具体装饰者角色(ConcreteDecorator):

            4-1: 

package comm.pattern.struct.decorator;

/**
 * 
 * @Title: Decorator.java
 * @Package: comm.pattern.struct.decorator
 * @Description: 装饰类
 * @author yangzhancheng
 * @2020年3月16日:上午12:05:54
 *
 */
class DecoratorA implements Component
{
    private Component component;   
    
    public DecoratorA(Component component)
    {
        this.component=component;
    }   
    
    public void operation()
    {
        component.operation();
        System.out.println("小李补充:‘他在活死人墓待过,练过玉女心经’;");
    }
}

       4-2:

package comm.pattern.struct.decorator;

/**
 * 
 * @Title: DecoratorB.java
 * @Package: comm.pattern.struct.decorator
 * @Description: 装饰类
 * @author yangzhancheng
 * @2020年3月16日:上午12:10:12
 *
 */
class DecoratorB implements Component {
	
	private Component component;

	public DecoratorB(Component component) {
		this.component = component;
	}

	public void operation() {
		component.operation();
		System.out.println("小黄补充:‘他有一只雕兄’。");
	}
}

    5:客户端(Client)\测试类

package comm.pattern.struct.decorator;

/**
 * 
 * @Title: Client.java
 * @Package: comm.pattern.action.templateMethod
 * @Description: 描述该文件做什么
 * @author yangzhancheng
 * @2020年3月1日:下午4:47:17
 *
 */
public class Client {

	public static void main(String[] args) {

		/**
		 * 定义基础信息,他是杨过
		 */
		Component component = new ConcreteComponent();
		component.operation();
		
		/**
		 * 增强一次,或者说是包装/装饰一下,使得人物信息更完美
		 */
		System.out.println("---------------------------------");
		Component partyComponentDec = new DecoratorA(component);
		partyComponentDec.operation();

		/**
		 * 再次增强一次,或者说是包装/装饰一下,使得人物信息更完美,还生动
		 */
		System.out.println("---------------------------------");
		Component allComponent = new DecoratorB(new DecoratorA(new ConcreteComponent()));
		allComponent.operation();
	}

}

  

    运行输出

小张说:'杨过,名过,字改之,是金庸武侠小说《神雕侠侣》的主人公';
---------------------------------
小张说:'杨过,名过,字改之,是金庸武侠小说《神雕侠侣》的主人公';
小李补充:'他在活死人墓待过,练过玉女心经';
---------------------------------
小张说:'杨过,名过,字改之,是金庸武侠小说《神雕侠侣》的主人公';
小李补充:'他在活死人墓待过,练过玉女心经';
小黄补充:'他有一只雕兄'。

  

四、优缺点

      装饰与外观模式的比较:装饰模式是对同一类接口进行功能增强;外观模式,是把子类集中到一起,让另外一个类统一展示。

      优点:

      1、装饰者是继承的有力补充,比继承灵活,不改变原有对象的情况下动态地给一个对象扩展功能,即插即用。

      2、通过使用不同装饰类以及这些装饰类的排列组合,可以实现不同效果。

  3、装饰者完全遵守开闭原则。

  缺点:

  1、会出现更多的代码,更多的类,增加程序复杂性。

  2、动态装饰时,多层装饰时会更复杂

五、源代码分析

  (一)springBoot 中 wapper

  (二)IO 工作流

    1、用Reader\BufferedReader\InputStreamReader\FileReader 说明,其中InputStreamReader\FileReader 看成是一个,因为他们是通过继承实现的。

      A、BufferedReader 与 Reader关系,定义了read()方法为输入。

 

       B、InputStreamReader\FileReader 和reader关系

 

 

 

 

     2、用角色模型分析设计模式

         A:抽象构件角色(Component):

public abstract class Reader implements Readable, Closeable {

    //读出流处理
    public int read() throws IOException {
        char cb[] = new char[1];
        if (read(cb, 0, 1) == -1)
            return -1;
        else
            return cb[0];
    }
    
//读出流处理 public int read(char cbuf[]) throws IOException { return read(cbuf, 0, cbuf.length); } abstract public int read(char cbuf[], int off, int len) throws IOException; }

      B:具体构件角色(ConcreteComponent):   

package java.io;
public class FileReader extends InputStreamReader {

    public FileReader(String fileName) throws FileNotFoundException {
        super(new FileInputStream(fileName));
    }

    public FileReader(File file) throws FileNotFoundException {
        super(new FileInputStream(file));
    }

    public FileReader(FileDescriptor fd) {
        super(new FileInputStream(fd));
    }

}
package java.io;

import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import sun.nio.cs.StreamDecoder;


public class InputStreamReader extends Reader {

    private final StreamDecoder sd;

    public int read() throws IOException {
        return sd.read();
    }

    public int read(char cbuf[], int offset, int length) throws IOException {
        return sd.read(cbuf, offset, length);
    }

}

 

      C:具体装饰者角色(ConcreteDecorator):

package java.io;

public class BufferedReader extends Reader {

    //构造 注意Reader类型就可以
    public BufferedReader(Reader in) {
        this(in, defaultCharBufferSize);
    }

    //重新增强了read方法
    public int read() throws IOException {
        synchronized (lock) {
            ensureOpen();
            for (;;) {
                if (nextChar >= nChars) {
                    fill();
                    if (nextChar >= nChars)
                        return -1;
                }
                if (skipLF) {
                    skipLF = false;
                    if (cb[nextChar] == '\n') {
                        nextChar++;
                        continue;
                    }
                }
                return cb[nextChar++];
            }
        }
    }

    //重新增强了read方法
    public int read(char cbuf[], int off, int len) throws IOException {
        synchronized (lock) {
            ensureOpen();
            if ((off < 0) || (off > cbuf.length) || (len < 0) ||
                ((off + len) > cbuf.length) || ((off + len) < 0)) {
                throw new IndexOutOfBoundsException();
            } else if (len == 0) {
                return 0;
            }

            int n = read1(cbuf, off, len);
            if (n <= 0) return n;
            while ((n < len) && in.ready()) {
                int n1 = read1(cbuf, off + n, len - n);
                if (n1 <= 0) break;
                n += n1;
            }
            return n;
        }
    }
	
    //read方法不是特别好用,增强了readLine方法,这个就是比较巧妙的用法
    public String readLine() throws IOException {
        return readLine(false);
    }
	

    //readLine的具体实现方法
    String readLine(boolean ignoreLF) throws IOException {
        StringBuffer s = null;
        int startChar;

        synchronized (lock) {
            ensureOpen();
            boolean omitLF = ignoreLF || skipLF;

        bufferLoop:
            for (;;) {

                if (nextChar >= nChars)
                    fill();
                if (nextChar >= nChars) { /* EOF */
                    if (s != null && s.length() > 0)
                        return s.toString();
                    else
                        return null;
                }
                boolean eol = false;
                char c = 0;
                int i;

                /* Skip a leftover '\n', if necessary */
                if (omitLF && (cb[nextChar] == '\n'))
                    nextChar++;
                skipLF = false;
                omitLF = false;

            charLoop:
                for (i = nextChar; i < nChars; i++) {
                    c = cb[i];
                    if ((c == '\n') || (c == '\r')) {
                        eol = true;
                        break charLoop;
                    }
                }

                startChar = nextChar;
                nextChar = i;

                if (eol) {
                    String str;
                    if (s == null) {
                        str = new String(cb, startChar, i - startChar);
                    } else {
                        s.append(cb, startChar, i - startChar);
                        str = s.toString();
                    }
                    nextChar++;
                    if (c == '\r') {
                        skipLF = true;
                    }
                    return str;
                }

                if (s == null)
                    s = new StringBuffer(defaultExpectedLineLength);
                s.append(cb, startChar, i - startChar);
            }
        }
    }

}

  

      D:客户端/测试端(Client)

package com.yang.springBoot010;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;

public class IOtest {

	public static void main(String[] args) throws Exception {

		BufferedReader bufferedReader = null;
		FileReader fReader = null;

		try {
			fReader = new FileReader(new File("/工作日志.txt"));

			bufferedReader = new BufferedReader(fReader);
			
			String strline = null;
			while (null != (strline = bufferedReader.readLine())) {
				System.out.println(strline);
			}
		} catch (Exception e) {
			System.out.println(e);
		} finally {
			if (bufferedReader != null) {
				bufferedReader.close();
			}
			if (fReader != null) {
				fReader.close();
			}
		}
	}
}

  

posted on 2020-03-16 00:25  茫然的法庭  阅读(89)  评论(0)    收藏  举报

导航