.NET重构(类型码的设计、重构方法)

阅读目录:

  • 1.开篇介绍
  • 2.不影响对象中的逻辑行为(枚举、常量、Entity子类来替代类型码)
  • 3.影响对象中的逻辑行为(抽象出类型码,使用多态解决)
  • 4.无法直接抽象出类型码(使用策略模式解决)

1】开篇介绍

说到类型码,我们都会很有印象,在某个Entity内部多多少少会出现一两个类型码来表示当前Entity在某个抽象角度属于哪一种层面,比如在EmployeeEntity中,基本上会有一个表示性别的Sex的属性,同时Sex属性的最终保存是在某个sex字段中的,它就是很典型的类型码元素;Sex类型码属性用来表达了在用性别这一个抽象角度对实体进行分类时,那么实体会存在着两种被归纳的层面(男、女);

在这个Sex类型码属性被使用到的任何一个逻辑的地方都会有可能因为它的值不同而进行不同的逻辑分支,就好比我们在EmployeeCollectionEntity对象中定义一个方法,用来返回指定类型的所有EmployeeEntity,我们简单假设在EmployeeeCollectionEntity的内部肯定有一块逻辑是用来根据当前方法的参数进行判断,然后调用不同的方法返回当前集合中的所有执行参数的EmployeeEntity;

上述只是一个简单的使用场景,但是足以能简单说明类型码的意义和使用场景,下面我们将针对上面提到的这一个简单的例子进行三种类型码的使用分析和如何重构设计;在类型码不被任何逻辑使用只是提供给外部一个简单的标识时,我们如何处理;在类型码会直接影响实体内部行为逻辑的情况下,我们如何处理;在类型码会影响实体内部逻辑的时候,但是我们又无法将其直接提取抽象出来时,我们如何处理;

我们带着这个三个简单的问题进行下面的具体分析;

2】不影响对象中的逻辑行为(枚举、常量、Entity子类来替代类型码)

在不影响对象内部逻辑的情况下,问题很好处理;既然不影响对象内部逻辑,那么它的表现形式起码对于实体内部逻辑来说无关紧要;这个时候我们对它的设计可以遵循一个原则就是OO,如果我们使用的是一个简单的数字来表示类型码的状态,那么我们就可以通过三个方式对它进行设计或者重构;

这里有一个小小问题的就是,如果我们正在进行一项局部DomainModel内部的重构时,我们的工作量会很大而且需要很好的单元测试来支撑;但是如果我们目前正在设计一个Entity问题就很简单;

下面我们用上面1】节提到的简单场景作为本节演示示例的领域模型;

EmployeeEntity 代码:

 1 public class EmployeeEntity
 2 {
 3     private int sex;
 4
 5     public int Sex
 6     {
 7         get { return sex; }
 8         set { sex = value; }
 9     }
10 } 

EmployeeCollectionEntity代码:

1 public class EmployeeCollectionEntity : List<EmployeeEntity>
2 {
3     public IEnumerable<EmployeeEntity> GetEntityBySex(int sex)
4     {
5         return from item in this where item.Sex== sex select item;
6     }
7 } 

测试代码,为了方便起见,我就没有特地创建UnitTests项目,而是简单的使用控制台程序模拟:

 1 EmployeeCollectionEntity empCollection = new EmployeeCollectionEntity()
 2             {
 3                 new EmployeeEntity() { Sex= 1 },
 4                 new EmployeeEntity() { Sex = 2 },
 5                 new EmployeeEntity() { Sex = 2 }
 6             };
 7
 8             var resultList = empCollection.GetEntityBySex(2);
 9             if (resultList.Count() == 2 && resultList.ToList()[0].Sex== 2 && resultList.ToList()[1].Sex==2)
10                 Console.WriteLine("is ok");
11
12             Console.ReadLine(); 

上述代码很简单,一个Employee用来表示员工实体,EmployeeCollectionEntity表示员工实体集,用来封装一组包含业务逻辑的Empoyee集合;目前在EmployeeCollectionEntity中有一个方法GetEntityBySex(int sex),用来根据性别类型码来获取集合内部中满足条件的所有EmpoyeeEntity,在单元测试中的代码,我们使用1表示女性,2表示男性,单元测试通过测试代码正确的查询出两组男性EmployeeEntity实体;

