Chapter3 单一职责原则

  • 单一职责原则(SRP):就一个类而言,应该仅有一个引起它变化的原因。

    Chapter4 开放-封闭原则

  • 开放-封闭原则:软件实体(类、模块、函数等)应该可以扩展,但是不可修改。

    Chapter5 依赖倒转原则

  • 依赖倒转原则:

1.高层模块不应该依赖底层模块。两个都应该依赖抽象。
2.抽象不应该依赖细节,细节应该依赖于抽象。

里氏替换原则

  • 里氏替换原则(LSP):子类型必须能够替换掉他们的父类型。

    Chapter6 装饰模式

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

Component是定义一个对象接口,可以给这些对象动态地添加职责。ConcreteComponent是定义了一个具体的对象,也可以给这个对象添加一些职责。Decorator,装饰抽象类,继承了Component,从外类来扩展Component类的功能,但对于Component来说,是无需知道Decorator的存在的。
至于ConcreteDecorator就是具体的装饰对象,起到给Component添加职责的功能。

Component类

1
2
3
4
abstract class Component
{
public abstract void Operation();
}

ConcreteComponent类

1
2
3
4
5
6
7
class ConcreteComponent : Component
{
public override void Operation()
{
Console.WriteLine("具体对象的操作");
}
}

Decorator类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
abstract class Decorator : Component
{
protect Component component;
public void SetComponent(Component component)
{
this.component = component;
}

public override void Operation()
{
if(component != null)
{
component.Operation();
}
}
}

ConcreteDecoratorA类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class ConcreteDecoratorA : Decorator
{
//本类的独有功能,以区别于ConcreteDecoratorB
private string addedState;

public override void Operation()
{
//首先运行原Component的Operation(),再执行本类的功能,
//如addedState,相当于对原Component进行了装饰。
base.operation();
addedState = "New State";
Console.WriteLine("具体装饰对象A的操作");
}
}

class ConcreteDecoratorB : Decorator
{
public override void Operation()
{
base.operation();
AddedBehavior();
Console.WriteLine("具体装饰对象B的操作");
}

private void AddedBehavior()
{
}
}

客户端代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
static void Main(string[] args)
{
ConcreteComponent c = new ConcreteComponent();
ConcreteDecoratorA d1 = new ConcreteDecoratorA();
ConcreteDecoratorB d2 = new ConcreteDecoratorB();

//装饰的方法是:首先用ConcreteComponent实例化对象c,
//然后用ConcreteDecoratorA的实例化对象d1来包装c,
//再用ConcreteDecoratorB的对象d2包装d1,
//最终执行d2的Operation()
d1.SetComponent(c);
d2.SetComponent(d1);
d2.operation();

Console.Read();
}

Chapter7 代理模式

代理模式(Proxy):为其他对象提供一种代理以控制对这个对象的访问。

Subject类,定义了 RealSubject 和 Proxy 的共用接口,这样就在任何使用RealSubject的地方都可以使用Proxy。

1
2
3
4
abstract class Subject
{
public abstract void Request();
}

RealSubject类,定义Proxy所代表的真实实体。

1
2
3
4
5
6
7
class RealSubject : Subject
{
public override void Request()
{
Console.WriteLine("真实的请求");
}
}

Proxy类,保存一个引用使得代理可以访问实体,并提供一个与Subject的接口相同的接口,这样代理就可以用来替代实体。

1
2
3
4
5
6
7
8
9
10
11
12
class Proxy : Subject
{
RealSubject realSubject;
public override void Request()
{
if(realSubject == null
{
realSubject = new RealSubject();
}
realSubject.Request();
}
}

客户端代码

1
2
3
4
5
6
7
static void Main(string[] args)
{
Proxy proxy = new Proxy();
proxy.Request();

Console.Read();
}

使用场景

  • 1.远程代理,也就是为一个对象在不同的地址空间提供局部代表。这样可以隐藏一个对象存在于不同地址空间的事实。
  • 2.虚拟代理,是根据需要创建开销很大的对象。通过它来存放实例化需要很长时间的真实对象。
  • 3.安全代理,用来控制真实对象访问时的权限。一般用于对象应该有不同的访问权限的时候。
  • 4.智能指引,是指当调用真实的对象时,代理处理另外一些事。如计算真实对象的引用次数,这样当该对象没有引用时,可以自动释放它;或当第一次引用一个持久对象时,将它装入内存;或在访问一个实际对象前,检查是否已经锁定它,以确保其他对象不能改变它。它们都是通过代理在访问一个对象时附加一些内务处理。

    Chapter8 工厂方法模式

  • 工厂方法模式(Factory Method): 定义一个用于创建对象的接口,让子类决定实例化哪个类。工厂方法使一个类的实例化延迟到其子类。

    简单工厂vs.工厂方法

  • 简单工厂模式的最大优点在于工厂类中包含了必要的逻辑判断,根据客户端的选择条件动态实例化相关的类,对于客户端来说,去除了与具体产品的依赖。
  • 工厂方法模式实现时,客户端需要决定实例化哪一个工厂来实现运算类,选择判断的问题还是存在的,也就是说,工厂方法把简单工厂的内部逻辑判断移到了客户端代码来进行。

