改善代码设计 —— 组织好你的“.NET技术”数据(Composing Data)

简介:   系列博客      1. 改善代码设计 —— 优化函数的构成(Composing Methods)      2. 改善代码设计 —— 优化物件之间的特性(Moving Features Between Objects)      3.

  系列博客

      1. 改善代码设计 —— 优化函数的构成(Composing Methods)

      2. 改善代码设计 —— 优化物件之间的特性(Moving Features Between Objects)

      3. 改善代码设计 —— 组织好你的数据(Composing Data)

      4. 改善代码设计 —— 简化条件表达式(Simplifying Conditional Expressions)

      5. 改善代码设计 —— 简化函数调用(Making Method Calls Simpler)

      6. 改善代码设计 —— 处理概括关系(Dealing with Generalization)

  1. Self Encapsulate Field (自封装值域)

  解释:

      大部分类 (class) 中都会有一些值域 (field), 随之还会有一些方法使用到了这些值域. "如果调用这些值域"这个问题分为两种观点: 1. 应该直接调用它们 2. 应该通过访问函数调用它们.

      我觉得大部分情况下直接调用比较方便, 过多的访问函数还会造成类中的函数过多, 当然将来如果我觉得直接调用带来了一些问题, 写一个一个的访问函数也并不是很困难.

      下面的例子主要说明如何给值域写一个访问函数, 并通过访问函数调用值域的值.

  冲动前:

 
 
private string _userName, _password;

public bool IsValid()
{
bool isValid = ! (String.IsNullOrEmpty(_userName) &&
String.IsNullOrEmpty(_password));
return isValid;
}

  冲动后:

 
 
private string _userName, _password;

public bool IsValid()
{
bool isValid = ! (String.IsNullOrEmpty(GetUserName()) &&
String.IsNullOrEmpty(GetPassword()));
return isValid;
}
private string GetUserName()
{
return _userName;
}
private string GetPassword()
{
return _password;
}

  2. Replace Data Value with Object (以物件取代数据值)

  解释:

      如果你发现代码中有很多字段或者值域似乎都在描述某一样东西, 可以考虑将这些字段或者值域封装到一个类中, 用这个物件代替原先代码中繁杂的字段和值域.

  冲动前:

 
 
class Order
{
public string CustomerName { get ; set ; }
public string CustomerAddress { get ; set ; }
public int CreditLevel { get ; set ; }
public int CustomerTel { get ; set ; }
// ...
}

  冲动后:

 
 
class Order
{
public Customer Customer { get ; set ; }
}

class Customer
{
public string CustomerName { get ; set ; }
public string CustomerAddress { get ; set ; }
public int CreditLevel { get ; set ; }
public int CustomerTel { get ; set ; }
// ...
}

  3. Change Value to Reference (将值对象改为引用对象)

  解释:

      一个类中有时包含值对象作为它的字段或者属性, 如订单 Order 类中包含了一个 客户 customerName 的字段. 如果同一个客户有好几份订单, 那么每一份订单就都会保存一次客户的姓名. 如果这个客户的姓名改变了, 那么就需要更改每份个订单中的 customerName.
      如果将 customerName 提取出去, 提炼一个 Customer 的类, 订单使用的是 Customer 实例的引用, 则只需更改实例中客户姓名的属性, 因为所有的订单都是引用的这个客户实例, 所以它们不需要作其它更改.

  冲动前:

 
 
class Order
{
private string _customerName;

public Order( string customerName)
{
this ._customerName = customerName;
}
上海企业网站制作}

  冲动后:

 
 
class Order
{
private Customer _customer;

public Order(Customer customer)
{
this ._customer = customer;
}
}

class Customer
{
public string CustomerName { get ; set ; }
}

  4. Change Reference to Value (将引用对象改为值对象)

  解释:

      如果给一个类传递引用类型变得不是你想要的效果, 比如我曾经做过一个类似于 MSN 的软件, 其中将人的头像, 昵称, 个性签名显示在一个自定义控件中, 然后在使用了 ListBox.Add(myControl), 结果我惊奇的发现最后好友列表里居然都是同一个 (最后一个) 人. 原来 ListBox.Add() 传递进去的都是引用的同一个 myControl 实例, 尽管代码貌似在动态的生成每一个人, 但其实都只修改了一个 myControl, 而 ListBox 仅仅了引用这一个对象. 后来我在代码中动态的生成不同对象的实例才解决了这个问题.

      除了这样, 还可以在你的类中定义一个值对象, 每一次生成一个类的实例时, 类中值对象的控制权总是属于自己.

  5. Replace Array with Object (用物件取代数组)

  解释:

      一个类中可能会包含很多字段, 你不能因为这些字段都是 string 类型, 就通通把它们放到一个 string[] 里, 在类的方法体中通过数组索引来获取你需要的值, 这样很难让别人记得你 string[0] 表示的是姓名, string[1] 表示的是他的住址...

      下面的将展示前后代码可读性的差距.

  冲动前:

 
 
string [] _person = new string [ 5 ];

