[读书笔记] 设计模式与游戏完美开发

最近在看《设计模式与游戏完美开发》,文章将记录一些要点和一些设计模式实现

面向对象的六大原则

  1. 单一职责原则
  2. 开闭原则,对扩展开放,对修改关闭
  3. 里氏替换原则,所有使用父类的地方必须能够使用其子类的对象替换。
  4. 依赖倒置原则,高层次模块不得依赖于低层次模块的实现细节
  5. 接口隔离原则,类与类之间的依赖关系应该建立在最小的接口之上
  6. 迪米特原则,也称为最少知识原则,一个对象应当对其他对象有最少的了解

GoF定义的23种设计模式及应用场景

  • 系统设计可以采用的设计模式:单例、状态(场景切换)、外观(保证高内聚)、中介者(保证低解耦)
  • 角色设计可以采用的设计模式:桥接(武器、技能)、策略(属性计算)、模板方法(重复流程)、状态(AI)
  • 对象管理可以采用的设计模式:工厂(角色产生)、建造者(生产流程与功能表现分离)、享元(子弹、特效)
  • 逻辑设计可以采用的设计模式:命令(撤销回退)、责任链(关卡)
  • 辅助功能可以采用的设计模式:组合(UI、红点)、观察者(好友、成就)、备忘录(存档)、访问者(管理某一类对象)
  • 游戏优化可以采用的设计模式:装饰、适配器、代理
  • 其他模式:迭代器、原型、解释器、抽象工厂

中介者模式

[读书笔记] 设计模式与游戏完美开发_第1张图片
关于中介者模式,我们想象在一家互联网公司,有多个Colleague(同事),Colleague(同事)之间需要合作开发,不管是采用哪种版本控制软件,都可以抽象为一个Mediator(中介者)。而每个Colleague(同事)需要有版本控制的权限,即Mediator引用,而ConcreteMediator(具体中介者)则为ConcreteColleague(具体同事)提供服务。

Mediator.cs

public abstract class Mediator
{
    public abstract void SendMessage(Colleague theColleage, string message);
}

ConcreateMediator.cs

public class ConcreateMediator : Mediator
{
    ConcreateColleague1 m_Colleague1 = null;
    ConcreateColleague2 m_Colleague2 = null;
    public void SetColleage1(ConcreateColleague1 theColleague)
    {
        m_Colleague1 = theColleague;
    }
    public void SetColleage2(ConcreateColleague2 theColleague)
    {
        m_Colleague2 = theColleague;
    }
    public override void SendMessage(Colleague theColleague, string message)
    {
        if (theColleague == m_Colleague1)
        {
            m_Colleague2.Request(message);
        } else if (theColleague == m_Colleague2)
        {
            m_Colleague1.Request(message);
        }
    }
}

Colleage.cs

public abstract class Colleague
{
    protected Mediator m_Mediator = null;
    public Colleague(Mediator mediator)
    {
        m_Mediator = mediator;
    }
    public abstract void Request(string message);
}

ConcreateColleage1.cs、ConcreateColleage2.cs

using UnityEngine;
public class ConcreateColleague1 : Colleague
{
    public ConcreateColleague1(Mediator mediator) : base(mediator)
    {
    }
    public void Action()
    {
        m_Mediator.SendMessage(this, "Concreate1执行");
    }
    public override void Request(string message)
    {
        Debug.Log("C2已接收:" + message);
    }
}
using UnityEngine;
public class ConcreateColleague2 : Colleague
{
    public ConcreateColleague2(Mediator mediator) : base(mediator)
    {
    }
    public void Action()
    {
        m_Mediator.SendMessage(this, "Concreate2执行");
    }
    public override void Request(string message)
    {
        Debug.Log("C2已接收:" + message);
    }
}

Test.cs

using UnityEngine;

public class Test : MonoBehaviour
{
    void Start()
    {
    	// 定义具体中介者、具体同事
        ConcreateMediator pMediator = new ConcreateMediator();
        ConcreateColleague1 pColleage1 = new ConcreateColleague1(pMediator);
        ConcreateColleague2 pColleage2 = new ConcreateColleague2(pMediator);
        // 添加同事
        pMediator.SetColleage1(pColleage1);
        pMediator.SetColleage2(pColleage2);
		// 同事执行行为,中介者负责派发消息
        pColleage1.Action();
        pColleage2.Action();
    }
    void Update()
    {
    }
}