    案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    //雷锋工厂
    interface IFactory
    {
    LeiFeng CreateLeiFeng();
    }
    //学雷锋的大学生工厂
    class UndergraduateFactory:IFactory
    {
    public LeiFeng CreateLeiFeng()
    {
    return new Undergraduate();
    }
    }
    //社区志愿者工厂
    class VolunteerFactory:IFactory
    {
    public LeiFeng CreateLeiFeng()
    {
    return new Volunteer();
    }
    }

    Chapter9 原型模式

    原型模式(Prototype):用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

    案例

  • 原型类
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    abstract class Prototype
    {
    private string id;

    public Prototype(string id)
    {
    this.id = id;
    }

    public string Id
    {
    get { return id; }
    }

    public abstract Prototype Clone();
    }
  • 具体原型类
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    class ConcretePrototype1:Prototype
    {
    public ConcretePrototype1(string id) : base(id)
    {

    }

    public override Prototype Clone()
    {
    //创建当前对象的浅表副本。
    //方法是创建一个新对象,然后将当前对象的非静态字段复制到该新对象。
    //如果字段是值类型的,则对该字段执行逐位复制。
    //如果字段是引用类型,则复制引用但不复制引用的对象;
    //因此,原始对象及其副本引用同一对象。
    return (Prototype)this.MemberwiseClone();
    }
    }
  • 客户端代码
    1
    2
    3
    4
    ConcretePrototype1 p1 = new ConcretePrototype1("I");
    ConcretePrototype1 c1 = (ConcretePrototype1)p1.Clone();
    //克隆类ConcretePrototype1的对象p1就能得到新的实例c1
    Console.WriteLine("Cloned : {0}",c1.Id);

    Chapter10 模板方法模式

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

    案例

  • AbstractClass

AbstractClass是抽象类,其实也就是一抽象模板,定义并实现了一个模版方法。
这个模版方法一般是一个具体方法,它给出了一个顶级逻辑的骨架,而逻辑的组成步骤在相应的抽象操作中,推迟到子类实现。
顶级逻辑也有可能调用一些具体方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
abstract class AbstarctClass
{
//一些抽象行为,放到子类去实现。
public abstract void PrimitiveOperation1();
public abstract void PrimitiveOperation2();

//模板方法,给出了逻辑的骨架,而逻辑的组成是一些相应的抽象操作,它们都推迟到子类实现。
public void TemplateMethod()
{
PrimitiveOperation1();
PrimitiveOperation2();
Console.WriteLine("");
}
}
  • ConcreteClass

实现父类所定义的一个或多个抽象方法。
每一个AbstractClass都可以有任意多个ConcreteClass。
与之对应,而每一个ConcreteClass都可以给出这些抽象方法(也就是顶级逻辑的组成步骤)的不同实现,从而使得顶级逻辑的实现各不相同。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class ConCreteClassA : AbstarctClass
{
public override void PrimitiveOperation1()
{
Console.WriteLine("具体类A方法1实现");
}

public override void PrimitiveOperation2()
{
Console.WriteLine("具体类A方法2实现");
}
}
class ConCreteClassB : AbstarctClass
{
public override void PrimitiveOperation1()
{
Console.WriteLine("具体类B方法1实现");
}

public override void PrimitiveOperation2()
{
Console.WriteLine("具体类B方法2实现");
}
}

Chapter11 迪米特法则

迪米特法则(LoD):如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用,如果其中一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。

Chapter12 外观模式

外观模式(Facade):为子系统中的一组接口提供一个一致的界面,此模块定义了一个高层接口,这个接口使得这一子系统更加容易使用。

案例

  • 四个子系统的类
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    class SubSystemOne 
    {
    public void MethodOne()
    {
    Console.WriteLine("子系统方法一");
    }
    }

    class SubSystemTwo
    {
    public void MethodTwo()
    {
    Console.WriteLine("子系统方法二");
    }
    }

    class SubSystemThree
    {
    public void MethodThree()
    {
    Console.WriteLine("子系统方法三");
    }
    }

    class SubSystemFour
    {
    public void MethodFour()
    {
    Console.WriteLine("子系统方法四");
    }
    }
  • 外观类
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    //外观类,它需要了解所有的子系统的方法和属性,进行组合,以备外界调用。
    class Facade
    {
    SubSystemOne one;
    SubSystemTwo two;
    SubSystemThree three;
    SubSystemFour four;

    public Facade()
    {
    one = new SubSystemOne();
    two = new SubSystemTwo();
    three = new SubSystemThree();
    four = new SubSystemFour();
    }

    public void MethodA()
    {
    Console.WriteLine("方法组A()");
    one.MethodOne();
    two.MethodTwo();
    four.MethodFour();
    }

    public void MethodB()
    {
    Console.WriteLine("方法组B()");
    two.MethodTwo();
    three.MethodThree();
    }
    }
  • 客户端调用
    1
    2
    3
    Facade facade = new Facade();
    facade.MethodA();
    facade.MethodB();

    Chapter13 建造者模式

    建造者模式(Builder):将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

    案例

