[读书笔记] 设计模式与游戏完美开发
最近在看《設(shè)計模式與游戲完美開發(fā)》,文章將記錄一些要點和一些設(shè)計模式實現(xiàn)
GoF定義的23種設(shè)計模式及應(yīng)用場景
- 系統(tǒng)設(shè)計可以采用的設(shè)計模式:單例、狀態(tài)(場景切換)、外觀(保證高內(nèi)聚)、中介者(保證低解耦)
- 角色設(shè)計可以采用的設(shè)計模式:橋接(武器、技能)、策略(屬性計算)、模板方法(重復(fù)流程)、狀態(tài)(AI)
- 對象管理可以采用的設(shè)計模式:工廠(角色產(chǎn)生)、建造者(生產(chǎn)流程與功能表現(xiàn)分離)、享元(子彈、特效)
- 邏輯設(shè)計可以采用的設(shè)計模式:命令(撤銷回退)、責任鏈(關(guān)卡)
- 輔助功能可以采用的設(shè)計模式:組合(UI、紅點)、觀察者(好友、成就)、備忘錄(存檔)、訪問者(管理某一類對象)
- 游戲優(yōu)化可以采用的設(shè)計模式:裝飾、適配器、代理
- 其他模式:迭代器、原型、解釋器、抽象工廠
中介者模式
關(guān)于中介者模式,我們想象在一家互聯(lián)網(wǎng)公司,有多個Colleague(同事),Colleague(同事)之間需要合作開發(fā),不管是采用哪種版本控制軟件,都可以抽象為一個Mediator(中介者)。而每個Colleague(同事)需要有版本控制的權(quán)限,即Mediator引用,而ConcreteMediator(具體中介者)則為ConcreteColleague(具體同事)提供服務(wù)。
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執(zhí)行");}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執(zhí)行");}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);// 同事執(zhí)行行為,中介者負責派發(fā)消息pColleage1.Action();pColleage2.Action();}void Update(){} }tips:有些情況下,Mediator會同時采用單例模式實現(xiàn),在這種情況下,盡量避免從Mediator派生ConcreateMediator,這是因為當ConcreateMediator作為單例使用時,會因為單例只會存在唯一示例,當父類有多個派生類時會有“白馬非馬”的邏輯詭辯。同時為了遵循開閉原則,也要盡量依賴“接口”,而非依賴“實現(xiàn)”。
里氏替換原則有至少以下兩種含義:
里氏替換原則是針對繼承而言的,如果繼承是為了實現(xiàn)代碼重用,那么共享的父類方法就應(yīng)該保持不變,不能被子類重新定義。 如果繼承的目的是為了多態(tài),而多態(tài)的前提就是子類覆蓋并重新定義父類的方法,應(yīng)該將父類定義為抽象類。不符合LSP的最常見的情況是,父類和子類都是可實例化的非抽象類,且父類的方法被子類重新定義,這一類的實現(xiàn)繼承會造成父類和子類間的強耦合,也就是實際上并不相關(guān)的屬性和方法牽強附會在一起,不利于程序擴展和維護。
工廠模式
工廠模式是最簡單的一種設(shè)計模式,由ConcreteCreate(具體工廠)產(chǎn)生ConcreateProduct(具體產(chǎn)品)
Creator.cs
ConcreateCreatorProductA.cs
using UnityEngine;public class ConcreateCreatorProductA : Creator {public ConcreateCreatorProductA(){Debug.Log("產(chǎn)生工廠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("生產(chǎn)A");} }tips:通過不同子類工廠產(chǎn)生不同品類的產(chǎn)品,當產(chǎn)品種類過多時,會導致“工廠子類暴增”。這時候可以配合FactoryMethod傳參實現(xiàn)工廠模式
建造者模式
對于一個工廠來說,一條流水線應(yīng)該可以生產(chǎn)多種產(chǎn)品嗎,即生產(chǎn)流程與產(chǎn)品裝配是可以解耦的。由Director(建造者指示者)對產(chǎn)品生產(chǎn)流程進行管控,而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("產(chǎn)品A工人:生產(chǎn)部件1");}public override void BuilderPart2(Product theProduct){theProduct.AddPart("產(chǎn)品A工人:生產(chǎn)部件2");} } using UnityEngine; using System.Collections;public class ConcreateBuilderB : Builder {public override void BuilderPart1(Product theProduct){theProduct.AddPart("產(chǎn)品B工人:生產(chǎn)部件1");}public override void BuilderPart2(Product theProduct){theProduct.AddPart("產(chǎn)品B工人:生產(chǎn)部件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();} }享元模式
書中描述的案例,個人以為不適合理解,故此處暫留
組合模式
“將對象以樹狀結(jié)構(gòu)進行組合,用以表現(xiàn)部分與整體的層次關(guān)系”,UI的層次結(jié)構(gòu)正好適合使用這種設(shè)計模式。
Component(組件界面)用來定義樹形結(jié)構(gòu),以及每個結(jié)點都能使用的操作,Composite(組合結(jié)點,即根結(jié)點),實現(xiàn)Component定義的各個方法,Leaf(葉結(jié)點,終端結(jié)點)
IComponent.cs
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("根節(jié)點執(zhí)行" + 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("葉節(jié)點執(zhí)行:" + 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中存在重名的問題,會產(chǎn)生性能問題。可以通過自己實現(xiàn)Find,保證只在指定的Canvas下查找
UI的功能在場景初始化時決定,保證UI邏輯與表現(xiàn)分離
責任鏈模式
責任鏈可以理解為數(shù)據(jù)結(jié)構(gòu)的鏈表,每個Handler都有下一個Handler的引用。在關(guān)卡系統(tǒng)的設(shè)計上,很多項目會采用CreateStage(創(chuàng)建關(guān)卡)、CheckNextStage(檢查進入下一關(guān))方法來對場景進行操作,而采用責任鏈模式,則可以使用Handler(關(guān)卡對象)替代,通過類化關(guān)卡數(shù)據(jù)、通關(guān)條件能夠提高關(guān)卡系統(tǒng)的靈活度。
Handler.cs
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);} }觀察者模式
對于發(fā)布-訂閱關(guān)系來說,一個Subject可以對應(yīng)多個Observer,Subject維護觀察者列表,并提供Notify()通知功能;Observer提供Update()方法,在Subject通知更新時調(diào)用。
Subject.cs
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 initializationvoid Start(){theSubject = new ConcreateSubject();theObserver = new ConcreateObserver(theSubject);theSubject.Attach(theObserver1); // 觀察者參與訂閱theSubject.SetState("Subject狀態(tài)1"); // 主題修改狀態(tài),并通知theObserver1.ShowState(); // 觀察者接收通知并執(zhí)行Update()操作} }備忘錄模式
在不違反封裝的原則下,獲取一個對象的內(nèi)部狀態(tài)并保留在外部。當我們想辦法去獲取游戲系統(tǒng)的數(shù)據(jù)時,現(xiàn)有系統(tǒng)難免被改動,而如果由游戲系統(tǒng)主動提供數(shù)據(jù),則可以保證系統(tǒng)的封裝性,這也是備忘錄模式的精髓。Originator(記錄擁有者)會自動產(chǎn)出需要保存的記錄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);elsem_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();} }總結(jié)
以上是生活随笔為你收集整理的[读书笔记] 设计模式与游戏完美开发的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: DOM相关(主要是var和let的区别用
- 下一篇: 蓝桥杯第八届省赛JAVA真题----字母