public string GetName()
{
return _person[ 0 ];
}
public string GetAdd()
{
return _person[ 1 ];
}

  冲动后:

 
 
private Person _person;

public string GetName()
{
return _person.Name;
}
public string GetAdd()
{
return _person.Address;
}

  6. Duplicate Observed Data (复制被监视数据)

  解释:

      一个良好的系统应该事先业务逻辑 (Business Logic) 与用户界面 (User Interface) 分离, 如果没有这样做, 常见的比如在 Program.cs 堆叠了大量的代码, 业务逻辑与用户界面非常紧密的耦合在了一起. 实现业务逻辑与用户界面分离, 最重要的实现是数据的同步机制, Duplicate Observed Data 重构手段用来完成这项工作. 由于例子较长, 可以独立成篇, 在以后介绍观察者模式 (Observer Pattern) 的时候会搬出来讨论. 大笑

  7. Change Unidirectional Association to Bidirectional (将单向关联改为双向)

  解释:

      如果两个 Class 都需要使用对方的特性, 但两者之间只有一条单向关联 (如只有调用方才能调用被调用的特性), 如果你想让被调用的那一方知道有哪些物件调用了自身, 这时就需要使用 Change Unidirectional Association to Bidirectional.

      通常的做法是在被调用方的代码里定义一个被调用方类型的集合 (Collection), 如 ArrayList<T>, List<T>(推荐使用 List<T>), 在[调用方] 调用 [被调用方] 的时候, 在 [被调用方] 里的集合中也添加一下自己的引用.

  8. Change Bidirectional Association to Unidirecti上海网站建设onal (将双向关联改为单向)

  解释:

      使用了Change Unidirectional Association to Bidirectional 之后, 如果需求变了, 发现双向关联是没有必要的, 这时就需要将双向关联改为单向, 也就是 Change Bidirectional Association to Unidirectional, 步骤与 Change Unidirectional Association to Bidirectional 相反.

  9. Replace Magic Number with Symbolic Constant (用符号常量取代魔法数)

  解释:

      新手在写程序的时候, 往往不注重命名 (name), 他们对程序的要求也就是能正确运行即可. 在 IDE 中随便拖几个 Button, 也不重新命名, 于是在代码中出现了类似 Button1, Button2, Button3...之类的, 你无法光看代码就能想象得出 Button1 对应 UI 中的哪一个按钮. 同样的坏习惯出现在代码的字段, 临时变量的命名, 到处是 a, aa, aaa...

      使用 Replace Magic Number with Symbolic Constant 吧! 如果你的 Button 是用于"发生", 那么你可以给它一个名字 —— btnSend; 如果你的一个 Int32型 变量用于表示一个人的年龄, 请给它一个名字 —— age.

  10. Encapsulate Field (封装值域)

  解释:

      如果你的 class 中存在一个 public 字段, 请将它设置为属性.

      面向对象的第一条原则就是封装 (encapsulation), 或者称之为数据隐藏 (Data Hiding), 如果一个字段想被外界访问到, 你应该将它设为属性, 在 C# 中设置属性比在 Java 中要方便许多, 因为 C# 编译器帮你做了一些额外工作.

  冲动前:

 
 
public string _name;

  冲动后:

 
 
public string Name { get ; set ; }

  11. Encapsulate Collection (封装集合)

  解释:

      如果你的 class 中有一个方法需要返回多个值, 在类中有一个集合 (Collection) 暂时保持这些返回值, 有些情况下应当避免让 [调用端] 直接访问这个集合. 如果 [调用端] 修改了你集合的某一项, 而 [被调用端] 不知道自己的集合被修改了, 而另外的一些方法仍然在调用被修改后的集合, 这样可能会造成无法预料的后果.

      Encapsulate Collection 建议你在 [被调用端] 将这个集合封装 (至少你将它的访问权限设置为 private) 起来, 有需要的话, 并提供修改这个集合的函数.

  12. Replace Record with Data Class (用数据取代记录)

  解释:

      与第二条 Replace Data Value with Object 和第五条 Replace Array with Object 做法相似, 目的是提取类中的数据到一个描述记录 (Record) 的类中, 方便以后可以对这个类进行扩展.

  13. Replace Subclass with Fields (用值域取代子类)

  解释:

      子类建立在父类之上再增加新的功能或者重载父类可能的变化行为, 有一种变化行为 (variant behavior) 成为常量函数 (constant method), 他们会返回一个硬编码 (hard-coded) 值, 这个值一般有这样的用途: 让不同的父类中的同一个访问函数返回不同的值, 你可以在父类中将访问函数声明为抽象函数, 并在不同的不同的子类中让它返回不同的值. 但如果子类中只有返回常量的函数, 没有其它的作用, 往往子类并没有太大的存在价值.

      你可以在父类中设计一些与子类返回值相应的值域, 再对父类做一些其它修改, 从而可以消除这些子类, 好处是避免不必要的子类带来的复杂性, 这就是 Replace Subclass with Fields.

  冲动前:

 
 
