设计模式GOF23(结构型模式:代理模式,适配模式,桥接模式,组合模式,装饰模式,外观模式,享元模式)

结构型模式
– 分类:
• 适配器模式、代理模式、桥接模式、装饰模式、组合模式、外观模式、享元模式
– 核心做用:是从程序的结构上实现松耦合,从而能够扩大总体的类结构,用来解决更大的问题。
 
结构型模式汇总
 
 
 
 
适配器的adapter模式
生活中的场景:
 
 
 
 
什么是适配器模式?
– 将一个类的接口转换成客户但愿的另一个接口。Adapter模式使得本来因为接口不兼容而不能一块儿工做的那些类能够在一块儿工做。
 
• 模式中的角色
– 目标接口(Target):客户所期待的接口。目标能够是具体的或抽象的类,也能够是接口。
– 须要适配的类(Adaptee):须要适配的类或适配者类。
– 适配器(Adapter):经过包装一个须要适配的对象,把原接口转换成目标接口。
 
 
 
 
 
 
 
 
类适配器:
class Adapter extends Adaptee implements Target{
    public void request() {
    super.specificRequest();
    }
}
对象适配器
class Adapter implements Target{ 
    private Adaptee adaptee;
     public Adapter (Adaptee adaptee) { 
        this.adaptee = adaptee;
     }
    public void request() { 
        this.adaptee.specificRequest();
     }
 }
工做中的场景
– 常常用来作旧系统改造和升级
– 若是咱们的系统开发以后不再须要维护,那么不少模式都是不必的,可是不幸的是,事实倒是维护一个系统的代价每每是开发一个系统的数倍。
 
• 咱们学习中见过的场景
– java.io.InputStreamReader(InputStream)
– java.io.OutputStreamWriter(OutputStream)
 
 
 
 
代理模式(Proxy pattern)
核心做用:
• 经过代理,控制对对象的访问!能够详细控制访问 某个(某类)对象 的方法,在调用这个方法前作前置处理,调用这个方法后作后置处理。(即:AOP的微观实现!)
AOP(Aspect Oriented Programming面向切面编程)的核心实现机制!
 
 
 
– 核心角色:
• 抽象角色
– 定义代理角色和真实角色的公共对外方法
• 真实角色
– 实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。
 
关注真正的业务逻辑!
• 代理角色
– 实现抽象角色,是真实角色的代理,经过真实角色的业务逻辑方法来实现抽象方法,并能够附加 本身的操做。
 
将统一的流程控制放到代理角色中处理!
 
 
 
应用场景:
– 安全代理:屏蔽对真实角色的直接访问。
– 远程代理:经过代理类处理远程方法调用(RMI)
– 延迟加载:先加载轻量级的代理对象,真正须要再加载真实对象。
 
• 好比你要开发一个大文档查看软件,大文档中有大的图片,有可能一个图片有100MB,在打开文件时不可能将全部的图片都显示出来,这样就能够 使用代理模式,当须要查看图片时,用proxy来进行大图片的打开。
 
• 分类:
– 静态代理(静态定义代理类)
– 动态代理(动态生成代理类)
• JDK自带的动态代理
• javaassist字节码操做库实现
• CGLIB
• ASM(底层使用指令,可维护性较差)
 
 
 
静态代理(静态定义代理类)
 
 
 
 
 
动态代理(动态生成代理类)
• JDK自带的动态代理
• javaassist字节码操做库实现
• CGLIB
• ASM(底层使用指令,可维护性较差)
 
 
动态代理相比于静态代理的优势
– 抽象角色中(接口)声明的因此方法都被转移到调用处理器一个集中的方法中处理,这样,咱们能够更加灵活和统一的处理众多的方法。
 
 
JDK自带的动态代理
– java.lang.reflect.Proxy
• 做用:动态生成代理类和对象
– java.lang.reflect.InvocationHandler(处理器接口)
• 能够经过invoke方法实现对真实角色的代理访问。
• 每次经过Proxy生成代理类对象对象时都要指定对应的处理器对象
Star realStar = new RealStar();
StarHandler handler = new StarHandler(realStar);
Star proxy = (Star) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),new Class[]{Star.class},handler); proxy.sing();

 