下面我们将逐步使用三种方式对这种类型的业务场景进行重新设计也可以称为重构;

第一:使用枚举类型替换类型码数字;

EmployeeEntity代码:

 1 public class EmployeeEntity
 2 {
 3     public enum EmployeeSex
 4     {
 5         Male,
 6         Female
 7     }
 8
 9     private EmployeeSex sex;
10
11     public EmployeeSex Sex
12     {
13         get { return sex; }
14         set { sex= value; }
15     }
16 } 

EmployeeCollectionEntity代码:

1 public class EmployeeCollectionEntity : List<EmployeeEntity>
2 {
3     public IEnumerable<EmployeeEntity> GetEntityBySex(EmployeeEntity.EmployeeSex sex)
4     {
5         return from item in this where item.Sex== sexselect item;
6     }
7 } 

测试代码:

 1 EmployeeCollectionEntity empCollection = new EmployeeCollectionEntity()
 2            {
 3                new EmployeeEntity() { Sex= EmployeeEntity.EmployeeSex.Female },
 4                new EmployeeEntity() { Sex= EmployeeEntity.EmployeeSex.Male },
 5                new EmployeeEntity() { Sex= EmployeeEntity.EmployeeSex.Male }
 6            };
 7
 8            var resultList = empCollection.GetEntityBySex(EmployeeEntity.EmployeeSex.Male);
 9            if (resultList.Count() == 2 && resultList.ToList()[0].Sex== EmployeeEntity.EmployeeSex.Male &&
10                resultList.ToList()[1].Sex== EmployeeEntity.EmployeeSex.Male)
11                Console.WriteLine("is ok");
12
13            Console.ReadLine(); 

通过使用枚举我们能很好的使用OOD的好处,这样代码中不会到处充斥这乱七八糟的魔幻数字;

第二:使用常量来代替类型码;

其实使用常量来代替类型码时,比较常见的业务场景是在和远程交互的时候,因为在我们将Entity翻译成某种传输对象的时候需要将它的属性使用字符串的形式表达;比如这里的EmployeeEntity,假设我们需要将某一个EmployeeEntity发送到某个消息队列,然后消息队列的后端程序需要将它直接插入到数据库中,这个时候,我们的DomainModel在消息队列的后端程序中是不存在的,也就是说并没有和数据库映射过,这里的属性类型码将是和数据库等价的字符串;所以如果我们在选择使用枚举还是常量来替代类型码是,选择的标准就是类型码是否需要持久化,也就是字符串化;

EmployeeEntity代码:

 1 public class EmployeeEntity
 2 {
 3     public const int Male = 2;
 4     public const int Female = 2;
 5
 6     private int sex;
 7
 8     public int Sex
 9     {
10         get { return sex; }
11         set { Sex= value; }
12     }
13 } 

EmployeeCollectionEntity代码:

1 public IEnumerable<EmployeeEntity> GetEntityBySex(int sex)
2 {
3 return from item in this where item.Sex== sexselect item;
4 }

测试代码:

 1 EmployeeCollectionEntity empCollection = new EmployeeCollectionEntity()
 2             {
 3                 new EmployeeEntity() { Sex= EmployeeEntity.Female},
 4                 new EmployeeEntity() { Sex= EmployeeEntity.Male },
 5                 new EmployeeEntity() { Sex= EmployeeEntity.Male}
 6             };
 7
 8             var resultList = empCollection.GetEntityBySex(EmployeeEntity.Male);
 9             if (resultList.Count() == 2 && resultList.ToList()[0].Sex== EmployeeEntity.Male &&
10                 resultList.ToList()[1].Sex == EmployeeEntity.Male)
11                 Console.WriteLine("is ok");
12
13             Console.ReadLine(); 

使用常量来代替类型码就是在接口上只能使用数字来表示IEnumerable<EmployeeEntity> GetEntityBySex(int sex),然后我们在调用的时候会直接使用常量类型empCollection.GetEntityBySex(EmployeeEntity.Male);

第三:使用Entity子类来替代类型码;

对于EmployeeEntity如果在Sex角度上存在继承体系,那么我们就可以使用Entity子类的方式来解决;现假设,对于性别为男和女都分别从EmployeeEntity上继承各自的体系,MaleEmployeeEntity为男,FemaleEmployeeEntity为女,当然真实场景中不会为了这一个小小的性别就独立出一个继承体系;

