1. 程式人生 > >Unity StrangeIoc框架 (一)

Unity StrangeIoc框架 (一)

 最近想專案中需要使用這個架構  因此 上網看了很多資料摸索   但是對於初學者來說大多數的資料不是那麼容易理解 而且文件也是英文的閱讀起來有點吃力  所以記錄一下自己閱讀的過程  方便以後翻閱和跟我一樣的新人學習其中也借鑑了一些前輩的資料 如有反感請聯絡我   立馬進行修改  謝謝

StrangeIoc 是依據控制反轉和解耦原理設計的,支援依賴注入。

控制反轉即Ioc(Inversion of Control) 它把傳統上由程式程式碼直接操控的物件的呼叫權交給容器,通過容器來實現物件元件的裝配和管理。所為的“控制反轉”概念就是對元件物件控制權的轉移,從程式程式碼本身轉移到了內部的容器。

依賴注入(Dependency Injection)    依賴注入的基本原則是:應用元件不應該負責查詢資源或者其他依賴的寫作物件。配置物件的工作應該由Ioc容器負責,

在使用時

Bingding(繫結)

strange的核心是繫結,我們可以將一個或多個物件與另外一個或多個物件繫結(連線)在一起,將介面與類繫結來實現介面,將事件與事件接收繫結在一起。或者繫結兩個類,一個類被建立時另一個類自動建立。

strange的binding由兩個必要部分和一個可選部分組成,必要部分是a key and a value key觸發value,因此一個事件可以觸發回撥,一個類的例項化可以觸發另一個類的例項化。可選部分是name,他可以區分使用相同key的兩個binding 下面三種繫結方法其實都是一樣的,語法不同而已

複製程式碼
1. Bind<IRoundLogic>().To<RoundLogic>();

2. Bind(typeof(IRoundLogic)).To(typeof(RoundLogic));

3. IBinding binding = Bind<IRoundLogic>();     //使用IBinding 的時候需要引用strange.framework.api; 名稱空間
   binding.To<RoundLogic>();

Bind<IRoundLogic>().To<RoundLogic>().ToName(“Logic”);    //使用非必要部分name
複製程式碼

繫結從層次上分為3種: injectionbinding           ,commandbinding,           mediationbing

注入繫結injectionbinding主要是用來繫結該型別物件到上下文,這樣使得程式中各個地方可以通過contextview訪問得到該物件。這種繫結會生成物件。這種繫結是為了生成物件並且注入到指定物件中用的

commandbinding是為了將命令繫結到方法中用的

mediationbing則是為了攔截view訊息,而將view注入中介mediator中,然後在view的awake方法裡面生成meidtaor物件

The injection extension(注入擴充套件)

在繫結擴充套件中最接近控制反轉的思想是注入

介面本身沒有實現方法,只定義類中的規則

複製程式碼
interface ISpaceship
{
    void input(float angle, float velocity);          
    IWeapon weapon{get;set;}   
}

//使用另一個類實現這個介面,寫法如下
Class Spaceship : ISpaceship
{
    public void input(float angle, float velocity)   
    {
        //do
    }

    public IWeapon weapon{get;set;}
}
複製程式碼

如果採用上面的寫法,Spaceship類裡面不用再寫檢測輸入的功能了,只需要處理輸入就可以了input只需要控制移動,不需要管是何種輸入方式  是手柄鍵盤或是其他  只需要進行處理

也不需要武器的邏輯,僅僅是注入武器例項就可以了。但是我們需要知道武器是什麼樣的武器 不同的武器造成不同的掉血  所以這塊的邏輯是需要處理的

複製程式碼
public interface IWeapon
{
    void Attack();
}

public class PhaserGun : IWeapon
{
    public void Attack(){//掉血邏輯
    }       
}

public class SquirtCannon : IWeapon
{
    public void Attack(){//掉血邏輯
    }       
}
複製程式碼

在ISpaceship中的程式碼進行一點修改

複製程式碼
interface ISpaceship
{
    void input(float angle, float velocity);  
    [Inject]        
    IWeapon weapon{get;set;}   
}
複製程式碼

加上Inject標籤  這樣就可以進行綁定了   將介面與類繫結來實現介面

[Inject]標籤實現介面,而不是例項化類

