《大话设计模式》——学习笔记之”行为型模式”(观察者&模板方法&命令&状态&职责链&解释器&中介者&访问者&策略&备忘录&迭代器)
观察者模式
定义: 定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己
Subject类,主题或抽象通知者
abstract class Subject{
private IList<Observer> observers = new List<Observer>();
//增加观察者
public void Attach(Observer observer){
observers.Add(observer);
}
//移除观察者
public void Detach(Observer observer){
observers.Remove(observer);
}
//通知
public void Notify(){
foreach(Observer o in observers){
o.Update();
}
}
}
Observer类,抽象观察者
abstract class Observer{
public abstract void Update();
}
ConcreteSubject类,具体主题或具体通知者
class ConcreteSubject : Subject{
private string subjectState;
//具体被观察者状态
public string SubjectState{
get {return subjectState;}
set {subjectState = value;}
}
}
ConcreteObserver类,具体观察者
class ConcreteObserver : Observer{
private string name;
private string observerState;
public ConcreteObserver(ConcreteSubject subject, string name){
this.subject = subject;
this.name = name;
}
public override void Update(){
observerState = subject.SubjectState;
Console.WriteLine("观察者{0}的新状态是{1}", name, observerState);
}
public ConcreteSubject Subject{
get {return subject;}
set {subject = value;}
}
}
客户端代码
static void Main(string[] args){
ConcreteSubject s = new ConcreteSubject();
s.Attach(new ConcreteObserver(s, "X"));
s.Attach(new ConcreteObserver(s, "Y"));
s.SubjectState = "ABC";
s.Notify();
Console.Read();
}
优点:
- 当一个对象的改变需要同时改变其他对象的时候,而且它不知道具体有多少对象有待改变时,就应该考虑使用观察者模式
- 观察者模式所做的工作就是在解除耦合,让耦合的双方都依赖于抽象,而不是依赖于具体,从而使得各自的变化都不会影响另一边的变化
- 目标和观察者不是紧密耦合的,它们可以属于一个系统中的不同抽象层次,目标所知道的仅仅是它有一系列的观察者,每个观察者实现Observer的简单接口,观察者属于哪一个具体类,目标是不知道的
模板方式模式
定义: 定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤
AbstractClass是抽象类,也就是抽象模板,定义并实现了一个模板方法,这个模板方法一般是一个具体方法,它给出了一个顶级逻辑的骨架,而逻辑的组成步骤在相应的抽象操作中,推迟到子类实现,顶级逻辑也有可能调用一些具体方法
abstract class AbstractClass{
//一些抽象行为,放到子类去实现
public void PrimitiveOperation1();
public void PrimitiveOperation2();
//模板方法,给出了逻辑的骨架,而逻辑的组成是一些相应的抽象操作,它们都推迟到子类实现
public void TemplateMethod(){
PrimitiveOperation1();
PrimitiveOperation2();
Console.WriteLine("");
}
}
AbstractClass的实现类
class ConcreteClassA : AbstractClass{
public override void PrimitiveOperation1(){
Console.WriteLine("具体类A方法1实现");
}
public override PrimitiveOperation2(){
Console.WriteLine("具体类A方法2实现");
}
}
...
客户端调用
static void Main(string[] args){
AbstractClass c;
c = new ConcreteClassA();
c.TemplateMethod();
Console.Read();
}
优点:
- 当要完成在某一细节层次一致的一个过程或一系列步骤,但其个别步骤在更详细的层次上的实现可能不同时,通常考虑用模板方法模式来处理
- 模板方法模式由一个抽象类组成,这个抽象类定义了需要覆盖的可能有不同实现的模板方法,每个从这个抽象类派生的具体类将为此模板实现新方法
命令模式
定义: 将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作
Command类,用来声明操作的接口
abstract class Command{
protected Receiver receiver;
public Command(Receiver receiver){
this.receiver = receiver;
}
abstract public void Execute();
}
ConcreteCommand类,将一个接收者对象绑定于一个动作,调用接收者相应的操作,以实现Execute
class ConcreteCommand : Command{
public ConcreteCommand(Receiver receiver) : base(receiver){
}
public override void Execute(){
receiver.Action();
}
}
Invoker类,要求该命令执行这个请求
class Invoker{
private Command command;
public void SetCommand(Command command){
this.command = command;
}
public void ExecuteCommand(){
command.Execute();
}
}
Receiver类,知道如何实施与执行一个与请求相关的操作,任何类都可能作为一个接收者
class Receiver{
public void Action(){
Console.WriteLine("执行请求!");
}
}
客户端代码,创建一个具体命令对象并设定它的接收者
static void Main(string[] args){
Receiver r = new Receiver();
Command c = new ConcreteCommand(r);
Invoker i = new Invoker();
i.SetCommand(c);
i.ExecuteCommand();
Console.Read();
}
优点:
- 较容易地设计一个命令队列
- 在需要的情况下,可以较容易地将命令记入日志
- 允许接收请求的一方决定是否要否决请求
- 可以容易地实现对请求的撤销和重做
- 由于加进新的具体命令类不影响其他的类,因此增加新的具体命令类很容易
- 命令模式把请求一个操作的对象与知道怎么执行一个操作的对象分割开
状态模式
定义:状态模式(State),当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类
State类,抽象状态类,定义一个接口以封装与Context的一个特定状态相关的行为
abstract class State{
public abstract void Handle(Context context);
}
ConcreteState类,具体状态,每一个子类实现一个与Context的一个状态相关的行为
class ConcreteStateA : State{
public override void Handle(Context context){
//设置ConcreteStateA的下一状态是ConcreteStateB
context.State = new ConcreteStateB();
}
}
class ConcreteStateB : State{
public override void Handle(Context context){
//设置ConcreteStateB的下一状态是ConcreteStateA
context.State = new ConcreteStateA();
}
}
Context类,维护一个ConcreteState子类的实例,这个实例定义当前的状态
class Context{
private State state;
//定义Context的初始状态
public Context(State state){
this.state = state;
}
//可读写的状态属性,用于读取当前状态和设置新状态
public State state{
get(return state;)
set{
state = value;
Console.WriteLine("当前状态:" + state.GetType().Name);
}
}
public void Request(){
//对请求做处理,并设置下一状态
state.Handle(this);
}
}
客户端代码
static void Main(string[] args){
Context c = new Context(new ConcreteStateA());
//不断的请求,同时改变状态
c.Request();
c.Request();
c.Request();
Console.Read();
}
好处:
- 状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况,把状态的判断逻辑转移到表示不同状态的一系列类当中,可以把复杂的判断逻辑简化
- 将特定的状态相关的行为都放入一个对象中,由于所有与状态相关的代码都存在于某个ConcreteState中,所以通过定义新的子类可以很容易地增加新的状态和转换
- 可以消除庞大的条件分支语句,通过把各种状态转移逻辑分布到State的子类之间,来减少相互间的依赖
- 当一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为时,就可以考虑使用状态模式
职责链模式
定义: 使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系,将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止
Handler类,定义一个处理请示的接口
abstract class Handler{
protected Handler successor;
public void SetSuccessor(Handler successor){ //设置继任者
this.successor = successor;
}
public abstract void HandleRequest(int request); //处理请求的抽象方法
}
ConcreteHandler类,具体处理者类,处理它所负责的请求,可访问它的后继者,如果可处理该请求就处理,否则就将该请求转发给它的后继者
ConcreteHandler1,请请求数在0到10之间则有权处理,否则转到下一位
class ConcreteHandler1 : Handler{
public override void HandleRequest(int request){
//处理此请求
if(request >= 0 && request < 10){
Console.WriteLine("{0} 处理请求 {1}", this.GetType().Name, request);
} else if(successor != null){
//转移到下一位
successor.HandleRequest(request);
}
}
}
ConcreteHandler2,当请求数在10到20之间则有权处理,否则转到下一位
class ConcreteHandler1 : Handler{
public override void HandleRequest(int request){
//处理此请求
if(request >= 10 && request < 20){
Console.WriteLine("{0} 处理请求 {1}", this.GetType().Name, request);
} else if(successor != null){
//转移到下一位
successor.HandleRequest(request);
}
}
}
客户端代码
static void Main(string[] args){
Handler h1 = new ConcreteHandler1();
Handler h2 = new ConcreteHandler2();
h1.SetSuccessor(h2);
int[] requests = {2, 5, 14};
foreach(int request in requests){
h1.HandleRequest(request);
}
Console.Read();
}
优点:
- 当客户提交一个请求时,请求是沿链传递直至有一个ConcreteHandler对象负责处理它
- 链中的对象自己并不知道链的结构,结果是职责链可简化对象的相互连接
- 可以动态地增加或修改处理一个请求的结构,增强了给对象指派职责的灵活性
- 使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系,将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止
- 有多个对象可以处理一个请求,哪个对象处理该请求事先并不知道,要在运行时刻自动确定,让请求发送者与具体处理者分离,让客户在不明确指定接收者的情况下,提交一个请求,然后由所有能处理这请求的对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止
解释器模式
定义: 给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子
AbstractExpression(抽象表达式),声明一个抽象的解释操作,这个接口为抽象语法树中所有的节点所共享
abstract class AbstractExpression{
public abstract void Interpret(Context context);
}
TerminalExpression(终结符表达式),实现与文法中的终结符相关联的解释操作,实现抽象表达式中所要求的接口,主要是一个interpret()方法,文法中每一个终结符都有一个具体终结表达式与之相对应
class TerminalExpression : AbstractExpression{
public override void Interpret(Context context){
Console.WriteLine("终端解释器");
}
}
NonterminalExpression(非终结符表达式),为文法中的非终结符实现解释操作,对文法中每一条规则R1,、R2…Rn都需要一个具体的非终结符表达式类,通过实现抽象表达式的interpret()方法实现解释操作,解释操作以递归方式调用上面所提到的代表R1、R2…Rn中各个符合的实例变量
class NonterminalExpression : AbstractExpression{
public override void Interpret(Context context){
Console.WriteLine("非终端解释器");
}
}
Context,包含解释器之外的一些全局信息
class Context{
private string input;
private string Input{
get {return input;}
set {input = value;};
}
private string output;
private string Output{
get {return output;}
set {output = value;};
}
}
客户端代码,构建表示该文法定义的语言中一个特定的句子的抽象语法树,调用解释操作
static void Main(string[] args){
Context context = new Context();
IList<AbstractExpression> list = new List<AbstractExpression>();
list.Add(new TerminalExpression());
list.Add(new NonterminalExpression());
list.Add(new TerminalExpression());
list.Add(new TerminalExpression());
foreach (AbstractExpression exp in list){
exp.Interpret(context);
}
Console.Read();
}
优点:
- 当有一个语言需要解释执行,并且可将语言中的句子表示为一个抽象语法树时,可使用解释器模式
- 可以很容易地改变和扩展文法,因为该模式使用类来表示文法规则,可使用继承来改变或扩展该文法,也比较容易实现文法,因为定义抽象语法树中各个节点的类的实现大体类似,这些类都易于直接编写
- 如果一种特定类型的问题发生的频率足够高,那么就可以考虑将该问题的各个实例表述为一个简单语言的句子,也就是说,通过构建一个解释器,该解释器解释这些句子来解决该问题(如正则表达式)
缺点:
- 解释器模式为文法中的每一条规则至少定义了一个类,因此包含许多规则的文法可能难以管理和维护,建议当文法非常复杂时,使用其他的技术如语法分析程序或编译器生成器来处理
中介者模式
定义: 用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互
Mediator类,抽象中介者类
abstract class Mediator{
public abstract void Send(string message, Colleague colleague); // 定义一个抽象的发送消息方法,得到同事对象和发送消息
}
Colleague类,抽象同事类
abstract class Colleague{
protected Mediator mediator;
public Colleague(Mediator mediator){
this.mediator = mediator;// 构造方法,得到中介者对象
}
}
ConcreteMediator类,具体中介者类
class ConcreteMediator : Mediator{
private ConcreteColleague1 colleague1;
private ConcreteColleague1 colleague1;
public ConcreteColleague1 Colleague1{
set {colleague1 = value;}
}
public ConcreteColleague2 Colleague2{
set {colleague2 = value;}
}
public override void Send(string message, Colleague colleague){
if(colleague == colleague1){
colleague2.Notify(message);
} else {
colleague1.Notify(message);
}
}
}
具体的同事对象
class ConcreteColleague1 : Colleague{
public ConcreteColleague1(Mediator mediator) : base(mediator){
}
public void Send(string message){
// 发送消息时通常是中介者发送出去的
mediator.Send(message, this);
}
public void Notify(string message){
Console.WriteLine("同事1得到消息:" + message);
}
}
class ConcreteColleague2 : Colleague{
public ConcreteColleague2(Mediator mediator) : base(mediator){
}
public void Send(string message){
// 发送消息时通常是中介者发送出去的
mediator.Send(message, this);
}
public void Notify(string message){
Console.WriteLine("同事2得到消息:" + message);
}
}
客户端调用:
static void Main(string[] args){
ConcreteMediator m = new ConcreteMediator();
//让两个具体同事类认识中介者对象
ConcreteColleague1 c1 = new ConcreteColleague1(m);
ConcreteColleague2 c2 = new ConcreteColleague2(m);
//让中介者认识各个具体同事类对象
m.Colleague1 = c1;
m.Colleague2 = c2;
c1.Send("吃过饭了吗?");
c2.Send("没有呢");
Console.Read();
}
优点:
- Mediator的出现减少了各个Colleague的耦合,使得可以独立地改变和复用各个Colleague类和Mediator
- 由于把对象如何协作进行了抽象,将中介作为一个独立的概念并将其封装在一个对象中,这样关注的对象就从对象各自本身的行为转移到它们之间的交互上来
- 中介者模式一般应用于一组对象以定义良好但是复杂的方式进行通信的场合,以及想定制一个分布在多个类中的行为,而又不想生成太多的子类的场合
- 用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显示地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互
- 对象间有许多连接,使得一个对象不大可能在没有其他对象的支持下工作,这对于应对变化是不利的
- 将集体行为封装一个单独的中介者对象来避免整个问题,中介者负责控制和协调一组对象间的交互,中介者充当一个中介以使组中的对象不再相互显示引用,这些对象仅知道中介者,从而减少了相互连接的数目
缺点:
- 由于ConcreteMediator控制了集中化,于是就把交互复杂性变为了中介者的复杂性,这就使得中介者会变得比任何一个ConcreteColleague都复杂(当系统出现了”多对多”交互复杂的对象群时,不要急于使用中介者模式,而要先反思系统在设计上是否合理)
访问者模式
定义: 表示一个作用于某对象结构中的各元素的操作,它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作
Visitor类,为该对象结构中ConcreteElement的每一个类声明一个Visit操作
abstract class Visitor{
public abstract void VisitConcreteElementA(ConcreteElementA concreteElementA);
public abstract void VisitConcreteElementB(ConcreteElementB concreteElementB);
}
ConcreteVisitor1和ConcreteVisitor2,类,具体访问者,实现每个由Visitor声明的操作,每个操作实现算法的一部分,而该算法片断乃是对应于结构中对象的类
class ConcreteVisitor1 : Visitor{
public override void VisitConcreteElementA(ConcreteElementA concreteElementA){
Console.WriteLine("{0}被{1}访问", concreteElementA.GetType().Name, this.GetType().Name);
}
public override void VisitConcreteElementB(ConcreteElementB concreteElementB){
Console.WriteLine("{0}被{1}访问", concreteElementB.GetType().Name, this.GetType().Name);
}
}
class ConcreteVisitor1 : Visitor{
...
}
Element类,定义一个Accept操作,它以一个访问者为参数
abstract class Element{
public abstract void Accept(Visitor visitor);
}
ConcreteElementA和ConcreteElementB类,具体元素,实现Accept操作
class ConcreteElementA : Element{
public override void Accept(Visitor visitor){
//充分利用双分派技术,实现处理与数据结构的分离
visitor.VisitConcreteElementA(this);
}
public void OperationA(){
}
}
class ConcreteElementB : Element{
public override void Accept(Visitor visitor){
//充分利用双分派技术,实现处理与数据结构的分离
visitor.VisitConcreteElementB(this);
}
public void OperationB(){
}
}
ObjectStructure类,能枚举它的元素,可以提供一个高层的接口以允许访问者访问它的元素
class ObjectStructure{
private IList<Element> elements = new List<Element>();
public void Attach(Element element){
elements.Add(element);
}
public void Detach(Element element){
elements.Remove(element);
}
public void Accept(Visitor visitor){
foreach (Element e in elements){
e.Accept(visitor);
}
}
}
客户端代码
static void Main(string[] args){
ObjectStructure o = new ObjectStructure();
o.Attach(new ConcreteElementA());
o.Attach(new ConcreteElementB());
ConcreteVisitor1 v1 = new ConcreteVisitor1();
ConcreteVisitor2 v2 = new ConcreteVisitor2();
o.Accept(v1);
o.Accept(v2);
Console.Read();
}
优点:
- 访问者模式适用于数据结构相对稳定的系统
- 把数据结构和作用于结构上的操作之间的耦合解脱开,使得操作集合可以相对自由地演化
- 访问者模式的目的是要把处理从数据结构分离出来,如果系统有比较稳定的数据结构,又有易于变化的算法的话,使用访问者模式就比较合适,因为访问者模式使得算法操作的增加变得容易
- 访问者模式的优点就是增加新的操作很容易,因为增加新的操作就意味着增加一个新的访问者,访问者模式将有关的行为集中到一个访问者对象中
- 访问者增加具体的Element是困难的,但增加依赖于复杂对象结构的构件的操作就变得很容易,仅需增加一个新的访问者即可在一个对象结构上定义一个新的操作
缺点:
- 使增加新的数据结构变得困难了(大多数情况下并不需要访问者模式,因为很难找到数据结构不变化的情况)
策略模式
定义:定义了算法家族,分别封装起来,让它们之间可以相互替换,此模式让算法的变化不会影响到使用算法的客户
Strategy类,定义了所有支持的算法的公共接口
//抽象算法类
abstract class Strategy{
//算法方法
public abstract void AlgorithmInterface();
}
ConcreteStrategy类,封装了具体的算法或行为,继承于Strategy
//具体算法A
class ConcreteStrategyA : Strategy{
//算法A实现方法
public override void AlgorithmInterface(){
Console.WriteLine("算法A实现");
}
}
//算法B
...
Context,用一个ConcreteStrategy来配置,维护一个对Strategy对象的引用
//上下文
class Context{
Strategy strategy;
//初始化时,传入具体的策略对象
public Context(Strategy strategy){
this.strategy = strategy;
}
//上下文接口
//根据具体的策略对象,调用其算法的方法
public void ContextInterface(){
strategy.AlgorithmInterface();
}
}
客户端代码
static void Main(string[] args){
Context context;
context = new Context(new ConcreteStrategyA());
context.ContextInterface();
context;
context = new Context(new ConcreteStrategyB());
context.ContextInterface();
...
}
优点:
- 策略模式是一种定义一系列算法的方法,从概念上来看,所有这些算法完成的都是相同的工作,只是实现不同,它可以以相同的方式调用所有的算法,减少了各个算法类与使用算法类之间的耦合
- 策略模式的Strategy类层次为Context定义了一系列的可供重用的算法或行为,继承有助于析取出这些算法中的公共功能
- 策略模式的优点是简化了单元测试,因为每个算法都有自己的类,可以通过自己的接口单独测试
- 继承提供了一种支持多种算法或行为的方法,我们可以生成一个类A的子类B、C、D,从而给它以不同的行为,但这样会将行为硬行编制到父类A当中,而将算法的实现与类A的实现混合起来,从而使得类A难以理解、难以维护和难以扩展,而且还不能动态地改变算法
- 将算法封装在独立的策略Strategy类中使得你可以独立于其类A改变它,使它易于切换、易于理解、易于扩展
- 当不同的行为堆砌在一个类中时,就很难避免使用条件语句来选择合适的行为,将这些行为封装在一个个独立的Strategy类中,可以在使用这些行为的类中消除条件语句
- 策略模式是用来封装算法的,但在实践中,我们发现可以用它来封装几乎任何类型的规则,只要在分析过程中听到需要在不同时间应用不同的业务规则,就可以考虑使用策略模式来处理这种变化的可能性
- 定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换,本模式使得算法可独立于使用它的客户而变化
备忘录模式
定义:备忘录(Memento):在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样以后就可将对象恢复到原先保存的状态
发起人(Originator)类
class Originator{
private string state;
public string State{
//需要保存的属性,可能有多个
get{ return state; }
set{ state = value; }
}
//创建备忘录,将当前需要保存的信息导入并实例化出一个Memento对象
public Memento CreateMemento(){
return (new Memento(state));
}
//恢复备忘录,将Memento导入并将相关数据恢复
public void SetMemento(Memento memento){
state = memento.State;
}
public void Show(){
Console.WriteLine("State = " + state);
}
}
备忘录(Memento)类
class Memento{
private string state;
//构造方法,将相关数据导入
public Memento(string state){
this.state = state;
}
//需要保存的数据属性,可以是多个
public string State{
get { return state; }
}
}
管理者(Caretaker)类
class Caretaker{
private Memento memento;
public Memento Memento{
get { return memento; }
set { memento = value; }
}
}
客户端程序
static void Main(string[] args){
//Originator初始状态,状态属性为"On"
Originator o = new Originator();
o.State = "On";
o.Show();
//保存状态,隐藏了Originator的实现细节
Caretaker c = new Caretaker();
c.Memento = o.CreateMemento();
o.State = "Off";
o.Show();
//恢复原初始状态
o.SetMemento(c.Memento);
o.Show();
Console.Read();
}
优点:
- Memento模式适用于功能比较复杂的,但需要维护或记录属性历史的类,或者需要保存的属性只是众多属性中的一小部分时,Originator可以根据保存的Memento信息还原到前一状态
- 使用备忘录可以把复杂的对象内部信息对其他的对象屏蔽起来
- 在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样以后就可将该对象恢复到原先保存的状态
- 可以避免暴露一些只应由对象A管理却又必须存储在对象A之外的信息,备忘录模式把可能很复杂的对象A的内部信息对其他对象屏蔽起来,从而保持了封装边界
迭代器模式
定义: 提供一种方法顺序访问一个聚合对象中各个元素,而又不暴露该对象的内部表示
迭代接口
public interface IEnumerator{
object Current{
get;
}
bool MoveNext();
void Reset(); //恢复初始化指向的位置,该位置位于集合中第一个元素之前
}
IEnumerable公开枚举数,该枚举数支持在非泛型集合上进行简单迭代
public interface IEnumerable{
IEnumerator GetEnumerator(); //返回一个循环访问集合的枚举数
}
客户端代码
static void Main(string[] args){
IList<string> a = new List<string>();
a.Add("..");
...
foreach (string item in a){
Console.WriteLine("{0} ...", item);
}
//foreach in中做的操作如下:
// IEnumerator<string> e = a.GetEnumerator();
// while(e.MoveNext()){
// Console.WriteLine(...);
// }
Console.Read();
}
优点:
- 为遍历不同的聚集结构提供如开始、下一个、是否结束、当前哪一项等统一的接口
- 迭代器模式分离了集合对象的遍历行为,抽象出一个迭代器类来负责,这样既可以做到不暴露集合的内部结构,又可让外部代码透明地访问集合内部的数据
- 需要访问一个聚集对象,而且不管这些对象是什么都需要遍历的时候,就应该考虑用迭代器模式
- 当需要对聚集有多种方式遍历时(从前往后/从后往前),可以考虑用迭代器模式