C#觀察者模式的應用與思考

来源:http://www.cnblogs.com/weity/archive/2017/07/21/7217887.html
-Advertisement-
Play Games

1:項目場景 在設計數據表的時候有時候為了將來統計或查詢的方便,我們會冗餘一些欄位。如有三張數據表,學校信息表、班級動態表、班級信息表。 班級動態由學校老師所發,可以進行評論點贊等操作,為了提升這種非結構化數據的訪問效率,存儲於Mongodb中,冗餘了學校名稱欄位,假設班級表也冗餘了學校名稱欄位。而 ...


1:項目場景

  在設計數據表的時候有時候為了將來統計或查詢的方便,我們會冗餘一些欄位。如有三張數據表,學校信息表、班級動態表、班級信息表。

  班級動態由學校老師所發,可以進行評論點贊等操作,為了提升這種非結構化數據的訪問效率,存儲於Mongodb中,冗餘了學校名稱欄位,假設班級表也冗餘了學校名稱欄位。而冗餘欄位的存在可能會帶來數據不一致問題。這邊需要保持修改學校名稱時,其他相關的表中學校名稱欄位保持一致,先看根據業務邏輯最直接的寫法。

1.1:先定義類

   /// <summary>
    /// 學校信息表
    /// </summary>
    public class School
    {
        public string RowGuid { get; set; }
        public string SchoolName { get; set; }
    }

    /// <summary>
    /// 班級動態(假設存儲於Mongodb,為按學校名稱模糊查詢時方便,冗餘學校名稱欄位)
    /// </summary>
    public class PersonDynamic
    {
        public string RowGuid { get; set; }
        public string Title { get; set; }
        public string MainContent { get; set; }
        /// <summary>
        /// 發佈人標識
        /// </summary>
        public string TeacherGuid { get; set; }
        /// <summary>
        /// 冗餘發佈人所在學校Guid
        /// </summary>
        public string SchoolGuid { get; set; }
        /// <summary>
        /// 冗餘發佈人所在學校名稱
        /// </summary>
        public string SchoolName { get; set; }
    }

    /// <summary>
    /// 班級信息 假設冗餘學校名稱
    /// </summary>
    public class Class
    {
        public string RowGuid { get; set; }
        public string ClassName { get; set; }
        public string SchoolGuid { get; set; }
        public string SchoolName { get; set; }
    }

1.2對應邏輯層設計

public class BlClass
    {
        //更新班級表中學校名稱數據
        public void UpdateClass(School school)
        {
            //string sql = string.Format("update ClassInfo set SchoolName ='{0}' where SchoolGuid ='{1}'", school.SchoolName, school.RowGuid);
            Console.WriteLine("完成修改班級表的學校名稱。");
        }
    }
   public class BlPersonDynamic
    {
        //更新班級動態數據表中學校名稱
        public void UpdatePersonDynamic(School school)
        {
            //MongoDBHelper.Update(……)
            Console.WriteLine("完成修改班級動態表的學校名稱。");
        }
    }
public class BlSchool
    {
        public void UpdateSchool(School school)
        {
            //修改學校名稱邏輯
            // string sql = string.Format("update Schoolinfo set SchoolName ='{0}' where RowGuid ='{1}'", school.SchoolName, school.RowGuid);
            Console.WriteLine("完成修改學校信息表的學校名稱。");
            new BlPersonDynamic().UpdatePersonDynamic(school);
            new BlClass().UpdateClass(school);

        }
    }

1.3:需求擴展:

  可以預見到,之後還會增加教師文章,學校新聞等等內容,裡面仍然可能冗餘學校名稱欄位,那麼更新學校名稱的邏輯則時常需要修改,違反了開閉原則。其他類中的學校名稱欄位依賴於學校表的學校名稱,當學校表中學校名稱改變時,其他表學校名稱也需要同步改變,由此想到了一種開發模式,觀察者模式。

2:使用觀察者模式改造

2.1百度百科中的定義

  觀察者設計模式定義了對象間的一種一對多的依賴關係,以便一個對象的狀態發生變化時,所有依賴於它的對象都得到通知並自動刷新。

2.2類圖

  

