第六次作业
总体总结
创建型模式
·Singleton模式解决的是实体对象个数的问题。除了Singleton之外,其他创建型模式解决的都是new所带来的的耦合关系。
·Factory Method、Abstract Factory、Builder都需要一个额外的工厂类来负责实例化“易变对象”,而Prototype则是通过原型(一个特殊的工厂类)来克隆“易变对象”。
·如果遇到“易变类”,起初的设计通常从Factory Method开始,当遇到更多的复杂变化时,在考虑重构为其他三种工厂模式(Abstract Factory、Builder、Prototype)。
结构型模式
·Adapter模式注重转换接口,将不吻合的接口适配对接
·Bridge模式注重接口与其实现,支持多维度变化
·Composite模式注重统一接口,将“一对多”的关系转换为“一对一”的关系
·Decorator模式注重稳定接口,在此前提下为对象扩展功能
·Facade模式注重简化接口,简化组件系统与外部客户程序的依赖关系
·Flyweight模式注重保留接口,在内部使用共享技术对对象存储进行优化
·Proxy模式注重假借接口,增加间接层来实现灵活控制
行为型模式(1)
·Template Method模式封装算法结构,支持算法步骤变化
·Strategy模式注重封装算法,支持算法的变化
·State模式注重封装与状态相关的行为,支持状态的变化
·Memento模式注重封装对象状态变化,支持状态保存/恢复
·Mediator模式注重封装对象间的交互,支持对象交互的变化
行为型模式(2)
·Chain Of Responsibility模式注重封装对象责任,支持责任的变化
·Command模式注重将请求封装为对象,支持请求的变化
·Iterator模式注重封装集合对象内部结构,支持集合的变化
·Interpreter模式注重封装特定领域变化,支持领域问题的频繁变化
·Observer模式注重封装对象通知,支持通信对象的变化
·Visitor模式注重封装对象操作变化,支持在运行时为类层次结构动态添加新的操作
图片分析:

细节总结(资料)
C#面向对象设计模式纵横谈
1、设计模式
设计模式描述了软件过程中某一类常见的问题的一般性的解决方案。
2、面向对象设计模式
面向对象设计模式描述了面向对象设计过程中、特定场景下、类与相互通信的对象之间常见的组织关系。
·面向对象设计模式解决的是“类与相互通信的对象之间的组织关系”,包括它们的角色、职责、协作方式几个方面。
·面向对象设计模式是“好的面向对象设计”,所谓“好的面向对象设计”是那些可以满足“应对变化,提高复用”的设计。
·面向对象设计模式描述的是软件设计,因此它是独立于编程语言的,但是面向对象设计模式的最终实现仍要使用面向对象编程语言来表达。
3、GoF 23种设计模式
《设计模式:可复用面向对象软件的基础》一书中描述了23种经典面向对象设计模式,创立了模式在软件设计中的地位。人们通常所说的设计模式隐含的表示“面向对象设计模式”。但这并不意味“设计模式”就等于“面向对象设计模式”,也不意味着GoF 23种模式就表示了所有的“面向对象设计模式”。GoF 23种设计模式是学习面向对象设计模式的起点,而不是终点。
4、设计模式的三大原则
·1.针对接口编程,而不是针对实现编程
客户不需要知道使用对象的特定类型,只需要知道对象拥有客户所期望的接口。
·2.优先使用对象组合,而不是类继承
继承在某种程度上破坏了封装性,子类父类耦合度高;而对象组合则只要求被组合的对象具有良好定义的接口,耦合度低。
·3.封装变化点
封装的代码不会影响到其他代码
5、具体的设计模式原则(5个)
1.单一职责原则(SRP)
一个类应该仅有一个引起它变化的原因(只有一个引起变化的原因)
2.开放封闭原则(OCP)
类模块应该是可扩展的,但是不可修改(对扩展开放,对更改封闭)
3.Liskov替换原则(LSP)
子类必须能够替换他们的基类
4.依赖倒置原则(DIP)
·高层模块不应该依赖于底层模块,二者都不应该依赖于抽象
·抽象不应该依赖于实现细节,实现细节应该依赖于抽象
5.接口隔离原则(ISP)
不应该强迫客户程序依赖于他们不用的方法
创建型模式
Singleton单件(创建模式):
模式分类:
从目的来看
创建型(Creational):负责对象创建。
结构型(Structural):处理类与对象的组合。
行为型(Behavioral):类与对象交互中的职责分配。
从范围来看
类模式处理类与子类的静态关系。
对象模式处理对象间的动态关系。
单线程Singleton模式的要点:
1,Singleton模式中的实例构造器可以设置为protected以允许子类派生。
2,Sinaleton模式一般不要支持lCloneable接口,因为这可能会导致多个对象实例,与Singleton模式的初衷违背。
3,Singleton模式一般不要支持序列化,因为这也有可能导致多个对象实例,同样与Singleton模式的初衷违背。
4,Sinaletom模式只考虑到了对象创建的管理,没有考虑对象销毁的管理。就支持垃圾回收的平台和对象的开销来讲,我们子般没有必要对其销毁进行特殊的管理。
5,不能应对多线程环境:在多线程环境下,使用Singleton模式仍然有可能得到Singleton类的多个实例对象。
Singleton模式扩展:
1,将一个实例扩展到n个实例,例如对象池的实现。
2,将new构造器的调用转移到其他类中,例如多个类协同工作环境中,某个局部环境只需要拥有某个类的一个实例。
3,理解和扩展Singleton模式的核心是“如何控制用户使用new对一个类的实例构造器的任意调用”。
Abstarct Factory抽象工厂
Abstract Factory模式的要点:
1,如果没有应对“多系列对象构建”的需求变化,则没有必要使用Abstract Factory模式,这时候使用单的静态工厂完全可以。
2,“系列对象”指的是这些对象之间有相互依赖、或作用的关系,例如游戏开发场景中的“道路”与“房屋”的依赖,“道路”写“地道”的依赖。
3,Abstract Factory模式主要在于应对“新系列”的需求变动。其缺点在于难以应对“新对象”的需求变动。
4,Abstract Factory模式经常和Factory Method模式共同组合来应对“对象创建”的需求变化。
Bulider生成器
Builder模式的要点
1,Builder模式主要用于“分步骤构建一个复杂的对象”。在这其中“分步骤”是一个稳定的算法,而复杂对象的各个部分则经常变化。
2,变化点在哪里,封装哪里—Builder模式主要在宇应对“复杂对象各个部分”的频繁需求变动。其缺点在手难以应对“分步骤构建算法"的需求变动。
3,Abstract Factory模式解决“系列对象”的需求变化,Builder模式解决“对象部分”的需求变化。Builder模式通常和Composite模式组合使用。
Factory Method工厂方法
Factory Method模式的要点
1,Factory Method模式主要用于隔离类对象的使用者和具体类型之间的耦合关系。面对一个经常变化的具体奚型,紧耦合吴系会导致软件的脆弱。
2,Factory Method模式通过面向对象的手法,将所要创建的具体对象工作延迟到子类,从而实现一种扩展(而非更改)的策略,较好地解决了这种紧耦合关系。
3,Factory Method模式解决“单个对象”的需求变化,Abstract Factory模式解决“系列对象”的需求变化,Builder模式解决“对象部分”的需求变化。
Prototype原型
Prototype模式的要点
1.Prototype模式同样用于隔离类对象的使用者和具体类型(易变类)之间的耦合关系,它同样要求
这些“易变类”拥有“稳定的接口”。
2.Prototype模式对于“如何创建易变类的实体对象”采用“原型克隆"的方法来做,它便得我们可以非常灵活地动态创建“拥有某些稳定接口”的新对象——所需工作仅仅是注册一个新类的对象(即原型),然后在任何需要的地方不断地Clone。
3.Prototype模式中的Clone方法可以利用.NET中的Object类的MemberwiseClone()方法或者序列化来实现深拷贝。
结构型模式
Adapter 适配器
“适配”即转换,在不改变原有实现的基础上,将不兼容的接口改为兼容的接口。
适配器中的角色:
1、目标接口(Target): 当前系统业务所期待的接口,可以是抽象类或者接口。
2、适配源类(Adaptee): 它是被访问和适配的现存组件库中的组件接口。
3、适配者类(Adapter): 它是一个转换器,通过集成或引用适配者的对象,把适配源接口转换成目标接口,让客户按目标接口的格式访问适配者
要点
1、这个模式主要应用于“希望复用一些现存的类,但是接口又与复用环境要求不一致的情况”,在遗留代码复用、类库迁徙等方面非常有用。
2、对象适配器和类适配器。但类适配器采用“多继承”的实现方式,带来不良的高耦合性所以一般不推荐使用。对象适配器采用“对象组合”的方式,更符合松耦合精神。
3、Adapter模式非常灵活。
4、Adapter模式本身要求我们尽可能地使用“面向接口的编程”风格,这样才能在后期很方便地适配。
Bridge桥接
桥接(Bridge)是用于把抽象化与实现化解耦,使得二者可以独立变化。这种类型的设计模式属于结构型模式,它通过提供抽象化和实现化之间的桥接结构,来实现二者的解耦。
要点:
1、Bridge模式使用“对象间的组合关系”解耦了抽象和实现之间固有的绑定关系,使抽象(Tank的型号)和实现(不同的平台)可以沿着各自的维度来变化。
2、Bridge模式有时候类似于多继承方案,但是多继承方案往往违 背单一职责原则(即一个类只有一个变化的原因),复用性比较差。Bridge模式是比多继承方案更好的解决方法。
3、Bridge模式的应用一般在“两个非常强的变化维度”,有时候即使 有两个变化的维度,但是某个方向的变化维度并不剧烈——换 言之两个变化不会导致纵横交错的结果,并不一定要使用 Bridge模式
Composite组合模式
将对象组合成树形结构以表示“部分-整体”的层次结构。Composite使得用户对单个对象和组合对象的使用具有一致性。
动机:客户代码过多地依赖于对象容 器复杂的内部实现结构,对象容器内部实现结构(而非抽 象接口)的变化将引起客户代码的频繁变化,带来了代码 的维护性、扩展性等弊端。
要点
1、Composite模式采用树形结构来实现普遍存在的对象容器,从而将“一 对多”的关系转化为“一对一”的关系,无需关心处理的是单个的对象,还是组合的对象容器。
2、将“客户代码与复杂的对象容器结构”解耦是Composite模式的核心思 想,解耦之后,客户代码将与纯粹的抽象接口——而非对象容器的复 内部实现结构——发生依赖关系,从而更能“应对变化”。
3、Composite模式在具体实现中,可以让父对象中的子对象反向追溯; 如果父对象有频繁的遍历需求,可使用缓存技巧来改善效率。
4、Composite模式中,是将“Add和Remove等和对象容器相关的方法”定 义在“表示抽象对象的Component类”中,还是将其定义在“表示对象容 器的Composite类。
Decorator装饰者模式
装饰者模式能够完美实现“对修改关闭,对扩展开放”的原则,也就是说我们可以在不修改被装饰者的前提下,扩展被装饰者的功能。
动机:过度上的使用继承来扩展对象的功能,由于继承为类型引入的静态特质,使得这种扩展方式缺乏灵活性,并且还会导致更多子类膨胀。
要点:
1、通过采用组合、而非继承的手法, Decorator模式实现了在运行时动 态地扩展对象功能的能力,而且可以根据需要扩展多个功能。避免了 单独使用继承带来的“灵活性差”和“多子类衍生问题”。
2、Decorator模式并非解决“多子类衍生的多继承”问题,Decorator模式 应用的要点在于解决“主体类在多个方向上的扩展功能”——是为“装饰” 的含义。
Facade外观模式
外观(Facade)模式又叫作门面模式,是一种通过为多个复杂的子系统提供一个一致的接口,而使这 些子系统更加容易被访问的模式。该模式对外有一个统一接口,外部应用程序不用关心内部子系统的具 体细节,这样会大大降低应用程序的复杂度,提高了程序的可维护性。
要点
1、从客户程序的角度来看, Facade模式不仅简化了整个组件系统的接口,同时对于组件内部与外部客 户程序来说,从某种程度上也达到了一种“解耦”的效果——内部子系统的任何变化不会影响到Façade 接口的变化。
2、Façade模式注重简化接口
3、Façade设计模式更注重从架构的层次去看整个系统,而不是单个类的层次。Façade很多时候更是一种 架构设计模式。
外观模式
外观(Facade)模式的结构比较简单,主要是定义了一个高层接口。它包含了对各个子系统的引用,客 户端可以通过它访问各个子系统的功能。
外观(Facade)模式包含以下主要角色。
1、外观(Facade)角色:为多个子系统对外提供一个共同的接口。
2、 子系统(Sub System)角色:实现系统的部分功能,客户可以通过外观角色访问它。
3、 客户(Client)角色:通过一个外观角色访问各个子系统的功能。
Flyweight享元模式
运用共享技术来有效地支持大量细粒度对象的复用。它通过共享已经 存在的对象来大幅度减少需要创建的对象数量、避免大量相似类的开销,从而提高系统资源的利用率。
要点
1、Flyweight设计模式主要解决面向对象的代价问题,一般不触及面向对象的抽象性问题。
2、Flyweight采用对象共享的做法来降低系统中对象的个数,从而降低细粒度对象给系统带来的内存压力。在具体实现方面,要注意对象状态的处理。
3、对象的数量太大从而导致对象内存开销加大——什么样的数量才算大?这需要我们仔细的根据具体应 用情况进行评估,而不能凭空臆断。
优缺点
优点是:相同对象只要保存一份,这降低了系统中对象的数量,从而降低了系统中细粒 度对象给内存带来的压力。
缺点是:
为了使对象可以共享,需要将一些不能共享的状态外部化,这将增加程序的复杂性。
读取享元模式的外部状态会使得运行时间稍微变长。
Proxy代理模式
由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不 适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。
要点
1、增强了一层阶层,来间接访问某些对象。
2、具体proxy设计模式的实现方法、实现粒度都相差 很大,有些可能对单个对象做细粒度的控制
3、不一定要求保持接口的一致性,只要能够 实现间接控制,有时候损及一些透明性是可以接受的。
行为型模式(1)
Template Method模板方法模式
定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。它是一种类行为型模式。
Template Method模板方法模式优缺点:
优点:
- 它封装了不变部分,扩展可变部分。它把认为是不变部分的算法封装到父类中实现,而把可变部分算法由子类继承实现,便于子类继续扩展。
- 它在父类中提取了公共的部分代码,便于代码复用。
- 部分方法是由子类实现的,因此子类可以通过扩展方式增加相应的功能,符合开闭原则。
缺点:
- 对每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象,间接地增加了系统实现的复杂度。
- 父类中的抽象方法由子类实现,子类执行的结果会影响父类的结果,这导致一种反向的控制结构,它提高了代码阅读的难度。
- 由于继承关系自身的缺点,如果父类添加新的抽象方法,则所有子类都要改一遍。
Strategy策略模式
该模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。
策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理。
Strategy策略模式的主要优缺点:
优点:
1、多重条件语句不易维护,而使用策略模式可以避免使用多重条件语句,如 if...else 语句、switch...case 语句。
2、策略模式提供了一系列的可供重用的算法族,恰当使用继承可以把算法族的公共代码转移到父类里面,从而避免重复的代码。
3、策略模式可以提供相同行为的不同实现,客户可以根据不同时间或空间要求选择不同的。
4、策略模式提供了对开闭原则的完美支持,可以在不修改原代码的情况下,灵活增加新算法。
5、策略模式把算法的使用放到环境类中,而算法的实现移到具体策略类中,实现了二者的分离。
缺点:
1、客户端必须理解所有策略算法的区别,以便适时选择恰当的算法类。
2、策略模式造成很多的策略类,增加维护难度。
State状态模式
对有状态的对象,把复杂的“判断逻辑”提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。
State状态模式是一种对象行为型模式,其主要优缺点:
优点:
1、结构清晰,状态模式将与特定状态相关的行为局部化到一个状态中,并且将不同状态的行为分割开来,满足“单一职责原则”。
2、将状态转换显示化,减少对象间的相互依赖。将不同的状态引入独立的对象中会使得状态转换变得更加明确,且减少对象间的相互依赖。
3、状态类职责明确,有利于程序的扩展。通过定义新的子类很容易地增加新的状态和转换。
缺点:
1、状态模式的使用必然会增加系统的类与对象的个数。
2、状态模式的结构与实现都较为复杂,如果使用不当会导致程序结构和代码的混乱。
3、状态模式对开闭原则的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源码,否则无法切换到新增状态,而且修改某个状态类的行为也需要修改对应类的源码。
Memento备忘录模式
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后当需要时能将该对象恢复到原先保存的状态。该模式又叫快照模式。
备忘录模式是一种对象行为型模式,其主要优缺点如下。
优点:
1、提供了一种可以恢复状态的机制。当用户需要时能够比较方便地将数据恢复到某个历史的状态。
2、实现了内部状态的封装。除了创建它的发起人之外,其他对象都不能够访问这些状态信息。
3、简化了发起人类。发起人不需要管理和保存其内部状态的各个备份,所有状态信息都保存在备忘录中,并由管理者进行管理,这符合单一职责原则。
缺点:
资源消耗大。如果要保存的内部状态信息过多或者特别频繁,将会占用比较大的内存资源。
Mediator中介者模式
中介者模式(Mediator)是一种对象行为型模式,其主要优缺点:
优点:
1、类之间各司其职,符合迪米特法则。
2、降低了对象之间的耦合性,使得对象易于独立地被复用。
3、将对象间的一对多关联转变为一对一的关联,提高系统的灵活性,使得系统易于维护和扩展。
缺点:
中介者模式将原本多个对象直接的相互依赖变成了中介者和多个同事类的依赖关系。当同事类越多时,中介者就会越臃肿,变得复杂且难以维护。
行为型模式(2)
Chain Of Responsibility责任链模式
使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系。将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理他为止。
责任链模式的角色:
1、抽象处理者(Handler)角色:定义出一个处理请求的接口。如果需要,接口可以定义出一个以设定和返回对下家的引用。
2、具体处理者(ConcreteHandler)角色:具体处理者接到请求后,可以选择将请求处理掉,或者将请求传给下家。
3、Client:向链上的具体处理者(ConcreteHandler)对象提交请求。
Chain Of Responsibility责任链模式的优缺点如下
优点:
1、降低耦合度 该模式使得一个对象无需知道是其他哪一个对象的其请求。对象仅需知道该请求会被“正确”地处理。接受者和发送者都没有对方的明确的信息,且链中的对象不需知道链的结构。
结果是,责任链可以简化对象的相互连接。它们仅需保持一个指向其后继者的引用,而不需要保持它所有的候选接受者的引用。
2、增强了给对象指派责任(Responsibility)的灵活性 当在对象中分配职责时,职责链给你更多的灵活性,你可以通过在运行时刻对该链进行动态的增加或者修改来增加或者改变处理一个请求的那些职责。你可以将这种机制与静态的特例化处理对象的继承机制结合起来使用。
缺点:
不保证被接受 既然一个请求没有明确的接受者,那么就不能保证它一定会被处理——该请求可能一直在链对的末端都得不到处理。一个请求也可能因该链没有被正确配置而得不到处理。
Command命令模式
将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。这样两者之间通过命令对象进行沟通,这样方便将命令对象进行储存、传递、调用、增加与管理。
Command命令模式的主要优缺点
优点:
1. 通过引入中间件(抽象接口)降低系统的耦合度。
2. 扩展性良好,增加或删除命令非常方便。采用命令模式增加与删除命令不会影响其他类,且满足“开闭原则”。
3. 可以实现宏命令。命令模式可以与组合模式结合,将多个命令装配成一个组合命令,即宏命令。
4. 方便实现 Undo 和 Redo 操作。命令模式可以与后面介绍的备忘录模式结合,实现命令的撤销与恢复。
5. 可以在现有命令的基础上,增加额外功能。比如日志记录,结合装饰器模式会更加灵活。
缺点:
1. 可能产生大量具体的命令类。因为每一个具体操作都需要设计一个具体命令类,这会增加系统的复杂性。
2. 命令模式的结果其实就是接收方的执行结果,但是为了以命令的形式进行架构、解耦请求与实现,引入了额外类型结构(引入了请求方与抽象命令接口),增加了理解上的困难。不过这也是设计模式的通病,抽象必然会额外增加类的数量,代码抽离肯定比代码聚合更加难理解。
Iterator迭代器模式
提供一个对象来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。
迭代器模式是一种对象行为型模式,其主要优缺点如下。
优点:
1、访问一个聚合对象的内容而无须暴露它的内部表示。
2、遍历任务交由迭代器完成,这简化了聚合类。
3、它支持以不同方式遍历一个聚合,甚至可以自定义迭代器的子类以支持新的遍历。
4、加新的聚合类和迭代器类都很方便,无须修改原有代码。
5、封装性良好,为遍历不同的聚合结构提供一个统一的接口。
缺点:
增加了类的个数,这在一定程度上增加了系统的复杂性。在日常开发中,我们几乎不会自己写迭代器。除非需要定制一个自己实现的数据结构对应的迭代器,否则,开源框架提供的 API 完全够用。
1. 模式的结构
迭代器模式主要包含以下角色。
1. 抽象聚合(Aggregate)角色:定义存储、添加、删除聚合对象以及创建迭代器对象的接口。
2. 具体聚合(ConcreteAggregate)角色:实现抽象聚合类,返回一个具体迭代器的实例。
3. 抽象迭代器(Iterator)角色:定义访问和遍历聚合元素的接口,通常包含 hasNext()、first()、next() 等方法。
4. 具体迭代器(Concretelterator)角色:实现抽象迭代器接口中所定义的方法,完成对聚合对象的遍历,记录遍历的当前位置。
Interpreter解释器模式
给分析对象定义一个语言,并定义该语言的文法表示,再设计一个解析器来解释语言中的句子。也就是说,用编译语言的方式来分析应用中的实例。这种模式实现了文法表达式处理的接口,该接口解释一个特定的上下文。
这里提到的文法和句子的概念同编译原理中的描述相同,“文法”指语言的语法规则,而“句子”是语言集中的元素。例如,汉语中的句子有很多,“我是中国人”是其中的一个句子,可以用一棵语法树来直观地描述语言中的句子。
解释器模式是一种类行为型模式,其主要优缺点如下。
优点:
1、扩展性好。由于在解释器模式中使用类来表示语言的文法规则,因此可以通过继承等机制来改变或扩展文法。
2、易实现。在语法树中的每个表达式节点类都是相似的,所以实现其文法较为容易。
缺点:
1、执行效率较低。解释器模式中通常使用大量的循环和递归调用,当要解释的句子较复杂时,其运行速度很慢,且代码的调试过程也比较麻烦。
2、会引起类膨胀。解释器模式中的每条规则至少需要定义一个类,当包含的文法规则很多时,类的个数将急剧增加,导致系统难以管理与维护。
3、可应用的场景比较少。在软件开发中,需要定义语言文法的应用实例非常少,所以这种模式很少被使用到。
Observer观察者模式
指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。这种模式有时又称作发布-订阅模式、模型-视图模式,它是对象行为型模式。
观察者模式是一种对象行为型模式,其主要优缺点如下。
优点:
1、降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系。符合依赖倒置原则。
2、目标与观察者之间建立了一套触发机制。
缺点:
1、目标与观察者之间的依赖关系并没有完全解除,而且有可能出现循环引用。
2、当观察者对象很多时,通知的发布会花费很多时间,影响程序的效率。
Visitor访问者模式
将作用于某种数据结构中的各元素的操作分离出来封装成独立的类,使其在不改变数据结构的前提下可以添加作用于这些元素的新的操作,为数据结构中的每个元素提供多种访问方式。它将对数据的操作与数据结构进行分离,是行为类模式中最复杂的一种模式。
访问者(Visitor)模式是一种对象行为型模式,其主要优缺点如下。
优点:
1、扩展性好。能够在不修改对象结构中的元素的情况下,为对象结构中的元素添加新的功能。
2、复用性好。可以通过访问者来定义整个对象结构通用的功能,从而提高系统的复用程度。
3、灵活性好。访问者模式将数据结构与作用于结构上的操作解耦,使得操作集合可相对自由地演化而不影响系统的数据结构。
4、符合单一职责原则。访问者模式把相关的行为封装在一起,构成一个访问者,使每一个访问者的功能都比较单一。
缺点:
1、增加新的元素类很困难。在访问者模式中,每增加一个新的元素类,都要在每一个具体访问者类中增加相应的具体操作,这违背了“开闭原则”。
2、破坏封装。访问者模式中具体元素对访问者公布细节,这破坏了对象的封装性。
3、违反了依赖倒置原则。访问者模式依赖了具体类,而没有依赖抽象类。
1. 模式的结构
访问者模式包含以下主要角色。
1. 抽象访问者(Visitor)角色:定义一个访问具体元素的接口,为每个具体元素类对应一个访问操作 visit() ,该操作中的参数类型标识了被访问的具体元素。
2. 具体访问者(ConcreteVisitor)角色:实现抽象访问者角色中声明的各个访问操作,确定访问者访问一个元素时该做什么。
3. 抽象元素(Element)角色:声明一个包含接受操作 accept() 的接口,被接受的访问者对象作为 accept() 方法的参数。
4. 具体元素(ConcreteElement)角色:实现抽象元素角色提供的 accept() 操作,其方法体通常都是 visitor.visit(this) ,另外具体元素中可能还包含本身业务逻辑的相关操作。
5. 对象结构(Object Structure)角色:是一个包含元素角色的容器,提供让访问者对象遍历容器中的所有元素的方法,通常由 List、Set、Map 等聚合类实现。
解释器模式
常用于对简单语言的编译或分析实例中,为了掌握好它的结构与实现,必须先了解编译原理中的“文法、句子、语法树”等相关概念。
1) 文法
文法是用于描述语言的语法结构的形式规则。没有规矩不成方圆,例如,有些人认为完美爱情的准则是“相互吸引、感情专一、任何一方都没有恋爱经历”,虽然最后一条准则较苛刻,但任何事情都要有规则,语言也一样,不管它是机器语言还是自然语言,都有它自己的文法规则。例如,中文中的“句子”的文法如下。
〈句子〉::=〈主语〉〈谓语〉〈宾语〉
〈主语〉::=〈代词〉|〈名词〉
〈谓语〉::=〈动词〉
〈宾语〉::=〈代词〉|〈名词〉
〈代词〉你|我|他
〈名词〉7大学生I筱霞I英语
〈动词〉::=是|学习
注:这里的符号“::=”表示“定义为”的意思,用“〈”和“〉”括住的是非终结符,没有括住的是终结符。
2) 句子
句子是语言的基本单位,是语言集中的一个元素,它由终结符构成,能由“文法”推导出。例如,上述文法可以推出“我是大学生”,所以它是句子。
3) 语法树
语法树是句子结构的一种树型表示,它代表了句子的推导结果,它有利于理解句子语法结构的层次。
解释器模式的结构与组合模式相似,不过其包含的组成元素比组合模式多,而且组合模式是对象结构型模式,而解释器模式是类行为型模式。
1. 模式的结构
解释器模式包含以下主要角色。
1. 抽象表达式(Abstract Expression)角色:定义解释器的接口,约定解释器的解释操作,主要包含解释方法 interpret()。
2. 终结符表达式(Terminal Expression)角色:是抽象表达式的子类,用来实现文法中与终结符相关的操作,文法中的每一个终结符都有一个具体终结表达式与之相对应。
3. 非终结符表达式(Nonterminal Expression)角色:也是抽象表达式的子类,用来实现文法中与非终结符相关的操作,文法中的每条规则都对应于一个非终结符表达式。
4. 环境(Context)角色:通常包含各个解释器需要的数据或是公共的功能,一般用来传递被所有解释器共享的数据,后面的解释器可以从这里获取这些值。
5. 客户端(Client):主要任务是将需要分析的句子或表达式转换成使用解释器对象描述的抽象语法树,然后调用解释器的解释方法,当然也可以通过环境角色间接访问解释器的解释方法。
浙公网安备 33010602011771号