  • Product类——产品类,由多个部件组成。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    class Product 
    {
    IList<string> parts = new List<string>();

    public void Add(string part)//添加产品部件
    {
    parts.Add(part);
    }

    public void Show()//列举所有的产品部件
    {
    Console.WriteLine("\n 产品 创建 ----");
    foreach (string part in parts)
    {
    Console.WriteLine(part);
    }
    }
    }
  • Builder类——抽象建造者类,确定产品由两个部件PartA和PartB组成,并声明一个得到产品建造后结果的方法GetResult。
    1
    2
    3
    4
    5
    6
    abstract class Builder
    {
    public abstract void BuildPartA();
    public abstract void BuildPartB();
    public abstract Product GetResult();
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class ConcreteBuilder1 : Builder//创建具体的两个部件是部件A和部件B
{
private Product product = new Product();

public override void BuildPartA()
{
product.Add("部件A");
}

public override void BuildPartB()
{
product.Add("部件B");
}

public override Product GetResult()
{
return product;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class ConcreteBuilder2 : Builder//创建具体的两个部件是部件X和部件Y
{
private Product product = new Product();

public override void BuildPartA()
{
product.Add("部件X");
}

public override void BuildPartB()
{
product.Add("部件Y");
}

public override Product GetResult()
{
return product;
}
}
  • Director类——指挥者类。
    1
    2
    3
    4
    5
    6
    7
    8
    class Director//用来指挥建造过程
    {
    public void Construct(Builder builder)
    {
    builder.BuildPartA();
    builder.BuildPartB();
    }
    }
  • 客户端代码,客户不需知道具体的建造过程。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    static void Main(string[] args)
    {
    Director director = new Director();
    Builder b1 = new ConcreteBuilder1();
    Builder b2 = new ConcreteBuilder2();

    director.Construct(b1);
    Product p1 = b1.GetResult();
    p1.Show();

    director.Construct(b2);
    Product p2 = b2.GetResult();
    p2.Show();

    Console.ReadKey();
    }
    ------------------------------------------------------------------------------
    产品 创建 ----
    部件A
    部件B

    产品 创建 ----
    部件X
    部件Y

    Chapter14 观察者模式

    观察者模式(Observer):观察者模式定义了一种一对多的依赖关系,让多个观察者对象同事监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使他们能够自动更新自己。
  • Subject类,可翻译为主题或抽象通知者,一般用一个抽象类或者一个接口实现。

它把所有对观察者对象的引用保存在一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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类,抽象观察者,为所有的具体观察者定义一个接口,在得到主题的通知时更新自己。

这个接口叫做更新接口。抽象观察者一般用一个抽象类或者一个接口实现。更新接口通常包含一个Update()方法,这个方法叫做更新方法。

1
2
3
4
abstract class Observer
{
public abstract void Update();
}
  • ConcreteSubject类,叫做具体主题或具体通知者,将有关状态存入具体观察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。

具体主题角色通常用一个具体子类实现。

1
2
3
4
5
6
7
8
9
10
11
class ConcreteSubject : Subject 
{
private string subjectState;

//具体被观察者状态
public string SubjectState
{
get { return subjectState; }
set { subjectState = value; }
}
}
  • ConcreteObserver类,具体观察者,实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协调。

具体观察者角色可以保存一个指向具体主题对象的引用。具体观察者角色通常用一个具体子类实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class ConcreteObserver : Observer 
{
private string name;
private string observerState;

private ConcreteSubject subject;
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; }
}
}

客户端代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
            ConcreteSubject s = new ConcreteSubject();

s.Attach(new ConcreteObserver(s,"X"));
s.Attach(new ConcreteObserver(s,"Y"));
s.Attach(new ConcreteObserver(s,"Z"));

s.SubjectState = "ABC";
s.Notify();

Console.ReadKey();
------------------------------------------------------------------------------
观察者X的新状态是ABC
观察者Y的新状态是ABC
观察者Z的新状态是ABC

Chapter15 抽象工厂模式

抽象工厂模式(Abstract Factory):提供一个创建一个系列相关或互相依赖对象的接口,而无需指定他们具体的类。

抽象工厂模式的优点

  • 1.最大的好处便是易于交换产品系列,由于具体工厂类 ,例如IFactory factory = new AccessFactory(),在一个应用中只需要在初始化的时候出现一次,这就使得改变一个应用的具体工厂变得非常容易,它只需要改变具体工厂即可使用不同的产品配置 。我们的设计不能去防止需求的更改,那么我们的理想便是让改动变得最小,现在如果你要更改数据库访问,我们只需要更改具体工厂就可以做到。
  • 2.第二大好处是,它让具体的创建实例过程与客户端分离,客户端是通过它们的抽象接口操纵实例,产品的具体类名也被具体工厂的实现分离,不会出现在客户代码中。

    Chapter16 状态模式

    状态模式(State):当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。

状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况。
把状态的判断逻辑转移到表示不同状态的一系列类当中,可以把复杂的判断逻辑简化。

  • State类,抽象状态类,定义一个接口以封装与Context的一个特定状态相关的行为。
    1
    2
    3
    4
    abstract class State
    {
    public abstract void Handle(Context context);
    }
  • ConcreteState类,具体状态,每一个子类实现一个与Context的一个状态相关的行为。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    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子类的实例,这个实例定义当前的状态。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    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);
    }
    }

客户端代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
            //设置Context的初始状态为ConcreteStateA
Context c = new Context(new ConcreteStateA());

c.Request();
c.Request();
c.Request();
c.Request();

Console.ReadKey();
------------------------------------------------------------------------------
当前状态:ConcreteStateB
当前状态:ConcreteStateA
当前状态:ConcreteStateB
当前状态:ConcreteStateA

状态模式好处与用处