2.3 C#使用委托事件方式實現觀察者模式

  此處改造先不考慮抽象觀察者 和 抽象主題,學校作為通知者,班級和班級動態作為觀察者角色,這樣的好處是不影響現有的觀察者邏輯,調整後的邏輯層。

//具體觀察者1
    public class BlClass
    {
        public void UpdateClass(School school)
        {
            // string sql = string.Format("update ClassInfo set SchoolName ='{0}' where SchoolGuid ='{1}'", school.SchoolName, school.RowGuid);
            Console.WriteLine("完成修改班級表的學校名稱。");
        }
    }
 //具體觀察者2
    public class BlPersonDynamic
    {
        //更新班級動態數據表中學校名稱
        public void UpdatePersonDynamic(School school)
        {
            //MongoDBHelper.Update(……)
            Console.WriteLine("完成修改班級動態表的學校名稱。");
        }
    }
//聲明一個委托
    public delegate void UpdateSchoolEvent(School school);

    //通知者
    public class BlSchool
    {
        //聲明一個事件
        public event UpdateSchoolEvent updateSchoolEvent;
        public void UpdateSchool(School school)
        {
            //首先更新學校表中名稱
            //string sql = string.Format("update Schoolinfo set SchoolName ='{0}' where RowGuid ='{1}'", school.SchoolName, school.RowGuid);
            Console.WriteLine("完成修改學校表數據!");
            //修改學校名稱後發起通知,觸發事件
            this.Notice(school);
        }
        public void Notice(School school)
        {
            //如果事件非空則調用
            updateSchoolEvent?.Invoke(school);
        }
    }

  這樣調整後則完成了學校 與 班級、動態之間的耦合度,新增其他需要更新的地方也不需要修改學校邏輯層。只需要在使用的地方註冊一下事件,如下麵的模擬調用:

//修改後的實體類信息
            School school = new School();
            school.RowGuid = Guid.NewGuid().ToString();
            school.SchoolName = "第一中學";

            //註冊修改學校名稱事件
            BlSchool blSchool = new BlSchool();
            blSchool.updateSchoolEvent += new BlPersonDynamic().UpdatePersonDynamic;
            blSchool.updateSchoolEvent += new BlClass().UpdateClass;

            //更新學校名稱
            blSchool.UpdateSchool(school);
            Console.ReadKey();

2.4思考一

  當做功能擴展的時候,原先的寫法是在學校邏輯層調整修改學校的邏輯,而使用觀察者模式之後仍然需要註冊事件的地方增加註冊事件,都是需要修改是否有區別?個人理解首先是解除了類之間的耦合度,其次我覺得開閉原則對修改關閉、對擴展開放,並不是意味著功能拓展不需要改變任何以前的代碼,而是拓展後改變需要花費代價的大小,如同一些配置參數的使用,也是為了最小的代價完成功能的修改或拓展。而此處學校業務邏輯層如果已經是封裝好的主程式邏輯,那麼修改需要花費較大的代價。

2.5思考二

  對於觀察者模式的抽象主題、和抽象觀察者的使用。比如在原先基礎上增加了學校新聞的功能,此時並不知道需要寫下麵這個 和以前相同參數的方法。

 //具體觀察者3
    public class BlSchoolNews
    {
        //更新班級動態數據表中學校名稱
        public void UpdateSchoolNews(School school)
        {
            //MongoDBHelper.Update(……)
            Console.WriteLine("完成修改學校新聞表的學校名稱。");
        }
    }

  那麼這裡則可以對具體的觀察者進行抽象,對於對修改學校感興趣的類,實現這個抽象的介面。抽象後如下

