Windows10(UWP)下的MEF

前言

最近在幫一家知名外企開發(fā)Universal Windows Platform的相關(guān)應(yīng)用辽剧,開發(fā)過程中不由感慨:項目分為兩種,一種叫做前人栽樹后人乘涼,一種叫做前人挖坑后人遭殃。不多說了莽囤,多說又要變成月經(jīng)貼了。

講講MEF切距。

MEF全稱Managed Extensibility Framework朽缎。我們做.Net的碰到依賴注入(DI:Dependency Injection)這一塊的內(nèi)容,一般會選擇使用Unity或者MEF谜悟,這也是Prism主要使用的兩種方式话肖。在.Net 4.0之前,MEF一直作為擴展的形式存在葡幸,但是.Net 4.0的時候最筒,已經(jīng)作為Framework的一部分了。但是.Net 4.0的MEF只是原始的版本礼患,后面MEF 2又加入了泛型類導(dǎo)入導(dǎo)出等等特性是钥。MEF 2不作為.Net的一部分掠归,又變成了以擴展包的形式存在缅叠,支持了包括.Net 4.5以及之后的平臺,我們可以通過Nuget獲取這個擴展虏冻,源碼也被托管在了codeplex平臺肤粱。

MEF2支持的平臺

  • NET Framework 4.5
  • Windows 8
  • Windows Phone 8.1
  • Windows Phone Silverlight 8
  • Portable Class Libraries

通常意義上,當我們講到MEF的時候厨相,一般都會去描述這是一個用來實行插件式開發(fā)的一套東西领曼。當插件式開發(fā)成為了一種可能鸥鹉,那就意味著我們的項目可以被完整的解耦,這就保證了我們程序的健壯性庶骄,同時在開發(fā)的過程中我們也避免了各種開發(fā)人員的沖突毁渗。

開始

怎樣開始寫一個基于MEF的程序?

假設(shè)我們現(xiàn)在寫的是一個UWP的項目单刁,并且我們采用C#+XAML的方式灸异。因為MVVM是XAML的主打的方式,可以很好的應(yīng)用綁定數(shù)據(jù)的這個模型羔飞,所以我們采用MVVM肺樟。

所以我們決定設(shè)計一個基于C#+XAML的通過MVVM模式來進行View和ViewModel的解耦,中間我們也可以實現(xiàn)一個觀察者模式的消息傳遞方式來進行View之間的相互傳參等等逻淌∶床看是確實很完美,可以很輕易的下載一個Mvvmlight來直接用卡儒。

我們看樣子已經(jīng)決定了View層和ViewModel層的問題田柔,那么對于一個完整的系統(tǒng),我們還缺少一些什么組件呢朋贬?我們可以還缺少數(shù)據(jù)凯楔,所以我們需要數(shù)據(jù)層,一般我們命名為Service層锦募,來進行跟數(shù)據(jù)庫或者服務(wù)器的通信摆屯;還缺什么呢?日志系統(tǒng)糠亩,我們需要進行運行過程中的一些數(shù)據(jù)統(tǒng)計虐骑,或者異常捕獲后的消息記錄,所以我們需要Log層赎线;還需要緩存層廷没,緩存我們的數(shù)據(jù)到內(nèi)存或者磁盤,這樣看上去我們的程序能夠運行的稍微好一點垂寥。

當我們決定了以后颠黎,我們現(xiàn)在需要寫的東西如下:

  • View
  • ViewModel
  • Service
  • Log
  • Cache

想想我們怎么處理這個問題呢?

public sealed class Hub
{
        
    public static Log Log { get; private set; }
    public static Service Service { get; private set; }
    public static Cache Cache { get; private set; }

    private static Hub instance = null;
    private static readonly object padlock = new object();

    Hub()
    {
        Log = new Log();
        Service = new Service();
        Cache = new Cache();
    }

    public static Hub Instance
    {
        get
        {
            if (instance == null)
            {
                lock (padlock)
                {
                    if (instance == null)
                    {
                        instance = new Hub();
                    }
                }
            }
            return instance;
        }
    }
}

上面的解決方案滞项,引入一個單例的Hub類狭归,然后各層作為只讀靜態(tài)屬性來提供各類功能,看上去不錯文判,我們也能很好的調(diào)用过椎。