状态模式的好处是将与特定状态相关的行为局部化,并且将不同状态的行为分割开来[DP]。

将特定的状态相关的行为都放入一个对象中,由于所有与状态相关的代码都存在于某个ConcreteState中,所以通过定义新的子类可以很容易地增加新的状态和转换[DP]。

状态模式通过把各种状态转移逻辑分布到State的子类之间,来减少相互间的依赖。

Chapter17 适配器模式

适配器模式(Adapter):将一个类的接口转换成成客户希望的另外一个接口。Adapter 模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。[DP]

Chapter18 备忘录模式

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

  • Originator(发起人)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    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,用以记录当前时刻它的内部状态,并可使用备忘录恢复内部状态。Originator可根据需要决定Memento存储Originator的哪些内部状态。
  • Memento(备忘录)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    class Memento
    {
    private string state;
    //构造方法,将相关数据导入
    public Memento(string state)
    {
    this.state = state;
    }
    //需要保存的数据,可以是多个
    public string State
    {
    get { return state; }
    }
    }
    负责存储Originator对象的内部状态,并可防止Originator以外的其他对象访问备忘录Memento。备忘录有两个接口,Caretaker只能看到备忘录的窄接口,它只能将备忘录传递给其他对象。Originator能够看到一个宽接口,允许它访问返回到先前状态所需的所有数据。
  • Caretaker(管理者)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    class Caretaker
    {
    private Memento memento;
    public Memento Memento
    {
    get { return memento; }
    set { memento = value; }
    }
    }
    负责保存好备忘录Memento,不能对备忘录的内容进行操作或检查。

客户端代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
            Originator o = new Originator();
o.State = "On";
o.Show();

Caretaker c = new Caretaker();
c.Memento = o.CreateMemento();

o.State = "Off";
o.Show();

o.SetMemento(c.Memento);
o.Show();

Console.ReadKey();
------------------------------------------------------------------------------
State = On
State = Off
State = On

Chapter19 组合模式

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

  • Component

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    abstract class Component
    {
    protected string name;
    public Component(string name)
    {
    this.name = name;
    }

    //通常都用Add和Remove方法来提供增加或移除树叶或树枝的功能。
    public abstract void Add(Component c);
    public abstract void Remove(Component c);
    public abstract void Display(int depth);
    }

    Component为组合中的对象声明接口,在适当情况下,实现所有类共有接口的默认行为。声明一个接口用于访问和管理Component的子部件。

  • Leaf

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    class Leaf : Component
    {
    public Leaf(string name) : base(name)
    {

    }

    //由于叶子节点没有再增加分枝和树叶,所以Add和Remove方法实现没有它没有意义。
    //
    public override void Add(Component c)
    {
    Console.WriteLine("Cannot add to a leaf");
    }

    public override void Remove(Component c)
    {
    Console.WriteLine("Cannot remove form a leaf");
    }

    //叶节点的具体方法,此处是显示其名称和级别。
    public override void Display(int depth)
    {
    Console.WriteLine("-" + depth + name);
    }
    }

    Leaf在组合中表示叶节点对象,叶节点没有子节点。

  • Composite

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    class Composite : Component
    {
    //一个子对象集合用来存储其下属的枝节点和叶节点
    private List<Component> children = new List<Component>();
    public Composite(string name) : base(name)
    {

    }

    public override void Add(Component c)
    {
    children.Add(c);
    }

    public override void Remove(Component c)
    {
    children.Remove(c);
    }

    //显示其枝节点名称,并对其下级进行遍历。
    public override void Display(int depth)
    {
    Console.WriteLine("-" + depth + name);
    foreach (Component component in children)
    {
    component.Display(depth + 2);
    }
    }
    }

    Composite定义有枝节点行为,用来存储子部件,在Component接口中实现与子部件有关的操作,比如增加Add和删除Remove。

客户端代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
            Composite root = new Composite("root");
root.Add(new Leaf("Leaf A"));
root.Add(new Leaf("Leaf B"));

Composite comp = new Composite("Composite X");
comp.Add(new Leaf("Leaf XA"));
comp.Add(new Leaf("Leaf XB"));
root.Add(comp);

Composite comp2 = new Composite("Composite XY");
comp2.Add(new Leaf("Leaf XYA"));
comp2.Add(new Leaf("Leaf XYB"));
root.Add(comp2);

root.Add(new Leaf("Leaf C"));

Leaf leaf = new Leaf("Leaf D");
root.Add(leaf);
root.Remove(leaf);

root.Display(1);

Console.ReadKey();
------------------------------------------------------------------------------
-1root
-3Leaf A
-3Leaf B
-3Composite X
-5Leaf XA
-5Leaf XB
-3Composite XY
-5Leaf XYA
-5Leaf XYB
-3Leaf C

