zoukankan      html  css  js  c++  java
  • 致我们工作中的设计模式之基本原则职责单一原则

             我昨天外出坐轻轨的时候,翻阅微博,看见有朋友发了这样一条微博:有对从小相恋的恋人,很幸运地进入了同一所大学学习,但不幸的是被附近的一个变态杀人魔给盯上了,他绑架了这对恋人,对他们说,你们可以由一个人活着,有剪刀石头布来决定,谁赢了谁活着,这对恋人商量好了,都出石头。但结果是男孩出了剪刀,女孩出了布,然后微博说,评论下这到底是什么情况。我一瞬间脑海中有很多想法,我想分几种情况来说明这件事,但是每写一种情况,总感觉不对,这个情况中有会包含另一种可能。于是我就想了想,只想从一点来论述这个问题,比如:1、男孩痴情,然后分析。2、女孩痴情,然后分析。我如果尝试着从一个论点中去分析,然后多种假设,最后估计自己都绕不清楚了,比如,我最开始想假设是男孩痴情,但有可能他认为女孩想变卦,然后自己的论点和自己的论证都对不上了。

             想来想去,发现这个和自己工作中的编程有点类似,我们有时候写一个类或者设计一个模块的时候,最开始的想法,是遇到了一个需求认为可以独立成一个模块,于是就去设计相关的类,但写的过程中发现似乎还有其他的代码需要加入,就也想往里面写,最后发现出问题了,最终都不知道这个类或者这个模块独立出来的目的是什么。基于这些思考,其实我觉得不难发现,我们独立模块或者独立类的目的,就是为了使代码的层次更清晰,结构更合理。所以在做系统架构设计的时候,我认为,没有什么好纠结的,大的需求大胆的独立成模块,小的需求大胆的独立成类或者组件。

             同样看看《大话设计模式》这本书中怎么开篇将这个话题的。书中通过大鸟和小菜对手机和电子设备的讨论来讲述这个话题,手机的目的是为了通信方便,但附带了照相的功能,一般来说相机又会有储存的功能。就这些电子设备本身而言,我们多数使用的还是它的主要功能。产品的主功能做得越好,产品的价值就能越最大限度地得到体现。

             “如果一个类承担的职责过多,就等于把这些职责耦合在一起,一个职责的变化可能会削弱或者抑制这个类完成其他的职责功能。这种耦合会导致脆弱的设计,当变化发生时,设计会遭受到意想不到的破坏。”

             上面这段话是书中对单一职责原则的描述。书中用设计俄罗斯方块游戏的实例说了下这种思想,窗体类和逻辑类进行分离设计。我看完后想到的是自己在开发一个ERP系统的过程中在实现单据的中间类映射的时候,由于这些类需要同数据库建立映射关系的同时,还会负担去处理单据的转化,字段神马的都比较接近,但是对字段的操作动作比较类似,也就是说可以封装几个不同的方法,进而使用同一个实体,最开始有这样的想法,最后很快就打消了这种念头,而是一个单据对应一个实体和操作,不妨我们通过代码来比较下这2种方式的优劣吧。

    Public class _PurchaseOrder
    {
            public bool Checked
            {
                get { return fChecked; }
                set { fChecked = value; }
            }
    
            string f MakeMaterialID;
            public string MakeMaterialID
            {
                get { return fMakeMaterialID; }
                set { fMakeMaterialID = value; }
            }
    
    string fMakeInStorageID;
            public string MakeInStorageID
            {
                get { return fMakeInStorageID; }
                set { fMakeInStorageID = value; }
            }
    
    
            string fMakeProduct_ID;
            public string MakeProduct_ID
            {
                get { return fMakeProduct_ID; }
                set { fMakeProduct_ID = value; }
            }
    } 

    这是最开始设计的实体类,该类型其实包括了2种实体功能,实现了餐饮行业ERP中发料单和退料单的转化2种功能,MakeMaterialId针对的是发料单的ID,MakeInStorageID针对的是退料单的ID,由于他们操作的都是原料,于是我就想到了,可以将这个操作都封装到一个类当中去,这样有很多东西都可以共用,这个想法在后面我发现是天真的,这其实就是违反了模式设计的单一职责原则。

        实体封装好了之后,涉及到具体业务操作的,我们来看看我添加的几个方法动作。

    //获取退料单据
    public
    static IList<_ PurchaseOrder> GetMakeInStorages(string MakeInStorageID, string txt) { } //获取发料单据 public static IList<_ PurchaseOrder> GetMakeMaterial(string MakeMaterialID, string txt) { }

    仔细查看这个我们不难发现,其实从技术逻辑上说都是获取PurchaseOrder类的泛型集合实体,通过不同的ID获取不同的单据数据。貌似这种简单的设计实现了我们的业务需求。

             仔细往下看,随着我们业务的深入,我们要求使用静态的方法快速调用,和实现数据的存储,这个时候麻烦就来了,采用静态的方法如果方法中随着业务的深入,也开始设计采用静态的变量来存储一些功能的时候,问题就会暴露出来,由于是静态的成员,静态成语隶属于类本身,所有的类调用都会基于那一块堆栈空间,也就说如果发料单据中要求其中一个静态成员的A初始值为0,而退料单据的初始值要求是1,显然在这里就会有问题,最最先使用了这个变量,这个变量的值就确定了,而后调用更改了这个静态变量的值,就会影响到其他模块。这是其一,将发料单据和退料单据的操作柔和到一个类当中碰到的一个问题。

             接着往下看,随着我们业务的展开,这个时候也许我们要封装一个操作,比如说,对这个类进行拓展,支持ToList(),ToJson() 等一系列的操作。这个就涉及到了代码规范,那么这个类ToList出来的到底是什么东西呢,从业务层面上来说,它是发料单和退料单的合体?我们在取得到这样一个类集合的时候,还需要对它进行一个区分,筛选发料单和退料单的数据?先不说因此和导致出多的代码,我们就说这样做,也在一定程度上折损了这个类的价值,建一个类的目的就是为了方便操作,清晰结构,这样做的目的不仅没有达到,甚至一定程度上混淆了代码,如果这块代码过段时间,需要另一个同事调用或者拓展,那么这个同事就要悲壮的去学习前面这个不是逻辑的逻辑啦。

             如何解决这个问题呢,直接上代码,再做解释:

    namespace ModelPro_Single
    {
        /// <summary>
        /// 订单类
        /// </summary>
        public class Order
        {
            public string OrderID { get; set; }
            public string ProductCode { get; set; }
        }
    
        Public static class OrderExtension
        {
            //扩建的方法必须是静态方法,参数里面必须含有this关键字,this关键字后面的类型为需要扩展的类型
            /// <summary>
            /// 发料单转换成订单
            /// </summary>
           public static Order ToOrderList(this _MakeMaterialOrder mmo)
            {
                Order ordList = new Order();
                if (mmo== null)
                {
                    return null;
                }
                else
                {
                    ord.OrderID = mmo.MakeMaterialID;
                    ord.ProductCode = mmo.MakeProduct_ID;
    
                }
                return ord;
            }
        }
    
        /// <summary>
        /// 发料类
        /// </summary>
        public class _MakeMaterialOrder
        {
            static bool fChecked;
            public static bool Checked
            {
                get { return fChecked; }
                set { fChecked = value; }
            }
    
            string fMakeMaterialID;
            public string MakeMaterialID
            {
                get { return fMakeMaterialID; }
                set { fMakeMaterialID = value; }
            }
    
            string fMakeProduct_ID;
            public string MakeProduct_ID
            {
                get { return fMakeProduct_ID; }
                set { fMakeProduct_ID = value; }
            }
    
        }
    
        /// <summary>
        /// 退料类
        /// </summary>
        public class _MakeInStorageOrder
        {
            static bool fChecked;
            public static bool Checked
            {
                get { return fChecked; }
                set { fChecked = value; }
            }
    
            string fMakeInStorageID;
            public string MakeInStorageID
            {
                get { return fMakeInStorageID; }
                set { fMakeInStorageID = value; }
            }
    
    
            string fMakeProduct_ID;
            public string MakeProduct_ID
            {
                get { return fMakeProduct_ID; }
                set { fMakeProduct_ID = value; }
            }
    
        }
    
        class Program
        {
            static void Main(string[] args)
            {
            }
        }
    }

    代码中实现了发料单到订单的转换,同理,可以实现对退料单的拓展实现。稍作一个解释,我首先是将类进行了拆分,依据基本的职责单一的原则,划分出了了退料类和发料类,由原先的一个类依据功能要求拆分成了两个类,并且每个类中的静态变量互不干预了,对于针对各个类的拓展,也相互不干预,针对_MakeMaterialOrder类的拓展,得到的订单全部都是由发料单转换成的订单,同理拓展出来的针对_MakeInStorageOrder类的拓展肯定都是由退料单转换成的订单。这样一来其实我们的代码量是多了许多,但是,其实功能结构就清晰了许多,也及不存在不是业务逻辑的逻辑了,干脆利索的代码。

             今天刮起了大风,下起了小雨,昨天还很热很热,这篇文章由两点钟开始写到现在,昨天的五点钟夕阳才刚刚下去,今天此时房间已是灯火通明了,最近的一些事着实让我要好好反思自己。

       末了,依然附上我的源代码:https://files.cnblogs.com/aspnetdream/ModelPro_Single.rar

            有需要讨论或指正错误的,可以随时评论或加我的MSN。

  • 相关阅读:
    mongodb 查询缓慢问题
    java中的移位运算符总结
    git push到GitHub的时候遇到! [rejected] master -> master (non-fast-forward)的问题
    Docker映射详解,没问题了!
    Alibaba开源的Java诊断工具 -- Arthas
    SpringBoot 程序启动时将数据库的字典表加载进内存中
    [Tips] 批量解析电子发票的工具
    [Bugs] ModuleNotFoundError: No module named 'conda'
    [Tips] vs code 代码自动格式化
    [Record] electron windows下配置
  • 原文地址:https://www.cnblogs.com/aspnetdream/p/3100186.html
Copyright © 2011-2022 走看看