但是有個問題,當這個類出現(xiàn)的時候戏仓,我們不希望Service等等類再被外部實例化疚宇。很不幸亡鼠,當Service等作為一個可以Public的屬性時,這個類本身為了訪問的一致性就也要不可以避免的被標記為Public敷待,這破壞了我們設(shè)立這個類的初衷间涵。

那我們怎么繼續(xù)解決這個問題?把Service等類都設(shè)計為單例榜揖。這也是一個解決方案浑厚。但無論是這種解決方案還是代碼里的解決方案,都強引用的意味都太強了根盒,稍加不慎钳幅,系統(tǒng)就會崩潰。

我們不希望引入Hub炎滞,也不想Service等類被設(shè)計為單例敢艰,同時具體的ViewModel中也不希望出現(xiàn)具體的Service的實例,那我們應(yīng)該怎么辦册赛?

答案:依賴注入钠导。

重新設(shè)計

保留我們之前所設(shè)想的所有的組件,引入接口來進入注入:

  • IService
  • ILog
  • ICache

看一下我們的ViewModel現(xiàn)在應(yīng)該是怎么樣的?

public class ViewModel
{
    ILog Log;
    IService Service;
    ICache Cache;

    public ViewModel(ILog log, IService service, ICache cache)
    {
            Log = log;
            Service = service;
            Cache = cache;
    }
}

又進了一步森瘪,我們只需要調(diào)用的時候給我們需要的實例就行了牡属。如果我們需要View,我們還能聲明一個IView的接口扼睬。

至此逮栅,我們設(shè)計還沒有引入MEF,看上去已經(jīng)相對比較好的解耦了窗宇,我們只有在調(diào)用ViewModel的時候措伐,引入具體的是實例,耦合發(fā)生在了此處军俊。

引入MEF

試想一下侥加,既然我們需要生成的實例的對象都已經(jīng)在我們的DLL之中,為什么我們還要手動的去生成一個實例粪躬,然后再傳到具體的構(gòu)造函數(shù)里面担败,它就不能自己尋找嗎?

假設(shè)我們的類都有一個別名,然后我們在需要引用的地方告訴告訴程序镰官,我們需要一個實現(xiàn)Ixxx接口的類提前,它的名字叫做xxx,這樣我們是不是更進了一部朋魔。如下:

public class ViewModel
{
    [Import("LogSample")]
    ILog Log;
    [Import("ServiceSample")]
    IService Service;
    [Import("CacheSample")]
    ICache Cache;

    public ViewModel()
    {
    }
}

當我們構(gòu)造函數(shù)完成后岖研,Log等對象就已經(jīng)自動在程序集中找到名為LogSample的的實現(xiàn)ILog的類卿操,Service也是警检,Cache也是孙援。

看下Log

[Export("LogSample", typeof(ILog))]
public class Log : ILog
{
}

到現(xiàn)在為止,我們主要關(guān)注具體的功能實現(xiàn)就好了扇雕。

MEF正式引入

為了簡化我們的程序拓售,更加關(guān)注MEF的本質(zhì),我們把程序設(shè)計為僅包括下列的組件

  • View
  • ViewModel
  • Service

建立我們的項目如下

Paste_Image.png

代碼已經(jīng)完整的托管到GitHub上镶奉,可以方便的查閱础淤。

我們在Service中寫了一個演示的功能:

[Export(Constant.Confing.SampleService,typeof(IService))]
public class SampleService : IService
{
    public void QueryData(int numuber, Action<int> action)
    {
        action(numuber);
    }
}

我們看一下我們的程序的主界面:

public sealed partial class MainPage : Page
{
    [Import(Constant.Confing.View1)]
    public IView View1 { get; set; }

    [Import(Constant.Confing.View2)]
    public IView View2 { get; set; }

    public MainPage()
    {
        this.InitializeComponent();
        this.Loaded += MainPage_Loaded;
    }

    private CompositionHost host;

    private void MainPage_Loaded(object sender, RoutedEventArgs e)
    {
        List<Assembly> assemblies = new List<Assembly>()
        {
            Assembly.Load(new AssemblyName("MEF.Service")),
            Assembly.Load(new AssemblyName("MEF.View")),
            Assembly.Load(new AssemblyName("MEF.ViewModel")),
            Assembly.Load(new AssemblyName("MEF.Abstract"))
            };
        ContainerConfiguration configuration = new ContainerConfiguration().WithAssemblies(assemblies);
        host = configuration.CreateContainer();
        host.SatisfyImports(this);
    }