开发框架中应用场景:
– struts2中拦截器的实现
– 数据库链接池关闭处理
– Hibernate中延时加载的实现
– mybatis中实现拦截器插件
– AspectJ的实现
– spring中AOP的实现
• 日志拦截
• 声明式事务处理
– web service
– RMI远程方法调用
– ...
– 实际上,随便选择一个技术框架都会用到代理模式!!
 
 
 
AOP(Aspect-Oriented Programming,面向切面的编程)
– 它是能够经过预编译方式和运行期动态代理实如今不修改源代码的状况下给程序动态统一添加功能的一种技术。它是一种新的方法论,它 是对传统OOP编程的一种补充。
 
 
 
经常使用术语:
– 切面(Aspect):其实就是共有功能的实现。
– 通知(Advice):是切面的具体实现。
– 链接点(Joinpoint):就是程序在运行过程当中可以插入切面的地点。
– 切入点(Pointcut):用于定义通知应该切入到哪些链接点上。
– 目标对象(Target):就是那些即将切入切面的对象,也就是那些被通知的对象
– 代理对象(Proxy):将通知应用到目标对象以后被动态建立的对象。
– 织入(Weaving):将切面应用到目标对象从而建立一个新的代理对象的过程。
 
 
 
开源的AOP框架
– AspectJ
 
 
桥接模式:
场景分析
– 商城系统中常见的商品分类,以电脑为类,如何良好的处理商品分类销售的问题?
– 这个场景中有两个变化的维度:电脑类型、电脑品牌。
 
 
桥接模式核心要点:
– 处理多层继承结构,处理多维度变化的场景,将各个维度设计成独立的继承结构,使各个维度能够独立的扩展在抽象层创建关联。
 
 
 
 
 
 
桥接模式总结
– 桥接模式能够取代多层继承的方案。 多层继承违背了单一职责原则,复用性较差,类的个数也很是多。桥接模式能够极大的减小子类的个 数,从而下降管理和维护的成本。
– 桥接模式极大的提升了系统可扩展性,在两个变化维度中任意扩展一个维度,都不须要修改原有的系统,符合开闭原则。
 
 
 
桥接模式实际开发中应用场景
– JDBC驱动程序
– AWT中的Peer架构
– 银行日志管理:
• 格式分类:操做日志、交易日志、异常日志
• 距离分类:本地记录日志、异地记录日志
– 人力资源系统中的奖金计算模块:
• 奖金分类:我的奖金、团体奖金、激励奖金。
• 部门分类:人事部门、销售部门、研发部门。
– OA系统中的消息处理:
• 业务类型:普通消息、加急消息、特急消息
• 发送消息方式:系统内消息、手机短信、邮件
 
 
 
组合模式
使用组合模式的场景:
– 把部分和总体的关系用树形结构来表示,从而使客户端可使用统一的方式处理部分对 象和总体对象。
• 组合模式核心:
– 抽象构件(Component)角色: 定义了叶子和容器构件的共同点
– 叶子(Leaf)构件角色:无子节点
– 容器(Composite)构件角色: 有容器特征,能够包含子节点
 
 
 
 
 
组合模式工做流程分析:
– 组合模式为处理树形结构提供了完美的解决方案,描述了如何将容器和叶子进行递归组合,使得用户在使用时能够一致性的对待容器和叶子。
– 当容器对象的指定方法被调用时,将遍历整个树形结构,寻找也包含这个方法的成员,并调用执行。其中,使用了递归调用的机制对整个结构进行处理。
 
使用组合模式,模拟杀毒软件架构设计
 
 
 
开发中的应用场景:
– 操做系统的资源管理器
– GUI中的容器层次图
– XML文件解析
– OA系统中,组织结构的处理
– Junit单元测试框架
• 底层设计就是典型的组合模式,TestCase(叶子)、TestUnite(容器),Test接口(抽象)
 
 
Junit单元测试框架底层设计
– 底层设计就是典型的组合模式,TestCase(叶子)、TestUnite(容器)、Test接口(抽象)
 
 
 
 
装饰模式:
 
 
 
