1. 程式人生 > >設計模式——工廠(Unity3D中的應用)

設計模式——工廠(Unity3D中的應用)

工廠模式是為了客戶端簡化物件的建立過程,使建立與表示分離。

程式碼案例:
在ARPG的技能系統中,因為技能的攻擊範圍不同,一般會有不同的技能選擇器。我們以此為案例實現一個建立技能選擇器的工廠模式。

public interface IAttackSelector
{
    GameObject[] FindTargets ();
}

public class SectorAttackSelector:IAttackSelector
{
    public GameObject[] FindTargets ()
    {
        return new GameObject[]{ new
GameObject("Sector1"),new GameObject("Sector2")}; } } public class CircleAttackSelector:IAttackSelector { public GameObject[] FindTargets () { return new GameObject[]{ new GameObject("circle1"),new GameObject("circle2")}; } } 不使用工廠模式建立Selector物件: public class Test:MonoBehaviour { { GameObject[] arr1 = CreateAttackSelector ("circle"
).FindTargets (); foreach (var item in arr1) Debug.Log (item.name); GameObject[] arr2 = CreateAttackSelector ("sector").FindTargets (); foreach (var item in arr2) Debug.Log (item.name); } private IAttackSelector CreateAttackSelector (string selector) { switch
(selector) { case "circle": return new CircleAttackSelector (); case "sector": return new SectorAttackSelector (); default : return null; } } } 如果有多個類建立Selector物件就會導致多次重寫CreateAttackSelector方法,這樣就導致了大量的重複程式碼,維護性就變得極差。 我們做得就是將CreateAttackSelector方法封裝到單獨一個類中。 public class Factory { public static IAttackSelector CreateAttackSelector (string selector) { switch (selector) { case "circle": return new CircleAttackSelector (); case "sector": return new SectorAttackSelector (); default : return null; } } 這是我們只要在需要建立Selector的地方通過Factory的CreateAttackSelector方法就可以了。 不需要在客戶端多次重寫CreateAttackSelector。 public class FactoryTest : MonoBehaviour { private void Start () { GameObject[] arr1 = Factory.CreateAttackSelector ("circle").FindTargets (); foreach (var item in arr1) Debug.Log (item.name); GameObject[] arr2 = Factory.CreateAttackSelector ("sector").FindTargets (); foreach (var item in arr2) Debug.Log (item.name); } } }

這樣似乎實現了程式碼的複用性。但是如果再次出現矩形、菱形等其他型別的選擇器時,就不得不再次修改CreateAttackSelector方法,如果選擇器有幾十種,就會使得switch語句極其龐大。
如何避免出現switch驚悚語句,答案就是使用反射。

public class Factory
{
    public static IAttackSelector CreateAttackSelector (string selectorName)
    {
        Type objType = Type.GetType(selectorName + "AttackSelector");
        return Activator.CreateInstance(objType) as IAttackSelector;
    }
}

工廠模式中就是應對多選一的場景的是簡單工廠,也稱為靜態工廠;還有一種多選多(一個系列)的場景,則使用抽象工廠。

例如:
遊戲中的角色資料、技能資料、任務資料都是動態獲取的。那麼獲取資料的方式就會有多中,可能從XML中獲取,也可能從資料庫中獲取,還有可能從普通檔案中獲取。因此我們就需要根據不同的資料型別建立不同的工廠來從不同的資料型別檔案中獲取資料。

public interface ISkillService
{
    string GetSkillData();
}
public class XMLSkillService:ISkillService
{
    public string GetSkillData ()
    {
        return "Skill";
    }   
}
public class DBSkillService:ISkillService
{
    public string GetSkillData ()
    {
        return "Skill";
    }
}


public interface ICharacterService
{
    string GetCharacterData();
}
public class XMLCharacterService: ICharacterService
{
    public string GetCharacterData ()
    {
        return "Character";
    }
}
public class DBCharacterService: ICharacterService
{
    public string GetCharacterData ()
    {
        return "Character";
    }
}

