语法糖

先介绍一个例子,如下两个表格数据:

左边表格
ItemCode 属性值1 属性值2 属性值3
A 20 30 40
B 30 30 44
C 25 30 44
D 34 30 60
右边表格
ItemCode 分类 属性值1 属性值2 属性值3
A 0 90 90 90
B 1 90 90 90
D 2 90 90 90
E 2 90 90 90

需求说明如下:

  1. 【分类】 列的值 有三种类型 0、1、2
  2. ItemCode是唯一的,在表格中不会重复
  3. 将左边的数据,按照ItemCode,复制到右边表格

          如果左右两边都有ItemCode,检查分类值,如果对应的分类值是0:则不复制。

                                                    如果分类值是1:则修改属性值1

                                                    如果分类值是2:则修改属性值 1- 3

   如果仅在右边的表格没有的数据,则把分类值修改成0,属性值不修改

   如果仅在左边的表格有的数据,则右边新增一条,分类值是2,属性值1-3复制过来

如上例子,复制后的结果如下:

ItemCode 分类 属性值1 属性值2 属性值3
A(不变) 0 90 90 90
B(修改1) 1 30 90 90
D(修改1-3) 2 34 30 60
E(仅右边) 0 90 90 90
C(仅左边) 2 25 30 44
 
针对上述要求,程序实现可能如下:
 
 public void CopyData(DataTable leftTable, DataTable rightTable)
        {
            //找出在仅在右边存在或者都存在的数据
            foreach (DataRow row in rightTable.Rows)
            {
                string itemCode = row["ItemCode"] as string;
                DataRow[] rows = leftTable.Select("ItemCode = '" + itemCode + "'");
                if (rows.Length == 0)
                {
                    //仅修改分类值
                    row["分类"] = 0;
                }
                else
                {
                    //左右都存在
                    int type = (int)row["分类"];
                    if (type > 0)
                    {
                        row["属性值1"] = row["属性值1"];
                        if (type == 2)
                        {
                            row["属性值2"] = row["属性值2"];
                            row["属性值3"] = row["属性值3"];
                        }
                    }
                }
            }

            //找出仅在左边存在数据
            foreach (DataRow row in leftTable.Rows)
            {
                string itemCode = row["ItemCode"] as string;
                DataRow[] rows = rightTable.Select("ItemCode = '" + itemCode + "'");
                if (rows.Length == 0)
                {
                    //在右边新增一条
                    DataRow newRow = rightTable.NewRow();
                    newRow["分类"] = 2; //分类值是2
                    newRow["ItemCode"] = itemCode;
                    newRow["属性值1"] = row["属性值1"];
                    newRow["属性值2"] = row["属性值2"];
                    newRow["属性值3"] = row["属性值3"];
                    rightTable.Rows.Add(newRow);
                }
                else
                {
                    //左右都存在
                    //什么也不做
                }
            }
        }
 
 

上面的代码应该算是清晰的。而且有了足够的注释,不算很难读懂。

但是,这个代码和业务逻辑描述的是一致的吗?换句话说:如果没有需求文档,单单看上面代码,能得到上面的总结的需求吗?

我觉得要整理出上面需求比较难,理由如下。

  • 从代码中看不出来分类值 是 仅有0、1、2
  • 如果没有这些注释,也很难读出需求三种描述的逻辑(两者都有、仅左边有、仅右边有)。尤其两个循环存在,让代码逻辑更加不清晰。

针对第一个问题,似乎很好解决。可以定义一个枚举类型来描述分类值就可以了,如下:

        public enum Enu分类
        {
            不改变 = 0,
            仅改变1 = 1,
            全部改变 = 2
        }

 

相应的代码可以修改成这样

                if (rows.Length == 0)
                {
                    //仅修改分类值
                    row["分类"] =(int) Enu分类.不改变;
                }
                else
                {
                    //左右都存在
                    Enu分类 type = (Enu分类)row["分类"];
                    switch(type)
                    {
                        case Enu分类.全部改变:
                            row["属性值1"] = row["属性值1"];
                            row["属性值2"] = row["属性值2"];
                            row["属性值3"] = row["属性值3"];
                            break;
                        case Enu分类.仅改变1:
                           row["属性值1"] = row["属性值1"];
                            break;
                        case Enu分类.不改变:
                            break;
                    }
                }
 