class Person
{
protected abstract bool isMale();
protected abstract char Code();
// ...
}
class Male : Person
{
protected override bool isMale()
{
return true ;
}
protected override char Code()
{
return ' M ' ;
}
}
class Female : Person
{
protected override bool isMale()
{
return false ;
}
protected override char Code()
{
return ' F ' ;
}
}

  冲动后:

 
 
class Person
{
public bool IsMale { get ; set ; }
public char Code { get ; set ; }

public Person( bool isMale, char code)
{
this .IsMale = IsMale;
this .Code = code;
}
public Person Male()
{
return new Person( true , ' M ' );
}
public Person Female()
{
return new Person( false , ' F ' );
}
// ...
}

      调用的时候这样: Person Create_Chen = Person.Male();

目录
相关文章
|
1天前
|
数据挖掘 定位技术
.NET Compact Framework下的GPS NMEA data数据分析(二)转
.NET Compact Framework下的GPS NMEA data数据分析(二)转
|
11天前
|
人工智能 开发框架 量子技术
【专栏】.NET 技术:驱动创新的力量
【4月更文挑战第29天】.NET技术,作为微软的开发框架,以其跨平台、开源和语言多样性驱动软件创新。它在云计算、AI/ML、混合现实等领域发挥关键作用,通过Azure、ML.NET等工具促进新兴技术发展。未来,.NET将涉足量子计算、微服务和无服务器计算,持续拓宽软件开发边界,成为创新的重要推动力。掌握.NET技术,对于开发者而言,意味着握有开启创新的钥匙。
|
11天前
|
开发框架 .NET C#
【专栏】理解.NET 技术,提升开发水平
【4月更文挑战第29天】本文介绍了.NET技术的核心概念和应用,包括其跨平台能力、性能优化、现代编程语言支持及Web开发等特性。文章强调了深入学习.NET技术、关注社区动态、实践经验及学习现代编程理念对提升开发水平的重要性。通过这些,开发者能更好地利用.NET构建高效、可维护的多平台应用。
|
11天前
|
机器学习/深度学习 vr&ar 开发者
【专栏】.NET 技术:引领开发新方向
【4月更文挑战第29天】本文探讨了.NET技术如何引领软件开发新方向,主要体现在三方面:1) 作为跨平台开发的先锋,.NET Core支持多操作系统和移动设备,借助.NET MAUI创建统一UI,适应物联网需求;2) 提升性能和开发者生产力,采用先进技术和优化策略,同时更新C#语言特性,提高代码效率和可维护性;3) 支持现代化应用架构,包括微服务、容器化,集成Kubernetes和ASP.NET Core,保障安全性。此外,.NET还不断探索AI、ML和AR/VR技术,为软件开发带来更多创新可能。
|
11天前
|
开发框架 Cloud Native 开发者
【专栏】剖析.NET 技术的核心竞争力
【4月更文挑战第29天】本文探讨了.NET框架在软件开发中的核心竞争力:1) .NET Core实现跨平台与云原生技术的融合,支持多操作系统和容器化;2) 提升性能和开发者生产力,采用JIT、AOT优化,提供C#新特性和Roslyn编译器平台;3) 支持现代化应用架构,包括微服务和容器化,内置安全机制;4) 丰富的生态系统和社区支持,拥有庞大的开发者社区和微软的持续投入。这些优势使.NET在竞争激烈的市场中保持领先地位。
|
11天前
|
开发框架 .NET 开发者
【专栏】领略.NET 技术的创新力量
【4月更文挑战第29天】.NET技术自ASP.NET起历经创新,现以.NET Core为核心,展现跨平台能力,提升性能与生产力,支持现代化应用架构。.NET Core使开发者能用同一代码库在不同操作系统上构建应用,扩展至移动和物联网领域。性能提升,C#新特性简化编程,Roslyn编译器优化代码。拥抱微服务、容器化,内置安全机制,支持OAuth等标准。未来.NET 6将引入更快性能、Hot Reload等功能,预示着.NET将持续引领软件开发潮流,为开发者创造更多机会。
|
4月前
|
开发框架 前端开发 .NET
ASP.NET CORE 3.1 MVC“指定的网络名不再可用\企图在不存在的网络连接上进行操作”的问题解决过程
ASP.NET CORE 3.1 MVC“指定的网络名不再可用\企图在不存在的网络连接上进行操作”的问题解决过程
46 0
|
16天前
|
开发框架 前端开发 JavaScript
JavaScript云LIS系统源码ASP.NET CORE 3.1 MVC + SQLserver + Redis医院实验室信息系统源码 医院云LIS系统源码
实验室信息系统(Laboratory Information System,缩写LIS)是一类用来处理实验室过程信息的软件,云LIS系统围绕临床,云LIS系统将与云HIS系统建立起高度的业务整合,以体现“以病人为中心”的设计理念,优化就诊流程,方便患者就医。
22 0
|
2月前
|
开发框架 前端开发 .NET
进入ASP .net mvc的世界
进入ASP .net mvc的世界
32 0
|
2月前
mvc.net分页查询案例——mvc-paper.css
mvc.net分页查询案例——mvc-paper.css
5 0