何时使用组合模式

  • 当你发现需求中是体现部分与整体层次的结构时,以及你希望用户可以忽略组合对象与单个对象的不同,统一地使用组合结构中的所有对象时,就应该考虑用组合模式了。

    Chapter20 迭代器模式

    迭代器模式(Iterator):提供一种方法顺序访问一个聚合对象中各个元素,而又不暴露该对象的内部表示。

  • Iterator

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    abstract class Iterator
    {
    //得到开始对象
    public abstract object First();
    //得到下一个对象
    public abstract object Next();
    //判断是否到结尾
    public abstract bool IsDone();
    //当前对象
    public abstract object CurrentItem();
    }

    迭代器抽象类

  • Aggregate

    1
    2
    3
    4
    5
    abstract class Aggregate
    {
    //创建迭代器
    public abstract Iterator CreateIterator();
    }

    聚集抽象类

  • ConcreteIterator

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    class ConcreteIterator : Iterator
    {
    //定义了一个具体聚集对象
    private ConcreteAggregate aggregate;
    private int current = 0;

    //初始化时将具体的聚集对象传入
    public ConcreteIterator(ConcreteAggregate aggregate)
    {
    this.aggregate = aggregate;
    }

    //得到聚集的第一个对象
    public override object First()
    {
    return aggregate[0];
    }

    //得到聚集的下一个对象
    public override object Next()
    {
    object ret = null;
    current++;
    if (current < aggregate.Count)
    {
    ret = aggregate[current];
    }
    return ret;
    }

    //判断当前是否遍历到结尾,到结尾返回true
    public override bool IsDone()
    {
    return current >= aggregate.Count ? true : false;
    }

    //返回当前的聚集对象
    public override object CurrentItem()
    {
    return aggregate[current];
    }
    }

    具体迭代器类,继承Iterator

  • ConcreteAggregate

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    class ConcreteAggregate : Aggregate
    {
    //声明一个IList泛型变量,用于存放聚合对象。
    private IList<object> items = new List<object>();
    public override Iterator CreateIterator()
    {
    return new ConcreteIterator(this);
    }
    //返回聚集总个数
    public int Count
    {
    get { return items.Count; }
    }
    //声明一个索引器
    public object this[int index]
    {
    get { return items[index]; }
    set { items.Insert(index, value); }
    }
    }

    具体聚集类 继承Aggregate

客户端代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
            ConcreteAggregate a = new ConcreteAggregate();
a[0] = "A";
a[1] = "B";
a[2] = "C";
a[3] = "D";
a[4] = "E";

Iterator i = new ConcreteIterator(a);
object item = i.First();
while (!i.IsDone())
{
Console.WriteLine(i.CurrentItem());
i.Next();
}

Console.ReadKey();
------------------------------------------------------------------------------
A
B
C
D
E

总地来说,迭代器(Iterator)模式就是分离了集合对象的遍历行为,抽象出一个迭代器类来负责,这样既可以做到不暴露集合的内部结构,又可让外部代码透明地访问集合内部的数据。

Chapter21 单例模式

单例模式(Singleton):保证一个类仅有一个实例,并提供一个访问它的全局访问点。

通常我们可以让一个全局变量使得一个对象被访问,但它不能防止你实例化多个对象。一个最好的办法就是,让类自身负责保存它的唯一实例。这个类可以保证没有其他实例可以被创建,并且它可以提供一个访问该实例的方法。

  • Singleton类
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    class Singleton
    {
    private static Singleton instance;
    //构造方法让其private,这就堵死了外界利用new创建此类实例的可能。
    private Singleton(){}
    //此方法是获得本类实例的唯一全局访问点
    public static Singleton GetInstance()
    {
    //若实例不存在,则new一个新实例,否则返回已有的实例。
    if (instance == null)
    {
    instance = new Singleton();
    }
    return instance;
    }
    }
    定义一个GetInstance操作,允许客户访问它的唯一实例。
    GetInstance是一个静态方法,主要负责创建自己的唯一实例。

    多线程时的单例

    lock语句:lock是确保当一个线程位于代码的临界区时,另一个线程不进入临界区。如果其他线程试图进入锁定的代码,则它将一直等待(即被阻止),直到该对象被释放。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    class Singleton
    {
    private static Singleton instance;
    //程序运行时创建一个静态只读的进程辅助对象
    private static readonly object syncRoot = new object();
    //构造方法让其private,这就堵死了外界利用new创建此类实例的可能。
    private Singleton(){}
    //此方法是获得本类实例的唯一全局访问点
    public static Singleton GetInstance()
    {
    //在同一个时刻加了锁的那部分程序只有一个线程可以进入
    lock (syncRoot)
    {
    //若实例不存在,则new一个新实例,否则返回已有的实例。
    if (instance == null)
    {
    instance = new Singleton();
    }
    return instance;
    }
    }
    }

    静态初始化

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    //阻止发生派生,而派生可能会增加实例
    public sealed class Singleton
    {
    //在第一次引用类的任何成员时创建实例。
    //公共语言运行库负责处理变量初始化。
    private static readonly Singleton instance = new Singleton();
    private Singleton() { }
    public static Singleton GetInstance()
    {
    return instance;
    }
    }

这样的实现与前面的示例类似,也是解决了单例模式试图解决的两个基本问题:全局访问和实例化控制,公共静态属性为访问实例提供了一个全局访问点。
不同之处在于它依赖公共语言运行库来初始化变量。由于构造方法是私有的,因此不能在类本身以外实例化Singleton类;因此,变量引用的是可以在系统中存在的唯一的实例。
不过要注意,instance变量标记为readonly,这意味着只能在静态初始化期间或在类构造函数中分配变量。由于这种静态初始化的方式是在自己被加载时就将自己实例化,所以被形象地称之为饿汉式单例类,原先的单例模式处理方式是要在第一次被引用时,才会将自己实例化,所以就被称为懒汉式单例类。

