抱歉,您的浏览器无法访问本站

本页面需要浏览器支持(启用)JavaScript


了解详情 >

结构型模式

适配器模式

定义: 将一个接口转换成客户期待的另一个接口.

使原本接口不兼容的类可以一起工作.

类型: 结构型

使用场景:

  • 已经存在的类, 它的方法和需求不匹配时(方法结果相同或相似).
  • 不是软件设计阶段考虑的设计模式, 是随着软件维护, 由于不同产品、不同厂家造成功能类似而接口不相同情况下的解决方案.

优点:

  • 能提高类的透明性和复用, 现有的类复用但不需要改变.
  • 目标类和适配器类解耦, 提高程序扩展性.
  • 符合开闭原则.

缺点:

  • 适配器编写过程需要全面考虑, 可能会增加系统的复杂性.
  • 增加系统代码可读难度.

扩展:

  • 对象适配器(符合组合复用原则, 使用委托机制实现.继承和组合的时候, 优先使用组合.)
  • 类适配器(通过类继承实现)

相关设计模式

  • 适配器模式和外观模式

Adapter Demo UML类图

类适配器

对象适配器

JDK中使用适配器模式的例子

javax.xml.bind.annotation.adapters.XmlAdapter

Spring中使用适配器模式的例子

org.springframework.aop.framework.adapter.AdvisorAdapter

org.springframework.orm.jpa.jpaVendorAdapter

org.springframework.web.servlet.HandlerAdapter

DispatcherServlet -> doDispatch -> 映射对应的controller

桥接模式

定义: 将抽象部分与它的具体实现部分分离, 使它们都可以独立地变化.

通过组合的方式建立两个类之间的联系, 而不是继承.

类型: 结构型

适用场景:

  • 抽象和具体实现之间增加更多的灵活性.
  • 一个类存在两个(或多个)独立变化的维度, 并且这两个(或多个)维度都需要独立进行扩展.
  • 不希望适用继承, 或因为多层继承导致系统类的个数剧增.

优点:

  • 分离抽象部分以及具体实现部分(因为使用组合, 就是说使用对象之间的关联解耦了抽象和实现之间固有的绑定关系, 使抽象和实现可以延着各自的维度变化扩展).
  • 提高系统的可扩展性.
  • 符合开闭原则.
  • 符合合成复用原则.

缺点:

  • 增加了系统的理解与设计难度.
  • 需要正确地识别出系统中两个独立变化的维度.

相关设计模式:

  • 桥接模式和组合模式(组合强调部分和整体之间的关系, 桥接是平衡级别上不同类的组合)
  • 桥接模式和适配器模式(相同点: 都是让2样东西配合工作;目的不一样, 适配器是改已有的接口, 让他们之间可以相互配合兼容, 而桥接是分离抽象和具体的实现, 然后在此之上使这些层次结构结合起来)

Bridge Demo UML类图

JDK中使用桥接模式的例子

JDBC就是一个很好的例子.JDBC为不同据库提供了统一的接口, 具体的实现让数据库的厂商来实现.

public interface Driver {}

com.mysql.cj.jdbc.Driver

java.sql.Driver

public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    static {
        try {
            // 注册驱动
            java.sql.DriverManager.registerDriver(new Driver());
        } catch (SQLException E) {
            throw new RuntimeException("Can't register driver!");
        }
    }
}

java.sql.DriverManager

public class DriverManager {
    // ...
    public static void registerDriver(java.sql.Driver driver, DriverAction da) throws SQLException {
        /* Register the driver if it has not already been added to our list */
        if (driver != null) {
            registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
        } else {
            // This is for compatibility with the original DriverManager
            throw new NullPointerException();
        }
        println("registerDriver:  " + driver);
    }
    // ...
    // getConnection
}

装饰者模式

定义: 在不改变原有对象的基础上, 将功能附加到对象上.

提供了比继承更有弹性的替代方案(扩展对象原有功能).

类型: 结构型

适用场景:

  • 扩展一个类的功能或者给一个类添加附加职责.
  • 动态地给一个对象添加功能, 这些功能可以动态撤销.