injectionBinder.Bind<IWeapon>().To<PhaserGun >();

單例對映

injectionBinder.Bind<IWeapon>().To<PhaserGun >().ToStringleton();

IWeapon weapon = PhaserGun.Get();

在繫結多個的時候就需要利用  名稱對映來進行區分

複製程式碼
injectionBinder.Bind<ISocialService>()
    .To<TwitterService>().ToSingleton()
    .ToName(ServiceTypes.PRIMARY);
    
injectionBinder.Bind<ISocialService>()
    .To<TwitterService>().ToSingleton()
    .ToName(ServiceTypes.SECONDARY);

injectionBinder.Bind<ISocialService>()
    .To<TwitterService>().ToSingleton()
    .ToName(ServiceTypes.TERTIARY);
複製程式碼

在[Inject]標籤處 也需要進行新增名稱

[Inject (ServiceTypes.TERTIARY)] //We mapped TwitterService to TERTIARY
public ISocialService socialService{get;set;}

值的對映

Configuration myConfig = loadConfiguration();
injectionBinder.Bind<IConfig>().ToValue(myConfig);

具體還有幾種對映就不說了  需要的可以去看看文件

 The reflector extension(反射擴充套件)

反射列表

複製程式碼
List<Type> list = new List<Type> ();
list.Add (typeof(Borg));
list.Add (typeof(DeathStar));
list.Add (typeof(Galactus));
list.Add (typeof(Berserker));
//count should equal 4, verifying that all four classes were reflected.
int count = injectionBinder.Reflect (list);
複製程式碼

反射所有已經通過injectionBinder對映的所有

injectionBinder.ReflectAll();

The dispatcher extension(排程程式擴充套件)

dispatcher相當於觀察者模式中的公告板,允許客戶監聽他,並且告知當前發生的事件。在strangeioc中,通過EventDispatcher方式實現,EventDispatcher繫結觸發器來觸發帶引數/不帶引數的方法   觸發器通常是String或列舉型別(觸發器可以理解為key,或者事件的名稱,名稱對應著觸發的方法)

如果有返回值,他將存在IEvent,一個簡單的值物件包含與該事件相關的任何資料,你可以寫你自己的事件滿足IEvent介面,strangeioc事件叫TmEvent

如果你再使用MVCSContext 有一個全域性的EventDispatcher 叫contextDispatcher 會自動注入,你可以用來傳遞事件

有兩種基本的事你可以去做EventDipatcher排程事件和監聽他們

dispatcher.AddListener("FIRE_MISSILE", onMissileFire);

事件會處於監聽狀態,知道FIRE_MISSILE事件被處罰,然後執行對應的onMissileFire方法

也可以通過列舉實現

dispatcher.AddListener(AttackEvent.FIRE_MISSILE, onMissileFire);

移除監聽

dispatcher.RemoveListener(AttackEvent.FIRE_MISSILE, onMissileFire);

更新監聽

dispatcher.UpdateListener(true, AttackEvent.FIRE_MISSILE, onMissileFire);

呼叫的方法可以有一個引數或者沒有,這取決於你關心的事件效率

複製程式碼
private void onMissileFire()
{
    //this works...
}

private void onMissileFire(IEvent evt)
{
    //...and so does this.
    Vector3 direction = evt.data as Vector3;
}
複製程式碼

排程事件

dispatcher.Dispatch(AttackEvent.FIRE_MISSILE);

這種形式的排程將生成一個新的TmEvent  呼叫任何監聽物件,但是因為你沒有提供資料,資料欄位的TmEvent當然會是零。 你也可以排程和提供資料:

Vector3 orientation = gameObject.transform.localRotation.eulerAngles;
dispatcher.Dispatch(AttackEvent.FIRE_MISSILE, orientation);

可以自己建立TmEvent排程

Vector3 orientation = gameObject.transform.localRotation.eulerAngles;
TmEvent evt = new TmEvent(AttackEvent.FIRE_MISSILE, dispatcher, this.orientation); dispatcher.Dispatch(evt);

 The command extension(命令擴充套件)

 除了繫結事件的方法,可以將其繫結到Commands。命令是控制器結構MVC的指揮者在strangeioc的MVCSContext中CommandBinder監聽每一個dispatcher的排程(當然你可以改變這個如果你想在自己的上下文)。訊號,下面描述,也可以繫結到命令。當一個事件或訊號被排程,