tips:有些情况下,Mediator会同时采用单例模式实现,在这种情况下,尽量避免从Mediator派生ConcreateMediator,这是因为当ConcreateMediator作为单例使用时,会因为单例只会存在唯一示例,当父类有多个派生类时会有“白马非马”的逻辑诡辩。同时为了遵循开闭原则,也要尽量依赖“接口”,而非依赖“实现”。

里氏替换原则有至少以下两种含义:
里氏替换原则是针对继承而言的,如果继承是为了实现代码重用,那么共享的父类方法就应该保持不变,不能被子类重新定义。 如果继承的目的是为了多态,而多态的前提就是子类覆盖并重新定义父类的方法,应该将父类定义为抽象类。不符合LSP的最常见的情况是,父类和子类都是可实例化的非抽象类,且父类的方法被子类重新定义,这一类的实现继承会造成父类和子类间的强耦合,也就是实际上并不相关的属性和方法牵强附会在一起,不利于程序扩展和维护。


工厂模式

[读书笔记] 设计模式与游戏完美开发_第2张图片
工厂模式是最简单的一种设计模式,由ConcreteCreate(具体工厂)产生ConcreateProduct(具体产品)
Creator.cs

public abstract class Creator
{
    public abstract Product FactoryMethod();
}

ConcreateCreatorProductA.cs

using UnityEngine;

public class ConcreateCreatorProductA : Creator
{
    public ConcreateCreatorProductA()
    {
        Debug.Log("产生工厂A");
    }
    public override Product FactoryMethod()
    {
        return new ConcreateProductA();
    }
}

Product.cs

public abstract class Product { }

ConcreateProductA

using UnityEngine;

public class ConcreateProductA : Product
{
    public ConcreateProductA()
    {
        Debug.Log("生产A");
    }
}

tips:通过不同子类工厂产生不同品类的产品,当产品种类过多时,会导致“工厂子类暴增”。这时候可以配合FactoryMethod传参实现工厂模式


建造者模式

[读书笔记] 设计模式与游戏完美开发_第3张图片
对于一个工厂来说,一条流水线应该可以生产多种产品吗,即生产流程与产品装配是可以解耦的。由Director(建造者指示者)对产品生产流程进行管控,而Builder(建造者)有很多ConcerteBuilder(具体建造者)则负责不同功能的装配。

Director.cs

public class Director
{
    public Product m_Product;
    public Director() { }
    public void Construct(Builder theBuilder)
    {
        m_Product = new Product();
        theBuilder.BuilderPart1(m_Product);
        theBuilder.BuilderPart2(m_Product);
    }
    public Product GetResult()
    {
        return m_Product;
    }
}

Builder.cs

public abstract class Builder
{
    public abstract void BuilderPart1(Product theProduct);
    public abstract void BuilderPart2(Product theProduct);
}

ConcreateBuilderA.cs

public class ConcreateBuilderA : Builder
{
    public override void BuilderPart1(Product theProduct)
    {
        theProduct.AddPart("产品A工人:生产部件1");
    }
    public override void BuilderPart2(Product theProduct)
    {
        theProduct.AddPart("产品A工人:生产部件2");
    }
}
using UnityEngine;
using System.Collections;

public class ConcreateBuilderB : Builder
{
    public override void BuilderPart1(Product theProduct)
    {
        theProduct.AddPart("产品B工人:生产部件1");
    }
    public override void BuilderPart2(Product theProduct)
    {
        theProduct.AddPart("产品B工人:生产部件2");
    }
}

Product.cs

using UnityEngine;
using System.Collections.Generic;

public class Product
{
    private List<string> m_Part = new List<string>();
    public Product() { }

    public void AddPart(string v)
    {
        m_Part.Add(v);
    }
    public void ShowProduct()
    {
        foreach(var part in m_Part)
        {
            Debug.Log(part);
        }
    }
}

Test.cs

using UnityEngine;

public class Test : MonoBehaviour
{
    Director director = new Director();
    Product theProduct = null;
    void Start()
    {
        director.Construct(new ConcreateBuilderA());
        theProduct = director.GetResult();
        theProduct.ShowProduct();
        
        director.Construct(new ConcreateBuilderB());
        theProduct = director.GetResult();
        theProduct.ShowProduct();
    }
}

享元模式

[读书笔记] 设计模式与游戏完美开发_第4张图片
书中描述的案例,个人以为不适合理解,故此处暂留


组合模式

“将对象以树状结构进行组合,用以表现部分与整体的层次关系”,UI的层次结构正好适合使用这种设计模式。

