1.應用程序的入口是一個類成爲ContextView,這是一個Monobehavior實例化MVCSContextapi
2.用MVCSContext來執行各類綁定。架構
3.派發器是一個通訊總線,容許你再程序發送消息,在mvcscontext中他們發送的是TimEvents, 或者你能夠按照上面的步驟重寫Context 來使用Signalsmvc
4.命令類由TimeEvents或信號觸發,而後他會執行一些app邏輯。app
5.模型存儲狀態框架
6.services與app意外的部分通訊(好比接入的faebook)ide
7.界面腳本附加到物體上 : 玩家與遊戲的交互模塊化
8.Mediators(中介)也是monobehavior 可是他也能夠將view部分和其餘部分隔離開來函數
這張圖戰士了這些部分是如何一塊兒工做的佈局
大體介紹完了 下面來如何創建工程post
ContextView 是一個Monobehaviour 用來實例你的Context(上下文) MyFirstProjectRoot 是ContextView的子類, 這裏是應用程序的開始
using System; using UnityEngine; using strange.extensions.context.impl; using strange.extensions.context.api;
namespace strange.examples.myfirstproject { public class MyFirstProjectRoot : ContextView { void Awake() { //Instantiate the context, passing it this instance. context = new MyFirstContext(this,ContextStartupFlags.MANUAL_MAPPING);
context.Start(); } } }
這裏要使用 strange.extensions.context.impl 和 using strange.extensions.context.api 命名空間
ContextView定義了一個屬性稱爲上下文固然是指咱們上下文。咱們只須要定義它是什麼咱們寫一個叫MyFirstContext的腳本。this 指的是MyFirstProjectRoot,他告訴Context 哪一個GameObject被認爲是ContextView。ContextStartupFlags.MANUAL_MAPPING代表一旦咱們開始一切將會繼續。 調用context.Start()讓它付諸行動 。 若是不調用Start則不會繼續進行
Context(上下文)是全部綁定發生的地方,若是沒有綁定,Strange應用只是一堆斷開鏈接的部分。Context是爲混亂帶來秩序的膠水。從咱們擴展MVCSContext,咱們獲得了一大堆的核心綁定,MVCSContext是爲了給咱們全部咱們須要乾淨的結構 一個控制反轉風格的應用:一個注射(injector)、命令總線、模型和服務支持,和中介界面。
using System; using UnityEngine; using strange.extensions.context.api; using strange.extensions.context.impl; using strange.extensions.dispatcher.eventdispatcher.api; using strange.extensions.dispatcher.eventdispatcher.impl; namespace strange.examples.myfirstproject { public class MyFirstContext : MVCSContext { public MyFirstContext (MonoBehaviour view) : base(view) { } public MyFirstContext (MonoBehaviour view, ContextStartupFlags flags) : base(view, flags) { } protected override void mapBindings() { injectionBinder.Bind<IExampleModel>() .To<ExampleModel>() .ToSingleton(); injectionBinder.Bind<IExampleService>() .To<ExampleService>() .ToSingleton(); mediationBinder.Bind<ExampleView>() .To<ExampleMediator>(); commandBinder.Bind(ExampleEvent.REQUEST_WEB_SERVICE) .To<CallWebServiceCommand>(); commandBinder.Bind(ContextEvent.START) .To<StartCommand>().Once (); } } }
像你看到的那樣,咱們擴展了MVCSContext,這意味着咱們繼承其全部映射(探索它類的深度 ,你會發現它的有趣)。咱們已經有一個injectionBinder和commandBinder和dispatcher調度員。注意,調度程序能夠在整個應用程序,和CommandBinder耦合,因此任何事件派遣能夠觸發回調也觸發命令commands和序列sequences。
這裏的映射是徹底符合你的期待若是你讀到的各類組件注入,咱們映射一個模型和一個服務都是單例。咱們將只有一個視圖(ExampleView)在這個例子中,咱們將它綁定到一箇中介(ExampleMediator)。最後,咱們映射兩個命令。這兩個比較重要的是StartCommand綁定到一個特殊的事件:ContextEvent.START.這是事件觸發啓動你的應用。你須要綁定一些命令或者隊列到它身上想init()爲進入你的應用程序。咱們綁定了.Once(),一個特殊的方法,在一次結束時被解開Unbinds。
注意這裏有一個postBindings()方法。這是一個十分有用的地方放一些你須要在綁定以後運行的代碼。可是他運行在Launch()以後,MVCSContext用這個方法去處理任何Views界面哪個在寄存器中更早(在mapBindings以後被調用)。另外一個明顯的和有用的狀況 在postBindings()中調用DontDestroyOnLoad(ContextView)。在你加載一個新的場景時用來保留ContextView(and the Context)。
ContextEvent.START 被處罰,由於它被綁上了StartCommand, 一個新的StartCommand實例將被實例化出來而且執行。
using System; using UnityEngine; using strange.extensions.context.api; using strange.extensions.command.impl; using strange.extensions.dispatcher.eventdispatcher.impl; namespace strange.examples.myfirstproject { public class StartCommand : EventCommand { [Inject(ContextKeys.CONTEXT_VIEW)] public GameObject contextView{get;set;} public override void Execute() { GameObject go = new GameObject(); go.name = "ExampleView"; go.AddComponent<ExampleView>(); go.transform.parent = contextView.transform; } } }
StartCommand 擴展 EventCommand 意味着這是固定的命令CommandBinder能夠處理, 他繼承的全部東西都來自command 和 EventCommand。特別是,繼承EventCommand意味着你獲得一個IEvent注入,而且你能夠訪問dispatcher。
若是你只是擴展命令,您不會有自動訪問這些對象,可是你依舊能夠手動注入他們
[Inject(ContextKeys.CONTEXT_DISPATCHER)] IEventDispatcher dispatcher{get;set;} [Inject] IEvent evt{get;set;}
注意所使用的兩種不一樣類型的注入。IEventDispatcher和GameObject 都是用名字建立多個實例。這是由於咱們想引用這些對象的很是具體的版本。咱們不但願是任意一個GameObject。咱們須要一個標記像ContextView。咱們也不接受任何舊IEventDispatcher。惟一一個將在上下文間通訊,他標誌爲ContextKeys.CONTEXT_DISPATCHER。另外一方面,Ievent是一個簡單的映射用於這個特殊的命令(技術上他映射到一個value),因此沒有必要的名字。
依賴咱們將使用在當前場景是ContextView,他們添加子視圖到它。
Execute()方法經過CommandBinder自動觸發。大多數狀況下 , 執行的順序是這樣的
命令不須要當即清理乾淨,可是咱們將會得一點。若是你查看了Execute()裏面的代碼,你將會發現他是純粹的Unity。建立一個GameObject,附上MonoBehaviour,而後設置它的父親爲ContextView。咱們使用的是具體的MonoBehaviour(代碼),然而,剛好是一個Strange IView,自從咱們在context中映射這個界面。
mediationBinder.Bind<ExampleView>().To<ExampleMediator>();
這個界面是自動調度的,這意味着一個新的ExampleMediator剛剛建立!
若是你花費了一些時間爲Unity編寫代碼,你建立一個界面,你須要調用Monobehavior,可是重點在於那個界面沒有在屏幕上顯示的東西。我不打算花時間執行ExampleView代碼。你能夠看下示例文件,若是怕你已經知道C#和Unity你不須要他。我只想引發兩位的注意力。首先:
public class ExampleView : View
經過擴展View,你將會獲得鏈接每一個View到Context的代碼。使用Strange 你再也不須要擴展View或者重寫裏面的方法。可是若是你不擴展View,你依舊須要實現IView 接口。這須要確保你MonoBehaviour上下文能夠操做。
第二項指出
[Inject] public IEventDispatcher dispatcher{get; set;}
注意 咱們注入IEventDispatcher。可是跟StartCommand不是同一個調度。仔細看看代碼第一個寫在EventCommand(我上面顯示)是這樣的
[Inject(ContextKeys.CONTEXT_DISPATCHER)] public IEventDispatcher dispatcher{get; set;}
經過命名注入,指定的命令使用常見的上下文調度員。這個界面不該該注入dispatcher。中介的目的是隔離應用程序的視圖 反之亦然Strange容許注入View。但這功能最好的時候嚴格限制,注入本地調度員與中介溝通很好。因此注入配置/佈局文件(這是有用的,若是你發佈到多個平臺)。但若是你聽個人勸告,不要注入入一個模型或服務或其餘池外擴展的視圖以及中介。
告訴你正確的方法:對於大多數開發人員來講,最難的是掌握整個框架的概念。一個視圖應該只顯示和輸入。當某些輸入發生,視圖應該通知媒體。中介Mediator(容許注入上下文調度員)抽象的觀點,關注與應用程序的其他部分。這個保護應用程序的視圖代碼,這一般和保護你的界面是混亂的,相反的狀況是如此。
注意,基本視圖類使用標準MonoBehaviour處理程序 Awake()
, Start()
, and OnDestroy()。若是你重寫這些處理程序,確保你調用了base.Awake()等。這樣Strange才能正常運行。
觀察調度者
using System; using UnityEngine; using strange.extensions.dispatcher.eventdispatcher.api; using strange.extensions.mediation.impl; namespace strange.examples.myfirstproject { public class ExampleMediator : EventMediator { [Inject] public ExampleView view{ get; set;} public override void OnRegister() { view.dispatcher.AddListener (ExampleView.CLICK_EVENT, onViewClicked); dispatcher.AddListener (ExampleEvent.SCORE_CHANGE, onScoreChange); view.init (); } public override void OnRemove() { view.dispatcher.RemoveListener (ExampleView.CLICK_EVENT, onViewClicked); dispatcher.RemoveListener (ExampleEvent.SCORE_CHANGE, onScoreChange); Debug.Log("Mediator OnRemove"); } private void onViewClicked() { Debug.Log("View click detected"); dispatcher.Dispatch(ExampleEvent.REQUEST_WEB_SERVICE, "http://www.thirdmotion.com/"); } private void onScoreChange(IEvent evt) { string score = (string)evt.data; view.updateScore(score); } } }
在最上方 咱們注入了ExampleView。這是調度者Mediator如何知道調度那個界面。介質能夠知道不少關於他們的界面。中介一般被認爲是「廢品(信口開河的)代碼」,由於它是很是特殊的細節視圖和應用程序。固然這個中介能夠知道視圖有一個調度者和這個調度這個程序的事件成爲ExampleView.CLICK_EVENT。經過監聽這個事件,中介創建了一個處理程序(onViewClicked())告訴其他的應用這個點擊意味着什麼。視圖不該該發送REQUEST_WEB_SERVICE事件。界面只是界面。它應該像這樣派發事件HELP_BUTTON_CLICKED, COLLISION, SWIPE_RIGHT。這應該是Mediator中介者的工做,映射這些事件到應用程序的其他有意義的部分。如REQUEST_HELP MISSILE_ENEMY_COLLISION PLAYER_RELOAD.他後面的事件映射到命令,這些命令會調用幫助系統,計算分數增長(增長得分模型)或肯定是否容許玩家從新加載。
OnRegister()和OnRemove()方法像Mediator調度者的構造與析構函數。OnRegister()在注入後發生。因此我常常用它來設置監聽和調用Init()方法實例界面、OnRemove()發生在Monobehavior的OnDestroy()被調用以後。它觸發的時候你能夠用來清理監聽。肯定你移除了你的監聽,不然會產生不正確的垃圾回收。
最後注意 經過擴展EventMediator咱們有公共的dispatcher。 調度者在總線監聽SCORE_CHANGE事件。
讓咱們看回Context 這行有咱們忽略的問題:
commandBinder.Bind(ExampleEvent.REQUEST_WEB_SERVICE).To<CallWebServiceCommand>();
這裏的意思是任什麼時候候公共的總線收到這個事件,它會啓動CallWebServiceCommand。可是他吸引你的注意力是由於經過不一樣的方法使用命令。
using System; using System.Collections; using UnityEngine; using strange.extensions.context.api; using strange.extensions.command.impl; using strange.extensions.dispatcher.eventdispatcher.api; namespace strange.examples.myfirstproject { public class CallWebServiceCommand : EventCommand { [Inject] public IExampleModel model{get;set;} [Inject] public IExampleService service{get;set;} public override void Execute() { Retain (); service.dispatcher.AddListener (ExampleEvent.FULFILL_SERVICE_REQUEST, onComplete); string url = evt.data as string service.Request(url); } private void onComplete(IEvent result) { service.dispatcher.RemoveListener (ExampleEvent.FULFILL_SERVICE_REQUEST, onComplete); model.data = result.data as string; dispatcher.Dispatch(ExampleEvent.SCORE_CHANGE, evt.data); Release (); } } }
咱們監聽service,調用一個方法。咱們使用事件中有效data數據來觸發mediator調度者service.Request(the url)。當service結束,他派發。觸發onComplate()。咱們取消監聽映射,設置一個值的模型,派發SCORE_CHANGE那個哪一個調度者收到就作相應的處理。
但若是你一直密切關注你會記得,我以前提到過,命令後當即清理乾淨在Execute()完成時。因此爲何不是這個命令不會被垃圾收集。答案是最頂端調用Retain()方法保留。Retain()標誌着這個命令爲免除清理。這將會保持知道調用了Release()以後。顯然,這意味着調用了Release()是很是重要的,不然會形成運行中的內存泄露風險。
通常來講你要遵照上下文邊界。畢竟,它的邊界是有緣由的 :它容許應用程序的部分功能的隔離,使程序變得更加模塊化。但有時有一些對象,也許是一個模型、一個服務、或者一個信號須要須要跨多個上下文訪問。
injectionBinder.Bind<IStarship>().To<HeartOfGold>().ToSingleton().CrossContext();
添加CrossContext()信號綁定須要實例化穿過context邊界。它將提供給全部孩子contexts。注意,也能夠覆蓋一個CrossContext綁定。若是你隱射局部的key,本地的綁定將會覆蓋CrossContext的那一個。
到這裏全部的文檔內容已經結束了。本身着手作幾個小栗子能快速的瞭解和適應這樣的架構。架構的學習 確實能夠改變人的編碼習慣 。 就這幾天對文檔的閱讀,感受收穫良多 。 但願有學習的小夥伴可以一塊兒交流交流。