複製程式碼
using strange.extensions.command.impl;
using com.example.spacebattle.utils;

namespace com.example.spacebattle.controller
{
    class StartGameCommand : EventCommand
    {
        [Inject]
        public ITimer gameTimer{get;set;}

        override public void Execute()
        {
            gameTimer.start();
            dispatcher.dispatch(GameEvent.STARTED);
        }
    }
}
複製程式碼

但非同步命令, 像網路請求   可以這樣做   Retain() and Release()

複製程式碼
using strange.extensions.command.impl;
using com.example.spacebattle.service;

namespace com.example.spacebattle.controller
{
    class PostScoreCommand : EventCommand
    {
        [Inject]
        IServer gameServer{get;set;}
        
        override public void Execute()
        {
            Retain();
            int score = (int)evt.data;
            gameServer.dispatcher.AddListener(ServerEvent.SUCCESS, onSuccess);
            gameServer.dispatcher.AddListener(ServerEvent.FAILURE, onFailure);
            gameServer.send(score);
        }

        private void onSuccess()
        {
            gameServer.dispatcher.RemoveListener(ServerEvent.SUCCESS, onSuccess);
            gameServer.dispatcher.RemoveListener(ServerEvent.FAILURE, onFailure);
            //...do something to report success...
            Release();
        }

        private void onFailure(object payload)
        {
            gameServer.dispatcher.RemoveListener(ServerEvent.SUCCESS, onSuccess);
            gameServer.dispatcher.RemoveListener(
            ServerEvent.FAILURE, onFailure);
            //...do something to report failure...
            Release();
        }
    }
}
複製程式碼

如果使用完不進行Release()可能會導致記憶體洩露 

對映命令

commandBinder.Bind(ServerEvent.POST_SCORE).To<PostScoreCommand>();

您可以將多個命令繫結到單個事件如果你喜歡

commandBinder.Bind(GameEvent.HIT).To<DestroyEnemyCommand>().To<UpdateScoreCommand>();

解除命令繫結Unbind

commandBinder.Unbind(ServerEvent.POST_SCORE);

一次性的指令

commandBinder.Bind(GameEvent.HIT).To<DestroyEnemyCommand>().Once();

按順序執行繫結   InSequence  會一直執行到最後的命令 或者其中一個命令失敗。  命令可以在任何時候呼叫Fail()   這會打破這個序列  這可以用於建立一個鏈相關的事件  為構建有序的動畫,或制定一個守衛,以確定是否應該執行一個命令。

commandBinder.Bind(GameEvent.HIT).InSequence()
    .To<CheckLevelClearedCommand>()
    .To<EndLevelCommand>()
    .To<GameOverCommand>();

 The signal extension(訊息擴充套件)

訊號是一個排程機制,另一種選擇EventDispatcher 相比於EventDispatcher  訊號有兩個優點  1. 分發結果不再建立例項,因此也不需要GC回收更多的辣雞  2. 更安全 當訊息與回撥不匹配時會斷開執行,官網也推薦使用Singal來相容後續版本

建立兩個訊號,每一個都有一個引數

複製程式碼
Signal<int> signalDispatchesInt = new Signal<int>();
Signal<string> signalDispatchesString = new Signal<string>();

signalDispatchesInt.AddListener(callbackInt);      //Add a callback with an int parameter
signalDispatchesString.AddListener(callbackString);    //Add a callback with a string parameter

signalDispatchesInt.Dispatch(42);        //dispatch an int
signalDispathcesString.Dispatch("Ender wiggin");      //dispatch a string

void callbackInt(int value){
    //Do something with this int
}

void callbackString(string value){
    //Do something with this string
}
複製程式碼

訊息最多可以使用四個引數

Signal<T, U, V, W> signal = new Signal<T, U, V, W>();

子類可以編寫自己的訊號

複製程式碼
    using System;
    using UnityEngine;
    using strange.extensions.signal.impl;

    namespace mynamespace
    {
        //We're typing this Signal's payloads to MonoBehaviour and int
        public class ShipDestroyedSignal : Signal<MonoBehaviour, int>
        {
        }
    }
     