由于饿汉式,即静态初始化的方式,它是类一加载就实例化的对象,所以要提前占用系统资源。然而懒汉式,又会面临着多线程访问的安全性问题,需要做双重锁定这样的处理才可以保证安全。所以到底使用哪一种方式,取决于实际的需求。

Chapter22 桥接模式

合成/聚合复用原则

  • 合成/聚合复用原则(CARP):尽量使用合成/聚合,尽量不要使用类继承。

合成(Composition)和聚合(Aggregation)都是关联的特殊种类。
聚合表示一种弱的‘拥有’关系,体现的是A对象可以包含B对象,但B对象不是A对象的一部分;
合成则是一种强的‘拥有’关系,体现了严格的部分和整体的关系,部分和整体的生命周期一样。

合成/聚合复用原则的好处是,优先使用对象的合成/聚合将有助于你保持每个类被封装,并被集中在单个任务上。这样类和类继承层次会保持较小规模,并且不太可能增长为不可控制的庞然大物。

桥接模式(Bridge):将抽象部分与它的实现部分分离,使它们都可以独立地变化。

  • Implementor类

    1
    2
    3
    4
    abstract class Implementor
    {
    public abstract void Operation();
    }
  • ConcreteImplementorA和ConcreteImplementorB等派生类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    class ConcreteImplementorA:Implementor
    {
    public override void Operation()
    {
    Console.WriteLine("具体实现A的方法执行");
    }
    }
    class ConcreteImplementorB : Implementor
    {
    public override void Operation()
    {
    Console.WriteLine("具体实现B的方法执行");
    }
    }
  • Abstraction类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    class Abstraction
    {
    protected Implementor implementor;
    public void SetImplementor(Implementor implementor)
    {
    this.implementor = implementor;
    }

    public virtual void Operation()
    {
    implementor.Operation();
    }
    }
  • RefinedAbstraction类

    1
    2
    3
    4
    5
    6
    7
    class RefinedAbstraction : Abstraction 
    {
    public override void Operation()
    {
    implementor.Operation();
    }
    }
  • 客户端

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
                Abstraction ab = new RefinedAbstraction();
    ab.SetImplementor(new ConcreteImplementorA());
    ab.Operation();
    ab.SetImplementor(new ConcreteImplementorB());
    ab.Operation();

    Console.ReadKey();
    ------------------------------------------------------------------------------
    具体实现A的方法执行
    具体实现B的方法执行

    Chapter23 命令模式

    命令模式(Command): 将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。

  • Command类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    abstract class Command
    {
    protected Receiver receiver;

    public Command(Receiver receiver)
    {
    this.receiver = receiver;
    }

    abstract public void Execute();
    }

    用来声明执行操作的接口。

  • ConcreteCommand类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    class ConcreteCommand : Command
    {
    public ConcreteCommand(Receiver receiver) : base(receiver)
    { }

    public override void Execute()
    {
    receiver.Action();
    }
    }

    将一个接收者对象绑定于一个动作,调用接收者相应的操作,以实现Execute。

  • Invoker类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    class Invoker
    {
    private Command command;

    public void SetCommand(Command command)
    {
    this.command = command;
    }

    public void ExecuteCommand()
    {
    command.Execute();
    }
    }

    要求该命令执行这个请求。

  • Receiver类

    1
    2
    3
    4
    5
    6
    7
    class Receiver
    {
    public void Action()
    {
    Console.WriteLine("执行请求!");
    }
    }

    知道如何实施与执行一个与请求相关的操作,任何类都可能作为一个接收者。

  • 客户端

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
                Receiver r = new Receiver();
    Command c = new ConcreteCommand(r);
    Invoker i = new Invoker();

    i.SetCommand(c);
    i.ExecuteCommand();


    Console.ReadKey();
    ------------------------------------------------------------------------------
    执行请求!

Chapter24 职责链模式

职责链模式(Chain of Responsibility):使多个对象都有机会来处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。

  • Handler类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    abstract class Handler
    {
    protected Handler successor;
    //设置继任者
    public void SetSuccessor(Handler successor)
    {
    this.successor = successor;
    }
    //处理请求的抽象方法
    public abstract void HandleRequest(int request);
    }

    定义一个处理请示的接口。

  • ConcreteHandler类 具体处理者类,处理它所负责的请求,可访问它的后继者,如果可处理该请求,就处理之,否则就将该请求转发给它的后继者。

ConcreteHandler1,当请求数在0到10之间则有权处理,否则转到下一位。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class ConcreteHandler1 : Handler
{
public override void HandleRequest(int request)
{
//0到10,处理此请求
if (request >= 0 && request < 10)
{
Console.WriteLine("{0}处理请求{1}", this.GetType().Name, request);
}
else if (successor != null)//转移到下一位
{
successor.HandleRequest(request);
}
}
}

ConcreteHandler2,当请求数在10到20之间则有权处理,否则转到下一位。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class ConcreteHandler2 : Handler
{
public override void HandleRequest(int request)
{
//10到20,处理此请求
if (request >= 10 && request < 20)
{
Console.WriteLine("{0}处理请求{1}", this.GetType().Name, request);
}
else if (successor != null)//转移到下一位
{
successor.HandleRequest(request);
}
}
}