[读书笔记] 设计模式与游戏完美开发_第5张图片
Component(组件界面)用来定义树形结构,以及每个结点都能使用的操作,Composite(组合结点,即根结点),实现Component定义的各个方法,Leaf(叶结点,终端结点)
IComponent.cs

using UnityEngine;

public abstract class IComponent
{
    protected string m_Value;
    public abstract void Operation();

    public virtual void Add(IComponent theComponent)
    {
        Debug.Log("子类未实现");
    }
    public virtual void Remove(IComponent theComponent)
    {
        Debug.Log("子类未实现");
    }
    public virtual IComponent GetChild(int index)
    {
        Debug.Log("子类未实现");
        return null;
    }
}

Composite.cs

using UnityEngine;
using System.Collections.Generic;

public class Composite : IComponent
{
    List<IComponent> m_Childs = new List<IComponent>();

    public Composite(string value)
    {
        m_Value = value;
    }
    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 override void Operation()
    {
        Debug.Log("根节点执行" + m_Value);
        foreach (IComponent theComponent in m_Childs)
        {
            theComponent.Operation();
        }
    }
}

Leaf.cs

using UnityEngine;

public class Leaf : IComponent
{
    public Leaf(string value)
    {
        m_Value = value;
    }
    public override void Operation()
    {
        Debug.Log("叶节点执行:" + m_Value);
    }
}

test.cs

using UnityEngine;

public class Test2 : MonoBehaviour
{
	IComponent theRoot;
    void Start()
    {
        theRoot = new Composite("Root");
        theRoot.Add(new Leaf("Leaf1"));
        theRoot.Add(new Leaf("Leaf2"));

        IComponent theChild1 = new Composite("Child1");
        theChild1.Add(new Leaf("child1.Leaf1"));
        theChild1.Add(new Leaf("child1.Leaf2"));
        theRoot.Add(theChild1);

        IComponent theChild2 = new Composite("Child1");
        theChild2.Add(new Leaf("child2.Leaf1"));
        theChild2.Add(new Leaf("child2.Leaf2"));
        theRoot.Add(theChild2);

        theRoot.Operation();
    }
}

tips:GameObject.Find()会遍历所有场景中的对象,而且在Unity中存在重名的问题,会产生性能问题。可以通过自己实现Find,保证只在指定的Canvas下查找
UI的功能在场景初始化时决定,保证UI逻辑与表现分离


责任链模式

[读书笔记] 设计模式与游戏完美开发_第6张图片
责任链可以理解为数据结构的链表,每个Handler都有下一个Handler的引用。在关卡系统的设计上,很多项目会采用CreateStage(创建关卡)、CheckNextStage(检查进入下一关)方法来对场景进行操作,而采用责任链模式,则可以使用Handler(关卡对象)替代,通过类化关卡数据、通关条件能够提高关卡系统的灵活度。
Handler.cs

public abstract class Handler
{
    protected Handler m_NextHandle = null;

    public Handler(Handler theNextHandle)
    {
        m_NextHandle = theNextHandle;
    }
    public virtual void HandleRequest(int cost)
    {
        if (m_NextHandle != null)
        {
            m_NextHandle.HandleRequest(cost);
        }
    }
}

ConcreateHandle1.cs

using UnityEngine;

public class ConcreateHandle1 : Handler
{
    public int m_CostCheck = 10;
    public ConcreateHandle1(Handler theNextHandle) : base(theNextHandle) { }

    public override void HandleRequest(int cost)
    {
        if (cost < m_CostCheck)
        {
            Debug.Log("ConcreateHandle2核准");
        } else
        {
            base.HandleRequest(cost);
        }
    }
}

ConcreateHandle2.cs

using UnityEngine;

public class ConcreateHandle2 : Handler
{
    public int m_CostCheck = 20;
    public ConcreateHandle2(Handler theNextHandle) : base(theNextHandle) { }

    public override void HandleRequest(int cost)
    {
        if (cost <= m_CostCheck)
        {
            Debug.Log("ConcreateHandle2核准");
        }
        else
        {
            base.HandleRequest(cost);
        }
    }
}

Test.cs

using UnityEngine;
using System.Collections;

public class Test : MonoBehaviour
{
    void Start()
    {
        ConcreateHandle2 theHandle2 = new ConcreateHandle2(null);
        ConcreateHandle1 theHandle1 = new ConcreateHandle1(theHandle2);

        theHandle1.HandleRequest(5);
        theHandle1.HandleRequest(20);
    }
}

观察者模式

[读书笔记] 设计模式与游戏完美开发_第7张图片
对于发布-订阅关系来说,一个Subject可以对应多个Observer,Subject维护观察者列表,并提供Notify()通知功能;Observer提供Update()方法,在Subject通知更新时调用。
Subject.cs