EmployeeEntity代码:

1 public abstract class EmployeeEntity
2 {
3     public abstract bool IsFemale { get; }
4     public abstract bool IsMale { get; }
5 } 

这个时候EmployeeEntity已经不在是一个真实的Employee了;

MaleEmployeeEntity代码:

 1 public class MaleEmployeeEntity : EmployeeEntity
 2 {
 3     public override bool IsFemale
 4     {
 5         get { return false; }
 6     }
 7     public override bool IsMale
 8     {
 9         get { return true; }
10     }
11 } 

FemaleEmployeeEntity代码:

 1 public class FemaleEmployeeEntity : EmployeeEntity
 2 {
 3     public override bool IsFemale
 4     {
 5         get { return true; }
 6     }
 7     public override bool IsMale
 8     {
 9         get { return false; }
10     }
11 } 

EmployeeCollectionEntity代码:

 1 public class EmployeeCollectionEntity : List<EmployeeEntity>
 2    {
 3        public IEnumerable<EmployeeEntity> FemaleEmployeeList
 4        {
 5            get
 6            {
 7                return from item in this where item.IsFemale select item;
 8            }
 9        }
10
11        public IEnumerable<EmployeeEntity> MaleEmployeeList
12        {
13            get
14            {
15                return from item in this where item.IsMale select item;
16            }
17        }
18    } 

测试代码:

 1 EmployeeCollectionEntity empCollection = new EmployeeCollectionEntity()
 2            {
 3                new FemaleEmployeeEntity(),
 4                new MaleEmployeeEntity() ,
 5                new MaleEmployeeEntity()
 6            };
 7
 8            var resultList = empCollection.MaleEmployeeList;
 9            if (resultList.Count() == 2 && resultList.ToList()[0].IsMale && resultList.ToList()[1].IsMale)
10                Console.WriteLine("is ok");
11
12            Console.ReadLine();

既然咱们不存在类型码了,那么就不会存在根据参数来获取数据的接口,所以我们稍微变换一下,将参数拆成具体的属性用来直接返回数据集合;

3】影响对象中的逻辑行为(抽象出类型码,使用多态解决)

上面2】节中讲到的方式都是类型码不影响程序具体业务逻辑的情况下的设计方式,但是一旦当类型码直接影响到我们DomainModel中的具体业务逻辑的情况下我就需要将类型码进行提取并抽象出继承体系,然后将具体的逻辑跟类型码继承体系走,这也是面向对象中的面向职责设计,将行为尽可能的放入它调用最平凡的对象中去;

现在假设EmployeeEntity中有一组订单OrderCollection,现在要根据EmployeeEntity的不同级别EmployeeLevel获取(GetDistributionOrders)需要配送的OrderCollection,这里有一个业务规则就是不同的等级在每次获取配送订单的时候是有不同的条件限制的,具体的条件限制跟当前的EmployeeLevel有关系,那么这个时候我们就需要将跟level相关的逻辑封装进EmployeeLevel中去;

图1:

Order代码:

1 public class Order
2 {
3     public DateTime SubmitDtime { get; set; }
4 }

OrderCollection代码:

1 public class OrderCollection : List<Order>
2 {
3
4 } 

EmployeeEntity代码:

 1 public class EmployeeEntity
 2 {
 3     public EmployeeLevel Level { get; set; }
 4
 5     public OrderCollection AllDistributeionOrders { get; set; }
 6
 7     public OrderCollection GetDistributionOrders()
 8     {
 9         return Level.GetDistributionOrders();//将逻辑推入到类型码之后的调用方式;
10     }
11 } 