但是,对于第2个问题,则比较复杂了。如果和能像需求说明那样简洁的实现呢?
仔细分析,实际上需求说明中表述两个两层意思:
1、对比左右两个表格,得到都存在的、仅左边、仅右边结果
2、三种数据处理方式如下:
   1)、都存在的处理方式按照分类
   2)、仅左边的,则在右边新增一行
   3)、仅右边的,则修改分类为0

    那么另外一种实现方式是

       1、做一个DataTable对比类,得到三种类型数据

       2、遍历这三种数据即可。

   实现方式如下:

  public void CopyData2(DataTable leftTable, DataTable rightTable)
        {
            DataTableComparer cmp = new DataTableComparer(leftTable, rightTable, "ItemCode");
            foreach(CompareResult result in cmp.Rows)
            {
                switch (result.Type)
                {
                    case ResultType.Both:
                        //左右都存在
                        Enu分类 type = (Enu分类)result.RightRow["分类"];
                        switch (type)
                        {
                            case Enu分类.全部改变:
                                result.RightRow["属性值1"] = result.LeftRow["属性值1"];
                                result.RightRow["属性值2"] = result.LeftRow["属性值2"];
                                result.RightRow["属性值3"] = result.LeftRow["属性值3"];
                                break;
                            case Enu分类.仅改变1:
                                result.RightRow["属性值1"] = result.LeftRow["属性值1"];
                                break;
                            case Enu分类.不改变:
                                break;
                        }
                        break;
                    case ResultType.LeftOnly:
                        DataRow newRow = rightTable.NewRow();
                        newRow["分类"] = Enu分类.全部改变;
                        newRow["ItemCode"] = result.LeftRow["ItemCode"];
                        newRow["属性值1"] = result.LeftRow["属性值1"];
                        newRow["属性值2"] = result.LeftRow["属性值2"];
                        newRow["属性值3"] = result.LeftRow["属性值3"];
                        rightTable.Rows.Add(newRow);
                        break;
                    case ResultType.RightOnly:
                        //仅修改分类值
                        result.RightRow["分类"] = (int)Enu分类.不改变;
                        break;
                }
            }
        }

 

TableComparer类实现如下:

 class DataTableComparer
    {

        public DataTableComparer(DataTable left, DataTable right, params string[] keys)
        {
            m_rows = CreateAllRows(left.Rows, right.Rows, keys);
        }

        private string GetKey(DataRow row, string[] keys)
        {
            StringBuilder sb = new StringBuilder();
            foreach (string key in keys)
            {
                sb.Append(Convert.ToString(row[key]) + "|");

            }
            return sb.ToString();
        }
        private Dictionary<string, CompareResult> CreateAllRows(IEnumerable leftRows, IEnumerable rightRows, string[] keys)
        {
            Dictionary<string, CompareResult> rows = new Dictionary<string, CompareResult>();
            foreach (DataRow row in leftRows)
            {
                string key = GetKey(row, keys);
                CompareResult result = new CompareResult();
                result.LeftRow = row;
                result.Type = ResultType.LeftOnly;
                rows.Add(key, result);
            }

            foreach (DataRow row in rightRows)
            {
                string key = GetKey(row, keys);
                if (rows.ContainsKey(key))
                {
                    CompareResult result = rows[key];
                    result.Type = ResultType.Both;
                    result.RightRow = row;
                }
                else
                {
                    CompareResult result = new CompareResult();
                    result.RightRow = row;
                    result.Type = ResultType.RightOnly;
                    rows.Add(key, result);
                }
            }

            return rows;
        }
        private Dictionary<string, CompareResult> m_rows;

        public IEnumerable<CompareResult> Rows
        {
            get
            {
                foreach (KeyValuePair<string, CompareResult> keyValue in m_rows)
                {
                    yield return keyValue.Value;

                }
            }
        }

    }

    public class CompareResult
    {
        public ResultType Type;
        public DataRow LeftRow;
        public DataRow RightRow;
    }

    public enum ResultType
    {
        Both,
        LeftOnly,
        RightOnly,

    }

 

 

结论:

  1.   软件代码中的注释对代码维护工作非常重要。但是,结构良好,更接近于自然语言描述的代码更重要。甚至,有时候可以替代注释的效果。
  2. 软件需求在转换为软件代码过程中,仍然有值得仔细推敲,整理的必要,这样才能保证在实现过程中,尽量与软件需求描述一致。
  3. 当然,上述代码中,第二种方案有时候也未必是最优的。如果考虑性能的话,大数据量的dataTable,可能第一种方案还是可取的。
  4. 以上没有使用Linq去实现,如果用到Linq的话,代码应该还可以简化。
posted @ 2010-07-25 16:16  dcll  阅读(579)  评论(0编辑  收藏  举报