using System.Collections.Generic;

public abstract class Subject
{
    List<Observer> m_Observer = new List<Observer>();
    public void Attach(Observer theObserver)
    {
        m_Observer.Add(theObserver);
    }
    public void Detach(Observer theObserver)
    {
        m_Observer.Remove(theObserver);
    }
    public void Notify()
    {
    	// 通知所有观察者
        foreach (Observer observer in m_Observer)
        {
            observer.Update();
        }
    }
}

ConcreateSubject.cs

using UnityEngine;
using System.Collections;

public class ConcreateSubject : Subject
{
    string m_SubjectState;
    public void SetState(string state)
    {
        m_SubjectState = state;
        Notify();
    }
    public string GetState()
    {
        return m_SubjectState;
    }
}

Observer.cs

using UnityEngine;
using System.Collections;

public abstract class Observer
{
    public abstract void Update();
}

ConcreateObserver.cs

using UnityEngine;

public class ConcreateObserver : Observer
{
    string m_ObjectState;
    ConcreateSubject m_Subject = null;

    public ConcreateObserver(ConcreateSubject subject)
    {
        m_Subject = subject;
    }
    public override void Update()
    {
        Debug.Log("ConcreteObserver.Update");
        Debug.Log("ConcreateObserver1:Subject" + m_ObjectState);
    }
}

Test.cs

using UnityEngine;
using System.Collections;

public class Test : MonoBehaviour
{
    ConcreateSubject theSubject = null;
    ConcreateObserver theObserver = null;
    // Use this for initialization
    void Start()
    {
        theSubject = new ConcreateSubject();
        theObserver = new ConcreateObserver(theSubject);
        theSubject.Attach(theObserver1); // 观察者参与订阅
        theSubject.SetState("Subject状态1"); // 主题修改状态,并通知
        theObserver1.ShowState(); // 观察者接收通知并执行Update()操作
    }
}

备忘录模式

[读书笔记] 设计模式与游戏完美开发_第8张图片
在不违反封装的原则下,获取一个对象的内部状态并保留在外部。当我们想办法去获取游戏系统的数据时,现有系统难免被改动,而如果由游戏系统主动提供数据,则可以保证系统的封装性,这也是备忘录模式的精髓。Originator(记录拥有者)会自动产出需要保存的记录Memento(记录保存者,我称之为记忆体),由Caretaker(记录看守者)维护多个Memento。

Originator.cs

using UnityEngine;

public class Originator
{
    string m_State;

    public void SetInfo(string state)
    {
        m_State = state;
    }
    public void ShowInfo()
    {
        Debug.Log("Originator State" + m_State);
    }

    public Memento CreateMemento()
    {
        Memento newMemento = new Memento();
        newMemento.SetState(m_State);
        return newMemento;
    }
    public void SetMemento(Memento m)
    {
        m_State = m.GetState();
    }
}

Memento.cs

public class Memento
{
    string m_State;
    public string GetState()
    {
        return m_State;
    }
    public void SetState(string state)
    {
        m_State = state;
    }
}

Caretaker.cs

using System.Collections.Generic;

public class Caretaker
{
    Dictionary<string, Memento> m_Mementos = new Dictionary<string, Memento>();
    public void AddMemento(string version, Memento theMemento)
    {
        if (m_Mementos.ContainsKey(version) == false)
            m_Mementos.Add(version, theMemento);
        else
            m_Mementos[version] = theMemento;
    }
    public Memento GetMemento(string version)
    {
        if (m_Mementos.ContainsKey(version) == false)
            return null;
        return m_Mementos[version];
    }
}

Test.cs

using System.Collections;

public class Test : MonoBehaviour
{
    void Start()
    {
        Originator theOriginator = new Originator();
        Caretaker theCaretaker = new Caretaker();

        theOriginator.SetInfo("version1");
        theOriginator.ShowInfo();
        theCaretaker.AddMemento("1", theOriginator.CreateMemento());

        theOriginator.SetInfo("version2");
        theOriginator.ShowInfo();
        theCaretaker.AddMemento("2", theOriginator.CreateMemento());

        theOriginator.SetInfo("version3");
        theOriginator.ShowInfo();

        theOriginator.SetMemento(theCaretaker.GetMemento("2"));
        theOriginator.ShowInfo();

        theOriginator.SetMemento(theCaretaker.GetMemento("1"));
        theOriginator.ShowInfo();
    }
}

你可能感兴趣的:(unity)