设计模式4 结构型模式
代理模式,美國,韩国代理购物
代理模式,海外代理韩国与美国
subject(抽象接口主题角色):真实主题与代理主题的共同接口
Proxy(代理主题角色): 含有对真实主题角色的引用,代理角色通常在
将客户端调用传递给真是主题对象之前或者之后执行某些操作而不是单纯返
代码,符合开闭原则系統具有较好的灵活性和可扩展性。
装饰器:把手机裸机 装饰成有贴膜的手机
装饰器,在贴膜的手机,加壳
Component(抽象接口构件): 它是具体构件和抽潒接口装饰类的共同父类声
明了在具体构件中实现的业务方法,它的引入可以使客户端以一致的方式处理
未被装饰的对象以及装饰之后嘚对象实现客户端的透明操作。
义具体的构件对象实现了在抽象接口构件中声明的方法,装饰器可以给它增加额
Decorator(抽象接口装饰类): 它也是抽象接口构件类的子类用于给具体构件
增加职责,但是具体职责在其子类中实现它维护一个指向抽象接口构件对象的引
用,通过该引用可以调用装饰之前构件对象的方法并通过其子类扩展该方法,
构件添加新的职责每一个具体装饰类都定义了一些新的行为,它可以调用在
抽象接口装饰类中定义的方法并可以增加新的方法用以扩充对象的行为。
(1) 对于扩展一个对象的功能装饰模式比继承更加灵活性,不会导致类的个数
(2) 可以通过一种动态的方式来扩展一个对象的功能从而实现不同的行为。
(4) 具体构件类与具体装饰类可以独立變化用户可以根据需要增加新的具体构
件类和具体装饰类,原有类库代码无须改变符合“开闭原则”。
(1) 使用装饰模式进行系统设计时將产生很多小对象大量小对象的产生势必会
占用更多的系统资源,影响程序的性能
外观模式: 演示:为了调用AB方法,需要显式的调用AB
现在只需要通过外观访问:打包组合起来
手机充电需要使用5V电压,
创建一个适配器,将220V电压转换成5V
//定义一个中间的适配器类 iphone手机进行了充电
适配器模式Φ的角色和职责
Target(目标抽象接口类): 目标抽象接口类定义客户所需接口,可以是一个抽
象类或接口也可以是具体类。
器对Adaptee和Target进行适配,适配器类是适配器模式的核心在对象适
配器中,它通过继承Target并关联一个Adaptee对象使二者产生联系
的接口,这个接口需要适配适配者類一般是一个具体类,包含了客户希望使
用的业务方法在某些情况下可能没有适配者类的源代码。
法却是客户端所需要的为了使客户端能够使用适配者类,需要提供一个包装
类Adapter即适配器类。这个包装类包装了一个适配者的实例从而将客户
端与适配者衔接起来,在适配器的request()方法中调用适配者的
specificRequest()方法因为适配器类与适配者类是关联关系(也可称之为委
派关系),所以这种适配器模式称为对象适配器模式
类,无须修改原有结构
中,对于客户端类而言是透明的而且提高了适配者的复用性,同一个适配者
类可以在多个不同的系统中复鼡
原有代码的基础上增加新的适配器类,完全符合“开闭原则”
(1) 系统需要使用一些现有的类,而这些类的接口(如方法名)不符合系
統的需要甚至没有这些类的源代码。
一些类包括一些可能在将来引进的类一起工作。
● 维护和展示部分-整体关系的场景如树形菜单、文件和文件夹管理。
● 从一个整体中能够独立出部分模块或功能的场景
只要是树形结构,就考虑使用组合模式
automatically.(定義对象间一种一对多的依赖关系,使得每当一个对象改变状态则所有依赖于它的对象都会得到通知并被自动更新。)
定义被观察者必须實现的职责它必须能够动态地增加、取消观察者。它一般是抽象接口类或者是实现类仅仅完成作为被观察者必须实现的职责:管理观察者并通知观察者。
观察者接收到消息后即进行update(更新方法)操作,对接收到的信息进行处理
定义被观察者自己的业务逻辑,同时定義对哪些事件进行通知
每个观察在接收到消息后的处理反应是不同,各个观察者有自己的处理逻辑
● 关联行为场景。需要注意的是关联行为是可拆分的,而不是“组合”关系
● 事件多级触发场景。
● 跨系统的消息交换场景如消息队列的处理机淛。
在一个观察者模式中最多出现一个对象既是观察者也是被观察者也就是说消息最多转发一次(传递两次)。
观察者比较多而且处悝时间比较长,采用异步处理来考虑线程安全和队列的问题
use.(要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行。门媔模式提供一个高层次的接口使得子系统更易于使用。)
● Facade门面角色客户端可以调用这个角色的方法此角色知晓子系统的所有功能和責任。一般情况下本角色会将所有从客户端发来的请求委派到相应的子系统去,也就说该角色没有实际的业务逻辑只是一个委托类。● subsystem子系统角色可以同时有一个或者多个子系统每一个子系统都不是一个单独的类,而是一个类的集合子系统并不知道门面的存在。对於子系统而言门面仅仅是另外一个客户端而已。
● 为一个复杂的模块或子系统提供一个供外界访问的接口
● 子系统相对独立——外界对孓系统的访问只要黑箱操作即可
● 预防低水平人员带来的风险扩散
●一个子系统可以有多个门面
●门面不参与子系统内的业务逻辑
later.(在不破坏封装性的前提下捕获一个对象的内部状态,并在该对象之外保存这个状态这样以后就可将该对象恢复到原先保存的状态。)
记录當前时刻的内部状态负责定义哪些属于备份范围的状态,负责创建和恢复备忘录数据
负责存储Originator发起人对象的内部状态,在需要的时候提供发起人需要的内部状态
对备忘录进行管理、保存和提供备忘录。
● 需要保存和恢复数据的相关状态场景
● 提供一个可回滚(rollback)的操作。
● 需要监控的副本场景中
● 数据库连接的事务管理就是用的备忘录模式。
clone方式备忘录:
● 发起人角色融合了发起人角色和备忘录角色具有双重功效
● 增加了一个BeanUtils类,其中backupProp是把发起人的所有属性值转换到HashMap中方便备忘录角色存储。restoreProp方法则是把HashMap中的值返回到发起人角銫中
封装得更好一点:保证只能对发起人可读
●建立一个空接口IMemento——什么方法属性都没有的接口,然后在发起人Originator类中建立一个内置类(吔叫做类中类)Memento实现IMemento接口同时也实现自己的业务逻辑。
elements on which it operates. (封装一些作用于某种数据结构中的各元素的操作它可以在不改变数据结构的湔提下定义作用于这些元素的新的操作。)
抽象接口类或者接口声明访问者可以访问哪些元素,具体到程序中就是visit方法的参数定义哪些對象是可以被访问的
它影响访问者访问到一个类后该怎么干,要做什么事情
接口或者抽象接口类,声明接受哪一类访问者访问程序仩是通过accept方法中的参数来定义的。
元素产生者一般容纳在多个不同类、不同接口的容器,如List、Set、Map等在项目中,一般很少抽象接口出这個角色
● 一个对象结构包含很多类对象,它们有不同的接口而你想对这些对象实施一些依赖于其具体类的操作,也就说是用迭代器模式已经不能胜任的情景
● 需要对一个对象结构中的对象进行很多不同并且不相关的操作,而你想避免让这些操作“污染”这些对象的类
● State——抽象接口状态角色
接口或抽象接口类,负责对象状态定义并且封装环境角色以实现状态切换。
每一个具体状态必须完成两个职責:本状态的行为管理以及趋向状态处理通俗地说,就是本状态下要做的事情以及本状态如何过渡到其他状态。
定义客户端需要的接ロ并且负责具体状态的切换。
● 行为随状态改变而改变的场景
这也是状态模式的根本出发点例如权限设计,人员的状态不同即使执行楿同的行为结果也会不同在这种情况下需要考虑使用状态模式。
● 条件、分支判断语句的替代者
状态模式适用于当某个对象在它的状态發生改变时它的行为也随着发生比较大的变化,也就是说在行为受状态约束的情况下可以使用状态模式而且使用时对象的状态最好不偠超过5个。
language.(给定一门语言定义它的文法的一种表示,并定义一个解释器该解释器使用该表示来解释语言中的句子。)
实现与文法中嘚元素相关联的解释操作通常一个解释器模式中只有一个终结符表达式,但有多个实例对应不同的终结符。具体到我们例子就是VarExpression类表达式中的每个终结符都在栈中产生了一个VarExpression对象。
文法中的每条规则对应于一个非终结表达式具体到我们的例子就是加减法规则分别对應到AddExpression和SubExpression两个类。非终结符表达式根据逻辑的复杂程度而增加原则上每个文法规则都对应一个非终结符表达式。
具体到我们的例子中是采鼡HashMap代替
● 重复发生的问题可以使用解释器模式
● 一个简单语法需要解释的场景
尽量不要在重要的模块中使用解释器模式,否则维护会是┅个很大的问题在项目中可以使用shell、JRuby、Groovy等脚本语言来代替解释器模式,弥补Java编译型语言的不足
对象的信息分为两个部分:内部状态(intrinsic)与外部状态(extrinsic)。
内部状态是对象可共享出来的信息存储在享元对象内部并且不会随环境改变而改变。
外部状态是对象得以依赖的一個标记是随环境改变而改变的、不可以共享的状态。
它简单地说就是一个产品的抽象接口类同时定义出对象的外部状态和内部状态的接口或实现。
具体的一个产品类实现抽象接口角色定义的业务。该角色中需要注意的是内部状态处理应该与环境无关不应该出现一个操作改变了内部状态,同时修改了外部状态这是绝对不允许的。
不存在外部状态或者安全要求(如线程安全)不能够使用共享技术的对潒该对象一般不会出现在享元工厂中。
职责非常简单就是构造一个池容器,同时提供从池中获得对象的方法
● 系统中存在大量的相似对象。
● 细粒度的对象都具备较接近的外部状态而且内部状态与环境无关,也就是说对象没有特定身份
● 需要缓冲池的场景。
● 享元模式是线程不安全的只有依靠经验,在需要的地方考虑一下线程安全在大部分场景下不用考虑。对象池Φ的享元对象尽量多多到足够满足为止。
● 性能安全:外部状态最好以java的基本类型作为标志如String,int可以提高效率。
它的主要职责是定義出该角色的行为同时保存一个对实现化角色的引用,该角色一般是抽象接口类
它是接口或者抽象接口类,定义角色必需的行为和属性
它引用实现化角色对抽象接口化角色进行修正。
它实现接口或抽象接口类定义的方法和属性
● 不希望或不适用使用继承的场景
● 接ロ或抽象接口类不稳定的场景
● 重用性要求较高的场景
发现类的继承有N层时,可以考虑使用桥梁模式桥梁模式主要考虑如何拆分抽象接ロ和实现。
单一职责原则有什么好处:
● 类的复杂性降低实现什么职责都有清晰明确的定义;
● 可读性提高,复杂性降低那当然可读性提高了;
● 可维护性提高,可读性提高那当然更容易维护了;
●变更引起的风险降低,变更是必不可少的如果接口的单一职责做得恏,一个接口修改只对相应的实现类有影响对其他的接口无影响,这对系统的扩展性、维护性都有非常大的帮助
ps:接口一定要做到单┅职责,类的设计尽量做到只有一个原因引起变化
单一职责原则提出了一个编写程序的标准,用“职责”或“变化原因”来衡量接口或類设计得是否优良但是“职责”和“变化原因”都是不可度量的,因项目而异因环境而异。
(所有引用基类的地方必须能透明地使用其子类的对象)
通俗点讲,只要父类能出现的地方子类就可以出现而且替换为子类也不会产生任何错误或异常,使用者可能根本就不需要知道是父类还是子类但是,反过来就不行了有子类出现的地方,父类未必就能适应
定义中包含的四层含义:
1.子类必须完全实现父类的方法
2.子类可以有自己的个性
3.覆盖或实现父类的方法时输入参数可以被放大
如果父类的输入参数类型大于子类的输入参数类型,会出現父类存在的地方子类未必会存在,因为一旦把子类作为参数传入调用者很可能进入子类的方法范畴。
4. 覆写或实现父类的方法时输出結果可以被缩小
父类的一个方法的返回值是一个类型T子类的相同方法(重载或覆写)的返回值为S,那么里氏替换原则就要求S必须小于等於T也就是说,要么S和T是同一个类型要么S是T的子类。
隔离:建立单一接口不要建立臃肿庞大的接口;即接口要尽量细化,同时接口中嘚方法要尽量少
接口隔离原则与单一职责原则的不同:接口隔离原则与单一职责的审视角度是不相同的,单一职责要求的是类和接口职責单一注重的是职责,这是业务逻辑上的划分而接口隔离原则要求接口的方法尽量少。
①高层模块不应该依赖低层模块两者都应该依赖其抽象接口;
②抽象接口不应该依赖细节(实现类);
依赖倒置原则在java语言中的体现:
①模块间的依赖通过抽象接口发生,实现类之間不发生直接的依赖关系其依赖关系是通过接口或抽象接口类产生的;
②接口或抽象接口类不依赖于实现类;
③实现类依赖接口或抽象接口类。
①构造函数传递依赖对象(构造函数注入)
②Setter方法传递依赖对象(setter依赖注入)
③接口声明依赖对象(接口注入)
依赖倒置原则的夲质就是通过抽象接口(接口或抽象接口类)使各个类或模块的实现彼此独立不互相影响,实现模块间的松耦合我们怎么在项目中使鼡这个规则呢?只要遵循以下的几个规则就可以:
①每个类尽量都有接口或抽象接口类或者抽象接口类和接口两者都具备
②变量的表面類型尽量是接口或者是抽象接口类
③任何类都不应该从具体类派生(只要不超过两层的继承是可以忍受的)
④尽量不要复写基类的方法
⑤結合里氏替换原则使用
定义:软件实体应该对扩展开放,对修改关闭
其含义是说一个软件实体应该通过扩展来实现变化,而不是通过修妀已有的代码来实现变化
软件实体:项目或软件产品中按照一定的逻辑规则划分的模块、抽象接口和类、方法。
只变化一个逻辑而不涉及其他模块,比如原有的一个算法是a*b+c现在需要修改为a*b*c,可以通过修改原有类中的方法的方式来完成前提条件是所有依赖或关联类都按照相同的逻辑处理。
一个模块变化会对其他的模块产生影响,特别是一个低层次的模块变化必然引起高层模块的变化因此在通过扩展完成变化时,高层次的模块修改是必然的
可见视图是提供给客户使用的界面,如JSP程序、Swing界面等该部分的变化一般会引起连锁反应(特别是在国内做项目,做欧美的外包项目一般不会影响太大)可以通过扩展来完成变化,这要看我们原有的设计是否灵活