设计模式与游戏完美开发阅读笔记

一.面向对象设计中常见的设计原则

1.单一职责原则

当设计封装一个类时,该类应该只负一件事

2.开-闭原则

一个类应该“对扩展开放,对修改关闭”
将系统功能的“操作方法”向上提升,抽象为“接口”,将“功能的实现”向下移动子类中。
当新增功能的时候,应该继承父类或者继承旧的子类,并在新的子类中实现新增的功能。

3.里氏替换原则

“子类必须能够替换父类”,父类中一定包含了可被子类重新实现的方法,客户端在实现的过程中,必须不能使用到“对象强制转换为子类”的语句

4.依赖倒置原则

(1)高增模块不应该依赖于底层模块,两者都应该依赖于抽象概念

(2)抽象接口不应该依赖于实现,而实现应该依赖于抽象接口

5.接口隔离原则

“客户端不应该被迫使用它们用不到的接口方法”

二、游戏主循环 -GameLoop

  • 游戏软件和一般应用软件的区别:一般应用软件程序启动后会等待用户去操作它,给它命令,以被动的方式等待用户决定要执行的功能,这类软件大多数都是以“事件驱动”的方法来设计的,而游戏软件必须需要不断地进行“画面更新”以及一些逻辑的更新
  • 简单写法:
void main()
{
    //初始
    GameInit();
    //游戏主循环
    while(IsGameOver()==false)
    {
        //玩家控制
        UserInput();
        //游戏逻辑更新
        UpdategameLogic();
        //画面更新
        Render();
    }
    //释放资源
    GameRelease();
}
  • 在Unity3D中实现游戏循环:每个游戏对象都可以加上一个“脚本组件”。在这个脚本组件中定义的类,必须继承MonoBehavior,并且在类中加入特定的方法(Awake,Start,Update,…)当脚本附着的对象实例化时,这些方法就会按照特定顺序被调用
  • 如何为“单一游戏系统”加入定期更新功能:
    1. 某些游戏系统类不想依赖Unity引擎,即不想继承MonoBehavior并且挂在某个空对象上
    2. 首先创建一个空对象挂一个脚本,脚本中定义update函数
    3. 创建普通的类,在脚本中的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(统一对外的界面):
      • 整合所有子系统的接口和功能,并提供高级界面供客户端使用
      • 接受客户端的信息后,将信息传给负责的子系统
  • 使用外观模式的有点:
    • 可将客户端减少了不必要的类引用以及功能整合
    • 节省时间
    • 易于分工开发
    • 增加系统的安全性

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接口
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.抽象工厂模式

  • 定义:提供一个能够建立整个类群你或有关联的对象,而不必指明它们的具体类
发布了33 篇原创文章 · 获赞 3 · 访问量 630

猜你喜欢

转载自blog.csdn.net/qq_43647628/article/details/104321588