【笔记】大话设计模式17-20
文章目录
- 【笔记】大话设计模式17-20
- 17 适配器模式
- 17.1 Example
- 17.2 定义
- 17.3 Show me the code
- 17.4 总结
- 18 备忘录模式
- 18.1 Example
- 18.2 定义
- 18.3 Show me the code
- 18.4 总结
- 19 组合模式
- 19.1 Example
- 19.2 定义
- 19.3 Show me the code
- 19.4 总结
- 20 迭代器模式
- 20.1 Example
- 20.2 定义
- 20.3 Show me the code
- .NET IEumerator:对非泛型几何的简单迭代接口
- 20.4 总结
17 适配器模式
17.1 Example
大头的一个外国朋友,要来中国旅游,但是他的朋友不懂中文,于是大头做起了他的随身翻译官。去哪个景点,大头朋友和当地人交流的时候,他们的对话,如果转换成一种语言,都是对的,因此,大头翻译的作用就是,和当地人交流时,获取当地语言的含义,然后转换成外语,即同一种语言适配到另一种语言,这就是适配器模式。
17.2 定义
适配器模式(Adapter)
:将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不能兼容而不能一起工作的那些类可以一起工作。
17.3 Show me the code
class Program
{
static void Main(string[] args)
{
Target target = new Adapter();
target.Request();
Console.Read();
}
}
class Target
{
public virtual void Request()
{
Console.WriteLine("普通请求");
}
}
class Adaptee
{
public void SpecificRequest()
{
Console.WriteLine("特殊请求");
}
}
class Adapter : Target
{
private Adaptee adaptee = new Adaptee();
public override void Request()
{
adaptee.SpecificRequest();
}
}
Adapter并没有直接实现Target的Request函数,而是根据需要,调用了需要适配的类的特殊请求函数SpecificRequest()。也就是需要翻译时,调用对方语言接口,将当地语言转换过去。
再看下NBA篮球翻译适配器:
class Program
{
static void Main(string[] args)
{
Player b = new Forwards("巴蒂尔");
b.Attack();
Player m = new Guards("麦克格雷迪");
m.Attack();
//Player ym = new Center("姚明");
Player ym = new Translator("姚明");
ym.Attack();
ym.Defense();
Console.Read();
}
}
//篮球运动员
abstract class Player
{
protected string name;
public Player(string name)
{
this.name = name;
}
public abstract void Attack();
public abstract void Defense();
}
//前锋
class Forwards : Player
{
public Forwards(string name)
: base(name)
{
}
public override void Attack()
{
Console.WriteLine("前锋 {0} 进攻", name);
}
public override void Defense()
{
Console.WriteLine("前锋 {0} 防守", name);
}
}
//中锋
class Center : Player
{
public Center(string name)
: base(name)
{
}
public override void Attack()
{
Console.WriteLine("中锋 {0} 进攻", name);
}
public override void Defense()
{
Console.WriteLine("中锋 {0} 防守", name);
}
}
//后卫
class Guards : Player
{
public Guards(string name)
: base(name)
{
}
public override void Attack()
{
Console.WriteLine("后卫 {0} 进攻", name);
}
public override void Defense()
{
Console.WriteLine("后卫 {0} 防守", name);
}
}
//外籍中锋
class ForeignCenter
{
private string name;
public string Name
{
get { return name; }
set { name = value; }
}
public void 进攻()
{
Console.WriteLine("外籍中锋 {0} 进攻", name);
}
public void 防守()
{
Console.WriteLine("外籍中锋 {0} 防守", name);
}
}
//翻译者
class Translator : Player
{
private ForeignCenter wjzf = new ForeignCenter();
public Translator(string name)
: base(name)
{
wjzf.Name = name;
}
public override void Attack()
{
wjzf.进攻();
}
public override void Defense()
{
wjzf.防守();
}
}
17.4 总结
就像电脑电源适配器和以前的万能充,只要有电源进来,我都能调整到匹配当前电脑或者电池的电压值,这就是适配器的作用。
使用适配器,需要在双方都不太容易修改的时候使用适配器模式适配。
18 备忘录模式
18.1 Example
阿三特别喜欢玩游戏。记得上大一的时候,宿舍还要断电,一断电后,宿舍里的交换机也停电了,会断网。不过还好,他玩的游戏有保存进度的功能,在快要停电的前3分钟,他只要保存好进度,第二天有电的时候,就能继续玩了。
这种保存进度的功能就相当于备忘录,等到下一次再读取的时候,就都回忆起来了。
18.2 定义
备忘录(Memento)
:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态
18.3 Show me the code
// 主函数
class Program
{
static void Main(string[] args)
{
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.Read();
}
}
// 设置备忘录状态
class Originator
{
private string state;
public string State
{
get { return state; }
set { state = value; }
}
public Memento CreateMemento()
{
return (new Memento(state));
}
public void SetMemento(Memento memento)
{
state = memento.State;
}
public void Show()
{
Console.WriteLine("State=" + state);
}
}
// 备忘录类
class Memento
{
private string state;
public Memento(string state)
{
this.state = state;
}
public string State
{
get { return state; }
}
}
class Caretaker
{
private Memento memento;
public Memento Memento
{
get { return memento; }
set { memento = value; }
}
}
看一个具体的打游戏保存状态的示例
class Program
{
static void Main(string[] args)
{
//大战Boss前
GameRole lixiaoyao = new GameRole();
lixiaoyao.GetInitState();
lixiaoyao.StateDisplay();
//保存进度
RoleStateCaretaker stateAdmin = new RoleStateCaretaker();
stateAdmin.Memento = lixiaoyao.SaveState();
//大战Boss时,损耗严重
lixiaoyao.Fight();
lixiaoyao.StateDisplay();
//恢复之前状态
lixiaoyao.RecoveryState(stateAdmin.Memento);
lixiaoyao.StateDisplay();
Console.Read();
}
}
class GameRole
{
//生命力
private int vit;
public int Vitality
{
get { return vit; }
set { vit = value; }
}
//攻击力
private int atk;
public int Attack
{
get { return atk; }
set { atk = value; }
}
//防御力
private int def;
public int Defense
{
get { return def; }
set { def = value; }
}
//状态显示
public void StateDisplay()
{
Console.WriteLine("角色当前状态:");
Console.WriteLine("体力:{0}", this.vit);
Console.WriteLine("攻击力:{0}", this.atk);
Console.WriteLine("防御力:{0}", this.def);
Console.WriteLine("");
}
//保存角色状态
public RoleStateMemento SaveState()
{
return (new RoleStateMemento(vit, atk, def));
}
//恢复角色状态
public void RecoveryState(RoleStateMemento memento)
{
this.vit = memento.Vitality;
this.atk = memento.Attack;
this.def = memento.Defense;
}
//获得初始状态
public void GetInitState()
{
this.vit = 100;
this.atk = 100;
this.def = 100;
}
//战斗
public void Fight()
{
this.vit = 0;
this.atk = 0;
this.def = 0;
}
}
//角色状态存储箱
class RoleStateMemento
{
private int vit;
private int atk;
private int def;
public RoleStateMemento(int vit, int atk, int def)
{
this.vit = vit;
this.atk = atk;
this.def = def;
}
//生命力
public int Vitality
{
get { return vit; }
set { vit = value; }
}
//攻击力
public int Attack
{
get { return atk; }
set { atk = value; }
}
//防御力
public int Defense
{
get { return def; }
set { def = value; }
}
}
//角色状态管理者
class RoleStateCaretaker
{
private RoleStateMemento memento;
public RoleStateMemento Memento
{
get { return memento; }
set { memento = value; }
}
}
结果:
角色当前状态:
体力:100
攻击力:100
防御力:100
角色当前状态:
体力:0
攻击力:0
防御力:0
角色当前状态:
体力:100
攻击力:100
防御力:100
18.4 总结
Memento模式比较适用于功能复杂的,需要维护或者记录属性历史的类,或者需要保存的属性只是众多属性中的一小部分时,Originatior可以根据保存的Memento信息还原到前一状态。
19 组合模式
19.1 Example
大头开了一家公司,因为效益还不错,公司逐步扩大,全国各个省份,甚至在国外都开设了分公司。
公司多了,那么就需要统一一套业务管理流程,否则各个分公司系统都不同,进行业务交流、合并与拆分的时候,将非常麻烦。
有了统一的一套系统,就能将所有公司管理起来,提高效率,也有利于发展。
19.2 定义
组合模式(Composite)
:将对象组合成树形结构以表示‘部分-整体’的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
19.3 Show me the code
class Program
{
static void Main(string[] args)
{
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"));
comp.Add(comp2);
root.Add(new Leaf("Leaf C"));
Leaf leaf = new Leaf("Leaf D");
root.Add(leaf);
root.Remove(leaf);
root.Display(1);
Console.Read();
}
}
abstract class Component
{
protected string name;
public Component(string name)
{
this.name = name;
}
public abstract void Add(Component c);
public abstract void Remove(Component c);
public abstract void Display(int depth);
}
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(new String('-', depth) + name);
foreach (Component component in children)
{
component.Display(depth + 2);
}
}
}
class Leaf : Component
{
public Leaf(string name)
: base(name)
{ }
public override void Add(Component c)
{
Console.WriteLine("Cannot add to a leaf");
}
public override void Remove(Component c)
{
Console.WriteLine("Cannot remove from a leaf");
}
public override void Display(int depth)
{
Console.WriteLine(new String('-', depth) + name);
}
}
结果:
-root
---Leaf A
---Leaf B
---Composite X
-----Leaf XA
-----Leaf XB
-----Composite XY
-------Leaf XYA
-------Leaf XYB
---Leaf C
看一下实际公司的例子
19.4 总结
- 当需求中是体现部分与整体层次的结构时,希望用户可以忽略组合对象与当个对象的不同,统一使用组合结构中的所有对象时,考虑用组合模式。
- ASP.NET中的TreeView控件就是典型的组合模式应用。所有的Web控件的基类都是System.Web.UI.Control,而Control基类中就有Add和Remove方法。
- 组合模式让客户可以一致地使用组合结构和单个对象,他们地方法相同,无差别。
20 迭代器模式
20.1 Example
早在阿三上学的时候,每次回家或者去学校,都要坐公交车宁井或者东井,但是因为这2趟公交车是郊区线,所以班次不是很多。因此,每次公交车上都是人山人海,能站在上面已经很不错了。那时候郊区线还不能刷卡,全靠售票员一个一个售票,从车头到车位,先记住刚上车的人,然后依次走过去售票,记忆力也是惊人,凭借售票员一个一个地去找人,完成售票过程。
20.2 定义
迭代器模式(Iterator)
:提供一种方法顺序访问一个聚合对象中各个元素,而不暴露该对象的内部表示。
20.3 Show me the code
static void Main(string[] args)
{
ConcreteAggregate bus = new ConcreteAggregate();
bus.Add("阿三");
bus.Add("大头");
bus.Add("小灵通");
bus.Add("Randy");
bus.Add("公交内部员工");
bus.Add("小偷");
Iterator i = new ConcreteIterator(bus);
//Iterator i = new ConcreteIteratorDesc(a);
object item = i.First();
while (!i.IsDone())
{
Console.WriteLine("{0} 请买车票!", i.CurrentItem());
i.Next();
}
Console.Read();
}
// 聚集抽象类
abstract class Aggregate
{
public abstract Iterator CreateIterator();
}
//具体的聚集抽象类
class ConcreteAggregate : Aggregate
{
// 声明一个IList泛型变量,用于存放聚合对象,用ArrayList同样可以实现。
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); }
}
}
// Iterator迭代器抽象类,可以实现不同方式的迭代器
abstract class Iterator
{
// 为遍历不同的聚集结构提供如开始、下一个、是否结束、当前哪一项等统一接口
public abstract object First();
public abstract object Next();
public abstract bool IsDone();
public abstract object CurrentItem();
}
// 具体迭代器,继承Iterator
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;
}
// 当前对象
public override object CurrentItem()
{
return aggregate[current];
}
// 判断当前是否遍历到结尾,到结尾返回TRUE
public override bool IsDone()
{
return current >= aggregate.Count ? true : false;
}
}
//倒序迭代器
class ConcreteIteratorDesc : Iterator
{
private ConcreteAggregate aggregate;
private int current = 0;
public ConcreteIteratorDesc(ConcreteAggregate aggregate)
{
this.aggregate = aggregate;
current = aggregate.Count - 1;
}
public override object First()
{
return aggregate[aggregate.Count - 1];
}
public override object Next()
{
object ret = null;
current--;
if (current >= 0)
{
ret = aggregate[current];
}
return ret;
}
public override object CurrentItem()
{
return aggregate[current];
}
public override bool IsDone()
{
return current < 0 ? true : false;
}
}
.NET IEumerator:对非泛型几何的简单迭代接口
public interface IEumerator
{
object Current
{
get;
}
bool MoveNext();
void Reset();
}
public interface IEnumerable
{
IEumerator GetEnumerator();
}
foreach in其实做了下面的等价内容
IEnumerator<string> e = bus.GetEnumerator();
while(e.MoveNext())
{
// work
}
20.4 总结
-
使用场景:当需要访问一个聚集对象,而且不管这些对象是什么都需要遍历的时候,就应该考虑用迭代器模式;
像
foreach in
、IEnumerable
接口都是为迭代器准备的。 -
迭代器模式是为了分离集合对象的遍历行为,抽象出一个迭代器类来负责,这样即可以不暴露集合的内部结构,又可让外部代码透明地访问集合内部的数据;
-
迭代器模式在访问数组、集合、列表,特别是数据库操作时,非常方便。