面向对象三大特性:封装、继承、多态
面向对象设计原则仍然是面向对象思想的体现
单一职责原则与接口隔离原则体现了封装的思想
开放封闭原则体现了对象的封装与多态
里氏替换原则是对对象继承的规范
依赖倒置原则是多态与抽象思想的体现
最小知识原则(迪米特法则)是封装基础强调松耦合
定义:对于一个类而言,应该仅有一个引起他变化的原因
意义:如果一个类承担的职责过多就等于把这些类耦合在一起,一个职责的变化可能会削弱或者抑制这个类完成其他职责的能力。这种耦合会导致脆肉的设计,当变化时,设计会遭受到意向不到的破坏
软件设计真正要做的许多内容,就是发现职责并把那些职责相互分离
判断是否应该分离类:看是否能想到多于一个动机去改变这个类,如果是那么这个类具有多于一个的职责
定义:软件实体(类、模块、函数等),应该可以扩展,但是不可以修改
就是对扩展开放,对更改封闭
面对需求,对程序的改动是通过增加新的代码进行的,而不是更改现有的代码
意义:设计面对需求改变却可以保持相对稳定,可以使系统在第一个版本以后不断推出新的版本
核心思想:在规模小时想到发生更大变化的可能,等到变化发生时可以立即采取行动
注意:无论模块多么封闭,都会存在一些无法对之封闭的变化,既然不可能完全封闭,设计人员必须对于自己设计得模块应对应那种变化做出选择,我们必须先猜出最有可能发生的变化种类,然后构造抽象来隔离那些变化
就是说在最初编写代码时,假设变化不会发生,当变化发生时我们就创建抽象来隔离以后同类型的变化
工作中:
定义:
针对接口编程,不要对实现编程
高层模块不应该依赖底层模块,两个都依赖于抽象,如果不管高层还是底层都依赖于抽象(接口或者抽象类)只要接口是稳定的,那么任何一个更改都不用担心其他收到影响,无论高层模块还是底层模块都可以很容易的被复用
倒转:高层模块依赖底层模块
依赖倒转其实可以说是面向对象程序设计的标志,用那种语言编写程序不重要,如果编写时都考率的是如何针对抽象编程而不是针对细节编程即程序中所有的依赖关系都是终止于抽象类或者接口,那就是面向对象程序设计,反之就是过程化程序设计了
定义:子类型必须能够替换调他们的父类型
一个软件实体如果使用的是一个父类的话,那么一定适用于其子类,而且他察觉不出父类对象和子类对象的区别,也就是说,在软件里面,把父类都替换成他的子类,程序的行为没有变化
意义:只有当子类可以替换调父类,软件单位功能不受影响时,父类才能真正被复用,而子类也能在父类的基础上添加新的行为
也可以说由于子类型的可替换性才使得使用父类型的模块再无修改的情况下可以扩展
依赖倒转:就是除了约定好的接口谁也不依靠谁
某种程度来讲,接口隔离原则可以看做是接口层的单一职责原则。
定义:
单一职责与接口隔离的区别:
单一职责原则注重的是职责;而接口隔离原则注重对接口依赖的隔离。
单一职责原则主要是约束类,其次才是接口和方法,它针对的是程序中的实现和细节; 而接口隔离原则主要约束接口,主要针对抽象,针对程序整体框架的构建。
隔离:权限控制也是一种隔离
接口隔离原则 ,就是要求在接口中尽量少公布public方法,接口是对外的承诺,承诺越少对系统的开发越有利
接口要高内聚
高内聚:提高接口、类、模块的处理能力,减少对外的交互。
一个接口只服务于一个子模块或业务逻辑
通过业务逻辑压缩接口中的public方法
定义:如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。
如果其中一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用
使用前提:在类的结构设计上,每一个类都应当尽量降低成员的访问权限。通俗的说:一个类包装号自己的private 状态,不需要让别的类知道的字段或行为就不要公开
迪米特法则其根本思想,是强调了类之间的松耦合
意义:类之间的耦合越弱,越有利于复用,一个处在弱耦合的类被修改,不会对有关系的类造成波及。
信息的隐藏促进了软件的复用
对象的继承关系是在编译时就定义好了,所以无法在运行时改变从父类继承的实现,子类的实现与父类有着非常紧密的依赖关系,以至于父类实现中的任何变化必然会导致子类发生变化,当你需要复用子类时,如果继承下来的实现不适合解决新的问题则父类必须重写或者被其他更合适的类替换,这种依赖关系限制了灵活性并最终限制了复用性
定义:尽量使用合成/聚合,尽量不要使用类继承
聚合:表示一种弱的拥有关系。A对象包含B对象,但是B对象不是A对象的一部分
合成(组合):合成则是一种强拥有关系,体现了严格的部分与整体的关系,部分和整体的生命周期一样
优点:优先使用对象的合成/聚合将有助于你保持每个类被封装,并被集中在单个任务上,这样类和类继承层次会保持在较小规模,并且不太可能增长为不可控的庞然大物
总体来说设计模式分为三大类:
创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
定义:使用工厂类来判断需要创建那个实例对象
工厂:创建对象的类
对象的类型由子类决定
不满足开放封闭原则,有新的需求可能更改工厂类
定义:定义了算法家族,分别进行封装起来,使算法可以相互替换,此模式使算法变化,不会影响使用算法的客户
实现:使用一个算法接口,每个策略去实现算法的接口,可以由使用者确定不同的策略对象,调用策略对象的方法 就像多态父类指针指向子类对象
策略模式可以和简单工厂类结合使用,由简单工厂决定实现哪一个策略对象
策略模式是一种定义一系列算法的方法,从概念上讲所有算法完成的是相同的工作只是实现不同,可以以相同的方式调用所有的算法,减少使用算法类与算法类的耦合
策略模式优点:
使用:策略模式就是用来封装算法的,但是在使用上,策略模式可以用来封装几乎任何类型的规则,只要在分析的过程中听到需要在不同时间应用不同的业务的规则就可以使用策略模式处理这种变化的可能性。
在使用中选择具体实现的职责可以由客户端对象承担,转给策略模式的上下文对象,但是也有使用简单工厂承担策略的选择。
任何变更都是需要成本的,优秀的开发者可以减少变动的成本
作用把所需要的功能按照正确的顺序串联起来进行控制
定义:动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生产子类更为灵活
component 是定义一个对象接口,可以给这些对象动态地添加职责,concrete component(具体组成部分)是定义一个具体的对象,也可以给这个对象添加一些职责。decorator(装饰者) 装饰抽象类,继承component(组成)从外来类扩展component类功能,但对与组成类来说不需要知道装饰类的存在,置于concrete decorator,具体装饰起到给组成添加职责的功能
将组成类当做参数传入装饰器类,装饰模式是利用设置组成函数,对对对象进行包装
每个装饰对象的实现就和如何使用这个对象分离开了,每个装饰对象只关心自己的功能,不需要关心如何被添加到对象链中
装饰模式是为已有功能动态添加更多功能的一种方式,可以在更上层的设计中加入装饰功能
把类中装饰功能从类中搬移去除(或移动到外一层)可以简化原有的类
有效的把类的核心职责和装饰功能区分开了,而且可以除去相关类中重复的装饰的逻辑
定义:为其他对象提供一种代理以控制对这个对象的访问
实现:
subject给代理类与真正的类定义统一的接口,实体的类完成实际的功能,代理类判断实体类的对象,在同样的接口上不实现接口,只调用对象的功能
应用:
实质:在访问对象时引入一定程度的间接性
引入:简单工厂违反了开放封闭原则,在增加功能时,需要在工厂内部修改判读逻辑新增实例化,简单工厂的优势在于工厂类中包含了必要的逻辑判断,根据客户端的选择条件动态实例化相关的类,对于客户端来说,去除了与具体产品的依赖
定义:定义一个用于创建对象的接口,让子类决定实例化哪一个类,工厂方法使一个类的实例化延迟到其子类
实现:具体产品的接口,实现具体产品,定义工厂接口,实现具体你的工厂方法返回具体的产品对象
优势:工厂方法模式实现时,客户端需要决定实施化哪一个工厂来实现具体对象,选择判断还存在,也就是说工厂方法把简单工厂的内部逻辑判断转移到客户端进行,修改时修改客户端就可以了
工厂方法克服了简单工厂违背开放-封闭原则的缺点,又保证了封装创建过程的优点
缺点:每增加一个产品,就要对应新的工厂类
定义:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象
实现:定义接口克隆自身对象的方法,具体原型类实现一个克隆自身的操作,使用时让一个原型类克隆自身,从而创建一个新的对象
优势:
一般在初始化信息不发生变化的情况下,克隆是最好的方法,既隐藏了对象创建的细节,又对性能是显著提高
不用重新初始化对象,而是动态地获得对象运行时的状态
使用时主要深浅拷贝
所有重复统一的事情可能会出错而且一旦出错难以改正
既然使用了继承,并且肯定这个继承有意义,就应该要成为字类的模板,所有重复的内容都应该上升到子类,而不是让子类重复;
再详细设计中如果细节不同,可以使用模板方法实现,在细节中使用虚函数,由具体使用的子类去重写
定义:定义一个操作中的算法骨架,而将一些步骤延迟到子类中(可以通过虚函数的方式),模板方法使得子类可以不改变一个算法的结构就可以重新定义某些算法的特定步骤
实现:父类中小细节可以使用虚函数替代,在子类中实现特定的虚函数
优势:模板方法模式是通过把不变行为转移到超类中,去除子类中的重复代码来体现他的优势,模板方法提供了一个很好的代码复用平台,当不变的和可变的行为在方法的子类实现中混合在一起的时候,不变的行为就会在子类中重复出现,我们通过模板方法模式把这些行为,搬移到单一的地方,这样就帮助子类摆脱重复的不变行为的影响
定义:为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用
作用:可以为难以维护的老系统开发一个新的外观类与老系统交互
实现:知道仔细听负责处理那些请求,将客户的请求代理给适当的子系统对象,子系统结合实现不同类的功能,处理外观类给的任务,子类中没有外观类的信息
注意:外观类,它需要了解所有的子系统的方法属性,尽心组合,以备外界调用,客户可以不了解子系统的具体情况,只了解外观者就可以了
三层架构:数据访问层、业务逻辑层,表示层
完成依赖倒转原则
将一个复杂对象的构建与表示进行分离,使得同样的构建过程可以创建不同的表示,建造者模式可以将产品内部表现和产品生成分开,在使用了建造者模式,那么用户就只需要指定需要建造的类型就可以得到他们,具体建造过程和细节就不需要知道了
定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示
使用:指挥者,构建使用建造者的接口,建造者为具体对象的各个部件指定的抽象接口,具体建造者实现各个部件
用途:用于创建一些复杂的对象,对象内部构建间的建造书序通常是稳定的,但对象内部的构建通常面临复杂变化
建造者模式的好处就是使得建造代码与表示代码分离,由于建造者隐蔽了该产品是如何组装的,所以若是需要改变一个产品的内部表示,只需要再定义一个具体的建造者就好了
通过编译约束
定义:定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象,这个主题对象在状态发生变化时,会通知所有观察者对象,使他们可以自动更新自己
观察者模式中分为两个角色:观察者和通知者
通知者:把所有对观察者对象的引用保存在一个聚合里。提供增加,减少,通知功能
观察者:得到被通知的对象时通知时更新自己的功能
特点:是依赖倒转原则的一种体现,基本功能是解耦合,具体的观察者需要根据更新功能更新自己,也是一种对象那托管的手段
缺点:通知者会依赖于观察者,观察者不完成更新功能通知者就不能完成操作
补充:
遇到问题不要只想着靠时间摆平一切,例如访问数据库是使用对象实例进行访问,为了对象访问不同的数据库,可以使用工厂方法模式的思想
定义一个用于创建对象的接口,让子类决定实例化哪一个类
注意:在独立项目数据访问时尽量工厂类将业务逻辑层与数据访问进行解耦
定义:提供一个创建一系列相关或相互依赖对象的接口,而无需指定他们具体的类
实现:抽象工厂接口,它里面应该包含所有类创建的抽象法
抽象工厂:好处
注意一个方法过长是坏味道(就是垃圾代码)
一个方法有很多判断分支,意味着他的责任过大了,无论是任何状态的改变,都需要通过它,面向对象设计其实就是希望做到代码的责任分解
逻辑分支过长,其实是违反了开放封闭原则
主要完成:把分支变成一个一个的类,增加时不会影响其他的类,状态变化在各自的类中完成
定义:当一个对象的内在状态改变时允许改变其他行为,这个对象看起来像改变了其类
把状态的判断逻辑转移到表示不同的一系列类中,可以把复杂逻辑简单化
实现:定义一个接口以封装一个特定状态的相关行为,每个子类实现一个相关行为,定义一个维护状态的类中有完成该状态代表行为的函数和决定当前状态的属性和设置当前状态的方法
优点:将特定状态的行为局部化,并将不同状态的行为分割开来
通俗的讲,是将特定的状态相关的行为都放入一个对象中,由于所有与状态相关的代码都存在于状态类中,所以通过定义新的子类可以很容易的增加新的状态和转换目的消除庞大的条件分支语句
意义:状态模式通过把各种状态转移逻辑封闭到状态子类中,来减少相互依赖
使用:当一个对象的行为取决于它的状态,并且必须在运行时刻根据状态改变它的行为时,就可以考虑使用状态模式
定义:将一个类的接口转换成客户希望的另外一个接口,适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作
适配器可以分为类适配器与对象适配器,类适配器是依赖于多重继承完成的
在软件开发中系统的数据和行为都正确,但是接口不符时,可以考虑适配器模式,目的是使控制范围之外的一个原有对象与某个接口匹配。适配器模式主要应用于希望复用一些现存的类,但是接口又与复用环境要求不一致的情况
实现:在适配器类中定于需要适配的类的对象,定义目标方法,在目标方法上调用适配的方法
使用:想使用一个已经存在的类,但是他的接口,也就是他的方法和我的要求不想同时,就应该考虑适配器模式,例如两个类所做的是相同或者相似,但是具有不同的接口时要使用他,可以统一接口,更简单更直接更紧凑,在双方都不太容易修改时在使用适配器模式适配
注:乱使用设计模式不如不使用设计模式,能在预防阶段解决不在设计后使用特殊方式解决
定义:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样以后就可以将该对象回复到原先保存的状态
实现:发起者负责创建、设置、恢复备忘录对象,备忘录类负责储存状态信息,防止其他人访问,管理者得到或者设置备忘录将备忘录成员设为私有成员
使用:需要维护或者记录属性历史的类,或者需要保存的属性只是众多属性中的一小部分时(区分使用栈保存全部状态)
解决整体与部分可以被一致对待的问题
定义:将对象组合成树型结构以表示“部分-整体”的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性
实现:组合中的统一接口,实现所有类共有接口的默认行为,定义枝结点继承实现所有功能(添加和删除部件),定义叶结点完成固有功能
使用:需求中是体现部分与整体层次结构时,以及希望用户可以忽略组合对象与单个对象的不同,统一的使用组合结构中所有对象时,就应该考虑组合模式了
好处:组合模式可以让客户一致的使用组合结构和单个对象
基本对象可以被组合成更复杂的的组合对象,组合对象右可以被组合,不断递归下去
需要对聚集有多种方式遍历,为遍历不同的聚集结构提供如开始,下一个,是否结束,当前哪一项的接口
定义:提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示
实现:用于定义得到开始对象,得到下一个对象,判断是否到结尾,当前对象等抽象方法,统一接口
迭代器模式就是分离了集合对象的遍历行为,抽象出一个迭代器来负责这样既可以做到不暴露集合内部结构,又可以让外部代码透明的访问集合内部的数据
实现方式:类的构造方法设为私有的,所有的类都有构造方法,不遍码则系统默认生成构造方法,若有显示定义的构造方法,默认的构造就会失败
客户端不考虑是否实例化的问题,把判断是否实例化的问题交给这个类处理
定义:保证一个类仅有一个实例,并提供一个访问它的全局访问点
注意:通常我们可以让一个全局变量使得一个对象被访问,但是不能防止你实例化多个对象,一个最好的办法就是让类自身负责保存它的唯一实例,这个类可以保证没有其他实例可以被创建,并且它可以提供一个访问实例的方法
实现:构造方法私有化,隔离new功能,提供一个类实例的唯一全局访问点(有时要考虑线程安全)
多线程时要注意线程安全,在使用实例时同时仅允许一个线程访问
线程安全多使用双层锁定的方法:
静态初始化方法,程序在静态初始化时,可以不同过开发人员显示的编写线程安全代码,就可以解决线程安全模式
单例模式的懒汉式和饿汉式
饿汉式:静态初始化方式在自己被加载时就将自己实例化
懒汉式:在对象第一次使用时就初始化
饿汉式:类一加载就实例化,会占用系统资源
懒汉式:有线程安全问题需要双层判断处理才可以保证线程安全
使用合成/聚合复用原则
定义:将抽象部分与它实现部分分离,使他们都可以独立变化
注:抽象与实现分离,并不是说,让抽象类与其派生类分离,因为没有意义,实现是指的是抽象类和它的派生类用来实现自己的对象
同一种事务可以按照不同规则进行抽象分类
意义:不同的实现有很多种,桥接模式的核心意图就是将实现独立处理,让实现各自变化,使得每种实现变化不会影响其他实现,从而达到应对变化的目的
实现:一个事务按照多种方式抽象,不同抽象间桥接,减少单一变化的影响
实现系统可能有多角度分类每一种分类都有可能变化,那么就把这种多角度分类出来使得独立变化,减少耦合
行为请求者与行为实现者这种关系的实现
定义:将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销操作
实现:有四个关键因素
命令的执行者,提供执行什么命令,命令执行,可以保存命令,记录命令等
命令:命令接收者绑定行为
命令行为:知道如何执行一个命令与请求的相关操作,提供命令活动
使用:创建活动对象,创建命令绑定行为,创建执行者,执行命令
优点:
敏捷开发原则:不要为代码添加基于猜测的,实际不需要的功能。如果不清楚一个系统是否需要命令模式,一般就不要急着去实现它,事实上,在需要的时候通过重构实现这个模式并不困难,只有真正需要如撤销、恢复等操作时在重构出来即可
同一个请求在多个对象中有有一定关联,请求进行传递直到请求完成为止
可以解决大量分支代码难以维护的问题
定义:使得多个对象都有机会处理这个请求,从而避免请求的发送者和接收者之间的耦合关系,将这个对象连城一条链,并且沿着这这条链传递该请求,直到有一个对象处理它为止
实现:定义一个处理请求接口 完成设置下一个对象功能,加上处理功能(包括处理不了转发下一个人)
优点:接收者和发送者都没有对方明确的信息,且链中的对象自己也并不知道链结构,结果是职责链可以简化对象相互链接,他们仅需要保持一个指向后继者的应用,而不需要保持它所有的候选接受者的引用
可以随时的增加或者修改处理一个请求的结构,增强了对象指派的灵活性
注意:一个请求可能到了末端都得不到处理,因为没有正确设置得不到处理,设计时要注意
引入:尽管将一个系统分割成许多对象通常可以增加其可复用性,但是对象间相互连接的激增又会降低其复用性,出现系统依赖性,大量的连接使得一个对象不可能在没有其他对象的支持下工作,系统表现为一个不可分割的整体
符合迪米特法则,没有直接联系就不必建立直接联系
定义:用一个中介对象来封装一系列的对象交交互,中介者使各对象不需要显式的相互引用,从而使得其耦合松散,而且可以独立的改变他们之间的交互
实现:抽象中介者,定义了同事对象到中介者对象的接口,具体中介者对象,实现抽象类的方法,它需要知所有具体同事类,并从具体同事接收消息,向具体同事对象发出命令
具体同事类,每一个具体同事只要知道自己的行为,而不了了解其它同事类的情况,但他们却都认识中介者对象
在中介者中完成消息转发,中介者知道如何给另一个对象通信
注意:中介者模式很容易在系统中应用,也很容易在系统中误用,当系统出现了“多对多”交互复杂的对象群时,不要急于使用中介者模式
优点:
多任务商家可以通过用户ID区分完成实例的共享
定义:运用共享技术有效地支持大量细粒度的对象
享元类,定义所有享元类的超类或者接口,通过这个接口,享元类可以接收并作用于外部状态
有继承完成享元的类,增加类内部存储空间
有不需要共享,只是使得共享成为可能,但是不强制共享
定义一个享元工厂,创建并且管理享元对象,主要是用来确保合理的共享元对象,当用户请求一个享元时,享元工厂对象提供一个已经创建的实例或者创建一个(如果实例不存在)
编程中注意内部状态与外部状态
内部状态:享元对象内部并且不会随环境改变的共享部分
外部状态:随着环境而改变的,不可以共享的状态就是外部状态
意义:享元模式可以避免大量非常相似类的开销1,在程序设计中,有时需要生成大量细粒度的类实例来表示数据,如果能发现这些实例除了几个参数以外基本上都是相同的,有时就能够受到大幅度的减少需要实例化的类的数量,如果能把那些参数转移到类实例的外面,在方法调用时将他们传递进来,就可以通过共享大幅度地减少单个实例的数目
使用:如果一个应用程序使用了大量的对象,而大量的这些对象造成了很大的存储开销时就应该考虑使用;还有就是对象大多数状态可以外部状态,如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象,此时可以考虑使用享元模式
就像linux 文件计数也是享元的体现
享元模式需要一个记录系统所有享元记录的享元表
定义:给定一个语言,定义它的文法的一种表示并定义一个解释器,这个解释器使用该表示来解释语言中的句子
使用:一种特定类型的问题发生的频率足够高,那么可能就值得将该问题的各个实例表述为一个简单地句子,这样就可以构建一个解释器,该解释器通过解释这些句子来解决该问题
例如:正则表达式
实现:包含解释器之外的的一些全局信息的类
抽象表达式,声明一个抽象的解释操作,接口为抽象语法树中所有节点共享
终结符表达式,实现与文法中的终结符相关联的解释操作
非终结符表达式,为文法中的非终结符实现解释操作对文法中的每一条规则,都需要一个具体的非终结符表达式类
意义:当有一个语言需要解释执行,并且你可以将该语言中的句子表示为一个抽象语法树时,可使用解释器模式
优点:容易地改变和扩展文法,因为该模式使用类来表示文法规则,可以使用继承来改变或者扩展该文法,也比较容易实现文法,因为定义抽象语法树中的各个节点的类的实现大体类似,这些类都易于直接编写
缺点:解释器模式为文法中的每一条规则至少定义一个类,因此包含许多规则的文法可能难以管理和维护
当文法非常复杂时,使用其他的技术如语法分析程序或者编译器来处理
双分派技术:在得到执行的操作决定于请求的种类和两个接收者类型,编程技术,可以使用参数决定第一次指派,在根据第一个操作的参数对象传入参数决定第二次分派,编程中使用函数重载可以简化分支语句
定义:表示一个作用于某个对象结构中的各元素的操作,它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作
实现:
被访问者:使用类的元素,使用访问者作为参数,执行访问者功能向访问者功能传入被访问者完成功能,要求数据结构相对稳定(因为访问者需要针对被访问者设计),定义行为接口,在其中直接定义访问者
访问者:定义针对被访问者类型集合定义访问操作,执行功能
访问者的目的是把数据处理与数据结构分离出来
使用:比较稳定的数据结构,又有易于变化的算法的话,使用访问者模式合适,访问者模式使得算法操作的增加变得更加容易
优点:增加新的操作很容易,增加新的操作相当于增加新的访问者,访问者将有关行为封装到访问者对象中了
缺点:增加新的数据结构变得困难