複製程式碼

訊號對映到命令

複製程式碼
protected override void addCoreComponents()
{
    base.addCoreComponents();
    injectionBinder.Unbind<ICommandBinder>();
    injectionBinder.Bind<ICommandBinder>().To<SignalCommandBinder>().ToSingleton();
}
複製程式碼

這告訴strangeioc 我們做了預設CommandBinder SignalCommandBinder取而代之。 所以是訊號觸發命令 而不是事件 。 strangeioc  只支援 事件或者訊號中的一個對映命令,而不是兩個都是。

訊號繫結   依舊使用commandBinder

commandBinder.Bind<SomeSignal>().To<SomeCommand>();

對映一個訊號到命令  會自動建立一個injection對映  你可以通過[Inject]標籤 檢索

[Inject]
public ShipDestroyedSignal shipDestroyedSignal{get; set;}

commandBinder.Bind<ShipDestroyedSignal>().To<ShipDestroyedCommand>();

在ShipMediator,我們注入訊號,然後排程

複製程式碼
[Inject]
public ShipDestroyedSignal shipDestroyedSignal{get; set;}

private int basePointValue; //imagining that the Mediator holds a value for this ship

//Something happened that resulted in destruction
private void OnShipDestroyed()
{
    shipDestroyedSignal.Dispatch(view, basePointValue);
}
複製程式碼

派遣一個訊號通過SignalCommandBinder對映結果的例項化ShipDestroyedCommand:

複製程式碼
using System;
using strange.extensions.command.impl;
using UnityEngine;

namespace mynamespace
{
    //Note how we extend Command, not EventCommand
    public class ShipDestroyedCommand : Command
    {
        [Inject]
        public MonoBehaviour view{ get; set;}

        [Inject]
        public int basePointValue{ get; set;}

        public override void Execute ()
        {
            //Do unspeakable things to the destroyed ship
        }
    }
}
複製程式碼

如您所見,對映的方法非常類似於訊號命令的方法使用事件

兩個重要問題:第一,而訊號支援多個相同型別的引數,注射。 因此不可能對一個訊號與相同型別的兩個引數對映到一個命令

複製程式碼
//This works
Signal<int, int> twoIntSignal = new Signal<int, int>();
twoIntSignal.AddListener(twoIntCallback);

//This fails
Signal<int, int> twoIntSignal = new Signal<int, int>();
commandBinder.Bind(twoIntSignal).To<SomeCommand>();
複製程式碼 複製程式碼
override public void Launch()
{
    base.Launch();
    //Make sure you've mapped this to a StartCommand!
    StartSignal startSignal= (StartSignal)injectionBinder.GetInstance<StartSignal>();
    startSignal.Dispatch();
}
複製程式碼

對映沒有命令的訊號 

一個訊號對映到一個命令會自動建立一個對映,您可以檢索通過注入到其他地方   但是如果你想注入訊號沒有繫結到一個命令  使用injectionBinder只需將它對映

injectionBinder.Bind<ShipDestroyedSignal>().ToSingleton();

The mediation extension(調解器(中介模式))

MediationContext是唯一一個專為unity設計的部分,因為mediation關心的是對view(GameObject)的操作。由於view部分天生的不確定性,我們推薦view由兩種不同的monobehavior組成:View and Mediator

view就是mvc中的v,一個view就是一個你可以編寫的邏輯,控制可見部分的monobehavior 這個類可以附加(拖拽)到unity編輯器來管理GameObject 但是不建議將mvc中的models和controller邏輯解除安裝view中

Mediator類的職責是執行view和整個應用的執行。他會獲取整個app中分發和接收時間和訊息。但是因為mediator的設計,建議使用命令模式(command)來做這部分功能

複製程式碼
using Strange.extensions.mediation.impl;
using com.example.spacebattle.events;
using com.example.spacebattle.model;
namespace com.example.spacebattle.view
{
    class DashboardMediator : EventMediator
    {
        [Inject]
        public DashboardView view{get;set;}

        override public void OnRegister()
        {
            view.init();
            dispatcher.AddListener
                (ServiceEvent.FULFILL_ONLINE_PLAYERS, onPlayers);
            dispatcher.Dispatch
                (ServiceEvent.REQUEST_ONLINE_PLAYERS);
        }
        