优点:

  • 继承的有力补充, 比继承灵活, 不改变原有对象的情况下给一个对象扩展功能.
  • 通过使用不同装饰类以及这些装饰类的排列组合, 可以实现不同效果.
  • 符合开闭原则.

缺点:

  • 会出现更多的代码, 更多的类, 增加程序复杂性.
  • 动态装饰、多层装饰时会更复杂.

相关模式:

  • 装饰者模式和代理模式(装饰者模式关注一个对象动态地添加方法, 代理模式关注控制对对象的访问)
  • 装饰者模式和适配器模式(都可以叫做包装模式)

Decorator Demo UML类图

JDK中装饰者模式例子

BufferedReader

public class BufferedReader extends Reader {
    // 被修饰者
    private Reader in;
    // ...

    /**
     * Creates a buffering character-input stream that uses an input buffer of
     * the specified size.
     *
     * @param  in   A Reader
     * @param  sz   Input-buffer size
     *
     * @exception  IllegalArgumentException  If {@code sz <= 0}
     */
    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;
    }
    // ...
}

Spring中装饰者模式的例子

spring-context-support依赖包下.

org.springframework.cache.transaction.TransactionAwareCacheDecorator

public class TransactionAwareCacheDecorator implements Cache {
    private final Cache targetCache;
    // ...
    // 传入被装饰对象 Cache
    public TransactionAwareCacheDecorator(Cache targetCache) {
        Assert.notNull(targetCache, "Target Cache must not be null");
        this.targetCache = targetCache;
    }
}

org.springframework.session.web.http.SessionRepositoryFilter.该类在spring-session-core包下.

public class SessionRepositoryFilter<S extends Session> extends OncePerRequestFilter {
    private final class SessionRepositoryRequestWrapper extends HttpServletRequestWrapper {

        private final HttpServletResponse response;
        // ...
        // 继承的是HttpServletRequestWrapper, 传入的是 HttpServletRequest.
         // 实际 HttpServletRequestWrapper 继承 HttpServletRequest .
        private SessionRepositoryRequestWrapper(HttpServletRequest request, HttpServletResponse response) {
        super(request);
        this.response = response;
        }
    }
}

Mybatis中使用装饰者的例子

LruCache

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);
  }
  // ...
}

组合模式

定义: 将对象组合成树型结构以表示”部分-整体”的层次结构.

组合模式使客户端对单个对象和组合对象保持一致的方式处理.

注意要有统一的接口实现或统一的抽象父类.

类型: 结构型

适用场景:

  • 希望客户端可以忽略组合对象与单个对象的差异时.
  • 处理一个树型结构时.

优点:

  • 清楚地定义分层次的复杂对象, 表示对象的全部或部分层次.
  • 让客户端忽略了层次的差异, 方便对整个层次结构进行控制.
  • 简化客户端代码.
  • 符合开闭原则.

缺点:

  • 限制类型时会较为复杂.
  • 使设计变得更加抽象.

相关模式:

  • 组合模式和访问者模式

Composite Demo UML类图

JDK中使用组合模式的例子

java.awt.Container

public class Container extends Component {
    // ... 添加自己的父类
    public Component add(Component comp) {
        addImpl(comp, null, -1);
        return comp;
    }
    // ...
}

Hashmap

public class HashMap<K,V> extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable {
    // ...
    public void putAll(Map<? extends K, ? extends V> m) {
        putMapEntries(m, true);
    }
    // ...
}

ArrayList

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
    // ... List 接口继承了Collection接口
    public boolean addAll(Collection<? extends E> c) {
        Object[] a = c.toArray();
        modCount++;
        int numNew = a.length;
        if (numNew == 0)
            return false;
        Object[] elementData;
        final int s;
        if (numNew > (elementData = this.elementData).length - (s = size))
            elementData = grow(s + numNew);
        System.arraycopy(a, 0, elementData, s, numNew);
        size = s + numNew;
        return true;
    }
    // ...
}

AbstractList

public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
    // ...
    public E set(int index, E element) {
        throw new UnsupportedOperationException();
    }

    public void add(int index, E element) {
        throw new UnsupportedOperationException();
    }

    public E remove(int index) {
        throw new UnsupportedOperationException();
    }
    // ...
}