public interface IFactory
{
    ICharacterService GetCharacterService ();
    ISkillService GetSkillService ();
}
public class XMLFactory:IFactory
{
    public ICharacterService GetCharacterService ()
    {
        return new XMLCharacterService ();
    }

    public ISkillService GetSkillService ()
    {
        return new XMLSkillService ();
    }
}
public class DBFactory:IFactory
{
    public ICharacterService GetCharacterService ()
    {
        return new DBCharacterService ();
    }

    public ISkillService GetSkillService ()
    {
        return new DBSkillService ();
    }
}

public class Test:MonoBehaviour
{
    private void Start()
    {
        ISkillService skillService = new DBFactory ().GetSkillService ();
        skillService.GetSkillData ();
        ICharacterService characterService = new DBFactory ().GetCharacterService ();
        characterService.GetCharacterData ();
    }
}

當出現從File普通檔案中獲取資料時,只需要建立FileFactory工廠類和FileCharacterService和FileSkillService就可以,不需要修改以前的程式碼,符合開閉原則。
1.但是這樣會帶來平行繼承的問題,如果此時增加從伺服器獲取資料就需要再次增加工廠類,使得客戶端呼叫者需記住使用哪個型別的工廠,因為我們希望資料來源來自一個地方,技能和角色資料統一來自XML或者來自資料庫,這樣如果中間修改資料來源,就需要客戶端呼叫地方全部替換為一個系列,這樣的修改對於客戶端而言是比較痛苦的。
2.如果此時增加一個獲取任務資料的方式,就會造成更麻煩的事情,我們必須要修改IFactory介面,增加GetTaskService()的方法。這樣一來所用工廠類都需要修改。
注:抽象工廠類適合解決縱向變化(比如增加File獲取資料方式,不需要修改以前的程式碼),而不適合解決橫向變化(增加獲取任務的方式,需要修改介面,改動較大)。雖然會帶來平行繼承的問題,但實際開發中可能並沒有那麼多平行類,因此抽象工廠還是比較有用的。

有沒好的方式解決平行繼承,切換資料獲取方式不需要修改客戶端程式碼:
那還得使用簡單工廠,但是需要做一下簡單修改:利用傳參的方式,通過switch…case 判斷來建立不同的Service,但是因為switch語句帶有會使程式碼膨脹的風險,所以改用反射,這是簡單工廠的實現方式。

public class FactoryNormal
{
    public static ISkillService CreateSkillService (string type)
    {
        Type objType = Type.GetType (type + "SkillService");
        return Activator.CreateInstance (objType) as ISkillService;
    }

    public static ICharacterService CreateCharacterService (string type)
    {
        Type objType = Type.GetType (type + "CharacterService");
        return Activator.CreateInstance (objType) as ICharacterService;
    }
}

public class Test:MonoBehaviour
    {
        private void Start()
        {
            ISkillService skillService = FactoryNormal.CreateSkillService ("XML");
            print(skillService.GetSkillData ());
            ICharacterService characterService = FactoryNormal.CreateCharacterService ("XML");
            print(characterService.GetCharacterData ());
        }
    }

解決了平行繼承的問題,但是客戶端呼叫比較麻煩,因為需要記住獲取資料的方式。這裡就不能使用傳統的簡單工廠,因為是多選多(一個系列),因此我們應該將type封裝在工廠類中。

public class FactoryNormal
{
    private static string type;

    static FactoryNormal()
    {
        type = Resources.Load<TextAsset> ("Version").text;
    }

    public static ISkillService CreateSkillService ()
    {
        return CreateService<ISkillService>("Skill");
    }

    public static ICharacterService CreateCharacterService ()
    {
        return CreateService<ICharacterService>("Character");
    }

    private static T CreateService<T>(string name) where T:class
    {
        Type objType = Type.GetType (type + name +"Service");
        return Activator.CreateInstance (objType) as T;
    }
}

