2015年7月30日 星期四

軟體架構之觀察者模式


    使用者新增bug回報後或需求單,如果不主動登入網頁時就永遠無法知道誰回報了BUG,無法有主動通知的概念,想要有一個有主動通知的提醒功能,於是想到了觀察者的模式,以後不論有新的系統需要主動偵測時,在新增物件後,自動有擴充效果。 


情境1:
    當接收的人員已經按下確定後,警告視窗不需提醒,如果A人員已經看過並按下確定後,無法再彈出,但B人員尚未看過,仍不斷跳出提醒,確定每位人員都能看到bug回報。  
















情境3: 為了有擴充的功能,採用觀察者的樣式, 觀察者 (Observer) 與被觀察者 (Observable) 之間採用鬆偶合 (loose-coupling/鬆綁) 的方式結合。因為雙方有實踐特定介面,因此不用知道彼此的細節。 


本案例中testBugObserverreleaseBugObserver有各自邏輯檢查是否有該登入者尚未讀取,所以在observer的父類別,訂出抽象類別的方法findNoRead ,由繼承的子類別覆寫findNoRead的邏輯,父類別SupperSubjectattach負責把加入繼承supperObserver的物件,再由父類別的SupperSubjectlistAllNoRead將所有繼承supperObserver的物件findNoRead去搜尋未讀的資料。

程式範例 觀察者模式又稱(發布/訂閱)模式
父類別:訂閱者的角色
    public abstract class supperObserver
    {     
        public abstract IList<viewData> findNoRead(string userID);
    }
子類別:繼承父類別訂閱者
public class testBugObserver : supperObserver
    {     
        public override IList<viewData> findNoRead(string userID)
        {
            entityNotifyData entityModel = new entityNotifyData();
            IList<viewData> liData = entityModel.liBugNoReadTestData(userID); //讀取資料
            return liData.ToList();
        }
    }
子類別:繼承父類別訂閱者 
public class releaseBugObserver : supperObserver
    {
        public override IList<viewData> findNoRead(string userID)
        {
            entityNotifyData entityModel = new entityNotifyData();
            IList<viewData> liData = entityModel.liBugNoReadReleaseData(userID); //讀取資料
            return liData.ToList();
        }
    }

  
    子類別繼承父類別supperObserver覆寫父類別的findNoRead(string userID)讓物件有各自不同的邏輯,後續也容易可以擴充新的物件。

  public abstract class supperSubject
    {   
        /// 登入者的帳號
        public string userID { get;    set;  }
        /// 訂閱者們
        private IList<supperObserver> liObservers = new List<supperObserver>();
        public void attach(supperObserver Observer)
        {
            liObservers.Add(Observer);
        }
         /// 取回所有未讀資料
        public List<viewData> listAllNoRead()
        {
            List<viewData> liViewData = new List<viewData>();
            foreach (supperObserver observer in liObservers)
            {
// 物件本身的findNoRead 有各自的邏輯,以達到擴充的效果
                liViewData.AddRange(observer.findNoRead(userID));
            }
            return liViewData;
        }
    }


public class subject : supperSubject
    {

    }
   
    觀察者和被觀察者的父類別先定義好彼此的關係,父類別的supperSubject 有兩個方法attach listAllNoRead
l   attach 由繼承supperSubject 的子類別用attach的方式加入有繼承supperObserver 的物件到父類別IList<supperObserver> liObservers陣列集合

l   listAllNoRead : 由繼承supperSubject 的子類別用listAllNoRead 逐一把陣列集合中的物件,呼叫物件覆寫父類別supperObserverfindNoRead

private void timer1_Tick(object sender, EventArgs e)
        {
            _liAllNoRead = null//未讀取的資料集合         
            //出版者 (觀察者)
            subject subjectObj = new subject();
            subjectObj.userID = "0137";
            //訂閱者(被觀察者)
            testBugObserver bugObserverObj = new testBugObserver();
            releaseBugObserver applyObj = new releaseBugObserver();
            subjectObj.attach(bugObserverObj);
            subjectObj.attach(applyObj);          
            _liAllNoRead = subjectObj.listAllNoRead();  //取回被觀察者們傳回的未讀取的資料                      
            new Thread(() =>
            {
                foreach (viewData noRead in _liAllNoRead)
                {
                    notifyIcon1.BalloonTipIcon = ToolTipIcon.Info;
                    notifyIcon1.BalloonTipText = noRead.type;
                    notifyIcon1.BalloonTipTitle = noRead.title;
                    notifyIcon1.ShowBalloonTip(5000);
                    Thread.Sleep(6000);
                }
            }).Start();  
        }


















上圖的Windows Gridview資料分別由各自的被觀察者releaseBugObservertestBugObserver 自已的findNoRead 找出來的資料

在正式版的公告-->新增bug --> 右下角跳出提醒視窗



在測試版的公告-->新增bug --> 右下角跳出提醒視窗











    這樣以後不論有多少網站,需要有提醒功能,只要加上新的observer的物件,就不用再修改任何程式,一樣就有提醒功能,如果觀念誤導大家時,請各位前輩多多指導,學習的道路上本來就是永無止盡的,期望大家也能更上一層樓。

沒有留言:

張貼留言