**定义:**访问者模式(Vistor Pattern),表示一个作用于对象结构中各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
**类型:**对象行为型模式。
概述:
访问者模式适用的场合比较狭窄,因为它的要求非常严。ConcreteElement要求具有一样的算法接口,并且ConcreteVisitor也要求一样的接口,这两点就使得访问者模式的使用范围比较窄。在《Head First Design Patterns》中也将访问者模式列为非常用模式之一。
举一个例子,有一个仓库(对象结构),里面储存不同的电器(元素),如电视、电脑(具体元素),仓库可以按电器新旧或大小(具体的访问者)来摆放电器产品。
类图:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-19_57b6b467f2b12.jpg)
参与者:
1. Client:构建抽象的访问者以及结构体对象。
1. Storage,具体生成并存储不同的商品(如Television、Computer)。
1. Goods,抽象商品类。
1. Television、Computer,具体的商品类,对相应的属性完成赋值。
1. Place,抽象的访问者类,提供抽象访问方法。
1. State、Size,具体的访问者类,根据相应商品属性来实现具体的摆放方法Put()。
示例代码:
~~~
using System;
using System.Collections.Generic;
using System.Text;
namespace Pattern24
{
abstract class Visitor
{
public abstract void PutTelevision(Television tv);
public abstract void PutComputer(Computer comp);
}
class SizeVisitor : Visitor
{
public override void PutTelevision(Television tv)
{
Console.WriteLine("按商品大小{0}排放", tv.Size);
}
public override void PutComputer(Computer comp)
{
Console.WriteLine("按商品大小{0}排放", comp.Size);
}
}
class StateVisitor : Visitor
{
public override void PutTelevision(Television tv)
{
Console.WriteLine("按商品新旧值{0}排放", tv.State);
}
public override void PutComputer(Computer comp)
{
Console.WriteLine("按商品新旧值{0}排放", comp.State);
}
}
abstract class Goods
{
public abstract void Operate(Visitor visitor);
private int nSize;
public int Size
{
get {return nSize;}
set {nSize = value;}
}
private int nState;
public int State
{
get {return nState;}
set {nState = value;}
}
}
class Television : Goods
{
public override void Operate(Visitor visitor)
{
visitor.PutTelevision(this);
}
}
class Computer : Goods
{
public override void Operate(Visitor visitor)
{
visitor.PutComputer(this);
}
}
class Storage
{
private IList list = new List();
public void Attach(Goods element)
{
list.Add(element);
}
public void Detach(Goods element)
{
list.Remove(element);
}
public void Operate(Visitor visitor)
{
foreach (Goods e in list)
{
e.Operate(visitor);
}
}
}
class Program
{
static void Main(string[] args)
{
Storage storage = new Storage();
storage.Attach(new Television());
storage.Attach(new Computer());
SizeVisitor v1 = new SizeVisitor();
StateVisitor v2 = new StateVisitor();
storage.Operate(v1);
storage.Operate(v2);
Console.Read();
}
}
}
~~~
适用性:
1. 一个对象结构包含很多类对象,它们有不同的接口,而你想对这些对象实施一些依赖于其具体类的操作。
1. 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而你想避免让这些操作“污染”这些对象的类。
Visitor使得你可以将相关的操作集中起来定义在一个类中。
当该对象结构被很多应用共享时,用Visitor模式让每个应用仅包含需要用到的操作。
1. 定义对象结构的类很少改变,但经常需要在此结构上定义新的操作。
改变对象结构类需要重定义对所有访问者的接口,这可能需要很大的代价。
如果对象结构类经常改变,那么可能还是在这些类中定义这些操作较好** **
优缺点:
1. 优点,完成新的模板方法是不用修改原有代码,通过添加新代码完成。
1. 缺点,当使用访问者模式时,打破了组合模式。
参考资料:
1. 《设计模式——可复用面向对象软件基础》
1. 《大话设计模式》
';
23——行为型模式之模板方法模型
最后更新于:2022-04-01 20:11:18
**定义:**模板方法模式(TemplateMethod Pattern),定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模块方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
类型:类行为型模式
概述:
模板方法模式应该算是所有设计模式中最简单,却也是应用得最多的模式了。模板方法模式其实就是多态的体现。
模板方法模式设计的要点是在父类中定义好一个操作中的算法骨架,然后具体实现延迟到子类中。模板方法其实与策略模式有一些类似的地方。不同点是,策略模式将算法抽象出来成为一个新的类,而模板方法则没有将算法抽象出来。主要体现是策略模式的算法相对复杂一点,而模板方法的算法相对简单一点。
举例,有一群人,可以按身高排序然后取其中高的一部分;也可以体量排序然后取其中体量适宜的一部分。
类图:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-19_57b6b467da063.jpg)
参与者:
1. People,基类,提供一个模板方法,并且提供抽象的接口。模板方法调用接口完成具体的算法。
1. HighPeople、FatPeople,根据想要完成的情况,来具体实现抽象接口**。**
示例代码:
~~~
using System;
using System.Collections.Generic;
using System.Text;
namespace Pattern23
{
abstract class People
{
public abstract void Sort();
public abstract void Remove();
public void TemplateMethod()
{
Sort();
Remove();
}
}
class HighPeople : People
{
public override void Sort()
{
Console.WriteLine("按身高排序");
}
public override void Remove()
{
Console.WriteLine("排除掉身高低于170cm的人");
}
}
class FatPeople : People
{
public override void Sort()
{
Console.WriteLine("按体重排序");
}
public override void Remove()
{
Console.WriteLine("排除体量超过80K以及少于60KG的人");
}
}
class Program
{
static void Main(string[] args)
{
People people;
people = new HighPeople();
people.TemplateMethod();
people = new FatPeople();
people.TemplateMethod();
}
}
}
~~~
适用性:
1. 一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。
1. 各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复。
首先识别现有代码中的不同之处,并且将不同之处分离为新的操作。
最后,用一个调用这些新的操作的模板方法来替换这些不同的代码。
1. 控制子类扩展。
优缺点:
1. 优点,完成新的模板方法是不用修改原有代码,通过添加新代码完成。
参考资料:
1. 《设计模式——可复用面向对象软件基础》
1. 《大话设计模式》
';
22——行为型模式之策略模式
最后更新于:2022-04-01 20:11:15
**定义:**策略模式(Strategy Pattern),定义一系列算法,把它们一个个封装起来,并且使它们可以相互替换。本模式使得算法可以独立于使用它的客户而变化。
类型:对象行为型模式。
概述:
策略模式是比较典型的对象行为型模式,它是将对处理对象的一系列不同算法都单独抽离出来,单独封装成一个个类。策略的出现,主要是为了解决不同算法替换时的逻辑判断,将逻辑判断移到Client中去。策略模式比较常见,但是也相对比较简单。
一系列算法,作为程序员,很容易就想到排序算法。那这里就以排序算法来举例了。有文本内容Context,如果录得的是乱序数据,那么排序最好选择快速排序(Quick Sort);如果是多部分都有序,只有个别数据随意插入其中,那么此时可以使用选择排序(Select Sort)。
类图:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-19_57b6b467b6109.jpg)
参与者:
1. Context,上下文,维护一个指向SortAlgo的指针,用来访问相应的排序算法。
1. SortAlgo,算法策略接口类,只是提供接口。
1. QuickSort、SelectSort,按照接口实现具体的算法。
示例代码:
~~~
using System;
using System.Collections.Generic;
using System.Text;
namespace Pattern22
{
// 抽象算法类
abstract class SortAlgo
{
//算法方法
public abstract void Sort();
}
// 快速排序算法
class QuickSort : SortAlgo
{
// 具体实现快速排序算法
public override void Sort()
{
Console.WriteLine("快速排序算法实现");
}
}
// 选择排序算法
class SelectSort : SortAlgo
{
// 具体实现选择排序算法
public override void Sort()
{
Console.WriteLine("选择排序算法实现");
}
}
//上下文
class Context
{
SortAlgo algo;
public Context(SortAlgo _algo)
{
this.algo = _algo;
}
//上下文接口
public void SortData()
{
algo.Sort();
}
}
class Program
{
static void Main(string[] args)
{
Context context;
// 当录入的是随机数据时,选择使用快速排序算法
context = new Context(new QuickSort());
context.SortData();
// 当录入一些比较有顺序的数据时,使用选择排序算法
context = new Context(new SelectSort());
context.SortData();
Console.Read();
}
}
}
~~~
适用性:
1. 多个类只区别在表现行为不同,可以使用Strategy模式,在运行时动态选择具体要执行的行为。
1. 在需要新添加其他算法来实现时。
1. 对客户端隐藏具体算法的实现细节。
优缺点:
1. 优点,将判断的逻辑移到类外面,方便通过添加新类来完成新的算法。
1. 缺点,如果算法类过多,会对Client端调用不同算法的判断带来麻烦。
参考资料:
1. 《设计模式——可复用面向对象软件基础》
1. 《大话设计模式》
';
21——行为型模式之状态模式
最后更新于:2022-04-01 20:11:13
定义:状态模式(State Pattern),允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。
类型:对象行为型模式
概述:
一个对象有一个状态,那么每一个状态又对应一些相应的行为。如果这个对象有很多状态,那么就要对应非常多的行为。那么对这些状态的判断以及根据状态完成相应的行为,会非常复杂。并且如果想添加一种新的状态时,需要修改很多的现有代码。这也是有违开闭原则的。状态模式正是在这样一种情况下提出来的。
状态模式将每种状态对应的行为抽象出来成为单独的新的对象,这样将状态转换显式化了。状态的变换不再依赖于Context内部的行为了。另外,将状态及行为提出来能够大为降低Context对象的复杂度。另外如果一个State对应多个Context时,State还可以被多个Context对象共享。
状态,我们立马会提出,今天状态不好,做什么都没劲;又或者是今天状态很好,做事得劲,饭也吃得多。那么我们就以一个人不同时刻的状态为例来讲解状态模式。
类图:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-19_57b6b467964dc.jpg)
参与者:
1. Human,也即Context通过抽象接口来调用状态对象的具体实现。
1. State,封装了与Human相关行为的接口。
1. Happy,Sad,具体实现了与相应状态下的行为。
示例代码:
~~~
using System;
using System.Collections.Generic;
using System.Text;
namespace Pattern21
{
//抽象状态
public abstract class State
{
public abstract void Eat();
public abstract void Walk();
}
// 高兴时的状态
public class Happy : State
{
public override void Eat()
{
human.Eat();
Console.WriteLine("很多!");
}
public override void Walk()
{
human.Walk();
Console.WriteLine("手舞足蹈的!");
}
public void Attach(Human _human)
{
human = _human;
}
private Human human;
}
// 伤心时的状态
public class Sad : State
{
public override void Eat()
{
human.Eat();
Console.WriteLine("很少!");
}
public override void Walk()
{
human.Walk();
Console.WriteLine("无精打采的!");
}
public void Attach(Human _human)
{
human = _human;
}
private Human human;
}
// 一个人
public class Human
{
private State current;
public void SetState(State s)
{
current = s;
}
public void Eat()
{
Console.Write("吃了");
}
public void Walk()
{
Console.Write("走起路来");
}
public void Show()
{
current.Eat();
current.Walk();
}
}
class Program
{
static void Main(string[] args)
{
// 定义一个有很多状态的对象
Human human = new Human();
// 定义一个高兴的状态
Happy hState = new Happy();
hState.Attach(human);
human.SetState(hState);
human.Show();
// 定义一个伤心的状态
Sad sad = new Sad();
sad.Attach(human);
human.SetState(sad);
human.Show();
// 还可以添加生病的状态,只需要添加新的类而不需要修改Human类
// ......
Console.Read();
}
}
}
~~~
适用性:
1. 一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为。
1. 一个操作中含有庞大的多分支的条件语句,且这些分支依赖于该对象的状态。
优缺点:
1. 优点,将状态判断的逻辑移到类外面,方便通过添加新类来添加新的状态。
1. 缺点,如果状态非常多,会导致有非常多的状态类,加大开销。
参考资料:
1. 《设计模式——可复用面向对象软件基础》
1. 《大话设计模式》
';
20——行为型模式之观察者模式
最后更新于:2022-04-01 20:11:11
**定义:**观察者模式(ObserverPattern),定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
类型:对象行为型模式
概述:
观察者模式也是比较难以理解,单看类图,会发现观察者模式与中介模式非常相似。如果不真正理解他们二者之间的区别,是难以真正理解观察者模式的。如果不理解观察者模式,更奢谈运用观察者模式了。
中介模式主要是解决一系列交互对象的耦合的问题,而提出了中介类。几个以前交互的对象现在统一通过中介类来完成交流。而观察者模式侧重的是一对多的通知的功能,即当发生某件事时,主题对象将向所有注册过的观察者对象发出通知消息。
中介模式侧重通过中介类来完成交流;而观察者模式侧重于一对多的通知。中介模式不需要知道具体的中介类的对象,需要交互的对象只需要知道一个通知中介对象的接口即可,具体是哪个中介来完成交互对象告诉的任务,交互对象不需要知道。但是交互对象必须知道有哪些交互对象,这样才能把对象A需要对象B做的事想办法通知到对象B。
观察者模式则,Subject对象不需要知道具体的观察者是谁,它只需要知道观察者的通知接口,就可以将消息通知到观察者。至于具体有哪些消息,则需要观察者自己去具体的Subject那里查看。
这里举一个例子,某电商有大促销,通过邮件发送促销消息给消费者。但是如果消费者想了解有哪些具体的促销信息,则即需要亲自去电商网站查看有哪些具体的促销信息。
类图:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-19_57b6b467733f3.jpg)
**参与者:**
1. Vender,提供接口来完成对观察者的注册通知。
1. YiXun,具体的电商,为消费者提供所需要查看具体信息。
1. Observer,提供Update接口。
1. CustomerA、CustomerB,具体的观察者,当有更新时去查看电商中自己喜欢的产品。
**示例代码:**
~~~
using System;
using System.Collections.Generic;
using System.Text;
namespace Design20
{
abstract class Vender
{
private IList observers = new List();
// 观察者注册
public void Register(Observer observer)
{
observers.Add(observer);
}
//通知
public void Notify()
{
foreach (Observer o in observers)
{
o.Update();
}
}
}
//具体通知者
class YiXun : Vender
{
private string subjecPhone;
private string subjecLaptop;
//具体通知者状态
public string SubjecPhone
{
get { return subjecPhone; }
set { subjecPhone = value; }
}
//具体通知者状态
public string SubjecLaptop
{
get { return subjecLaptop; }
set { subjecLaptop = value; }
}
}
abstract class Observer
{
public abstract void Update();
}
class CustomerA : Observer
{
private YiXun subject;
public CustomerA(
YiXun subject)
{
this.subject = subject;
}
//更新
public override void Update()
{
Console.WriteLine("消费者A查看到的是{0}", this.subject.SubjecPhone);
}
public YiXun Subject
{
get { return subject; }
set { subject = value; }
}
}
class CustomerB : Observer
{
private YiXun subject;
public CustomerB(
YiXun subject)
{
this.subject = subject;
}
//更新
public override void Update()
{
Console.WriteLine("消费者B查看到的是{0}", this.subject.SubjecLaptop);
}
public YiXun Subject
{
get { return subject; }
set { subject = value; }
}
}
class Program
{
static void Main(string[] args)
{
YiXun subject = new YiXun();
subject.Register(new CustomerA(subject));
subject.Register(new CustomerB(subject));
// 来了新手机和新笔记本电脑,然后通知到所有注册的消费者.
// 然后消费者去查看自己感兴趣的产品有哪些.
subject.SubjecPhone = "IPhone5s";
subject.SubjecLaptop = "ThinkPad";
subject.Notify();
Console.Read();
}
}
}
~~~
**适用性:**
1. 一组对象以定义良好但是复杂的方式进行通信。产生的相互依赖关系结构混乱且难以理解。
1. 一个对象引用其他很多对象并且直接与这些对象通信,导致难以复用该对象。
1. 想定制一个分布在多个类中的行为,而又不想生成太多的子类。
优缺点:
1. 优点,降低命令发起者与命令接受者之间的耦合,发布者通过接口通知所有观察者。
1. 缺点,具体的观察者依赖了具体的发布者。
参考资料:
1. 《设计模式——可复用面向对象软件基础》
1. 《大话设计模式》