概述
第二部分:23种设计模式(下)
17、门面模式
门面模式的定义:要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行。门面模式提供一个高层次的接口,使得子系统更易于使用。
门面模式(Facade Pattern)也叫做外观模式,是一种比较常用的封装模式,门面模式注重“统一的对象”,也就是提供一个访问子系统的接口,除了这个接口不允许有任何访问子系统的行为发生。
门面模式的通用类图如下:
来看一下门面模式的角色:
1)Facade门面角色
客户端可以调用这个角色的方法。此角色知晓子系统的所有功能和责任,一般情况下,本角色会将所有从客户端发来的请求委派到相应的子系统去,也就是说该角色没有实际的业务逻辑,只是一个委托类。
2)subsystem子系统角色
可以同时有一个或者多个子系统。每一个子系统都不是一个单独的类,而是一个类的集合。子系统并不知道门面的存在。对于子系统而言,门面仅仅是另外一个客户端而已。
门面模式的通用源码如下:
代码清单:子系统
public class ClassA{
public void doSomethingA(){
//业务逻辑
}
}
public class ClassB{
public void doSomethingB(){
//业务逻辑
}
}
public class ClassC{
public void doSomethingC(){
//业务逻辑
}
}
注:我们认为这3个类属于近邻,处理相关的业务,因此应该被认为是一个子系统内的不同逻辑处理模块,对于此子系统的访问需要通过门面进行。
代码清单:门面对象
public class Facade{
//被委托的对象
private ClassA a = new ClassA();
private ClassB b = new ClassB();
private ClassC c = new ClassC();
//提供给外部访问的方法
public void methodA(){
this.a.doSomethingA();
}
public void methodB(){
this.b.doSomethingB();
}
public void methodC(){
this.c.doSomethingC();
}
}
门面模式的优点:
1)减少系统的相互依赖
2)提高了灵活性
3)提高安全性
门面模式的缺点:
门面模式最大的缺点就是不符合开闭原则,对修改关闭,对扩展开放。
门面模式的使用场景:
1)为一个复杂的模块或子系统提供一个供外界访问的接口
2)子系统相对独立—外界对子系统的访问只要黑箱操作即可
3)预防低水平人员带来的风险扩散
注:一个子系统可以有多个门面;门面不参与子系统的业务逻辑;
18、备忘录模式
备忘录模式的定义:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保持这个状态。这样以后就可将该对象恢复到原先保存的状态。
备忘录模式的通用类图如下:
来看一下类图中的三个角色;
1)Originator发起人角色
记录当前时刻的内部状态,负责定义哪些属于备份范围的状态,负责创建和恢复备忘录数据。
2)Memento备忘录角色
负责存储Originator发起人对象的内部状态,在需要的时候提供发起人需要的内部状态
3)Caretaker备忘录管理员角色
对备忘录进行管理、保存和提供备忘录。
备忘录模式的使用场景
1)需要保存和恢复数据的相关状态场景
2)提供一个可回滚(rollback)的操作;比如Word中的CTRL+Z组合键,IE浏览器中的后退按钮,文件管理器上的backspace键等。
3)需要监控的副本场景中。
4)数据库连接的事务管理就是用的备忘录模式。
19、访问者模式
访问者模式的定义:封装一些作用于某种数据结构中的各元素的操作,它可以在不改变数据结构的前提下定义作用这些元素的新的操作。
访问者模式的通用类图如下:
来看一下这几个角色的职责:
1)Visitor—抽象访问者
抽象类或者接口,声明访问者可以访问哪些元素,具体到程序中就是visit方法的参数定义哪些对象是可以被访问的。
2)ConcreteVisitor—具体访问者
它影响访问者访问到一个类后该怎么干,要做什么事情
3)Element—抽象元素
接口或者抽象类,声明接受哪一类访问者访问,程序上是通过accept方法中的参数来定义的。
4)ConcreteElement—具体元素
实现accept方法,通常是visitor.visit(this),基本上形成了一种模式。
5)ObjectStruture—结构对象
元素产生者,一般容纳在多个不同类、不同接口的容器,如List、Set、Map等,在项目中一般很少抽象出这个角色。
访问者模式通用源码如下:
代码清单:抽象元素
public abstract class Element{
//定义业务逻辑
public abstract void doSomething();
//允许谁来访问
public abstract void accept(IVisitor visitor);
}
抽象元素有两类方法:一是本身的业务逻辑,也就是元素作为一个业务处理单元必须完成的职责;另外一个是允许哪一个访问者来访问。
代码清单:具体元素
public class ConcreteElement2 extends Element{
//完善业务逻辑
public void doSomething(){
//业务处理
}
//允许哪个访问者访问
public void accept(IVisitor visitor){
visitor.visit(this);
}
}
注:一般是有几个具体元素就有几个访问方法
代码清单:抽象访问者
public interface IVisitor{
//可以访问哪些对象
public void visit(ConcreteElement1 el1);
public void visit(ConcreteElement2 el2);
}
代码清单:具体访问者
public class Visitor implements IVisitor{
//访问el1元素
public void visit(ConcreteElement1 el1){
el1.doSomething();
}
//访问el2元素
public void visit(ConcreteElement2 el2){
el2.doSomething();
}
}
注:结构对象是产生出不同的元素对象,我们使用工厂方法模式来模拟
代码清单:结构对象
public class ObjectStruture{
//对象生成器,这里通过一个工厂方法模式模拟
public static Element createElement(){
Random rand = new Random();
if(rand.nextInt(100)>50){
return new ConcreteElement1();
}else{
return new ConcreteElement2();
}
}
}
代码清单:场景类
public class Client{
public static void main(String[] args){
for(int i=0;i<10;i++){
//获得元素对象
Element e1 = ObjectStructure.createElement();
//接受访问者访问
e1.accept(new Visitor());
}
}
}
访问者模式的优点:
1)符合单一职责原则
2)优秀的扩展性
3)灵活性非常高
访问者模式的缺点:
1)具体元素对访问者公布细节
2)具体元素变更比较困难
3)违背了依赖倒置原则
注:访问者模式是一种集中规整模式,特别适用于大规模重构的项目;
20、状态模式
状态模式的定义:当一个对象内在状态改变时允许其改变行为,这个对象看起来像改变了其类。
状态模式的通用类图如下:
来看一下状态模式的3个角色:
1)State—抽象状态角色
接口或抽象类,负责对象状态定义,并且封装环境角色以实现状态切换。
2)ConcreteState—具体状态角色
每一个具体状态必须完成两个职责:本状态的行为管理以及趋向状态处理,通俗地说,就是本状态下要做的事情,以及本状态如何过渡到其他状态。
3)Context—环境角色
定义客户端需要的接口,并且负责具体状态的切换。
状态模式通用源码如下:
代码清单:抽象环境角色
public abstract class State{
//定义一个环境角色,提供子类访问
protected Context context;
//设置环境角色
public void setContext(Context _context){
this.context = _context;
}
//行为1
public abstract void handle1();
//行为2
public abstract void handle2();
}
注:抽象环境中声明一个环境角色,提供各个状态类自行访问,并且提供所有状态的抽象行为,由各个实现类实现。
代码清单:环境角色
public class ConcreteState1 extends State{
@Override
public void handle1(){
//本状态下必须处理的逻辑
}
@Override
public void handle2(){
//设置当前状态为state2
super.context.setCurrentState(Context.STATE2);
//过渡到state2状态,由Context实现
super.context.handle2();
}
}
public class ConcreteState2 extends State{
@Override
public void handle1(){
//设置当前状态为state1
super.context.setCurrentState(Context.STATE1);
//过渡到state1状态,由Context实现
super.context.handle1();
}
@Override
public void handle2(){
//本状态下必须处理的逻辑
}
}
代码清单:具体环境角色
public class Context{
//定义状态
public final static State STATE1 = new ConcreteState1();
public final static State STATE2 = new ConcreteState2();
//当前状态
private State CurrentState;
//获得当前状态
public State getCurrentState(){
return CurrentState;
}
//设置当前状态
public void setCurrentState(State currentState){
this.CurrentState = currentState;
//切换状态
this.CurrentState.setContext(this);
}
//行为委托
public void handle1(){
this.CurrentState.handle1();
}
public void handle2(){
this.CurrentState.handle2();
}
}
环境状态有两个不成文的约束:
1)把状态对象声明为静态常量,有几个状态对象就声明几个静态常量。
2)环境角色具有状态抽象角色定义的所有行为,具体执行使用委托方式。
代码清单:具体环境角色
public class Client{
public static void main(String[] args){
//定义环境角色
Context context = new Context();
//初始化状态
context.setCurrentState(new ConcreState1());
//行为执行
context.handle1();
context.handle2();
}
}
状态模式的优点:
1)结构清晰
2)遵循设计原则
3)封装性非常好
状态模式的缺点:
子类会太多,也就是类膨胀。
状态模式的使用场景:
1)行为随状态改变而改变的场景
2)条件、分支判断语句的替代者
21、解释器模式
解释器模式的定义:给定一门语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。
解释器模式的通用类图如下:
来看一下解释器模式的4个角色:
1)AbstractExpression—抽象解释器
具体的解释任务由各个实现类完成,具体的解释器分别由TerminalExpression和NonterminalExpression完成。
2)TerminalExpression—终结符表达式
实现与文法中的元素相关联的解释操作,通常一个解释器模式中只有一个终结符表达式,但有多个实例,对应不同的终结符。
3)NonterminalExpression—非终结符表达式
文法中的每条规则对应于一个非终结符表达式,非终结符表达式根据逻辑的复杂程度而增加,原则上每个文法规则都对应一个非终结符表达式。
4)Context—环境角色
解释器模式的通用源码如下:
代码清单:抽象表达式
public abstract class Expression{
//每个表达式必须有一个解析任务
public abstract Object interpreter(Context ctx);
}
代码清单:终结符表达式
public class TerminalExpression extends Expression{
//通常终结符表达式只有一个,但是有多个对象
public Object interpreter(Context ctx){
return null;
}
}
注:通常,终结符表达式比较简单,主要是处理场景元素和数据的转换。
public class NonterminalExpression extends Expression{
public NonterminalExpression(Expression...expression){
}
public Object interpreter(Context ctx){
//进行文法处理
return null;
}
}
代码清单:客户类
public class Client{
public static void main(String[] args){
Context ctx = new Context();
//通常定义一个语法容器,容纳一个具体的表达式,通常为ListArray、LinkedList、Stack等类型
Stack<Expression> stack = null;
for(;;){
//进行语法判断,并产生递归调用
}
//产生一个完整的语法书,由各个具体的语法分析进行解析
Expression exp = stack.pop();
//具体元素进入场景
exp.interpreter(ctx);
}
}
解释器模式的优点:
解释器是一个简单语法分析工具,它最显著的优点就是扩展性,修改语法规则只要修改相应的非终结符表达式就可以了,若扩展语法,则只要增加非终结符类就可以了。
解释器模式的缺点:
1)解释器模式会引起类膨胀
2)解释器模式采用递归调用方法
3)效率问题
解析器模式使用的场景:
1)重复发生的问题可以使用解释器模式
2)一个简单语法需要解释的场景
22、享元模式
享元模式的定义:使用共享对象可有效地支持大量的细粒度的对象。
要求细粒度对象,那么不可避免地使得对象数量多且行之相近,那我们就将这些对象的信息分为两个部分:
内部状态(intrinsic)与外部状态(extrinsic)。
1)内部状态
内部状态是对象可共享出来的信息,存储在襄垣对象内部并且不会随环境改变而改变。
2)外部状态
外部状态是对象得以依赖的一个标记,是随环境改变而改变的、不可以共享的状态。
享元模式的通用类图如下:
来看一下享元模式角色:
1)Flyweight—抽象享元角色
简单地说就是一个产品的抽象类,同时定义出对象的外部状态和内部状态的接口或实现。
2)ConcreteFlyweight—具体享元角色
具体的一个产品类,实现抽象角色定义的业务。
3)unsharedConcreteFlyweight—不可共享的享元角色
不存在外部状态或者安全要求(如线程安全)不能够使用共享技术的对象,该对象一般不会出现在享元工厂中。
4)FlyweightFactory—享元工厂
职责非常简单,就是构造一个池容器,同时提供从池中获得对象的方法
享元模式的目的在于运用共享技术,使得一些细粒度的对象可以共享。
享元模式的使用场景:
1)系统中存在大量的相似对象。
2)细粒度的对象都具备较接近的外部状态,而且内部状态与环境无关,也就是说对象没有特定身份。
3)需要缓冲池的场景。
23、桥梁模式
桥梁模式的定义:也叫做桥接模式,将抽象和实现解耦,使得两者可以独立地变化。
桥梁模式的通用类图如下:
来看一下桥梁式中的4个角色:
1)Abstraction—抽象化角色
它的主要职责是定义出该角色的行为,同时保存一个对实现化角色的引用,该角色一般是抽象类。
2)Implementor—实现化角色
它是接口或者抽象类,定义角色必需的行为和属性。
3)RefinedAbstraction—修正抽象化角色
它引用实现化角色对抽象化角色进行修正。
4)ConcreteImplementor—具体实现化角色
它实现接口或抽象类定义的方法和属性
桥梁模式的通用源码如下:
代码清单:实现化角色
public interface Implementor{
//基本方法
public void doSomething();
public void doAnything();
}
代码清单:具体实现化角色
public class ConcreteImplementor1 implements Implementor{
public void doSomething(){
//业务逻辑处理
}
public void doAnything(){
//业务逻辑处理
}
}
public class ConcreteImplementor2 implements Implementor{
public void doSomething(){
//业务逻辑处理
}
public void doAnything(){
//业务逻辑处理
}
}
代码清单:抽象化角色
public abstract class Abstraction{
//定义对实现化角色的引用
private Implementor imp;
//约束子类必须实现该构造函数
public Abstraction(Implementor _iimp){
this.imp = _imp;
}
//自身的行为和属性
public void request(){
this.imp.doSomething();
}
//获得实现化角色
public Implementor getImp(){
return imp;
}
}
代码清单:具体抽象化角色
public class RefinedAbstraction extends Abstraction{
//覆写构造函数
public RefinedAbstraction(Implementor _imp){
super(_imp);
}
//修正父类的行为
@Override
public void request(){
/*
*业务处理...
*/
super.request();
super.getImp().doAnything();
}
}
代码清单:场景类
public class Client{
public static void main(String[] args){
//定义一个实现化角色
Implementor imp = new ConcreteImplementor1();
//定义一个抽象化角色
Abstraction abs = new RefinedAbstraction(imp);
//执行行为
abs.request();
}
}
桥梁模式的优点:
1)抽象和实现分离
2)优秀的扩充能力
3)实现细节对客户透明
桥梁模式的使用场景:
1)不希望或不适用使用继承的场景
2)接口或抽象类不稳定的场景
3)重用性要求较高的场景
注:
上一篇:23种设计模式(中)
推荐阅读:秦小波著《设计模式之禅》
最后
以上就是纯情画笔为你收集整理的设计模式概述—23种设计模式(下)的全部内容,希望文章能够帮你解决设计模式概述—23种设计模式(下)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复