ConcreteHandler3,当请求数在20到30之间则有权处理,否则转到下一位。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class ConcreteHandler3 : Handler
{
public override void HandleRequest(int request)
{
//20到30,处理此请求
if (request >= 20 && request < 30)
{
Console.WriteLine("{0}处理请求{1}", this.GetType().Name, request);
}
else if (successor != null)//转移到下一位
{
successor.HandleRequest(request);
}
}
}

客户端代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
            Handler h1 = new ConcreteHandler1();
Handler h2 = new ConcreteHandler2();
Handler h3 = new ConcreteHandler3();
h1.SetSuccessor(h2);
h2.SetSuccessor(h3);

int[] requests = { 2, 5, 14, 22, 18, 3, 27, 20 };
//循环给最小处理者提交请求,不同的数额,由不同权限处理者处理。
foreach (int request in requests)
{
h1.HandleRequest(request);
}

Console.ReadKey();
------------------------------------------------------------------------------
ConcreteHandler1处理请求2
ConcreteHandler1处理请求5
ConcreteHandler2处理请求14
ConcreteHandler3处理请求22
ConcreteHandler2处理请求18
ConcreteHandler1处理请求3
ConcreteHandler3处理请求27
ConcreteHandler3处理请求20

职责链的好处

这当中最关键的是当客户提交一个请求时,请求是沿链传递直至有一个ConcreteHandler对象负责处理它。

这就使得接收者和发送者都没有对方的明确信息,且链中的对象自己也并不知道链的结构。结果是职责链可简化对象的相互连接,它们仅需保持一个指向其后继者的引用,而不需保持它所有的候选接受者的引用。这也就大大降低了耦合度了。

Chapter25 中介者模式

中介者模式(Mediator):用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地互相引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。

  • Mediator类

    1
    2
    3
    4
    5
    //定义一个抽象的发送消息方法,得到同事对象和发送信息
    abstract class Mediator
    {
    public abstract void Send(string message, Colleague colleague);
    }

    抽象中介者类

  • Colleague类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    abstract class Colleague
    {
    protected Mediator mediator;
    //构造方法,得到中介者对象
    public Colleague(Mediator mediator)
    {
    this.mediator = mediator;
    }
    }

    抽象同事类

ConcreteColleague1和ConcreteColleague2等各种同事对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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);
}
}

ConcreteMediator类 具体中介者类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class ConcreteMediator : Mediator
{
private ConcreteColleague1 colleague1;
private ConcreteColleague2 colleague2;
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);
}
}
}

客户端代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
            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.ReadKey();
------------------------------------------------------------------------------
同事2得到信息:吃过饭了吗?
同事1得到信息:没有呢,你打算请客?

Chapter26 享元模式

享元模式(Flyweight):运用共享技术有效地支持大量细粒度的对象。

  • Flyweight类

    1
    2
    3
    4
    abstract class Flyweight
    {
    public abstract void Operation(int extrinsicstate);
    }

    它是所有具体享元类的超类或接口,通过这个接口,Flyweight可以接受并作用于外部状态。

  • ConcreteFlyweight类

    1
    2
    3
    4
    5
    6
    7
    class ConcreteFlyweight : Flyweight
    {
    public override void Operation(int extrinsicstate)
    {
    Console.WriteLine("具体Flyweight:" + extrinsicstate);
    }
    }

    继承Flyweight超类或实现Flyweight接口,并为内部状态增加存储空间。

  • UnsharedConcreteFlyweight类

    1
    2
    3
    4
    5
    6
    7
    class UnsharedConcreteFlyweight : Flyweight
    {
    public override void Operation(int extrinsicstate)
    {
    Console.WriteLine("不共享的具体Flyweight:" + extrinsicstate);
    }
    }

    指那些不需要共享的Flyweight子类。
    因为Flyweight接口共享成为可能,但它并不强制共享。

  • FlyweightFactory类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    class FlyweightFactory
    {
    private Hashtable flyweights = new Hashtable();
    public FlyweightFactory()
    {
    //初始化工厂时,先生成三个实例。
    flyweights.Add("X",new ConcreteFlyweight());
    flyweights.Add("Y", new ConcreteFlyweight());
    flyweights.Add("Z", new ConcreteFlyweight());
    }

    //根据客户端请求,获得已经生成的实例。
    public Flyweight GetFlyweight(string key)
    {
    return (Flyweight)flyweights[key];
    }
    }

    是一个享元工厂,用来创建并管理Flyweight对象。
    它主要是用来确保合理地共享Flyweight,当用户请求一个Flyweight时,FlyweightFactory对象提供一个已创建的实例或者创建一个(如果不存在的话)。

客户端代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
            int extrinsicstate = 22;//代码外部状态

FlyweightFactory f = new FlyweightFactory();

Flyweight fx = f.GetFlyweight("X");
fx.Operation(--extrinsicstate);

Flyweight fy = f.GetFlyweight("Y");
fy.Operation(--extrinsicstate);

Flyweight fz = f.GetFlyweight("Z");
fz.Operation(--extrinsicstate);

Flyweight uf = new UnsharedConcreteFlyweight();
uf.Operation(--extrinsicstate);

Console.ReadKey();
------------------------------------------------------------------------------
具体Flyweight:21
具体Flyweight:20
具体Flyweight:19
不共享的具体Flyweight:18

内部状态与外部状态