Mybatis中使用组合模式的例子

SqlNode.核心是MixedSqlNode.

public class MixedSqlNode implements SqlNode {
  private final List<SqlNode> contents;

  public MixedSqlNode(List<SqlNode> contents) {
    this.contents = contents;
  }

  @Override
  public boolean apply(DynamicContext context) {
    contents.forEach(node -> node.apply(context));
    return true;
  }
}

SqlNode UML类图(部分)

代理模式

定义: 为其他对象提供一种代理, 以控制对这个对象的访问.

代理对象在客户端和目标对象之间起到中介的作用.

类型: 结构型

适用场景:

  • 保护目标对象.
  • 增强目标对象.

优点:

  • 代理模式能将代理对象与真实被调用的目标对象分离.
  • 一定程度上降低了系统的耦合度, 扩展性好.
  • 保护目标对象.
  • 增强目标对象.

缺点:

  • 代理模式会造成系统设计中类的数目增加.
  • 在客户端和目标对象增加一个代理对象, 会造成请求处理速度变慢.
  • 增加系统的复杂度

扩展:

  • 静态代理
  • 动态代理(无法代理类, 但是可以代理接口, 生成新类)
  • CGLib代理(通过继承来实现, 生成的代理类就是业务类的子类)

Spring代理选择:

  • 当Bean有实现接口时, Spring就会用JDK的动态代理
  • 可以强制使用 Cglib
    • 在 spring 配置中加入<aop: aspect- autoproxy proxy-target-class="true"/>
  • 参考链接: https: //docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html

代理速度对比:

  • CGLib, 基于 ASM 生成字节码的.比 java 反射效率要高.
  • JDK动态代理.

相关设计模式

  • 代理模式和装饰者模式(一个是加行为, 一个是控制访问)
  • 代理模式和适配器模式

Proxy Demo UML类图

JDK中使用代理模式的例子

java.lang.reflect.proxy

Spring中使用代理模式的例子

ProxyFactoryBean

JdkDynamicAopProxy

CglibAopProxy

Mybatis中使用代理模式的例子

MapperProxyFactory, Mapper的代理工厂.

public class MapperProxyFactory<T> {

  private final Class<T> mapperInterface;
  private final Map<Method, MapperMethodInvoker> methodCache = new ConcurrentHashMap<>();

  public MapperProxyFactory(Class<T> mapperInterface) {
    this.mapperInterface = mapperInterface;
  }

  public Class<T> getMapperInterface() {
    return mapperInterface;
  }

  public Map<Method, MapperMethodInvoker> getMethodCache() {
    return methodCache;
  }

  @SuppressWarnings("unchecked")
  protected T newInstance(MapperProxy<T> mapperProxy) {
    // 使用JDK的动态代理模式
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

  public T newInstance(SqlSession sqlSession) {
    final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }

}

MapperProxy

public class MapperProxy<T> implements InvocationHandler, Serializable {
  // ...
  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      if (Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, args);
      } else {
        return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
  }
  private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
    try {
      // 享元模式 命中就返回, 没有就创建
      MapperMethodInvoker invoker = methodCache.get(method);
      if (invoker != null) {
        return invoker;
      }
      return methodCache.computeIfAbsent(method, m -> {
        if (m.isDefault()) {
          try {
            if (privateLookupInMethod == null) {
              return new DefaultMethodInvoker(getMethodHandleJava8(method));
            } else {
              return new DefaultMethodInvoker(getMethodHandleJava9(method));
            }
          } catch (IllegalAccessException | InstantiationException | InvocationTargetException
              | NoSuchMethodException e) {
            throw new RuntimeException(e);
          }
        } else {
          return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
        }
      });
    } catch (RuntimeException re) {
      Throwable cause = re.getCause();
      throw cause == null ? re :  cause;
    }
  }
}

享元模式

定义: 提供了减少对象数量从而改善应用所需对象结构的方式

运用共享技术有效地支持大量细粒度的对象.

类型: 结构型