        override public void OnRemove()
        {
            dispatcher.RemoveListener
                (ServiceEvent.FULFILL_ONLINE_PLAYERS, onPlayers);
        }

        private void onPlayers(IEvent evt)
        {
            IPlayers[] playerList = evt.data as IPlayers[];
            view.updatePlayerCount(playerList.Length);
        }
    }
}
複製程式碼

1.DashboardView注入可以使 Mediator 知道具體的view

2.注入完成後OnRegister()方法會立即執行,可以用這個方法來做初始化  像上面做的那樣 初始化 然後做重要的資料請求

3.contextDispatcher可以擴充套件任何的EventMediator

4.OnRemove()清理時使用,當一個view銷燬前被呼叫,移除時記得刪除你的監聽

5.例子中的view暴露兩個介面init()和updatePlayerCount(float value),但是程式在設計時 你需要更多,但是原則是相同的  限制中介除了薄任務之間的傳遞資訊的檢視和其他應用程式

繫結一個介面到Mediator

mediationBinder.Bind<DashboardView>().To<DashboardMediator>();

值得注意的幾點

1.不是所有的MonoBehaviour被限制為一個View 

2.中介者繫結是例項對例項的,也就是說一個view對應一個mediator,如果有很多view,也就會有很多的mediator

The context extension(上下文擴充套件)

MVCSContext包含EventDispatcher(事件分發),injectionBinder(注入繫結),MediationBinder(中介繫結),CommandBinder(命令繫結)

可以重新將CommandBinder繫結到SignalCommandBinder   命令和中介依託注入,context可以為命令和中介的繫結提供關聯

建立一個專案,需要重新MVCSContext或者Context,一個app也可以包換多個Contexts 這樣可以使你的app更高的模組化,因此,一個app可以獨立的設計為聊天模組,社交模組  最終他們會整合到一起成為一個完整的app

相關推薦

Unity StrangeIoc框架

 最近想專案中需要使用這個架構  因此 上網看了很多資料摸索   但是對於初學者來說大多數的資料不是那麼容易理解 而且文件也是英文的閱讀起來有點吃力  所以記錄一下自己閱讀的過程  方便以後翻閱和跟我一樣的新人學習其中也借鑑了一些前輩的資料 如有反感請聯絡我   立馬進行修改  謝謝 StrangeIoc

Unity StrangeIoc框架

MVCSContex :the big picture  1.應用程式的入口是一個類成為ContextView,這是一個Monobehavior例項化MVCSContext 2.用MVCSContext來執行各種繫結。 3.派發器是一個通訊匯流排,允許你再程式傳送訊息,在mvcscontext中他們傳送的是T

Unity3D的MVC框架——StrangeIOC與Extension的擴充套件介紹

Unity3D的MVC框架(一)——Strange IOC與Extension的擴充套件介紹 StrangeIoC框架是一個超輕量級和高度可擴充套件反轉(IOC)框架,專門為c#和Unity編寫,它是基於MVC框架擴充套件的。即MVCS框架。M(models),

Unity實現AR類爐石傳說遊戲——開發框架

遊戲流程圖 玩家類 每場對戰中有兩個玩家例項 每個玩家擁有: 卡組 牌庫 手牌 場上的英雄 生命值,能量值等屬性 卡組 vector作為容器儲存 vector<Card> CardsCombination; 由

【SSH之旅】步步學習Hibernate框架:關於持久化

stc localhost 對象 schema hbm.xml java let pass [] 在不引用不論什麽框架下,我們會通過平庸的代碼不停的對數據庫進行操作,產生了非常多冗余的可是又有規律的底層代碼,這樣頻繁的操作數據庫和大量的底層代碼的反復

基於 EntityFramework、Autofac 的 UnitOfWork 框架

map 包括 應用 問題 pos web 新的 ges 解釋 之前公司項目參考 NopCommerce 開發了一套系統,但是不支持 UnitOfWork,最近想開發新的項目,所以就基於原有的基礎上又添加 UnitOfWork 支持,由於目前正在逐步完善中,所以可能存在各種問

web框架之基礎簡介