    private void View1_Click(object sender, RoutedEventArgs e)
    {
        IView view = host.GetExport(typeof(IView), Constant.Confing.View1) as IView;
        frame.Content = view;
    }

    private void View2_Click(object sender, RoutedEventArgs e)
    {
        IView view = host.GetExport(typeof(IView), Constant.Confing.View2) as IView;
        frame.Content = view;
    }
}

將所有的程序集加入容器之中,然后通過容器去創(chuàng)建對象哨苛。

View的代碼:

[Export(Constant.Confing.View1,typeof(IView))]
public sealed partial class View1 : UserControl, IView
{
    IService Service;
    IViewModel ViewModel;
    [ImportingConstructor]
    public View1(
        [Import(Constant.Confing.SampleService)]IService service, 
        [Import(Constant.Confing.ViewModel1)]IViewModel viewModel)
    {
        this.InitializeComponent();
        this.Service = service;
        this.ViewModel = viewModel;
    }

    private void Button_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e)
    {
        Service.QueryData(ViewModel.Number, ShowValue);
    }

    private void ShowValue(int i)
    {
        Btn.Content = i;
    }
}

演示

初始的狀態(tài):

Paste_Image.png

當我們點擊Show View 1按鈕時鸽凶,容器去創(chuàng)建View1的實例,View1所需要的實例建峭,又會根據(jù)導(dǎo)入導(dǎo)出的原則去創(chuàng)建玻侥。創(chuàng)建完成后,

Paste_Image.png

點擊View 1 Click后亿蒸,會將ViewModel層的數(shù)據(jù)傳給Service凑兰,Service又調(diào)用回掉函數(shù),將數(shù)據(jù)放置到UI上边锁。

Paste_Image.png

也可以點擊Show View 2進行相應(yīng)的操作姑食。

Paste_Image.png
Paste_Image.png

總結(jié)

本文講述了一個簡單的MEF在UWP下的引用,體現(xiàn)了MEF通過依賴注入的方式將程序更好的解耦茅坛。閱讀本文希望對你有所幫助音半。

謝謝~

代碼下載:http://files.cnblogs.com/files/youngytj/uwp_MEF.zip

參考資料

Unity
《MEF程序設(shè)計指南》博文匯總
Prism
Prism與MVVM、Unity贡蓖、MEF關(guān)系
依賴注入那些事兒
System.Composition

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末祟剔,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子摩梧,更是在濱河造成了極大的恐慌物延,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件仅父,死亡現(xiàn)場離奇詭異叛薯,居然都是意外死亡,警方通過查閱死者的電腦和手機笙纤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進店門耗溜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人省容,你說我怎么就攤上這事抖拴。” “怎么了?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵阿宅,是天一觀的道長候衍。 經(jīng)常有香客問我,道長洒放,這世上最難降的妖魔是什么蛉鹿? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮往湿,結(jié)果婚禮上妖异,老公的妹妹穿的比我還像新娘。我一直安慰自己领追,他們只是感情好他膳,可當我...
    茶點故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著绒窑,像睡著了一般矩乐。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上回论,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天散罕,我揣著相機與錄音,去河邊找鬼傀蓉。 笑死欧漱,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的葬燎。 我是一名探鬼主播误甚,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼谱净!你這毒婦竟也來了窑邦?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤壕探,失蹤者是張志新(化名)和其女友劉穎冈钦,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體李请,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡瞧筛,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了导盅。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片较幌。...
    茶點故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖白翻,靈堂內(nèi)的尸體忽然破棺而出乍炉,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布岛琼,位于F島的核電站底循,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏衷恭。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一纯续、第九天 我趴在偏房一處隱蔽的房頂上張望随珠。 院中可真熱鬧帅刊,春花似錦欣喧、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽显沈。三九已至,卻和暖如春逢唤,著一層夾襖步出監(jiān)牢的瞬間拉讯,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工鳖藕, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留魔慷,地道東北人。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓著恩,卻偏偏與公主長得像院尔,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子喉誊,可洞房花燭夜當晚...
    茶點故事閱讀 44,781評論 2 354

推薦閱讀更多精彩內(nèi)容