EmployeeLevel代码:

 1 public abstract class EmployeeLevel
 2 {
 3     public EmployeeEntity employee;
 4     public abstract OrderCollection GetDistributionOrders();
 5 }
 6
 7 public class Normal : EmployeeLevel
 8 {
 9     public override OrderCollection GetDistributionOrders()
10     {
11         if (employee.AllDistributeionOrders == null && employee.AllDistributeionOrders.Count == 0) return null;
12         var orders = from order in employee.AllDistributeionOrders
13                      where order.SubmitDtime <= DateTime.Now.AddDays(-5)//Normal 推迟五天配送
14                      select order;
15
16         if (orders.ToList().Count == 0) return null;
17
18         OrderCollection result = new OrderCollection();
19
20         orders.ToList().ForEach(order => { result.Add(order); });
21
22         return result;
23
24     }
25 }
26
27 public class Super : EmployeeLevel
28 {
29     public override OrderCollection GetDistributionOrders()
30     {
31         if (employee.AllDistributeionOrders == null && employee.AllDistributeionOrders.Count == 0) return null;
32         var orders = from order in employee.AllDistributeionOrders
33                      where order.SubmitDtime <= DateTime.Now.AddDays(-1)//Super 推迟一天配送
34                      select order;
35
36         if (orders.ToList().Count == 0) return null;
37
38         OrderCollection result = new OrderCollection();
39
40         orders.ToList().ForEach(order => { result.Add(order); });
41
42         return result;
43     }
44 } 

测试代码:

 1 OrderCollection orderColl = new OrderCollection();
 2            orderColl.Add(new Order() { SubmitDtime = DateTime.Now.AddDays(-2) });
 3            orderColl.Add(new Order() { SubmitDtime = DateTime.Now.AddDays(-7) });
 4            EmployeeEntity employee = new EmployeeEntity()
 5            {
 6                AllDistributeionOrders = orderColl
 7            };
 8
 9            EmployeeLevel level = new Super() { employee = employee };
10            employee.Level = level;
11
12            var result = employee.GetDistributionOrders();
13            if (result.Count == 2)
14                Console.WriteLine("Is ok");
15
16            Console.ReadLine(); 

我们定义了两个EmployeeLevel,一个是Normal的,也就是普通的,他的配送限制条件是:配送必须推迟五天;二个Super,也就是超级的,他的配送只推迟一天;这样的逻辑分支,如果我们没有将类型码抽象出来进行设计,那么我们将面临着一个条件分支的判断,当后面需要加入其他Level的时候我们就会慢慢的陷入到判断分支的泥潭;

4】无法直接抽象出类型码(使用策略模式解决)

在3】节中,我们能很好的将类型码抽象出来,但是如果我们面临着一个重构项目时,我们很难去直接修改大面积的代码,只能平衡一下将类型码设计成具有策略意义的方式,不同的类型码对应着不同的策略方案;

我们还是拿3】节中的示例来说,现在假设我们在重构一个直接使用int作为类型码的EmployeeEntity,那么我们不可能去直接修改EmployeeEntity内部的逻辑,而是要通过引入策略工厂将不同的类型码映射到策略方法中;

图2:

由于该节代码比较简单,所以就不提供示例代码,根据上面的UML类图基本上可以知道代码结构;

 

作者:王清培

出处:http://www.cnblogs.com/wangiqngpei557/

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面

时间: 2024-07-29 22:52:21

.NET重构(类型码的设计、重构方法)的相关文章

重构——32以State/Strategy取代类型码(Replace Type Code with State/Strategy)

以State/Strategy取代类型码(Replace Type Code with State/Strategy) 你有一个类型码,它会影响到类的行为,但是你无法通过继承手法消除它:以状态对象取代类型码 一.动机 本项重构与Replace Type Code with Subclass很相似,但如果"类型码的值在对象生命期中发生变化"或"其他原因使得宿主类不能被继承",你可以使用本重构,本重构使用State模式或者Strategy模式 二.做法 1.使用Self

重构——30以类取代类型码(Replace Type Code with Class)

以类取代类型码(Replace Type Code with Class) 类之中有一个数值类型码,但它并不影响类的行为:以一个新的类替换该数值类型码 一.动机 让编译器可以进行类型检查,减少bug 二.做法 1.为类型码建立一个类 2.修改源类实现,并让它使用上述新建的类 3.编译,测试 4.对于源类中每一个使用类型码的函数,相应建立一个函数,让新函数使用新建的类 5.逐一修改源类用户,让它们使用新接口 6.每修改一个用户,编译并测试 7.删除使用类型码的新接口,并删除保存旧类型码的静态变量

重构——31以子类取代类型码(Replace Type Code with SubClass)

