《研磨设计模式》--摘录笔记(二)

《研磨设计模式》--摘录笔记(二)

14 代器模式(GoF的著作中划分为行为

定义:提供一种方法顺序访问一个聚合对象中的各个元素,而又不需暴露该对象的内部表

本质:控制访问聚合对象中的元素。

使用步骤:【涉及名词】

Iterator:法代器接口。定义访问和遍历元素的接口。

Concretelterator:具体的迭代器实现对象。实现对聚合对象的遍历,并跟踪遍历时的当前位置。

Aggregate:聚合对象。定义创建相应迭代器是对象的接口。

ConcreteAggregate:具体聚合对象。实现创建相应的迭代器对象。

1.定义迭代器接口Iterator,并承诺访问遍历元素需要的操作方法;2.具体的迭代器Concretelterator实现Iterator接口,持有聚合对象ConcreteAggregate,同时构造函数以ConcreteAggregate为参数

扫描二维码关注公众号,回复: 2173645 查看本文章

3.定义抽象的聚合对象Aggregate类,提供创建迭代器对象的接口方法createIterator()

4.定义具体聚合对象ConcreteAggregate继承Aggregate实现创建对象的

5.客户端使用someOperation ()方法体:例如先创建一个数组names,再以names为参数创建Aggregate,创建迭代器对象Iterator--Iterator it = aggregate.createIterator ();

优点:

• 更好的封装性

• 迭代器模式可以让你访问一个聚合对象的内容, 而无须暴露该聚合对象的内部表示,从而提高聚合对象的封装性。

• 可以以不同的遍历方式来遍历一个聚合。

• 使用迭代器模式. 使得聚合对象的内容和具体的迭代算法分离开。这样就可以通过使用不同的代器的实例、不同的遍历方式来遍历一个聚合对象了,比如上面示例的带迭代策略的迭代器。

• 迭代器简化了聚合的接口。

• 有了迭代器的接口, 则聚合本身就不需要再定义这些接口了,从而简化了聚合的接口定义。

• 简化客户端调用

• 迭代器为遍历不同的聚合对象提供了一个统一的接口, 使得客户端遍历聚合对象的内容变得更简单。

• 同一个聚合上可以有多个遍历

• 每个迭代器保持它自己的遍历状态, 比如前面实现中的代索引位置, 因此可以对同一个聚合对象同时进行多个遍历。


缺点:---

何时选用:

• 如果你希望提供访问一个聚合对象的内容,但是又不想暴露它的内部表示的时候,可以使用迭代器模式来提供迭代器接口,从而让客户端只是通过迭代器的接口来访问聚合对象,而无须关心聚合对象的内部实现。

• 如果你希望有多种遍历方式可以访问聚合对象, 可以使用迭代器模式。

• 如果你希望为遍历不同的聚合对象提供一个统一的接口, 可以使用迭代器模式。

15 章 组合模式(GoF的著作中划分为结构型)

定义:将对象组合成树形结构以表示“部分一整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性

本质:统一叶子对象和组合对象。

使用步骤:【涉及名词】

Component:抽象的组件对象, 为组合中的对象声明接口, 让客户端可以通过这个接口来访问和管理整个对象结构, 可以在里面为定义的功能提供缺省的实现。

Leaf::叶子节点对象, 定义和实现叶子对象的行为, 不再包含其他的子节点对象。

Composite:组合对象, 通常会存储子组件, 定义包含子组件·的那些组件的行为,并实现在纽件接口中定义的与子组件有关的操作。

Client:客户端, 通过组件接口来操作组合结胸里面的级件对象。

1.定义抽象的组件对象类Component,提供对子组件对象可能有的功能方法;提供对子对象的增加,移除,获取的方法并给出默认的实现;

2.定义组合对象Composite,继承Component,并持有子组件的List,重新实现(覆盖)父类中的默认方法;

3.定义叶子对象Leaf,仅覆盖父类子组件对象可能有的功能方法,对子对象的增加,移除,获取的方法使用父类的默认实现。

4.Client组合成层次结构,略。

优点:定义了包含基本对象和组合对象的类层次结构;统一了组合对象和叶子对象;简化了客户端调用;更容易扩展。

缺点:

组合模式的缺点是很难限制组合中的组件类型。

容易地加新的组件也会带来一些问题, 比如很难限制组合中的组件类型。

何时选用:

• 如果你想表示对象的部分一整体层次结构,可以选用组合模式,把整体和部分的操作统-起来, 使得层次结构实现更简单,从外部来使用这个层次结构也容易。

• 如果你希望统一地使用组合结构中的所有对象, 可以选用组合模式, 这正是组合模式提供的主要功能。

16 章 模板方法模式(GoF的著作中划分为行为型)

定义:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

本质:固定算法骨架。

使用步骤:【涉及名词】

所谓原语操作就是抽象的操作, 必须要由子类提供实现。

AbstractClass:抽象类。用来定义算法骨架和原语操作, 具体的子类通过重定义这些原语操作来实现一个算法的各个步骤。在这个类里面, 还可以提供算法中通用的实现。

ConcreteClass:具体实现类。用来实现算法骨架中的某些步骤,完成与特定子类相关的功能。

1.定义模板方法、原语操作1.2...等的抽象类AbstractClass,在模版方法中,组织原语操作的顺序;

2.具体实现类ConcreteClass实现原语操作,继承抽象类AbstractClass

优点:实现代码复用。

缺点:算法骨架不容易升级。

何时选用:

需要固定定义算法骨架,实现一个算法的不变的部分,并把可变的行为留给子类来实现的情况。

各个子类中具有公共行为,应该抽取出来, 集中在-个公共类中去实现,从而避免代码重复。

需要控制子类扩展的情况。模板方法模式会在特定的点来调用子类的方法.这样只允许在这些点进行扩展。

17 策略模式(GoF的著作中划分为行为型)

定义:定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。

本质:分离算法,选择实现。

使用步骤:【涉及名词】

Strategy 策略接口,用来约束一系列具体的策略算法。Context使用这个接口来调用具体的策略实现定义的算法。

ConcreteStrategy: 具体的策略实现, 也就是具体的算法实现。

Context:上下文, 负责和具体的策略类交互。通常上下文会持有一个真正的策略实现,上下文还可以让具体的策略类来获取上下文的数据,甚至让具体的策略类来回调上下文的方法。

1.定义策略,即定义算法的Strategy接口;

2.定义具体的策略ConcreteStrategy实现策略接口,egConcreteStrategyAConcreteStrategyBConcreteStrategyC....

3.定义上下文对象Context,通常会持有一个具体的策略对象Strategy。并且构造函数以Strategy为参数。

上下文对客户端提供的操作接口,可以有参数和返回值---常内部会调具体的策略对象进行算法运算。

优点:定义一系列算法;避免多重条件语句;更好的扩展性。

缺点:客户必须了解每种策略的不同;增加对象数目;只适合扁平的算法结构。

何时选用:

• 出现有许多相关的类,仅仅是行为有差别的情况下,可以使用策略模式来使用多个行为中的一个来配置一个类的方法, 实现算法动态切换。

• 出现同一个算法,有很多不同实现的情况下, 可以使用策略模式来把这些“不同的实现” 实现成为一个算法的类层次。

• 需要封装算法中, 有与算法相关数据的情况下, 可以使用策略模式来避免暴露这些跟算法相关的数据结构。

• 出现抽象一个定义了很多行为的类,并且是通过多个if-else 语句来选择这些行为的情况下,可以使用策略模式来代替这些条件语句。

18  状态模式(GoF的著作中划分为行为型)

定义:允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。

本质:根据状态来分离和选择行为。

使用步骤:【涉及名词】

• Context 环境,也称上下文,通常用来定义客户感兴趣的接口,同时维护一个来具体处理当前状态的实例对象。

• State:状态接口,用来封装与上下文的一个特定状态所对应的行为。

ConcreteState:具体实现状态处理的类,每个类实现一个跟上下文相关的状态的具体处理。

1.定义状态接口State,来封装与Context的一个特定状态相关的行为;

2.定实现State的具体状态ConcreteStateegConcreteStateAConcreteStateB...

3.定义上下文类Context定义客户感兴趣的接口,持有-个State类型的对象实例。

优点:简化应用逻辑控制;更好地分离状态和行为;更好的扩展性;显示化进行状态转换。

缺点:---

何时选用:

• 如果一个对象的行为取决于它的状态,而且它必须在运行时刻根据状态来改变它的行为, 可以使用状态模式, 来把状态和行为分离开。虽然分离开了,但状态和行为是有对应关系的,可以在运行期间,通过改变状态,就能够调用到该状态对应的状态处理对象上去,从而改变对象的行为。

• 如果一个操作中含有庞大的多分支话旬, 而且这些分支依赖于该对象的状态, 可以使用状态模式, 把各个分支的处理分散包装到单独的对象处理类中,这样,这些分支对应的对象就可以不依赖于其他对象而独立变化了。

19  备忘录模式(GoF的著作中划分为行为型)

定义:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样,以后就可将该对象恢复到原先保存的状态。

本质:保存和恢复内部状态。

使用步骤:【涉及名词】

Memento:备忘录。主要用来存储原发器对象的内部状态,但是具体需要存储哪些数据是由原发器对象来决定的。另外备忘录应该只能由原发器对象来访问它内部的数据,原发器外部的对象不应该访问到备忘录对象的内部数据。

Originator: 原发器。使用备忘录来保存某个时刻原发器自身的状态,也可以使用备忘录来恢复内部状态。

Caretaker:备忘录管理者,或者称为备忘录负责人。主要负责保存备忘录对象,但是不能对备忘录对象的内容进行操作或检查。

1.定义备忘录对象的窄接口Memento---没有任何方法定义;

2.定义原发器对象类Originator,它里面会有备忘录对象的实现,因为真正的备忘录对象当

作原发器对象的一个私有内部类来实现了---private static classMementoImpl ImplementsMemento】。

此内部私有类,对外部仅提供构造函数和getter方法;

3.定义保存备忘录的对象类Caretaker,有一个持有备忘录Memento的属性,提供保存和获取备忘录对象的方法。

保存和恢复内部状态,谁的状态?---原发器的内部状态(属性)!

优点:更好的封装性;简化了原发器;窄接口和宽接口。

缺点:可能导致高开销

如果需要缓存的数据量很大,或者是特别频繁地创建备忘录对象,开销是很大的。

何时选用:

• 如果必须保存一个对象在某一个时刻的全部或者部分状态,方便在以后需要的时候,可以把该对象恢复到先前的状态,可以使用备忘录模式。使用备忘录对象来封装和保存需要保存的内部状态,然后把备忘录对象保存到管理者对象中,在需要的时候,再从管理者对象中获取备忘录对象,来恢复对象的状态。

• 如果需要保存一个对象的内部状态, 但是如果用接口来让其他对象直接得到这些需要保存的状态, 将会暴露对象的实现细节并破坏对象的封装性, 这时可以使用备忘录模式, 把备忘录对象实现成为原发器对象的内部类,而且还是私有的, 从而保证只有原发器对象才能访问该备忘录对象。这样既保存了需要保存的状态,

又不会暴露原发器对象的内部实现细节。

20 元模式(GoF的著作中划分为结构型)

定义:运用共享技术有效地支持大量细粒度的对象。

本质:分离与共享。

使用步骤:【涉及名词】

Flyweight:享元接口, 通过这个接口Flyweight可以接受并作用于外部状态。通过这个接口传入外部的状态, 在享元对象的方法处理中可能会使用这些外部的数据。

• ConcreteFlyweight: 具体的享元实现对象,必须是可共享的.需要封装Flyweight的内部状态。

UnsharedConcreteFlyweight:非共享的享元实现对象,并不是所有的Flyweight实现对象都需要共享。非共享的享元实现对象通常是对共享享元对象的组合对象

FlyweightFactory: 享元工厂,主要用来创建并管理共享的事元对象, 并对外提供访问共享享元的接口。

Client:享元客户端, 主要的工作是维持一个对Flyweight的引用,计算或存储享元对象的外部状态, 当然这里可以访问共享和不共享的Flyweight对象。

1.定义享元接口Flyweight,通过这个接口享元可以接受并作用于外部状态;

2.享元接口的实现类ConcreteFlyweight,持有描述内部状态的属性,且为构造函数参数;

3.享元接口的实现类UnsharedConcreteFlyweight,也持有描述内部状态的属性,且为构造函数参数;

4.定义享元工厂类FlyweightFactory,持有缓存多个Flyweight对象的Map容器,并提供获取Flyweight对象的方法,该方法以Key为参数,先查找map,没有则创建后加入map中。

4.Client...

优点:减少对象数量,节省内存空间。

缺点:维护共享对象,需要额外开销。

何时选用:

• 如果一个应用程序使用了大量的细粒应对象,可以使用享元模式来减少对象数量。

• 如果由于使用大量的对象,造成很大的存储开销,可以使用享元模式来减少对象数量,并节约内存。

• 如果对象的大多数状态都可以转变为外部状态, 比如通过计算得到,或是从外部传入等, 可以使用享元模式来实现内部状态和外部状态的分离。

• 如果不考虑对象的外部状态,可以用相对较少的共享对象取代很多组合对象, 可以使用享元模式来共享对象, 然后组合对象来使用这些共享对象。

21 解释器模式(GoF的著作中划分为行为型)

定义:给定一个语言,定义它的文法的一种表示, 并定义一个解释器,这个解释器使用该表示来解释语言中的句子。

本质:分离实现,解释执行。

使用步骤:【涉及名词】

AbstractExpression: 定义解释器的接口, 约定解释器的解释操作。

TerminalExpression,终结符解释器,用来实现语法规则中和终结符相关的操作,不再包含其他的解释器, 如果用组合模式来构建抽象语法树的话, 就相当于组合模式中的叶子对象, 可以有多种终结符解释器。

NonterminalExpression:非终结符解释器,用来实现语法规则中非终结符相关的操作,通常一个解释器对应一个语法规则,可以包含其他的解释器, 如果用组合模式来构建抽象语法树的话,就相当于组合模式中的组合对象。可以有多种非终结符解释器。

Context: 上下文, 通常包含各个解释器需要的数据或是公共的功能。

Client:客户端, 指的是使用解释器的客户端, 通常在这里将按照语言的语法做的表达式转换成为使用解释器对象描述的抽象语法树, 然后调用解释操作。

1.定义抽象表达式的抽象类AbstractExpression,定义一个执行解释的方法,解释方法以Context为参数;

2.定义终结符表达式类TerminalExpression,继承AbstractExpression

3.定义非终结符表达式类NonterminalExpression,继承AbstractExpression

4.定义类Context,包含解释器外的一些全局信息。

5.Client通常在这里将按照语言的语法做的表达式转换成为使用解释器对象描述的抽象语法树,然后调用解释操作。

优点:易于实现语法;易于扩展新的语法。

缺点:不适合复杂的语法。对效率要求很高的情况下,不适合使用解释器。

何时选用:

当有一个语言需要解释执行,并且可以将该语言中的句子表示为一个抽象语法树的时候,可以考虑使用解释器模式。

22 章 装饰模式(GoF的著作中划分为结构型)

定义:动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式比生成子类更为灵活。

本质:动态组合。

使用步骤:【涉及名词】

Component:组件对象的接口,可以给这些对象动态地添加职责。

ConcreteComponent:具体的组件对象,实现组件对象接口,通常就是被装饰挂在装饰的原始对象,也就是可以给这个对象添加职责。

Decorator:所有装饰器的抽象父类,需要定义一个与组件接口一致的接口,并持有一个Component对象, 其实就是持有一个被装饰的对象。

注意这个被装饰的对象不一定是最原始的那个对象了,也可能是被其他装饰器装饰过后的对象.反正都是实现的同一个接口,也就是同一类型.

ConcreteDecorator: 实际的装饰将对象,实现具体要向被装饰对象添加的功能。

1.定义组件对象Component抽象类;

2.定义具体组件对象ConcreteComponent继承Component

3.定义装饰器抽象类Decorator,继承Component,并持有组件对象Component---被装饰的具体对象且构造含数以它为参数。

4.定义具体装饰器对象ConcreteDecorator,继承Decorator,在对象中提供添加新职责的方法。ConcreteDecoratorAConcreteDecoratorBConcreteDecoratorC...

优点:比继承更灵活;更容易复用功能;简化高层定义。

缺点:会产生很多细粒度对象。装饰模式是把一系列复杂的功能,分散到每个装饰器当中, 一般一个装饰器只实现一个功能,这样会产生很多细粒度的对象,而且功能越复杂,需要的细粒度对象越多。

何时选用:

• 如果需要在不影响其他对象的情况下,以动态、透明的方式给对象添加职责,可以使用装饰模式, 这几乎就是装饰模式的主要功能。

• 如果不适合使用子类来进行扩展的时候,可以考虑使用装饰模式。因为装饰模式是使用的“对象组合” 的方式。所谓不适合用子类扩展的方式.比如,扩展功能需要的子类太多,造成子类数目里爆炸性增长。

23 章 职责链模式(GoF的著作中划分为行为型)

定义:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。

本质:分离职责,动态组合。

使用步骤:【涉及名词】

Handler: 定义职责的接口, 通常在这里定义处现请求的方法, 可以在这里实现后继链。

ConcreteHandler: 实现职责的类, 在这个类中实现对在它职责范围内请求的处理,如果不处理,就继续转发请求给后继者。

Client:职责链的客户端,向链上的具体处理对象提交请求,让职责链负责处理。

1.定义Handler接口抽象类,并持有后继的职责对象Handler--successor,提供设置后继对象的方法和处理请求的方法。

2.定义具体的职责实现对象类ConcreteHandler继承HandleregConcreteHandler1ConcreteHandler2...

 在此类中实现处理请求的方法,及什么时候转发处理请求给后继对象;

3.Client可以创建多个Handler对象实例,比如:h1h2h3...,设置h1的后继者为h2h2的后继者为h3

调用h1的处理请求的方法。

优点:请求者和接收者松散耦合;动态组合职能。

缺点:产生很多细粒度对象;不一定能被处理。可能会出现某个请求把整个链传递完了, 都没有职责对象处理它。需要提供默认的处理。

何时选用:

• 如果有多个对象可以处理同一个请求, 但是具体由哪个对象来处现该请求, 是运行时刻动态确定的。这种情况可以使用职责链模式, 把处理请求的对象实现成为职责对象,然后把它们构成一个职责链, 当请求在这个链中传递的时候,具体由哪个职责对象来处理,会在运行时动态判断。

• 如果你想在不明确指定接收者的情况下,向多个对象中的其中一个提交请求的话,可以使用职责链模式。职责链模式实现了请求者和接收看之间的解耦请求者不需要知道究竟是哪一个接收者对象来处理了请求。

•如果想要动态指定处理一个请求的对象集合, 可以使用职责链模式。职责链模式能动态地构建职责链,也就是动态地来决定到底哪些职责对象来参与到处理请求中来,相当于是动态地指定了处理一个请求的职责对象集合。

24  桥接模式(GoF的著作中划分为结构型)

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

本质:分离抽象和实现。

使用步骤:【涉及名词】

Abstraction,抽象部分的接口。通常在这个对象中, 要维护一个实现部分的对象引用,抽象对象里面的方法, 需要调用实现部分的对象来完成。这个对象中的方法, 通常都是和具体的业务相关的方法。

RefinedAbstraction:扩展抽象部分的接口。通在这些对象中,定义跟实际业务相关的方法,这些方法的实现通常会使用Abstraction 中定义的方法, 也可能需要调用实现部分的对象来完成。

Implementor:定义实现部分的接口。这个接口不用和Abstraction中的方法一致,通常是由Implementor接口提供基本的操作。而Abstraction定义的是基于这些基本操作的业务方法,也就是说Abstraction 定义了基于这些基本操作的较高层次的操作。

Concretelmplementor: 真正实现Implementor接口的对象。

1.定义Implementor接口,可以与抽象部分接口的方法一样

2.定义Abstraction接口为抽象类,持有一个实现部分的对象Implementor,构造函数以Implementor为参数;

3.定义具体的实现类Concretelmplementor实现ImplementoregConcretelmplementorAConcretelmplementorBConcretelmplementorC....

4.定义RefinedAbstraction类,继承Abstraction,提供扩展的方法。

优点:分离抽象和实现部分;更好的扩展性;可以动态地切换实现;可以减少子类的个数。

缺点:---

何时选用:

• 如果你不希望在抽象部分和实现部分采用固定的绑定关系,可以采用桥接模式,来把抽象部分和实现部分分开, 然后在程序运行期间来动态地设置抽象部分需要用到的具体的实现, 还可以动态地切换具体的实现。

• 如果出现抽象部分和实现部分都能够扩展的情况, 可以采用桥接模式, 让抽象部分和实现部分独立地变化, 从而灵活地进行单独扩展, 而不是搅在一起,扩展一边就会影响到另一边。

• 如果希望实现部分的修改不会对客户产生影响, 可以采用桥接模式。由于客户是面向抽象的接口在运行, 实现部分的修改可以独立于抽象部分, 并不会对客户产生影响, 也可以说对客户是透明的。

• 如果采用继承的实现方案, 会导致产生很多子类, 对于这种情况, 可以考虑采用桥接模式, 分析功能变化的原因, 看看是否能分离成不同的纬度, 然后通过桥接模式来分离它们, 从而减少子类的数目。

25  访问者模式(GoF的著作中划分为行为型)

定义:表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元索的类的前提下定义作用于这些元素的新操作。

本质:预留通路,回调实现。

使用步骤:【涉及名词】

Visitor:访问者口,为所有的访问者对象声明一个visit方法,用来代表为对象结构添加的功能, 理论上可以代表任意的功能。

ConcreteVisitor:具体的访问者实现对象,实现要真正被添加到对象结构中的功能.

Element: 抽象的元素对象,对象结构的顶层接口, 定义接受访问的操作。

ConcreteElement:具体元素对象, 对象结中具体的对象,也是被访问的对象,通常会回调访问者的真实功能,同时开放自身的数据供访问者使用。

ObjectStructure: 对象结构,通常包含多个被访问的对象,它可以遍历多个被访问的对象,也可以让访问者访问它的元素。可以是一个复合或是一个集合, 如一个列表或无序集合。

这个ObjectStructure 并不是我们在前面讲到的对象结构,前面一直讲的对象结构是指的一系列对象的定义结构,是概念上的东西,而ObjectStructure可以看成是对象结构中的一系列对象的一个集合,是用来辅助客户端访问这一系列对象的。

1.定义访问者接口Visitor,在接口中承诺访问ABC..等元素的方法---相当于给元素添加访问者的功能;

2.定义被访问元素的接口---抽象类Element,提供接受访问者访问的accept()方法,以Visitor为方法参数;

3.定义具体元素类ConcreteElement继承ElementegConcreteElementAConcreteElementBConcreteElementC...

4.定义访问者具体实现ConcreteVisitoregConcreteVisitor1ConcreteVisitor2...),对于要访问A元素,则方法以ConcreteElementA为参数,对B元素以此类推。

5.定义类ObjectStructure,通常在这里对元素对象进行遍历,让访问者能访问到所有的元素,该对象持有一个Element的容器,提供给客户端操作的高层接口handleRequest(Visitorvisitor)方法。提供容器添加元素的方法。

Client使用,NewObjectStructureVisitor,元素A,元素B;将AB元素加入ObjectStructure的容器中,调用handleRequest方法。

优点:好的扩展性;好的复用性;分离无关的行为。

缺点:对象结构变化很困难;破环封装。

易混模式:装饰器模式

装饰模式更多的是实现对已有功能的加强、修改或者完全全新实现;

而访问者模式更多的是实现为对象结构添加新的功能。

何时选用:

• 如果想对一个对象结构实施一些依赖于对象结构中具体类的操作,可以使用访问者模式。

• 如果想对一个对象结构中的各个元素进行很多不同的而且不相关的操作, 为了避免这些操作使类变得杂乱,可以使用访问者模式。把这些操作分散到不同的访问者对象中去,每个访问者对象实现同一类功能。

• 如果对象结构很少变动,但是需要经常给对象结构中的元素对象定义新的操作,可以使用访问者模式。


猜你喜欢

转载自blog.csdn.net/dusuanyun/article/details/52556564