在享元对象内部并且不会随环境改变而改变的共享部分,可以称为是享元对象的内部状态,而随环境改变而改变的、不可以共享的状态就是外部状态了。
事实上,享元模式可以避免大量非常相似类的开销。
在程序设计中,有时需要生成大量细粒度的类实例来表示数据。如果能发现这些实例除了几个参数外基本上都是相同的,有时就能够受大幅度地减少需要实例化的类的数量。
如果能把那些参数移到类实例的外面,在方法调用时将它们传递进来,就可以通过共享大幅度地减少单个实例的数目。

也就是说,享元模式Flyweight执行时所需的状态是有内部的也可能有外部的,内部状态存储于ConcreteFlyweight对象之中,而外部对象则应该考虑由客户端对象存储或计算,当调用Flyweight对象的操作时,将该状态传递给它。

Chapter27 解释器模式

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

解释器模式需要解决的是,如果一种特定类型的问题发生的频率足够高,那么可能就值得将该问题的各个实例表述为一个简单语言中的句子。
这样就可以构建一个解释器,该解释器通过解释这些句子来解决该问题。

  • AbstractExpression(抽象表达式)

    1
    2
    3
    4
    abstract class AbstractExpression 
    {
    public abstract void Interpret(Context context);
    }

    声明一个抽象的解释操作,这个接口为抽象语法树中所有的节点所共享。

  • TerminalExpression(终结符表达式)

    1
    2
    3
    4
    5
    6
    7
    class TerminalExpression : AbstractExpression
    {
    public override void Interpret(Context context)
    {
    Console.WriteLine("终端解释器");
    }
    }

    实现与文法中的终结符相关联的解释操作。
    实现抽象表达式中所要求的接口,主要是一个interpret()方法。
    文法中每一个终结符都有一个具体终结表达式与之相对应。

  • NonterminalExpression(非终结符表达式)

    1
    2
    3
    4
    5
    6
    7
    class NontermianlExpression : AbstractExpression
    {
    public override void Interpret(Context context)
    {
    Console.WriteLine("非终端解释器");
    }
    }

    为文法中的非终结符实现解释操作。
    对文法中每一条规则R1、R2……Rn都需要一个具体的非终结符表达式类。通过实现抽象表达式的interpret()方法实现解释操作。
    解释操作以递归方式调用上面所提到的代表R1、R2……Rn中各个符号的实例变量。

  • Context

    1
    2
    3
    4
    5
    6
    7
    class Context
    {
    private string input;
    public string Input { get { return input; } set { input = value; } }
    private string output;
    public string Output { get { return output; } set { output = value; } }
    }

    包含解释器之外的一些全局信息。

客户端代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
            Interpreter.Context context = new Interpreter.Context();
IList<AbstractExpression> list = new List<AbstractExpression>();
list.Add(new TerminalExpression());
list.Add(new NontermianlExpression());
list.Add(new TerminalExpression());
list.Add(new TerminalExpression());

foreach (AbstractExpression exp in list)
{
exp.Interpret(context);
}

Console.ReadKey();
------------------------------------------------------------------------------
终端解释器
非终端解释器
终端解释器
终端解释器

解释器模式好处

用了解释器模式,就意味着可以很容易地改变和扩展文法,因为该模式使用类来表示文法规则,你可使用继承来改变或扩展该文法。也比较容易实现文法,因为定义抽象语法树中各个节点的类的实现大体类似,这些类都易于直接编写。

解释器模式也有不足的,解释器模式为文法中的每一条规则至少定义了一个类,因此包含许多规则的文法可能难以管理和维护。建议当文法非常复杂时,使用其他的技术如语法分析程序或编译器生成器来处理。

Chapter28 访问者模式

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

访问者模式的目的是要把处理从数据结构分离出来。
很多系统可以按照算法和数据结构分开,如果这样的系统有比较稳定的数据结构,又有易于变化的算法的话,使用访问者模式就是比较合适的,因为访问者模式使得算法操作的增加变得容易。
反之,如果这样的系统的数据结构对象易于变化,经常要有新的数据对象增加进来,就不适合使用访问者模式。

  • Visitor类
    1
    2
    3
    4
    5
    abstract class Visitor 
    {
    public abstract void VisitConcreteElementA(ConcreteElementA concreteElementA);
    public abstract void VisitConcreteElementB(ConcreteElementB concreteElementB);
    }
    为该对象结构中ConcreteElement的每一个类声明一个Visit操作。

ConcreteVisitor1和ConcreteVisitor2类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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 ConcreteVisitor2 : 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);
}
}

具体访问者,实现每个由Visitor声明的操作。每个操作实现算法的一部分,而该算法片断乃是对应于结构中对象的类。

  • Element类
    1
    2
    3
    4
    abstract class Element 
    {
    public abstract void Accept(Visitor visitor);
    }
    定义一个Accept操作,它以一个访问者为参数。

ConcreteElementA和ConcreteElementB类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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 OperationA() { }
}

具体元素,实现Accept操作。

  • ObjectStructure类
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    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);
    }
    }
    }
    能枚举它的元素,可以提供一个高层的接口以允许访问者访问它的元素。

客户端代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
            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.ReadKey();
------------------------------------------------------------------------------
ConcreteElementA被ConcreteVisitor1访问
ConcreteElementB被ConcreteVisitor1访问
ConcreteElementA被ConcreteVisitor2访问
ConcreteElementB被ConcreteVisitor2访问