/// <summary>
    /// 抽象一個更新學校介面
    /// </summary>
    public interface IUpdateSchool
    {
        void AfterUpdateSchool(School school);
    }

    //具體觀察者1
    public class BlClass : IUpdateSchool
    {
        public void AfterUpdateSchool(School school)
        {
            this.UpdateClass(school);
        }

        private void UpdateClass(School school)
        {
            // string sql = string.Format("update ClassInfo set SchoolName ='{0}' where SchoolGuid ='{1}'", school.SchoolName, school.RowGuid);
            Console.WriteLine("完成修改班級表的學校名稱。");
        }
    }
    //具體觀察者2
    public class BlPersonDynamic : IUpdateSchool
    {
        public void AfterUpdateSchool(School school)
        {
            this.UpdatePersonDynamic(school);
        }
        //更新班級動態數據表中學校名稱
        private void UpdatePersonDynamic(School school)
        {
            //MongoDBHelper.Update(……)
            Console.WriteLine("完成修改班級動態表的學校名稱。");
        }
    }
    //具體觀察者3
    public class BlSchoolNews : IUpdateSchool
    {
        public void AfterUpdateSchool(School school)
        {
            this.UpdateSchoolNews(school);
        }
        //更新班級動態數據表中學校名稱
        private void UpdateSchoolNews(School school)
        {
            //MongoDBHelper.Update(……)
            Console.WriteLine("完成修改學校新聞表的學校名稱。");
        }
    }

  模擬調用

 //修改後的實體類信息
            School school = new School();
            school.RowGuid = Guid.NewGuid().ToString();
            school.SchoolName = "第一中學";

            //註冊修改學校名稱事件
            BlSchool blSchool = new BlSchool();
            blSchool.updateSchoolEvent += new BlPersonDynamic().AfterUpdateSchool;
            blSchool.updateSchoolEvent += new BlClass().AfterUpdateSchool;
            blSchool.updateSchoolEvent += new BlSchoolNews().AfterUpdateSchool;
            //更新學校名稱
            blSchool.UpdateSchool(school);
            Console.ReadKey();

 

   

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 流 數據流 用於傳輸數據。IO流 Input/Output流。數據從外部流向程式 輸入流;數據從程式流向外部的時候--輸出流。讀取一個文件 數據從文件流向程式 輸入流;向一個文件中寫入數據 數據從程式流向文件 輸出流 根據數據流動的方向:輸入流/輸出流 根據數據傳輸的形式:位元組流/字元流 輸入流 輸 ...
  • 要求: Readme: 運行程式,輸入薪水,根據商品列表的序號選擇購買的商品,可以選擇多次購買,或者不購買 流程圖: 代碼: ...
  • #列印實心菱形,思路:分上下,i控制行,j控制列印的空格數,k控制列印的*數量row = int(input("please input a raw number: "))for i in range(row): #i控制行數 for j in range(row-1-i):#空格數和行數的關係 p ...
  • XML文件處理 XML文件處理,有好幾種方式,這裡介紹一下xml.etree.ElementTree as ET。 註意:xml.etree.ElementTree模塊在應對惡意結構數據時顯得並不安全。 每個element對象都具有以下屬性: 1. tag:string對象,表示數據代表的種類; 2 ...
  • 一.ServletContext 介面(javax.servlet)定義:public interface ServletContext原理: Tomcat啟動的時候,需要識別webapps下的各個WEB應用,識別各個WEB應用的同時為每個WEB應用創建對應的對象ServletContext,一個W... ...
  • 最近在做ProtoBuf相關的項目,其中用到了動態解析,網上看了下相關資料和博文都比較少,自己來寫一個記錄一下學習過程。 Protocol Buffers是結構化數據格式標準,提供序列化和反序列方法,用於存儲和交換。語言中立,平臺無關、可擴展。目前官方提供了C++、Java、Python API,也 ...
  • 設計模式之單列模式 1,何為單列模式? 即singleton 在某個類採用了單列模式之後 其只能有一個實列對象 ,並且這個實列對象只能有內部自己創建並提供給外部的調用。 2.實現單列模式的方法 分為 :餓漢式 ,懶漢式 下麵為餓漢式實現代碼: 餓漢式 通過創建一個靜態成員變數 在類載入的時候直接創建 ...
  • thinkphp的CBD模式 核心保留了最關鍵的部分,併在重要位置設置了標簽用以標記,其他功能都採用行為擴展和驅動的方式組合,開發人員可以根據自己的需要,對某個標簽位置進行行為擴展或者替換,就可以方便的定製框架底層,也可以在應用層添加自己的標簽位置和添加應用行為。而標簽位置類似於AOP概念中的“切麵 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...