茄子在线看片免费人成视频,午夜福利精品a在线观看,国产高清自产拍在线观看,久久综合久久狠狠综合

    <s id="ddbnn"></s>
  • <sub id="ddbnn"><ol id="ddbnn"></ol></sub>

  • <legend id="ddbnn"></legend><s id="ddbnn"></s>

    三種觀察者模式的C#實(shí)現(xiàn)
    來源:易賢網(wǎng) 閱讀:3563 次 日期:2015-04-08 14:44:58
    溫馨提示:易賢網(wǎng)小編為您整理了“三種觀察者模式的C#實(shí)現(xiàn)”,方便廣大網(wǎng)友查閱!

    說起觀察者模式,估計(jì)在園子里能搜出一堆來。所以寫這篇博客的目的有兩點(diǎn):

    觀察者模式是寫松耦合代碼的必備模式,重要性不言而喻,拋開代碼層面,許多組件都采用了Publish-Subscribe模式,所以我想按照自己的理解重新設(shè)計(jì)一個(gè)使用場(chǎng)景并把觀察者模式靈活使用在其中

    我想把C#中實(shí)現(xiàn)觀察者模式的三個(gè)方案做一個(gè)總結(jié),目前還沒看到這樣的總結(jié)

    現(xiàn)在我們來假設(shè)這樣的一個(gè)場(chǎng)景,并利用觀察者模式實(shí)現(xiàn)需求:

    場(chǎng)景:未來智能家居進(jìn)入了每家每戶,每個(gè)家居都留有API供客戶進(jìn)行自定義整合,所以第一個(gè)智能鬧鐘(smartClock)先登場(chǎng),廠家為此鬧鐘提供了一組API,當(dāng)設(shè)置一個(gè)鬧鈴時(shí)間后該鬧鐘會(huì)在此時(shí)做出通知,我們的智能牛奶加熱器,面包烘烤機(jī),擠牙膏設(shè)備都要訂閱此鬧鐘鬧鈴消息,自動(dòng)為主人準(zhǔn)備好牛奶,面包,牙膏等。

    這個(gè)場(chǎng)景是很典型的觀察者模式,智能鬧鐘的鬧鈴是一個(gè)主題(subject),牛奶加熱器,面包烘烤機(jī),擠牙膏設(shè)備是觀察者(observer),觀察者只需要訂閱這個(gè)主題即可實(shí)現(xiàn)松耦合的編碼模型,讓我們通過三種方案逐一實(shí)現(xiàn)此需求。

    一、利用.net的Event模型來實(shí)現(xiàn)

    .net中的Event模型是一種典型的觀察者模型,在.net出身之后被大量應(yīng)用在了代碼當(dāng)中,我們看事件模型如何在此種場(chǎng)景下使用,

    首先介紹下智能鬧鐘,廠家提供了一組很簡(jiǎn)單的API

    public void SetAlarmTime(TimeSpan timeSpan)

    {

    _alarmTime = _now().Add(timeSpan);

    RunBackgourndRunner(_now, _alarmTime);

    }

    SetAlarmTime(TimeSpan timeSpan)用來定時(shí),當(dāng)用戶設(shè)置好一個(gè)時(shí)間后,鬧鐘會(huì)在后臺(tái)跑一個(gè)類似于while(true)的循環(huán)對(duì)比時(shí)間,當(dāng)鬧鈴時(shí)間到了后要發(fā)出一個(gè)通知事件出來

    protected void RunInBackgournd(Func<DateTime> now,DateTime? alarmTime )

    {

    if (alarmTime.HasValue)

    {

    var cancelToken = new CancellationTokenSource();

    var task = new Task(() =>

    {

    while (!cancelToken.IsCancellationRequested)

    {

    if (now.AreEquals(alarmTime.Value))

    {

    //鬧鈴時(shí)間到了

    ItIsTimeToAlarm();

    cancelToken.Cancel();

    }

    cancelToken.Token.WaitHandle.WaitOne(TimeSpan.FromSeconds(2));

    }

    }, cancelToken.Token, TaskCreationOptions.LongRunning);

    task.Start();

    }

    }

    其他代碼并不重要,重點(diǎn)在當(dāng)鬧鈴時(shí)間到了后要執(zhí)行ItIsTimeToAlarm(); 我們?cè)谶@里發(fā)出事件以便通知觀察者,.net中實(shí)現(xiàn)event模型有三要素,

    1.為主題(subject)要定義一個(gè)event, public event Action<Clock, AlarmEventArgs> Alarm;

    2.為主題(subject)的信息定義一個(gè)EventArgs,即AlarmEventArgs,這里面包含了事件所有的信息

    3.主題(subject)通過以下方式發(fā)出事件

    var args = new AlarmEventArgs(_alarmTime.Value, 0.92m);

    OnAlarmEvent(args);

    OnAlarmEvent方法的定義

    public virtual void OnAlarm(AlarmEventArgs e)

    {

    if(Alarm!=null)

    Alarm(this,e);

    }

    這里要注意命名規(guī)范,事件內(nèi)容-AlarmEventArgs,事件-Alarm(動(dòng)詞,例如KeyPress),觸發(fā)事件的方法 void OnAlarm(),這些命名都要符合事件模型的命名規(guī)范。

    智能鬧鐘(SmartClock)已經(jīng)實(shí)現(xiàn)完畢,我們?cè)谂D碳訜崞?MilkSchedule)中訂閱這個(gè)Alarm消息:

    public void PrepareMilkInTheMorning()

    {

    _clock.Alarm += (clock, args) =>

    {

    Message =

    "Prepraring milk for the owner, The time is {0}, the electric quantity is {1}%".FormatWith(

    args.AlarmTime, args.ElectricQuantity*100);

    Console.WriteLine(Message);

    };

    _clock.SetAlarmTime(TimeSpan.FromSeconds(2));

    }

    在面包烘烤機(jī)中同樣可以用_clock.Alarm+=(clock,args)=>{//it is time to roast bread}訂閱鬧鈴消息。

    至此,event模型介紹完畢,實(shí)現(xiàn)過程還是有點(diǎn)繁瑣的,并且事件模型使用不當(dāng)會(huì)有memory leak的問題,當(dāng)觀察者(obsever)訂閱了一個(gè)生命周期較長(zhǎng)的主題(該主題生命周期長(zhǎng)于觀察者),該觀察者將不會(huì)被垃圾回收(因?yàn)檫€有引用指向主題),詳見Understanding and Avoiding Memory Leaks with Event Handlers and Event Aggregators,開發(fā)者需要顯示退訂該主題(-=)。

    園子里老A也寫過一篇如何利用弱引用解決該問題的博客:如何解決事件導(dǎo)致的Memory Leak問題:Weak Event Handlers。

    二、利用.net中IObservable<out T>和IObserver<in T>實(shí)現(xiàn)觀察者模式

    IObservable<out T> 顧名思義-可觀察的事物,即主題(subject),Observer很明顯就是觀察者了。

    在我們的場(chǎng)景中智能鬧鐘是IObservable,該接口只定義了一個(gè)方法IDisposable Subscribe(IObserver<T> observer);該方法命名讓人有點(diǎn)犯暈,Subscribe即訂閱的意思,不同于之前提到過的觀察者(observer)訂閱主題(subject)。在這里是主題(subject)來訂閱觀察者(observer),其實(shí)這里也說得通,因?yàn)樵谠撃P拖?,主題(subject)維護(hù)了一個(gè)觀察者(observer)列表,所以有主題訂閱觀察者之說,我們來看鬧鐘的IDisposable Subscribe(IObserver<T> observer)實(shí)現(xiàn):

    public IDisposable Subscribe(IObserver<AlarmData> observer)

    {

    if (!_observers.Contains(observer))

    {

    _observers.Add(observer);

    }

    return new DisposedAction(() => _observers.Remove(observer));

    }

    可以看到這里維護(hù)了一個(gè)觀察者列表_observers,鬧鐘在到點(diǎn)了之后會(huì)遍歷所有觀察者列表將消息逐一通知給觀察者

    public override void ItIsTimeToAlarm()

    {

    var alarm = new AlarmData(_alarmTime.Value, 0.92m);

    _observers.ForEach(o=>o.OnNext(alarm));

    }

    很明顯,觀察者有個(gè)OnNext方法,方法簽名是一個(gè)AlarmData,代表了要通知的消息數(shù)據(jù),接下來看看牛奶加熱器的實(shí)現(xiàn),牛奶加熱器作為觀察者(observer)當(dāng)然要實(shí)現(xiàn)IObserver接口

    public  void Subscribe(TimeSpan timeSpan)

    {

    _unSubscriber = _clock.Subscribe(this);

    _clock.SetAlarmTime(timeSpan);

    }

    public  void Unsubscribe()

    {

    _unSubscriber.Dispose();

    }

    public void OnNext(AlarmData value)

    {

    Message =

    "Prepraring milk for the owner, The time is {0}, the electric quantity is {1}%".FormatWith(

    value.AlarmTime, value.ElectricQuantity * 100);

    Console.WriteLine(Message);

    }

    除此之外為了方便使用面包烘烤器,我們還加了兩個(gè)方法Subscribe()和Unsubscribe(),看調(diào)用過程

    var milkSchedule = new MilkSchedule();

    //Act

    milkSchedule.Subscribe(TimeSpan.FromSeconds(12));

    三、Action函數(shù)式方案

    在介紹該方案之前我需要說明,該方案并不是一個(gè)觀察者模型,但是它卻可以實(shí)現(xiàn)同樣的功能,并且使用起來更加簡(jiǎn)練,也是我最喜歡的一種用法。

    這種方案中,智能鬧鐘(smartClock)提供的API需要設(shè)計(jì)成這樣:

    public void SetAlarmTime(TimeSpan timeSpan,Action<AlarmData> alarmAction)

    {

    _alarmTime = _now().Add(timeSpan);

    _alarmAction = alarmAction;

    RunBackgourndRunner(_now, _alarmTime);

    }

    方法簽名中要接受一個(gè)Action<T>,鬧鐘在到點(diǎn)后直接執(zhí)行該Action<T>即可:

    public override void ItIsTimeToAlarm()

    {

    if (_alarmAction != null)

    {

    var alarmData = new AlarmData(_alarmTime.Value, 0.92m);

    _alarmAction(alarmData);

    }

    }

    牛奶加熱器中使用這種API也很簡(jiǎn)單:

    _clock.SetAlarmTime(TimeSpan.FromSeconds(1), (data) =>

    {

    Message =

    "Prepraring milk for the owner, The time is {0}, the electric quantity is {1}%".FormatWith(

    data.AlarmTime, data.ElectricQuantity * 100);

    });

    在實(shí)際使用過程中我會(huì)把這種API設(shè)計(jì)成fluent api,調(diào)用起來代碼更清晰:

    智能鬧鐘(smartClock)中的API:

    public Clock SetAlarmTime(TimeSpan timeSpan)

    {

    _alarmTime = _now().Add(timeSpan);

    RunBackgourndRunner(_now, _alarmTime);

    return this;

    }

    public void OnAlarm(Action<AlarmData> alarmAction)

    {

    _alarmAction = alarmAction;

    }

    牛奶加熱器中進(jìn)行調(diào)用:

    _clock.SetAlarmTime(TimeSpan.FromSeconds(2))

    .OnAlarm((data) =>

    {

    Message =

    "Prepraring milk for the owner, The time is {0}, the electric quantity is {1}%".FormatWith(

    data.AlarmTime, data.ElectricQuantity * 100);

    });

    顯然改進(jìn)后的寫法語義更好:鬧鐘.設(shè)置鬧鈴時(shí)間().當(dāng)報(bào)警時(shí)(()=>{執(zhí)行以下功能})

    這種函數(shù)式寫法更簡(jiǎn)練,但是也有明顯的缺點(diǎn),該模型不支持多個(gè)觀察者,當(dāng)面包烘烤機(jī)使用這樣的API時(shí),會(huì)覆蓋牛奶加熱器的函數(shù),即每次只支持一個(gè)觀察者使用。

    更多信息請(qǐng)查看IT技術(shù)專欄

    更多信息請(qǐng)查看技術(shù)文章
    易賢網(wǎng)手機(jī)網(wǎng)站地址:三種觀察者模式的C#實(shí)現(xiàn)
    由于各方面情況的不斷調(diào)整與變化,易賢網(wǎng)提供的所有考試信息和咨詢回復(fù)僅供參考,敬請(qǐng)考生以權(quán)威部門公布的正式信息和咨詢?yōu)闇?zhǔn)!

    2026上岸·考公考編培訓(xùn)報(bào)班

    • 報(bào)班類型
    • 姓名
    • 手機(jī)號(hào)
    關(guān)于我們 | 聯(lián)系我們 | 人才招聘 | 網(wǎng)站聲明 | 網(wǎng)站幫助 | 非正式的簡(jiǎn)要咨詢 | 簡(jiǎn)要咨詢須知 | 新媒體/短視頻平臺(tái) | 手機(jī)站點(diǎn) | 投訴建議
    工業(yè)和信息化部備案號(hào):滇ICP備2023014141號(hào)-1 云南省教育廳備案號(hào):云教ICP備0901021 滇公網(wǎng)安備53010202001879號(hào) 人力資源服務(wù)許可證:(云)人服證字(2023)第0102001523號(hào)
    云南網(wǎng)警備案專用圖標(biāo)
    聯(lián)系電話:0871-65099533/13759567129 獲取招聘考試信息及咨詢關(guān)注公眾號(hào):hfpxwx
    咨詢QQ:1093837350(9:00—18:00)版權(quán)所有:易賢網(wǎng)
    云南網(wǎng)警報(bào)警專用圖標(biāo)