-a 程序 我想 pos 客戶 創建 當前 自動 art http的請求聲明周期:域名----DNS服務器---IP地址---基於tcp協議的http協議發送請求協議,服務端返回響應頭+響應體(我們所看到的頁面(是經過js渲染的,接收的是字符串))服務端(web服務)根據我

自制權限框架jsp標簽

目錄 用戶 request ref 基於 -i rar 發現 tro 一、概述   在我們的系統中,很多時候都用到了權限。最簡單的權限就是登錄。登錄了,我就可以自己的相關信息;沒有登錄,就不能看到。   目前比較流行的權限框架就是apache shiro和spring se

MyBatis框架

通過 ons sql dstat ride 開源 alt div feed MyBatis介紹:   MyBatis 本是apache的一個開源項目iBatis, 2010年這個項目由apache software foundation 遷移到了google code,並且

Spring框架

原因 getmethod 意思 myba model 找我 except 類型 程序代碼 Spring:   Spring是一個開源框架,Spring是於2003 年興起的一個輕量級的Java 開發框架,由 Rod Johnson在其著作 Expert One-On-On

編寫python web框架:簡介

== web 方法 nvi ever pytho 必須 ext sim 編寫一個最簡單的應用: def app(environ, start_response): start_response(‘200 OK‘, [(‘Content-Type‘, ‘tex

我為什麽支持高校的業務系統放棄Apache Struts2框架

文章 廣東 我們 然而 所有 以及 erl 業務 spring 月初聽聞廣東某高校(中山大學)發布通知:關於停止使用Apache Struts2開發框架的通知 鑒於S2漏洞較多、維護難度較大,為防範控制網絡安全風險,經研究決定,新建信息化項目不得使用S2; 在用S2的信息

Skynet服務器框架 Linux下的安裝和啟動

進行 harbor 的人 新源 中心 -a 編譯 加載模塊 問題 根據雲風博客的描述,Skynet 的核心功能就是解決一個問題: 把一個符合規範的 C 模塊,從 動態庫(so文件)中啟動起來,綁定一個永不重復(即使模塊退出)的數字id做為其 handle。模塊 被稱為

利用 Composer 步構建自己的 PHP 框架

highlight odi yii 2 org lar getc group bsp 空間 “一個時代結束了,另一個時代開始了。” Framework Interoperability Group(框架可互用性小組),簡稱 FIG,成立於 2009 年。FIG 最初由幾位知

Java源碼解析——集合框架——ArrayList

cloneabl trac RR ... 需要 pub 復雜 每次 靈活 ArrayList源碼分析 ArrayList就是動態數組,是Array的復雜版本,它提供了動態的增加和減少元素、靈活的設置數組的大小。 一、類聲明 public class ArrayLi

Windows環境下,從零開始搭建Nodejs+Express+Ejs框架---安裝nodejs

直接 分享圖片 完成 info pre download png 安裝包 get 第一步,安裝nodejs https://nodejs.org/en/download/ 這個是nodejs的官網,由於操作系統是win7 64位的,所以,我下載的是node-v8.11.1-

TensorFlow框架 張量、計算圖、會話

type ont 權重 src target col flow imp mooc 參考:中國大學MOOC 北京大學 曹健《TensorFlow筆記》 基於TensorFlow的NN:用張量表示數據,用計算圖搭建神經網絡,用會話執行計算圖,優化線上的權重(參數),得

Asp.net MVC 搭建屬於自己的框架

C4D pagedlist del tran 6.0 ext 才有 應該 frame 網址:https://www.cnblogs.com/sggx/p/4555255.html 為什麽要自己搭框架?   大家夥別急,讓我慢慢地告訴你!大家有沒有這種感覺,從一家跳槽到另一家

分布式服務框架

不能 企業應用 service ide 宕機 因此 style 質量 作用 一、RPC   RPC(Remote Process Call),即遠程服務調用,被廣泛地應用在很多企業應用中,是早期主要的服務治理方案,其流程較為簡單,客戶端consumer攜帶參數發送RPC請求

初探AngularJs框架

{} ack -o fff ati gist htm bootstra 如果 一、需要準備的環境 Nodejs:https://nodejs.org/en/download/ Python:https://www.python.org/downloads/