致我们工作中的设计模式之基本原则---职责单一原则

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

posted @ 2013-05-26 17:27  aspnetdream  阅读(1543)  评论(3编辑  收藏  举报