以子类取代类型码(Replace Type Code with SubClass) 你有一个不可变的类型码,它会影响类的行为:以子类取代这个类型码 一.动机 1.借助多态来处理变化行为,标志就是switch if-then-else 2.把对不同类行为的了解从类用户那儿转移到了类自身 二.做法 1.使用Self Encapsulate Field将类型码自我封装起来 2.为类型码的每一个数值建立一个相应的子类.在每个子类中覆写类型码的取值函数,使其返回相应的类型码值 3.每建立一个新的子类,编译

由学习《软件设计重构》所想到的代码review(二)

前言 对于一个程序员来讲如何来最直接的来衡量他的技术能力和产出呢?我想最直观的作法是看他的代码编写能力,就拿我经常接触的一些程序员来看,他们买了很多技术重构类书籍,但是看完后代码编写能力并没有显著提高.有人说可以用代码review工具啊,但是像市面上的这些代码review工具,只能帮助我们解决表面的bug和规范点,还无法帮助我们发现更深层次的设计问题. 上一篇<由学习<软件设计重构>所想到的代码review(一)>我结合<软件设计重构>这本书谈谈在进行代码review的

由学习《软件设计重构》所想到的代码review(一)

前言 对于一个程序员来讲如何来最直接的来衡量他的技术能力和产出呢?我想最直观的作法是看他的代码编写能力,就拿我经常接触的一些程序员来看,他们买了很多技术重构类书籍,但是看完后代码编写能力并没有显著提高.有人说可以用代码review工具啊,但是像市面上的这些代码review工具,只能帮助我们解决表面的bug和规范点,还无法帮助我们发现更深层次的设计问题. 下面我将结合<软件设计重构>这本书谈谈在进行代码review的时候,需要关注的哪些点. 一.技术债务 何为技术债务? 技术债务是有意或无意的做

架构之坑系列1:重构中的过度设计与高可用银弹

这是一个坑系列,会说一些在系统设计.系统架构上的坑,这些都是我想到哪说到哪,有像这篇一样比较宏观的坑,后面的文章也会有到具体技术细节的(比如某个函数,某个系统调用)坑.总之,到处都是坑,这些坑有些是我经历过的,有些是听说的,你也可以留言说说你遇到的坑.   第一部分,我们从重构这个场景来看看系统架构的设计中过度设计这个坑.首先,我们这里说的重构,和<重构:改善既有代码的设计>这本书中的重构不太一样,这是本好书,他主要说的是代码级别的重构,这种重构是需要在编码的时候时时刻刻进行的,更多的是一种编

《重构HTML:改善Web应用的设计(修订版)》——第1章 重构1.1 为何重构

第1章 重构 重构.什么是重构?为何要重构? 简明扼要地说,重构是在不改变程序行为的基础上进行小的改动,是代码基逐渐改善的过程,通常也需依赖于一些自动化工具的帮助.重构的目标是移除长年积聚下来的烂码①,以得到更清晰和更容易维护.除错以及添加新功能的代码. 严格地说,重构实际上并不涉及除错,也不增加新功能.但在实践中,重构的过程总是会碰到需要修正的错误,也会有需要加入新功能的时候.重构通常会化繁为简,化难为易.改善代码的第一步是重组代码. 举个例子,无论是为了开始新的学期,还是从事新项目或新工作而

代码重构(五):继承关系重构规则

陆陆续续的发表了多篇关于重构的文章了,还是那句话,重构是一个项目迭代开发中必不可少的一个阶段.其实重构伴随着你的项目的整个阶段.在前几篇关于重构的文章中我们谈到了函数的重构.类的重构.数据的重构以及条件表达式的重构,那么今天咱们就来聊聊继承关系的重构.当然还是延续前几篇博客的风格,我们在博客中的代码实例依然使用Swift语言来实现,当然还是那句话,使用什么语言无所谓,关键是看重构的场景以及重构的思想. "重构"不仅仅可以改善你既有的代码设计,还可以改变你组织代码的思路,使你的程序在设计

什么是重构,什么不是重构

英文原文:What Refactoring is, and what it isn't  有时候,会有程序员跑到我这里说他们不喜欢某个东西的设计,"我们需要给它来个全面的重构",来纠正里面的错误.哦,哦.这听起来可不是个好主意.而且这听起来也不是重构- 重构(Refactoring)这个词最初由 Martin Fowler 和 Kent Beck 给下的定义,它是 一种修改,使软件的内部结构更容易理解,在不改变软件的可见行为方式前提下使软件更容易变更-它是一种有节制的整理代码.使 bu