实现细节:
– Component抽象构件角色:
• 真实对象和装饰对象有相同的接口。这样,客户端对象就可以以与真实对象相同的方式同装饰对象交互。
– ConcreteComponent 具体构件角色(真实对象):
• io流中的FileInputStream、FileOutputStream
– Decorator装饰角色:
• 持有一个抽象构件的引用。装饰对象接受全部客户端的请求,并把这些请求转发给真实的对象
。这样,就能在真实对象调用先后增长新的功能。
– ConcreteDecorator具体装饰角色:
• 负责给构件对象增长新的责任。
 
 
开发中使用的场景:
– IO中输入流和输出流的设计
– Swing包中图形界面构件功能
– Servlet API 中提供了一个request对象的Decorator设计模式的默认实现类HttpServletRequestWrapper,HttpServletRequestWrapper 类,加强了request对象的功能。
– Struts2中,request,response,session对象的处理
 
 
IO流实现细节:
– Component抽象构件角色:
• io流中的InputStream、OutputStream、Reader、Writer
– ConcreteComponent 具体构件角色:
• io流中的FileInputStream、FileOutputStream
– Decorator装饰角色:
• 持有一个抽象构件的引用:io流中的FilterInputStream、FilterOutputStream
– ConcreteDecorator具体装饰角色:
• 负责给构件对象增长新的责任。Io流中的BufferedOutputStream、BufferedInputStream等。
 
 
 
 
总结:
– 装饰模式(Decorator)也叫包装器模式(Wrapper)
– 装饰模式下降系统的耦合度,能够动态的增长或删除对象的职责,并使得须要装饰的具体构建类和具体装饰类能够独立变化,以便增长新 的具体构建类和具体装饰类。
• 优势
– 扩展对象功能,比继承灵活,不会致使类个数急剧增长
– 能够对一个对象进行屡次装饰,创造出不一样行为的组合,获得功能更增强大的对象
– 具体构建类和具体装饰类能够独立变化,用户能够根据须要本身增长新的具体构件子类和具体装饰子类。
• 缺点
– 产生不少小对象。大量小对象占据内存,必定程度上影响性能。
– 装饰模式易于出错,调试排查比较麻烦。
 
 
装饰模式和桥接模式的区别:
– 两个模式都是为了解决过多子类对象问题。但他们の诱因不同。桥
模式是对象自身现有机制沿着多个维度变化,是既有部分不稳定。装
饰模式是为了增长新的功能。
 
 
 
外观模式
迪米特法则(最少知识原则):
– 一个软件实体应当尽量少的与其余实体发生相互做用。
 
 
 
 
外观模式核心:
– 为子系统提供统一的入口。封装子系统的复杂性,便于客户端调用。
 
 
 
基本案例
– 注册公司流程(不使用外观模式)
 
 
 
 
 
基本案例
– 注册公司流程(使用外观模式)
 
 
 
 
开发中常见的场景
– 频率很高。哪里都会遇到。各类技术和框架中,都有外观模式的使用。如:
• JDBC封装后的,commons提供的DBUtils类,Hibernate提供的工具类、Spring JDBC工具类等
 
 
 
享元模式
 
• 场景:
– 内存属于稀缺资源,不要随便浪费。若是有不少个彻底相同或类似的对象,咱们能够经过享元模式,节省内存。
• 核心:
– 享元模式以共享的方式高效地支持大量细粒度对象的重用。
– 享元对象能作到共享的关键是区分了内部状态和外部状态。
• 内部状态:能够共享,不会随环境变化而改变
• 外部状态:不能够共享,会随环境变化而改变
 
享元模式实现:
– FlyweightFactory享元工厂类
• 建立并管理享元对象,享元池通常设计成键值对
– FlyWeight抽象享元类
• 一般是一个接口或抽象类,声明公共方法,这些方法能够向外界提供对象的内部状态,设置外部状态。
– ConcreteFlyWeight具体享元类
• 为内部状态提供成员变量进行存储
– UnsharedConcreteFlyWeight非共享享元类
• 不能被共享的子类能够设计为非共享享元类
 
享元模式实现的UML图:

 

 

享元模式开发中应用的场景:
– 享元模式因为其共享的特性,能够在任何“池”中操做,
好比:线程池、数据库链接池。
– String类的设计也是享元模

 

 

优势
– 极大减小内存中对象的数量
– 相同或类似对象内存中只存一份,极大的节约资源,提升系统性能
– 外部状态相对独立,不影响内部状态
 缺点
– 模式较复杂,使程序逻辑复杂化
– 为了节省内存,共享了内部状态,分离出外部状态,而读取外部状态
使运行时间变长。用时间换取了空间。