zoukankan      html  css  js  c++  java
  • C#观察者模式的应用与思考

    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();

     

       

  • 相关阅读:
    spring中Bean的懒加载
    ActiveMQ学习教程
    Maven中解决jar包冲突的三种方式
    Spring的日志管理
    mybatis使用collection查询集合属性规则
    springcloud之Feign伪装
    springcloud之Hystrix熔断入门
    springcloud的负载均衡Ribbon入门
    springcloud的Eureka启动时报错:Connection refused: connect
    springboot启动过程中出现You must configure either the server or JDBC driver (via the serverTimezone configuration
  • 原文地址:https://www.cnblogs.com/weity/p/7217887.html
Copyright © 2011-2022 走看看