修改Resources資料夾下Version檔案中內容就可以切換資料獲取方式(“XML”換成“DB”就可以將從XML檔案中獲取資料的方式切為從DB中);如果增加檔案資料獲取方式,不需要修改Factory類,若果增加任務資料,只需要在Factory類中增加一個CreateTaskService的方法即可,改動相對也比較小。

關於抽象工廠和簡單工廠的對比:(工廠方法模式不再舉例)
靜態(簡單)工廠模式(Switch…case變龐大) 改進:(通過反射解決Switch驚悚現身)

抽象工廠模式(多選多 成系列建立)(平行繼承體系) 改進:(簡單工廠方式實現抽象抽象工場思維,利用反射) 配置注入 設定注入 構造注入

工廠方法 模式(多選一)(工廠實現類增多,平行繼承體系)(廢棄)

相關推薦

設計模式——工廠Unity3D應用

工廠模式是為了客戶端簡化物件的建立過程,使建立與表示分離。 程式碼案例: 在ARPG的技能系統中,因為技能的攻擊範圍不同,一般會有不同的技能選擇器。我們以此為案例實現一個建立技能選擇器的工廠模式。 public interface IAttackSel

常用設計模式總結面試常問

Singleton(單例模式) 一句話總結:一個類在Java虛擬機器中只有一個物件,並提供一個全域性訪問點。 生活中例子:太陽、月亮、國家主席等。 解決什麼問題:物件的唯一性,效能浪費太多。 專案裡面怎麼用:資料庫連線物件,屬性配置檔案的讀取物件。 模式結構:分為餓漢式和懶漢式(

設計模式教程Design Patterns Tutorial筆記之一 建立型模式Creational Patterns

目錄 · 概述 · Factory     · What is the Factory Design Pattern?     · 

設計模式總結Java語言實現

有人說,為什麼要學習設計模式,有些設計模式寫起來十分複雜,在平時程式設計時不會刻意去使用它。但是,設計模式是程式碼規範的一種體現,學號設計模式併合理應用,可以避免bug的出現,增強程式碼的魯棒性,便於後

設計模式筆記二十一 —— 中介者模式

中介者模式(Mediator):用一箇中介者物件來封裝一系列的物件互動。中介者物件使各物件不需要顯式地相互引用,從而使其耦合鬆散,而且可以獨立的改變它們之間的互動。中介者模式很容易在系統中應用,也很容易在系統中誤用。當系統出現了“多對多”互動複雜的物件群時,不要急於使用中介者

J2EE設計模式-DaoData Access Object模式

在過去 18 個月中,我參加了一個由有才華的軟體工程師組成的小組,構建定製的、基於 Web 的供應鏈管理應用程式。我們的應用程式訪問範圍廣泛的永續性資料,包括配送狀態、供應鏈衡量(metrics)、庫存、貨運發票、專案管理資料和使用者資訊。我們用 JDBC API 連線到我們公司的不同資料庫平臺上,並在整

軟體設計模式學習二十一中介者模式

> 對於那些存在物件之間複雜互動關係的系統,中介者模式提供了一種簡化複雜互動的解決方案,即通過引入一箇中介者,將原本物件之間的兩兩互動轉化為每個物件與中介者之間的互動 ## 模式動機 以微信聊天為例,可以使用者與使用者直接聊天,也可以通過微信群聊天。前者的話,使用者要和別的使用者加為好友,即使用者

軟體設計模式學習二十二備忘錄模式

> 備忘錄模式提供了一種物件狀態的撤銷實現機制,當系統中某一物件需要恢復到某一歷史狀態時可以使用備忘錄模式來進行設計 ## 模式動機 人人都有後悔的時候,在軟體使用過程中難免會出現一些誤操作,如不小心刪除了某些文字或圖片,資料填入錯誤等,對於這些誤操作,需要提供一種後悔藥機制,讓系統可以回到誤操作

軟體設計模式學習二十四狀態模式

> 狀態模式用於解決系統中複雜物件的狀態轉換以及不同狀態下行為的封裝問題 ## 模式動機 很多情況下,一個物件的行為取決於一個或多個動態變化的屬性,這樣的屬性叫做狀態。一個物件可以擁有多個狀態,這些狀態可以相互轉換,當物件狀態不同時,其行為也有所差異。 假設一個人就是物件,人根據心情不同會有很多

軟體設計模式學習二十七訪問者模式

> 訪問者模式是一種較為複雜的行為型設計模式,它包含訪問者和被訪問元素兩個主要組成部分,這些被訪問的元素具有不同的型別,且不同的訪問者可以對其進行不同的訪問操作 ## 模式動機 對於系統中某些物件,它們儲存在同一個集合中,且具有不同的型別。對於該集合中的物件,可以接受一類稱為訪問者的物件來訪問,不

設計模式——工廠模式在開發應用簡單計算器

下面以一個簡單的計算器為例子來說明,怎麼用工廠模式。 如果你沒有用任何設計模式,你可能會這樣寫: package com.meritit; import java.util.Scanner; public class MainClass { public stati

設計模式(9)----- 補充spring工廠設計模式手寫

package com.DesignPatterns.ad.factory6; public interface BeanFactory { Object getBean(String id); }     package com.DesignPattern

Note8:C#設計模式工廠方法模式VS 簡單工廠模式 & 抽象工廠模式

工廠方法模式 blog 抽象工廠 nbsp strong str cnblogs note 設計 一、資源說明 (1)本文有參考:http://www.cnblogs.com/zhili/p/FactoryMethod.html 待更!Note8:C#設計模式—工廠方法

c++ 設計模式9 Abstract Factory 抽象工廠模式

構建 數據庫 strac 無需 div exec oracl dfa tle 5.2 抽象工廠模式 動機:在軟件系統中,經常面臨著“一系列相互依賴的對象”的創建工作;同時,由於需求的變化,往往存在更多系列對象的創建工作。 代碼示例: 實現利用數據庫的業務邏輯,支持多數據

c++ 設計模式8 Factory Method 工廠方法

更改 itl logs 客戶 eos image 分享 一個 工廠方法模式 5. “對象創建”類模式 通過“對象創建”類模式繞開new,來避免對象創建(new)過程中所導致的緊耦合(依賴具體類),從而支持對象創建的穩定。它是接口抽象之後的第一步工作。 5.1 工廠方法 動機

iOS經常使用設計模式——工廠方法簡單工廠模式工廠方法模式, 抽象工廠模式

csdn bst 設計 cto mod 基類 load 引用 角色 1. 簡單工廠模式 怎樣理解簡單工廠,工廠方法。 抽象工廠三種設計模式? 簡單工廠的生活場景。賣早點的小攤販。他給你提供包子,饅頭,地溝油烙的煎餅等,小販是一個工廠。它生產包子,饅頭,地溝油烙的

java設計模式-工廠模式springweb為例子

core puts 臃腫 設計 需要 utm 實現 reat jar 一般而言,工廠模式分為3種,簡單工廠模式,工廠方法模式,抽象工廠模式。這三種工廠模式逐層深入吧。 一,從springWeb.jar包使用抽象工廠模式的一個例子聊起 之前對spring各種癡迷,所以在需要發

設計模式のFactoryPattern工廠模式----創建模式

virt 連接 時也 有意 result 加減 set 增加 bre 一、產生背景   設計一個連接服務器的框架,需要三個協議,"POP3"、"IMAP"、"HTTP",可以把這三個作為產品類,共同實現一個接口。 二、通常的做法 1、定義一個接口(或抽象方法)

設計模式-工廠方法Factory Method

log face inf str ace 對象實例 method tee 就是 2018-1-20 by Atlas 應用場景 Template Method Pattern是在父類建立處理邏輯的大綱骨架,而在子類補充具體的處理內容。把Template Method