一.面向对象设计中常见的设计原则
1.单一职责原则
当设计封装一个类时,该类应该只负一件事
2.开-闭原则
一个类应该“对扩展开放,对修改关闭”
将系统功能的“操作方法”向上提升,抽象为“接口”,将“功能的实现”向下移动子类中。
当新增功能的时候,应该继承父类或者继承旧的子类,并在新的子类中实现新增的功能。
3.里氏替换原则
“子类必须能够替换父类”,父类中一定包含了可被子类重新实现的方法,客户端在实现的过程中,必须不能使用到“对象强制转换为子类”的语句
4.依赖倒置原则
(1)高增模块不应该依赖于底层模块,两者都应该依赖于抽象概念
(2)抽象接口不应该依赖于实现,而实现应该依赖于抽象接口
5.接口隔离原则
“客户端不应该被迫使用它们用不到的接口方法”
二、游戏主循环 -GameLoop
- 游戏软件和一般应用软件的区别:一般应用软件程序启动后会等待用户去操作它,给它命令,以被动的方式等待用户决定要执行的功能,这类软件大多数都是以“事件驱动”的方法来设计的,而游戏软件必须需要不断地进行“画面更新”以及一些逻辑的更新
- 简单写法:
void main()
{
//初始
GameInit();
//游戏主循环
while(IsGameOver()==false)
{
//玩家控制
UserInput();
//游戏逻辑更新
UpdategameLogic();
//画面更新
Render();
}
//释放资源
GameRelease();
}
- 在Unity3D中实现游戏循环:每个游戏对象都可以加上一个“脚本组件”。在这个脚本组件中定义的类,必须继承MonoBehavior,并且在类中加入特定的方法(Awake,Start,Update,…)当脚本附着的对象实例化时,这些方法就会按照特定顺序被调用
- 如何为“单一游戏系统”加入定期更新功能:
- 某些游戏系统类不想依赖Unity引擎,即不想继承MonoBehavior并且挂在某个空对象上
- 首先创建一个空对象挂一个脚本,脚本中定义update函数
- 创建普通的类,在脚本中的update函数中调用该类的更新函数。
- 游戏系统处理逻辑类最好不要对Unity3D有依赖,提高移植到其他项目的可能性,而需要挂在游戏角色上的脚本才需要继承MonoBehavior
三、游戏角色管理系统
- 所谓的游戏角色管理:角色管理系统类会将当前游戏产生的角色类对象“记录”下来,并提供接口让客户端可以新增,删除,获取这些的被记录的游戏对象,利用C#的List类来记录。
四、设计模式
1.状态模式
- 定义:让一个对象的行为随着内部状态的改变而变化,而该对象也像是换了类一样。
- 参与者:
- Context:(状态拥有者)
- State(状态接口类)
- ConcreteState(具体状态类)
- 状态拥有类
class Context
{
private State _state = null;
public void Request(int value)
{
_state.handle(value); //处理具体表现出的状态
}
public void SetState(State state)
{
_state = state;
}
}
该类是状态的拥有者,初始化的时候就将状态定义好,然后状态转换通过Request接口由状态实现类实现。
- 状态接口类
abstract class State
{
protected Context _context; //持有者,用于转换状态
public State(Context context)
{
_context = context;
}
public abstract void handle(int value);
}
该类是状态的接口类,所有状态实现类的父类,提高虚函数handle是每个状态实现类的实现函数,保存context对象用于子类中实现类状态类的转换
- 状态实现类
class StateA : State
{
public StateA(Context context) : base(context) { }
public override void handle(int value)
{
Console.WriteLine("A.handle");
if (value > 10)
_context.SetState(new StateB(_context));
}
}
- 使用场景:
- 利用状态模式实现游戏场景的转换
- 角色AI:使用状态模式来控制角色的AI行为
- 游戏服务器连线状态:不同的状态,会有不同的封包信息处理方式,需要分别实现
- 关卡进行状态:如果是通关型游戏,进入关卡时通常会分成多个阶段,包含加载数据、显示关卡信息,倒数通知开始,关卡结束和分数计算,可以使用不同的状态类来实现。
- 使用状态模式优点
- 减少错误的发生并降低维护难度
- 状态执行环境单一化
- 项目之间可以共享场景
- 使用状态模式缺点
- 在状态类过多的情况下会产生冗余的情况。
2.外观模式
- 定义:为子系统定义一组统一的接口,这个高级的接口会让子系统更容易被使用
- 参与者:
- client(客户端,客户):
- 一般可能是某个状态
- subSystem(子系统)
- Facade(统一对外的界面):
- 整合所有子系统的接口和功能,并提供高级界面供客户端使用
- 接受客户端的信息后,将信息传给负责的子系统
- client(客户端,客户):
- 使用外观模式的有点:
- 可将客户端减少了不必要的类引用以及功能整合
- 节省时间
- 易于分工开发
- 增加系统的安全性
3.单例模式
- 定义:确认类只有一个对象,并提供一个全局的方法来获取这个对象
- 使用方法:
class Myclass
{
private static Myclass instance;
public static getInstance()
{
get
{
if(instance==null)
{
instance = new Myclass();
}
return instance;
}
}
}
- 注意:使用单例模式的类必须只能产生一个对象且不能够被继承。
- 单例模式少用:违反了开-闭原则,方法返回的是实体类而不是接口类,也就是说程序员想要增加功能必须修改实体类而不是继承子类
- 少用单例模式时如何方便引用单一对象
- 让类具有计数功能来限制对象数量:在构造函数中维护一个计数器,当超过1的时候调用Assert函数停用程序
- 设置为类的引用,让对象可以被取用
- 分别设置
- 指定类的静态成员
- 其他应用方式:
- 网络在线游戏的客户端:可以使用单例模式来限制连接数,预防无用产生过多连接。
- 日志工具
4.中介者模式
- 定义:定义一个接口用来封装一群对象的互动行为。中介者通过移除对象之间的引用,来减少它们之间的耦合度,并且能改变它们的互动独立性。
- 参与者:
- Colleage(同事接口)
- 拥有一个Mediator属性成员,用于向中介者发送消息
- ConcreteColleage(同事实现类)
- Mediator(中介者接口)、ConcreteMediator(中介者接口实现类)
- Colleage(同事接口)
- Colleage接口
public abstract class Colleage
{
protected Mediator _mediator = null;
public Colleage(Mediator mediator)
{
_mediator = mediator;
}
//接收中介者传来的消息
public abstract void Request(string Message);
}
接口中统一定义了Request方法,用于接收中介者发回来的消息,由于每个子系统触发消息的条件不同,发送消息的函数在自己类中实现,其中引用中介类,用于调用发送消息的方法
- ConcreteColleage类
public class ConcreteColleage1 : Colleage
{
public ConcreteColleage1(Mediator mediator) : base(mediator){}
//接收中介者传来的消息
public override void Request(string Message)
{
Console.WriteLine(string.Format("College1接收到了一个{0}", Message));
}
public void Action()
{
//do something...
_mediator.SendMessage(this, "hello,2");
}
}
实现类中实现了自己的发送消息函数
- Mediator接口和实现类
public abstract class Mediator
{
public abstract void SendMessage(Colleage colleage,string message);
}
public class ConcreteMeadiator : Mediator
{
ConcreteColleage1 cc1 = null;
ConcreteColleage2 cc2 = null;
public void setCollege1(ConcreteColleage1 c1)
{
cc1 = c1;
}
public void setCollege2(ConcreteColleage2 c2)
{
cc2 = c2;
}
public override void SendMessage(Colleage colleage, string message)
{
if(cc1 == colleage)
{
cc2.Request(message);
}
else if(cc2 == colleage)
{
cc1.Request(message);
}
}
}
接口中定义了发送消息的函数,实现类中实现了接收消息的函数,中介者类中引用了所有维护的Colleage类了定义,只要需要用到该类就会调用set该Colleage类的方法实例化,就可以用于接收该类的消息了
- 中介者模式优点
- 不会引入太多其他的系统
- 系统被依赖的程度也降低
- 中介者模式缺点
- 身为模式中的中介者角色类,也会存在着接口过大的风险,此时必须再配合其他模式来进行优化
5.桥接模式
- 起因:若干个角色和武器之间可以任意的排列组合,第一种方法在武器类中有枚举,类中维护一个属性表明属于哪种属性,然后角色类中面对不同的武器播放不同的音效和处理不同的逻辑,这样在新增武器或角色处理起来很麻烦
- 定义:将抽象与实现分离,使二者可以独立变化
- 应用1:如果要避免被限制在只能以“继承实现”来完成功能实现,可考虑使用桥接模式。(关系呈现“交叉组合汇编”的情况)
- 应用2:当两个群组因为功能上的需求,想要连接合作,但有希望两组类可以各自发展不受彼此影响也可以用桥接模式。
- 中心思想:两个群组分为抽象群组和实现群组,每个抽象群组的实现都能对应实现群组的实现,然后将抽象群组和实现群组分别集成出接口类,这样集成“抽象接口”的子类实现功能时,只要通过“实现类”的对象引用来调用实现功能即可
- 参与成员:
- 抽象体接口和抽象体实现
- 实现体接口和实现体
- 需求接口和需求实现
//需求类接口
public abstract class Ishape
{
protected RenderEngine _render = null;
public void SetRender(RenderEngine render)
{
_render = render;
}
public abstract void draw();
}
public class Sphere : Ishape
{
public override void draw()
{
_render.Render();
}
}
public class Cube : Ishape
{
public override void draw()
{
_render.Render();
}
}
需求接口中保留实现接口的引用,可以调用实现类的方法
- 实现接口和实现类实现
//绘图引擎接口
public abstract class RenderEngine
{
public abstract void Render();
}
public class DirectX : RenderEngine
{
public override void Render()
{
Console.WriteLine("DirectX render");
}
}
public class OpenGL: RenderEngine
{
public override void Render()
{
Console.WriteLine("OpenGL render");
}
}
- 最佳使用场景:角色和特效、武器、载具之间的联系。
6.策略模式
- 起因:因为角色的不同,角色类中的有些属性初始化的时候需要对不同的角色做不同的计算,导致同类型的计算分布在角色类中,不易阅读,且新增角色类型需要对这些计算全部改动。
- 定义:定义一组算法,并封装每个算法,让它们可以彼此交换使用。策略模式让这些算法在客户端使用它们时能更加独立。
- 应用场景:因条件的不同而需有所选择时。
- 参与者:
- 策略接口类:提供“策略客户端”可以使用的方法
- 策略实现类:不同算法的实现
- 策略客户端:拥有一个策略接口类的对象引用,并通过对象引用获取想要的计算结果
- 如何消除if-else正确使用策略模式:首先将角色的属性提取成一个单独的接口,并派生出士兵和敌人两个属性类,分别记录自己独有的属性,然后定义算法接口,分别派生士兵和敌人的算法类实现计算算法,通过属性接口调用算法,实现计算,会根据接口的类型判断选择士兵算法还是敌人算法,接口类型又由角色类中设定
- 使用策略模式的优点
- 让角色属性变得好维护
- 不必在针对角色类型编写程序代码
- 计算公式的替换更为方便
7.模板方法模式
- 定义:在一个操作方法中定义算法的流程,其中某些步骤由子类完成。魔板方法模式让子类在不变更原有算法流程的情况下,还能够重新定义其中的步骤。简单说就是:在父类中简单的实现算法的定义(有哪些步骤组成),有些步骤的具体实现由子类实现,因为有些步骤会根据当前情况而发生具体的变化。
- 参与者:
- 算法定义类
- 算法步骤的实现类
- 算法定义类:
public abstract class Myclass
{
public void step()
{
step1();
Step2();
}
protected abstract void step1();
protected abstract void step1();
}
- 算法实现类:
public class sx1 : Myclass
{
protected override void step1()
{
...
}
protected override void step2()
{
...
}
}
public class sx2: Myclass
{
protected override void step1()
{
...
}
protected override void step2()
{
...
}
}
- 其他应用方式:
- 对于游戏角色施展一个法术时候,会有许多特定的检查条件,如魔力是否足够,是否还在冷却时间内,对象是否在法术施展范围内等。应该将检查流程固定下来,真正的检查功能交给各个法术子类去实现。
- 在线游戏的角色登录
8.工厂方法模式
- 定义:定义一个可以产生对象的接口,但是让子类决定要产生哪一个类的对象。工厂方法模式让类的实例化程序延迟到子类中实施。
- 参与者:
- 产品接口和产品类
- 工厂接口和工厂类
具体实现:工厂接口中定义了两个函数分别是生成士兵和敌人,然后在工厂类中具体实现,返回为已经实例化的产品接口。其中可以用传入参数的方法,也可以用模板的方法减少switchcase语句。
- 应用情况:当类的对象产生时,如果出现下列情况:
- 需要复杂的流程
- 需要加载外部资源
- 有对象上限
- 可重复使用
9.建造者模式
- 起因:工厂类是将生产对象的地点全部集中到一个地点管理,但是如何在生产对象的过程中,能更有效率并且更具弹性,则需要搭配其他的设计模式。建造模式就是常用来搭配使用的模式之一。
- 定义:将一个复杂对象的构建流程与它的对象表现分离出来,让相同的构建流程可以产生不同的对象行为表现。
- 两个原则:“流程分析安排”和“功能分开实现”
- 参与者说明:
- Director建造指式者
- Builder功能实现接口
- ConcreteBuilder功能实现者
- Product产品
- 在重构后的角色工厂中,只简单负责角色的“产生”,而复杂的功能组装工作则交给新增加的角色建造者系统来完成。
- 将工厂类中的加工方法抽象出来,首先由建造指示者中的类来进行角色的产生,以及在constructor函数中定义加工的粗略步骤,传入功能实现接口,具体实现由功能实现者实现。然后在指示者将加工好的对象返回出去。
10.享元模式-游戏属性的管理
- 起因:用来解决“大量且重复的对象”的管理问题。有些敌人具有相同的角色属性却是一个类实例化出的不同对象。
- 定义:使用共享的方式,让一大群小规模的对象能更有效地运行。比如让一些公共属性为每个角色所共享,而不是每个角色都持有一个属性对象。
- 对象中那些“只能读取不能写入”的共享部分被称为“内在状态”,而随着游戏运行而发生变化的,称为“外在状态”,享元模式就是讲“内在状态”统一管理
- 参与者:
- 工厂类
- 可以共享的组件
- 不可以共享的组件
- 共享的组件
public class SharedComponent
{
private string attr1;
private string attr2;
public SharedComponent(string _attr1,string _attr2)
{
attr1 = _attr1;
attr2 = _attr2;
}
public string getAttr1()
{
return attr1;
}
public string getAttr2()
{
return attr2;
}
public void oprator()
{
...
}
}
- 不共享的组件
public class UnsharedComponent
{
private SharedComponent shar = null;
private string attr1;
private string attr2;
public UnsharedComponent(string _attr1,string _attr2)
{
attr1 = _attr1;
attr2 = _attr2;
}
public void setSharedComponent(SharedComponent _shar)
{
shar = _shar;
}
public void setSharedComponent(SharedComponent _shar)
{
shar = _shar;
}
public void opreator()
{
...
}
}
- 工厂类
public class ComponetFactory
{
Dictionary<int,SharedComponent> shared = new Dctionary<int,SharedComponent>();
//设置共享组件
public bool SetComponent(int key,SharedComponent share)
{
if(shared.constainsKey(key)
{
return false;
}
shared[key] = share;
return false;
}
//获得共享组件
public SharedComponent GetSharedComponent(key)
{
return shared[key];
}
//设置并获得非共享组件
public UnsharedComponent GetSharedComponent(strin g content1,string content2)
{
return new UnsharedComponent(content1,content2);
}
//获得所有组件
public UnsharedComponent GetComponent(int key,string content1,string content2)
{
SharedComponent share = GetSharedComponent(key);
UnsharedComponent unshare = new UnsharedComponent(content1,content2);
unshare.SetSharedComponent(share);
return unshare;
}
}
11.组合模式
- 定义:将对象以树状结构组合,用以表现部分,全体的层次关系。组合模式让客户端在操作各个对象或组合对象时是一致的。
- 参与者:
- 组件界面:定义树状结构中,每一个节点可以使用的操作方法。作为组合节点和叶节点的接口
- 组合节点:会包含叶节点的对象,会实现组件界面中与子节点操作相关的方法。如Add,Remove,GetChild等
- 叶节点:包含实际的对象
- 组件界面
public abstract class IComponent
{
protected string m_value;
//一般操作
public abstract void Operation();
public virtural void Add(IComponent theComponent){}
public virtural void Remove(IComponent theComponent){}
public virtural void GetChild(IComponent theComponent){}
}
- 组合节点
public abstract class Composite : IComponent
{
private List<Icomponent> m_childs = new List<Icomponent>();
public Composite(string value)
{
m_value = value;
}
//一般操作
public abstract void Operation()
{
foreach(IComponent child in m_childs)
{
child.Operation();
}
}
public override void Add(IComponent theComponent)
{
m_childs.Add(theComponent);
}
public override void Remove(IComponent theComponent)
{
m_childs.Remove(theComponent);
}
public override IComponent GetChild(int index)
{
return m_childs[index];
}
}
- 叶节点
public class Leaf : Icomponent
{
public Leaf(string value)
{
m_value = value;
}
public override void Operation()
{
//..do sth about value
}
}
- 使用Unity需要注意的问题:
不要直接使用GameObject.Find()寻找UI,先Find()到Canvas,再利用Transform找到真正的对象。 - 如何设计游戏界面:首先定义游戏界面接口,所有界面类都继承于该接口,其中定义了GameObject类型m_RootUI作为每个界面类的一个根节点,可以找到该类的所有节点,然后定义一些常用方法,如IsVisible,show,hide,initialize,release,update等,而在各个界面类中保存各个叶节点(具体UI控件)的引用,在初始化的过程中就会指定m_RootUI指向的根节点。
- UITool:与UI有关的工具类,用于实现在Canvas对象下面找到特定名称的游戏对象
public static class UITool
{
private static GameObject canvas_obj = null;
//寻找限定在Canvas画布的UI界面
public static GameObject FindUIGameObject(string UIName)
{
if(canvas_obj == null)
canvas_obj = UnityTool.FindGameObject("Canvas");
if(canvas_obj == null)
return null;
return UnityTool.FindChildGameObject(canvas_obj,UIName);
}
//获取UI组件
public static T GetUIComponent<T>(GameObject Container,string UIName) where T : UnityEngine.Component
{
//找出子对象
GameObject ChildgameObject = UnityTool.FindChildGameObject(Container,UIName);
T comp = ChildGameObject.GetComponent<T>();
return comp;
}
}
- UnityTool的实现
public static class UnityTool
{
public static GameObject FindGameOject(string Name)
{
GameObject obj = GameObject.Find(Name);
return obj;
}
public static GameObject FindChildGameObject(GameObject Container,string name)
{
Transform trans = null;
//是不是Container本身
if(Container.name == gameobjectName)
trans = Container.trans;
else
{
Transform[] allChildren = Container.transform.GetComponentsInChildren<Transform>();
foreach(Transform child in alllChildren)
{
if(child.name == gamobjectName)
trans = child;
}
}
return trans.gameObject;
}
}
12.命令模式
- 定义:将请求封装成为对象,让你可以将客户端的不同请求参数化,并配合队列、记录、复原等方法来执行请求的操作。
- 请求的封装
- 请求的操作
- 参与者
- 命令接口:定义命令封装后要具备的操作界面
- 命令实现:实现命令封装和界面,会包含每一个命令参数和Receiver。
- Receiver功能执行者:被封装在命令实现类中,真正执行功能的类对象
- 客户端/命令发起者
- 命令管理者:命令对象的管理容器或管理类,并负责要求每个命令执行其功能
- 功能执行者
public class Receiver1
{
public Receiver1(){}
public void Action(string Commend)
{
do something...
}
}
public class Receiver2
{
public Receiver2(){}
public void Action(string Commend)
{
do something...
}
}
- 命令接口
public abstract class Command
{
public abstract void Execute();
}
- 命令实现
//与执行者1绑定
public class ConcreteComand1 : Command
{
Receiver1 receiver = null;
string m_command;
public ConcreteCommand1(Receiver1 _receiver , string commend)
{
receiver = _receiver;
m_commend = commend;
}
public override void Execute()
{
receiver1.Action(m_command);
}
}
//与执行者2绑定
public class ConcreteComand2 : Command
{
Receiver2 receiver = null;
string m_command;
public ConcreteCommand2(Receiver2 _receiver , string commend)
{
receiver = _receiver;
m_commend = commend;
}
public override void Execute()
{
receiver2.Action(m_command);
}
}
命令管理类
public class Invoker
{
List<Command> command = new List<Command>();
//加入命令
public void AddCommand(Command _command)
{
commands.Add(_command);
}
//执行命令
public void ExecuteCommand()
{
foreach(Command _command in command)
_command.Execute();
command.Remove(_command);
}
}
- 命令模式适用情况:
- 需要实现大量的请求命令时
- 当命令不是立即被执行,需要一定条件才能执行时
- 命令模式缺点
- 类过多
- 请求对象不需要被管理,所以一些命令会立即执行就不用命令模式
- 其他应用方式:
- 对于Client/Server间数据封包的传递,大多都会使用命令模式。
13.责任链模式-关卡设计
- 定义:让一群对象都有机会来处理一项请求,以减少请求发送者与接受者之间的耦合度。所有的接受者对象串接起来,让请求沿着串接传递,直到有一个对象可以处理为止。
- 参与者
- Handler请求接收者接口
- ConcreteHandler请求接收者实现
- Client请求发送者,将接受者串接
- 请求接收者接口
public class Handler
{
protected Handler m_nextHandler = null;
public virtual void HandleRequest(int Cost)
{
if(m_nextHandler!=null)
{
m_nextHandler.HandleRequest();
}
}
}
- 请求接受者实现
public class ConcreteHandler : Handler
{
private int costRequest = null;
public ConcreteHandler(Handler handler,int _costRequst):base(handler)
{
costRequest = _costRequest;
}
public override void HandleRequest(int cost)
{
if(cost<costRequest)
..handle by myself
else
m_nextHandler.HandleRequest(cost);
}
}
14.观察者模式-成就系统
- 起因:能让“游戏事件的产生与通知”独立成为一个系统,并且让其它系统能通过“订阅”或“关注”的方式,来追踪游戏事件系统发生的事。
- 定义:在对象之间定义一个一对多的连接方法,当一个对象变换状态时,其他关联对象都会自动收到通知。
- 参与者
- 主题接口和主题实现
- 观察者接口和观察者实现
- 事件委托和观察者模式实例:猫和老鼠
//自定义的CatShoutEventArgs类,继承自EventArgs
//用作保存Cat对象的Name属性,还可以扩展其他的功能
public class CatShoutEventArgs : EventArgs
{
public string CatName { get; set; }
public CatShoutEventArgs(string name)
{
this.CatName = name;
}
}
//定义一个委托 名叫CatShoutEventHandler
public delegate void CatShoutEventHandler
(object sender,CatShoutEventArgs e);
//猫类
public class Cat
{
public string Name { get; set; } //猫的名字
//在猫类里定义一个事件CatShout,返回值类型是定义的委托
public event CatShoutEventHandler CatShout;
public void Shout()
{
Console.WriteLine("喵喵喵");
//判断委托内是否为空,若不为空,执行该委托
if (CatShout != null)
{
//new一个CatShoutEventArgs类,传入参数是自身的Name
CatShoutEventArgs e = new CatShoutEventArgs(Name);
//执行CatShout事件,传入自身和e
CatShout(this, e);
}
}
//无参和带参构造
public Cat() { }
public Cat(string name)
{
this.Name = name;
}
}
//老鼠类
public class Rat
{
public string Name { get; set; } //老鼠的名字
//老鼠的逃跑方法
public void Run(object sender, CatShoutEventArgs e)
{
//打出一句话,包括了猫的名字和老鼠的名字
Console.WriteLine(e.CatName + "来了! " + Name + "跑了!");
}
//无参和带参构造
public Rat() { }
public Rat(string name)
{
this.Name = name;
}
}
15.备忘录模式-存盘功能
- PlayerPref类:Unity3D引擎提供的类,使用Key-Value的形式将信息存放在文件系统中,不需自行指定文件路径及名称,适合存储简单的数据
- 定义:在不违反封装的原则下,获取一个对象的内部状态并保留在外部,让该对象可以再日后恢复到原先保留时的状态。
- 参与者:
- 记录拥有者
- 记录保存者:无法获取记录拥有者的信息,必须由记录拥有者主动设置和读取。
- 管理记录保存者:可以增加对象管理容器来保存多个记录保存者。
- 记录拥有者
public class Originator
{
string m_state;
public void SetInfo(string state)
{
m_state = state;
}
public void ShowInfo(){..show}
//产生要存储的记录
public Memento CreateMemento()
{
Memento newMemento = new Memento();
newMemento.SetState(m_state);
return newMemento;
}
//恢复记录
public void SetMemento(Memento m)
{
m_state = m.GetState();
}
}
- 记录保持者
public class Memento
{
string m_state;
public string GetState()
{
return m_state;
}
public void SetState(string state)
{
m_state = state;
}
}
- 记录管理者
public class Caretaker
{
Dictionary<string,Memento> m_Mementos = new Dictionary<string,Memento>();
//增加记录
public void AddMemento(string version,Memento mem)
{
if(m_Mementos.ContainsKey(version)==false)
{
m_Mementos.Add(version,mem);
}
else
m_Mementos[version] = mem;
}
//读取记录
public Memento GetMemento(string version)
{
if(m_Mementos.ContainsKey(version)==false)
return null;
return m_Mementos[version];
}
}
16.访问者模式
- 定义一个能够在一个对象结构中对于所有元素执行的操作。访问者让你可以定义一个新的操作,而不必更改到被操作元素的类接口
- 主要用于遍历某个容器干某些事情
- 访问者
public abstract class IShapeVisitor
{
public virtual void VisitSphere(Sphere sphere){}
public virtual void VisitCube(Cube cube){}
}
- 容器
public class ShapeContainer
{
List<IShape> m_shapes = new List<IShape>();
public void AddShape(IShape theShape);
//共享的访问者接口
public void RunVisitor(IShapeVisitor visitor)
{
foreach(IShape shape in m_shapes)
shape.RunVisitor(visitor);
}
}
- 形状接口和形状类
public abstract class Ishape
{
protected RenderEngine _render = null;
public void SetRender(RenderEngine render)
{
_render = render;
}
public abstract void draw();
public abstract void RunVisitor(IShapeVisitor visitor);
}
public class Sphere : Ishape
{
public override void draw()
{
_render.Render();
}
public override void RunVisitor(IShapeVisitor visitor)
{
visitor.VisitSphere(this);
}
}
public class Cube : Ishape
{
public override void draw()
{
_render.Render();
}
}
- 绘图功能的Visitor
public class DrawVisitor : IShapeVisitor
{
public override void VisitSphere(Sphere sphere)
{
sphere.Draw();
}
public override void VisitCube(Cube cube)
{
cube.Draw();
}
}
17.装饰模式|适配器模式|代理模式
- 三者都是解决:在现有系统中存在A和B两个类,并存储B继承A的关系,现有一个新增功能要加入这个系统中,而这个新功能以C类来实现,那么这个C类要如何加入到原有的系统之中。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LAXEdFD0-1581693017351)(D://1.png)]
(1)装饰模式-前缀字尾
- 定义:动态地附加额外的责任给一个对象。装饰模式提供了一个灵活的选择,让子类可以用来扩展功能。
- 参与者:
- 形状接口和形状实现
- 形状装饰者和形状装饰者的实现(外框装饰者)
主要用于:“目标早已经存在,而装饰需求之后才出现”,适合后期增加系统功能时使用。
- 形状装饰者接口
public abstract class IShapeDecorator : IShape
{
IShape shape;
public IShapeDecorator(IShape _shape)
{
shape = _shape;
}
public override void Draw()
{
shape.Draw();
}
}
- 附带额外功能的类
public abstract class Border
{
public override void DrawOnShape(IShape shape)
{
//装饰外框
}
}
外框装饰者
public class BorderDecorator : IShapeDecorator
{
public BorderDecorator(Ishape shape):base(shape){}
public overrride Draw()
{
base.Draw();
Border border = new Border();
border.DrawOnShape(shape);
}
}
(2)适配器模式-俘兵
- 定义:将一个类的接口转换成为客户端期待的类接口。适配器模式让原本接口不兼容的类能一起合作。
- 参与者:
- client客户端:客户端预期使用的是Target目标接口对象
- Target目标接口:定义提供给客户端使用的接口
- adaptee被转换类:与客户端预期接口不同的类
- Adapter适配器
- 目标接口
public abstract class Target
{
public abstract void Request();
}
- 适配器
public abstract class Adapter : Target
{
private Adaptee ad = null;new Adaptee();
public Adapter(Adaptee _ad)
{
ad = _ad;
}
public override void Request()
{
//change ad..
}
}
- 被转换类(略)
(3)代理模式-加载速度的优化
- 代理模式与装饰模式的区别:对于代理模式来说,它可以选择新功能是否要执行,而装饰模式则是除了原有之外,也一定要执行新功能。
- 定义:提供一个代理者位置给一个对象,好让代理者可以控制存取这个对象。
- 客户端
- Subject操作接口
- RealSubject功能执行
- Proxy代理者
- subject(略)
- RealSubject(略)
- Proexy
public class Proxy : Subject
{
RealSubject rs = new RealSubject();
//权限控制
public bool ConnectRemote{get;set}
public Proxy()
{
Connectremote = fasle;
}
public override void Request()
{
if(Connectremote)
rs.Request();
else
..报错
}
}
18.迭代器模式
- 定义:在不知道集合内部细节的情况下,提供给一个按序方法存取一个对象集合体的每一个单元。
19.原型模式
- 定义:使用原型对象来产生指定类的对象,所以产生对象时,是使用原型对象来完成
19.解释器模式
- 定义:定义一个程序设计语言所需要的语句,并提供解释来解析(执行)该语言。
20.抽象工厂模式
- 定义:提供一个能够建立整个类群你或有关联的对象,而不必指明它们的具体类