适用场景:

  • 常常应用于系统底层的开发, 以便解决系统的性能问题(例如: String, 存在就返回, 不存在就创建放在缓存池当中; 数据库连接池).
  • 系统有大量的相似对象、需要缓冲池的场景.

优点:

  • 减少对象的创建, 降低内存中对象的数量, 降低系统内存, 提高效率
  • 减少内存之外的其他资源占用(例如: 时间,文件句柄,窗口句柄)

缺点:

  • 关注内/外部状态, 关注线程安全问题(Hashmap,Hashtable).
  • 使系统、程序的逻辑复杂化.

扩展:

  • 内部状态
  • 外部状态(不可以共享)

相关设计模式

  • 享元模式和代理模式(代理类创建消耗大)
  • 享元模式和单例模式(容器单例)

Flyweight Demo UML类图

JDK中使用享元模式的例子

java.lang.Integer

public final class Integer extends Number implements Comparable<Integer> {
    // 常出现面试题
    @HotSpotIntrinsicCandidate
    public static Integer valueOf(int i) {
        // -128 ~ 127
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        // new出来的肯定不是同一个对象
        return new Integer(i);
    }
    // ...
    private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer[] cache;
        static Integer[] archivedCache;

        static {
            // high value may be configured by property
            int h = 127;
            String integerCacheHighPropValue =
                VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                try {
                    int i = parseInt(integerCacheHighPropValue);
                    i = Math.max(i, 127);
                    // Maximum array size is Integer.MAX_VALUE
                    h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
                } catch( NumberFormatException nfe) {
                    // If the property cannot be parsed into an int, ignore it.
                }
            }
            high = h;

            // Load IntegerCache.archivedCache from archive, if possible
            VM.initializeFromArchive(IntegerCache.class);
            int size = (high - low) + 1;

            // Use the archived cache if it exists and is large enough
            if (archivedCache == null || size > archivedCache.length) {
                Integer[] c = new Integer[size];
                int j = low;
                for(int k = 0; k < c.length; k++)
                    c[k] = new Integer(j++);
                archivedCache = c;
            }
            cache = archivedCache;
            // range [-128, 127] must be interned (JLS7 5.1.7)
            assert IntegerCache.high >= 127;
        }

        private IntegerCache() {}
    }
}

同理, java.lang.Long也使用了享元模式.

Tomcat中使用享元模式的例子

public class GenericKeyedObjectPool<K, T> extends BaseGenericObjectPool<T>
        implements KeyedObjectPool<K, T>, GenericKeyedObjectPoolMXBean<K> {
    // ...
    public T borrowObject(final K key, final long borrowMaxWaitMillis) throws Exception {
        // ...
        PooledObject<T> p = null;
        // 双端队列.用来保存对象池的对象
        final ObjectDeque<T> objectDeque = register(key);

        try {
            while (p == null) {
                create = false;
                p = objectDeque.getIdleObjects().pollFirst();
                if (p == null) {
                    p = create(key);
                    if (p != null) {
                        create = true;
                    }
                }
                if (blockWhenExhausted) {
                    if (p == null) {
                        if (borrowMaxWaitMillis < 0) {
                            p = objectDeque.getIdleObjects().takeFirst();
                        } else {
                            p = objectDeque.getIdleObjects().pollFirst(
                                    borrowMaxWaitMillis, TimeUnit.MILLISECONDS);
                        }
                    }
                    if (p == null) {
                        throw new NoSuchElementException(
                                "Timeout waiting for idle object");
                    }
                } else {
                    if (p == null) {
                        throw new NoSuchElementException("Pool exhausted");
                    }
                }
        // ...
            }
        } finally {
            deregister(key);
        }

        updateStatsBorrow(p, System.currentTimeMillis() - waitTime);

        return p.getObject();
    }
}

@Override
public void returnObject(final K key, final T obj) {
    // private final Map<K, ObjectDeque<T>> poolMap = new ConcurrentHashMap<>();
    final ObjectDeque<T> objectDeque = poolMap.get(key);

    if (objectDeque == null) {
        throw new IllegalStateException(
            "No keyed pool found under the given key.");
    }

    final PooledObject<T> p = objectDeque.getAllObjects().get(new IdentityWrapper<>(obj));

    if (p == null) {
        throw new IllegalStateException(
            "Returned object not currently part of this pool");
    }
    // ...
}

