定义 在不改变原有对象的基础上,将功能附加到对象上。提供了比继承更有弹性的替代方案(扩展原有对象功能)。
类型 结构型
应用场景 1 2 ◆扩展一个类的功能或给一个类添加附加职责 ◆动态的给一个对象添加功能,这些功能可以再动态的撤销
优点 1 2 3 ◆继承的有力补充,比继承灵活,不改变原有对象的情况下给一个对象扩展功能。一般我们可以使用继承实现功能的扩展,如果需要扩展的功能种类繁多,那么势必会生成很多子类,这无疑增加了系统的复杂性。并且使用继承需要提前预知哪些功能,因为继承关系在编译的时候就确定了。而装饰者模式可以动态地加入。其实装饰者模式也是建立在继承的关系基础之上的,注意,这不意味着就不使用继承了,继承也是扩展形式之一,只是某些时候不一定能达到弹性设计的最佳方式。 ◆通过使用不同装饰类以及这些装饰类的排列组合,可以实现不同效果 ◆符合开闭原则,装饰者和被装饰者可以独立变化,原有的代码不需要改变。其实装饰者做的是把装饰功能从类中移出去,这样简化了原来被装饰的类,同时把类的核心职责和装饰功能区分开。
缺点 1 2 ◆会出现更多的代码,更多的类,增加程序复杂性。装饰者模式可能会比继承方式的使用的类要少,但是对象很多,并且这些对象类型是一样的,因为装饰者会继承被装饰类,而被装饰类又有具体的实体,这些实体对象类型又一样,所有排查问题增加了难度。 ◆动态装饰时,多层装饰时会更复杂
关联的设计模式 装饰者模式和代理模式
1 2 1 装饰者模式关注动态地添加方法,代理模式关注于控制对对象的访问 2 代理模式中的代理类可以对它的客户隐藏一个对象的具体信息,通常在使用代理模式的时候常常在代理类中创建一个对象的实例,装饰者模式通常把原始对象作为一个参数传入装饰者的构造器,这是使用上的不同。
装饰者模式和适配器模式
1 2 3 1 两者都是包装者模式 2 装饰者和被装饰者可以实现相同的接口或者装饰者是被装饰者的子类 3 适配器和被适配的类有不同的接口,也有可能是部分接口是重合的
简单需求 买煎饼的时候可以根据自身情况要求加鸡蛋或者香肠,卖煎饼的根据需求去做煎饼。
装饰者模式演练 非装饰者模式 1 2 需求:加一个鸡蛋加一元,一个火腿两元,现在a买一个煎饼,b买加蛋的煎饼,c买加肠的煎饼 方案:通过堆积类完成需求,但是面对众多的需求会类爆炸的。
煎饼类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package com.design.pattern.decorator.v1;public class BatterCake { public String getDesc () { return "煎饼" ; } public int cost () { return 5 ; } }
鸡蛋煎饼类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package com.design.pattern.decorator.v1;public class BatterCakeWithEgg extends BatterCake { @Override public String getDesc () { return super .getDesc() + " 加一个鸡蛋" ; } @Override public int cost () { return super .cost() + 1 ; } }
香肠煎饼类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package com.design.pattern.decorator.v1;public class BatterCakeWithSausage extends BatterCake { @Override public String getDesc () { return super .getDesc() + " 加一个香肠" ; } @Override public int cost () { return super .cost() + 2 ; } }
客户端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 package com.design.pattern.decorator.v1;import lombok.extern.slf4j.Slf4j;import org.junit.Test;@Slf 4jpublic class Client { @Test public void test () { BatterCake batterCake = new BatterCake(); log.info(batterCake.getDesc() + "销售价格为 " + batterCake.cost()); BatterCakeWithEgg batterCakeWithEgg = new BatterCakeWithEgg(); log.info(batterCakeWithEgg.getDesc() + "销售价格为 " + batterCakeWithEgg.cost()); BatterCakeWithSausage batterCakeWithSausage = new BatterCakeWithSausage(); log.info(batterCakeWithSausage.getDesc() + "销售价格为 " + batterCakeWithEgg.cost()); } }
装饰者模式 1 2 需求:现在肠和蛋随机,a 加2蛋2肠 b加1蛋2肠 方案:使用装饰类添加功能,为煎饼加鸡蛋、加香肠
要求:
所谓装饰者模式,通用做法要有抽象的实体类和确定的实体类,同时要有抽象的装饰者和确定的装饰者。现在实体类是煎饼,装饰者是鸡蛋和香肠。
关联:
煎饼实体类继承煎饼抽象类,装饰者抽象类也继承煎饼抽象类,通过它们的父类组合来达到煎饼实体类和装饰者抽象类的关系
抽象煎饼类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package com.design.pattern.decorator.v2;public abstract class AbstractBatterCake { public abstract String getDesc () ; public abstract int cost () ; }
煎饼类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package com.design.pattern.decorator.v2;public class BatterCake extends AbstractBatterCake { @Override public String getDesc () { return "煎饼" ; } @Override public int cost () { return 5 ; } }
抽象装饰类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 package com.design.pattern.decorator.v2;public abstract class AbstractDecorator extends AbstractBatterCake { private AbstractBatterCake batterCake; public AbstractDecorator (AbstractBatterCake batterCake) { this .batterCake = batterCake; } @Override public String getDesc () { return batterCake.getDesc(); } @Override public int cost () { return batterCake.cost(); } protected abstract void handle () ; }
鸡蛋装饰类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package com.design.pattern.decorator.v2;import lombok.extern.slf4j.Slf4j;@Slf 4jpublic class EggDecorator extends AbstractDecorator { public EggDecorator (AbstractBatterCake batterCake) { super (batterCake); } @Override public String getDesc () { return super .getDesc() + " 加一个鸡蛋" ; } @Override public int cost () { return super .cost() + 1 ; } @Override protected void handle () { log.info("鸡蛋装饰者特有的处理" ); } }
香肠装饰类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package com.design.pattern.decorator.v2;import lombok.extern.slf4j.Slf4j;@Slf 4jpublic class SauseDecorator extends AbstractDecorator { public SauseDecorator (AbstractBatterCake batterCake) { super (batterCake); } @Override public String getDesc () { return super .getDesc() + " 加一个香肠" ; } @Override public int cost () { return super .cost() + 2 ; } @Override protected void handle () { log.info("香肠装饰者特有的处理方式" ); } }
客户端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 package com.design.pattern.decorator.v2;import lombok.extern.slf4j.Slf4j;import org.junit.Test;@Slf 4jpublic class Client { @Test public void test () { AbstractBatterCake batterCake; batterCake = new BatterCake(); batterCake = new EggDecorator(batterCake); batterCake = new EggDecorator(batterCake); ((EggDecorator) batterCake).handle(); batterCake = new SauseDecorator(batterCake); ((SauseDecorator) batterCake).handle(); log.info(batterCake.getDesc() + " 一共卖了" + batterCake.cost() + "元" ); } }
装饰者模式在源码中的使用 BufferedReader
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 public class BufferedReader extends Reader { private Reader in; private char cb[]; private int nChars, nextChar; public BufferedReader (Reader in, int sz) { super (in); if (sz <= 0 ) throw new IllegalArgumentException("Buffer size <= 0" ); this .in = in; cb = new char [sz]; nextChar = nChars = 0 ; } public BufferedReader (Reader in) { this (in, defaultCharBufferSize); } private void fill () throws IOException { int dst; if (markedChar <= UNMARKED) { dst = 0 ; } else { int delta = nextChar - markedChar; if (delta >= readAheadLimit) { markedChar = INVALIDATED; readAheadLimit = 0 ; dst = 0 ; } else { if (readAheadLimit <= cb.length) { System.arraycopy(cb, markedChar, cb, 0 , delta); markedChar = 0 ; dst = delta; } else { char ncb[] = new char [readAheadLimit]; System.arraycopy(cb, markedChar, ncb, 0 , delta); cb = ncb; markedChar = 0 ; dst = delta; } nextChar = nChars = delta; } } int n; do { n = in.read(cb, dst, cb.length - dst); } while (n == 0 ); if (n > 0 ) { nChars = dst + n; nextChar = dst; } } } public abstract class Reader implements Readable , Closeable { }
InputStream类型作为被装饰类型,它的装饰者有很多,如上图中列出FilerInputStream、BufferedInputStream以及 LineInputStream。在装饰者内部本质上都是使用InputStream的实例操作的。
BufferedInputStream
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 public class BufferedInputStream extends FilterInputStream { private static int DEFAULT_BUFFER_SIZE = 8192 ; private static int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8 ; protected volatile byte buf[]; private static final AtomicReferenceFieldUpdater<BufferedInputStream, byte []> bufUpdater = AtomicReferenceFieldUpdater.newUpdater (BufferedInputStream.class, byte[].class, "buf"); protected int count; protected int pos; protected int markpos = -1 ; protected int marklimit; private InputStream getInIfOpen () throws IOException { InputStream input = in; if (input == null ) throw new IOException("Stream closed" ); return input; } public BufferedInputStream (InputStream in) { this (in, DEFAULT_BUFFER_SIZE); } public BufferedInputStream (InputStream in, int size) { super (in); if (size <= 0 ) { throw new IllegalArgumentException("Buffer size <= 0" ); } buf = new byte [size]; } }
Servlet的HttpServletRequestWrapper
HttpServletRequestWrapper继承了ServletRequestWrapper也实现了HttpServletRequest,它们的 公共父类是ServletRequest.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 public class HttpServletRequestWrapper extends ServletRequestWrapper implements HttpServletRequest { public HttpServletRequestWrapper (HttpServletRequest request) { super (request); } private HttpServletRequest _getHttpServletRequest () { return (HttpServletRequest) super .getRequest(); } public String getAuthType () { return this ._getHttpServletRequest().getAuthType(); } public Cookie[] getCookies() { return this ._getHttpServletRequest().getCookies(); } public long getDateHeader (String name) { return this ._getHttpServletRequest().getDateHeader(name); } } public class ServletRequestWrapper implements ServletRequest { private ServletRequest request; public ServletRequestWrapper (ServletRequest request) { if (request == null ) { throw new IllegalArgumentException("Request cannot be null" ); } this .request = request; } public ServletRequest getRequest () { return this .request; } public void setRequest (ServletRequest request) { if (request == null ) { throw new IllegalArgumentException("Request cannot be null" ); } this .request = request; } public Object getAttribute (String name) { return this .request.getAttribute(name); } public Enumeration getAttributeNames () { return this .request.getAttributeNames(); } }
MyBatis的FifoCache
MyBatis的Cache模块中使用大量的装饰者模式,在decorators包下都是的,下面列举最近最少使用策略的装饰类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 public class LruCache implements Cache { private final Cache delegate; private Map<Object, Object> keyMap; private Object eldestKey; public LruCache (Cache delegate) { this .delegate = delegate; setSize(1024 ); } @Override public String getId () { return delegate.getId(); } @Override public int getSize () { return delegate.getSize(); } public void setSize (final int size) { keyMap = new LinkedHashMap<Object, Object>(size, .75F , true ) { private static final long serialVersionUID = 4267176411845948333L ; @Override protected boolean removeEldestEntry (Map.Entry<Object, Object> eldest) { boolean tooBig = size() > size; if (tooBig) { eldestKey = eldest.getKey(); } return tooBig; } }; } }