外观模式

定义: 又叫门面模式, 提供了统一的接口, 用来访问子系统中的一群接口.

外观模式定义了一个高层接口, 让子系统更容易使用.

类型: 结构型

适用场景:

  • 子系统越来越复杂, 增加外观模式提供简单接口调用.
  • 构建多层系统结构, 利用外观对象作为每层的入口, 简化层间调用.

优点:

  • 简化了调用过程, 无需了解深入子系统, 防止带来风险.
  • 减少系统依赖, 松散耦合.
  • 更好地划分访问层次.
  • 符合迪米特原则, 即最少知道原则.

缺点:

  • 增加子系统、扩展子系统行为容易引入风险.
  • 不符合开闭原则.

与之相关的模式

  • 外观模式和中介模式
  • 外观模式和单例模式(结合使用)
  • 外观模式和抽象工厂模式

Facade Demo UML类图

Spring中外观模式例子

JdbcUtils

public abstract class JdbcUtils {
    public static <T> T extractDatabaseMetaData(DataSource dataSource, DatabaseMetaDataCallback<T> action)
    throws MetaDataAccessException {

            Connection con = null;
            try {
                con = DataSourceUtils.getConnection(dataSource);
                DatabaseMetaData metaData;
                metaData = con.getMetaData();
            }
            catch (SQLException ex) {
                if (DataSourceUtils.isConnectionTransactional(con, dataSource)) {
                    // Probably a closed thread-bound Connection - retry against fresh Connection
                    DataSourceUtils.releaseConnection(con, dataSource);
                    con = null;
                    logger.debug("Failed to obtain DatabaseMetaData from transactional Connection - " +
                        "retrying against fresh Connection", ex);
                    con = dataSource.getConnection();
                    metaData = con.getMetaData();
                }else {
                    throw ex;
                }
            }
            if (metaData == null) {
                // should only happen in test environments
                throw new MetaDataAccessException("DatabaseMetaData returned by Connection [" + con + "] was null");
            }
                return action.processMetaData(metaData);
            }
            catch (CannotGetJdbcConnectionException ex) {
                throw new MetaDataAccessException("Could not get Connection for extracting meta-data", ex);
            }
            catch (SQLException ex) {
                throw new MetaDataAccessException("Error while extracting DatabaseMetaData", ex);
            }
            catch (AbstractMethodError err) {
            throw new MetaDataAccessException(
                    "JDBC DatabaseMetaData method not implemented by JDBC driver - upgrade your driver", err);
            }
            finally {
                DataSourceUtils.releaseConnection(con, dataSource);
            }
        }
}

Mybatis中外观模式例子

org.apache.ibatis.session.Configuration

public class Configuration {
    // ...
    public MetaObject newMetaObject(Object object) {
        return MetaObject.forObject(object, objectFactory, objectWrapperFactory, reflectorFactory);
    }
    // ...
}

Tomcat中外观模式例子

RequestFacadeRequest是同级的, 而Request类中实际的操作是RequestFacade.

org.apache.catalina.connector.RequestFacade

public class RequestFacade implements HttpServletRequest {
    // ...
    private final class GetAttributePrivilegedAction
            implements PrivilegedAction<Enumeration<String>> {
        @Override
        public Enumeration<String> run() {
            return request.getAttributeNames();
        }
    }

    private final class GetParameterMapPrivilegedAction
            implements PrivilegedAction<Map<String,String[]>> {
        @Override
        public Map<String,String[]> run() {
            return request.getParameterMap();
        }
    }
    // ...
}

org.apache.catalina.connector.Request

public class Request implements HttpServletRequest {
    /**
     * The facade associated with this request.
     */
    protected RequestFacade facade = null;
}

org.apache.catalina.session.StandardSessionFacade

public class StandardSessionFacade implements HttpSession {
    @Override
    public long getCreationTime() {
        return session.getCreationTime();
    }
    @Override
    public String getId() {
        return session.getId();
    }
    @Override
    public long getLastAccessedTime() {
        return session.getLastAccessedTime();
    }
}

评论