【代码精进】| 总结/Edison Zhou


作为一个后端工程师,想必在职业生涯中都写过一些不好维护的代码。本文是我学习《代码之丑》的学习笔记,今天第三天,品品大类和长参数列表的味道。

上一篇:一天一点代码坏味道(2)

1大类

对于我们来说,一个人理解的东西是有限的,没有人能够同时面对所有细节

因此,人类选择面对复杂事物的解决方案都是分而治之。

那么,如果一个类里面的内容太多,它就会超过一个人的理解范畴。

问题来了,大类是如何变大的?

职责不单一

单一职责原则是衡量软件设计好坏的一把简单而有效的尺子,通常来说,很多类之所以巨大,大部分情况下都是因为其违反了这个原则。

坏味道代码:

public class User
{public long UserId { get; set; }public string Name { get; set; }public string NickName { get; set; }public string Email { get; set; }public string PhoneNumber { get; set; }public AuthorType AuthorType { get; set; }public ReviewStatus AuthorReviewStatus { get; set; }public EditorType EditorType { get; set; }...
}

有经验的童鞋应该一眼就发现了其中包含了不同类型的用户的信息,既有用户基本信息,还有作者相关信息,最后还有编辑类型...

三种不同的角色,三种不同诉求的业务方关心的是不同的内容,只是因为她们都是这个系统的用户,就把它们都放在了用户类中。后续需求一变动,这个用户类就会被反复修改。

针对上面这个场景,需要对其中的不同角色进行拆分:

public class User
{public long UserId { get; set; }public string Name { get; set; }public string NickName { get; set; }public string Email { get; set; }public string PhoneNumber { get; set; }...
}public class Author
{public long UserId { get; set; }public AuthorType AuthorType { get; set; }public ReviewStatus AuthorReviewStatus { get; set; }...
}public class Editor
{public long UserId { get; set; }public EditorType EditorType { get; set; }...
}

字段未分组

拆分之后,User类还是很大,再仔细看,发现其实可以将部分字段进行分组, 比如Email和PhoneNumber都属于用户的联系方式,便可以再次分解。

这里引入一个Contact类,将Email和PhoneNumber放了进去,以后如果还有其他联系方式如QQ、微信之类的需求,也都可以统一放到Contact类中。

public class User
{public long UserId { get; set; }public string Name { get; set; }public string NickName { get; set; }public Contact Contact { get; set; }...
}public class Contact
{public string Email { get; set; }public string PhoneNumber { get; set; }...
}

由此,我们可以看出,将大类分解成小类,其实也是在做设计工作。欢迎体会软件设计之美!

2长参数列表

方法之间传递参数再常见不过,但是如果不限制参数个数,长参数列表就会出现在你我的项目代码之中,它带来的不可维护度是巨大的。

和大类一样,我们也需要对长参数列表进行拆解。

那么,有哪些拆解方式呢?

将参数列表封装成对象

这是一个熟知的重构方法,记得我在10年前阅读王涛老师《你必须知道的.NET》一书中就了解了这个技巧。

那么,不妨看一个长参数列表的坏味道:

public void CreateBook(string title, string introduction, URL coverUrl, BookType type,BookChannel channel, string protagonists,string tags, bool completed)
{var book = new Book(){Title = title,Introduction = introduction,CoverUrl = coverUrl,Type = type,Channel = channel,Protagonists = protagonists,Tags = tags,Completed = completed};_repository.Save(book);
}

将其封装为一个类型:

public class NewBookParameters
{public string Title { get; set; }public string Introduction { get; set; }public URL CoverUrl { get; set; }public BookType Type { get; set; }public BookChannel Channel { get; set; }public string Protagonists { get; set; }public string Tags { get; set; }public bool Completed { get; set; }
}

那么,CreateBook方法就改为这个样子?

public void CreateBook(NewBookParameters parameters)
{var book = new Book(){Title = parameters.Title,Introduction = parameters.Introduction,CoverUrl = parameters.CoverUrl,Type = parameters.Type,Channel = parameters.Channel,Protagonists = parameters.Protagonists,Tags = parameters.Tags,Completed = parameters.Completed};_repository.Save(book);
}

我想,可能我们还是会觉得怪怪的,没有什么大的简化。那么,如果我们给NewBookParameters方法再改改呢?

public class NewBookParameters
{public string Title { get; set; }public string Introduction { get; set; }public URL CoverUrl { get; set; }public BookType Type { get; set; }public BookChannel Channel { get; set; }public string Protagonists { get; set; }public string Tags { get; set; }public bool Completed { get; set; }public Book NewBook(){return new Book(){Title = this.Title,Introduction = this.Introduction,CoverUrl = this.CoverUrl,Type = this.Type,Channel = this.Channel,Protagonists = this.Protagonists,Tags = this.Tags,Completed = this.Completed};}
}

这个时候的CreateBook方法就可以极大简化了:

public void CreateBook(NewBookParameters parameters)
{var book = parameters.NewBook();_repository.Save(book);
}

一般情况下,将长长的参数列表封装为一个类,可以解决大部分场景下的问题。

动与静的分离

还有一些场景,不能简单地将长参数封装为一个类,比如有些原本属于静态结构的部分却以动态参数的方式进行传递,无形之间使得参数列表变长了。

那么,不妨看一个这样的坏味道:

public void GetChapters(long bookId, HttpClient httpClient, ChapterProcessor processor)
{var requestUri = GenerateRequestUri(bookId);var response = httpClient.GetAsync(requestUri).Result;var chapters = GenerateChapters(response);processor.Process(chapters);
}

在这三个参数中,几乎每次传递的bookId是不一样的,但是httpClient和processor却是一样的。换句话说,bookId是变化的,而httpClient和processor却是不怎么变化的。

用专业术语来讲,这就是动数据与静数据的耦合,需要将其拆开。可以将静态不变的数据作为所在类的一部分,通过依赖注入的方式注入进去即可。

重构代码如下:

public void GetChapters(long bookId)
{var requestUri = GenerateRequestUri(bookId);var response = _httpClient.GetAsync(requestUri).Result;var chapters = GenerateChapters(response);_processor.Process(chapters);
}

移除标记参数

有些时候,我们喜欢将flag参数写在参数列表中,各种flag满天飞,一不小心堆积多了,也就会容易产生混乱。

比如,下面这个坏味道:

public void EditChapter(long chapterId, string title, string content, bool isApproved)
{...
}

之所以有最后这个flag参数,是因为逻辑代码会根据这个flag参数走不通的处理流程。于是,又到了追问自己的时刻,这个方法的初心(业务)是为了什么?

为了贴近业务,对于flag参数需要适量移除:

// 普通编辑,需要审核
public void EditChapter(long chapterId, string title, string content)
{...
}
// 资深编辑,无须审核
public void EditChapterWithApproval(long chapterId, string title, string content)
{...
}

可以看到,分解成两个方法之后,就消除了flag参数,但需要注意的是不要重复,对于公共部分需要封装尽可能复用以保持两个方法的尽可能独立。

3小结

本文总结了两类坏味道,一是大类,二是长参数列表。无论是长函数方法、大类 还是 长参数列表,它们的背后都在告诉我们一件事情,即编写“短小”的代码的重要性,而要编写“短小”的代码,需要我们在设计的时候就能“分离关注点”。

最后,感谢郑晔老师的这门《代码之丑》课程,让我受益匪浅!我也诚心把它推荐给关注EdisonTalk公众号的各位童鞋!

参考资料

郑晔,《代码之丑》(推荐订阅学习)

Martin Flower著,熊杰译,《重构:改善既有代码的设计》(推荐至少学习第三章)


.NET代码质量 | 一天一点代码坏味道(3)相关推荐

  1. .NET代码质量 | 一天一点代码坏味道(4)

    [代码精进]| 总结/Edison Zhou 作为一个后端工程师,想必在职业生涯中都写过一些不好维护的代码.本文是我学习<代码之丑>的学习笔记,今天最后一天,一起品品滥用控制语句的味道,再 ...

  2. .NET代码质量 | 一天一点代码坏味道(2)

    [代码精进]| 总结/Edison Zhou 作为一个后端工程师,想必在职业生涯中都写过一些不好维护的代码.本文是我学习<代码之丑>的学习笔记,今天第二天,品品重复代码和长函数方法的味道. ...

  3. java代码质量 圈复杂度,代码圈复杂度(Cyclomatic Complexity,CC)和Oclint工具 介绍...

    什么是代码圈复杂度 圈复杂度是一种度量程序复杂度的方法,由 Thomas McCabe 于 1976年定义,用来衡量一个模块判定结构的复杂程度,数量上表现为独立路径条数,即合理的预防错误所需测试的最少 ...

  4. 快速改善代码质量的20条代码规范

    目录 1.关于命名 2.关于注释 3.关于代码风格 4.关于编码技巧 5.统一编码规范 1.关于命名 命名的关键是能准确达意.对于不同作用域的命名,我们可以适当地选择不同的长度. 我们可以借助类的信息 ...

  5. 消除代码中的坏味道,编写高质量代码

    消除代码中的坏味道,编写高质量代码 Intro 想要写出较好的代码,保证代码的高质量需要时刻警惕代码中的坏味道,今天分享一下,我觉得平时写的代码中可能会出现的坏味道代码的一些示例 常见的坏味道代码 B ...

  6. 从哪些维度评判代码质量的好坏?如何具备写出高质量代码的能力?

    文章目录 如何评价代码质量的高低? 最常用的评价标准有哪几个? 1. 可维护性(maintainability) 2. 可读性(readability) 3. 可扩展性(extensibility) ...

  7. 重构笔记——代码的坏味道(上)

    本文是在学习中的总结,欢迎转载但请注明出处:http://blog.csdn.net/pistolove/article/details/42041757 在重构入门篇中,简单地介绍了重构的定义.为何 ...

  8. 代码质量与规范,那些年你欠下的技术债

    提到"质量"二字时,我们的第一反应往往是"有多少BUG?""性能好不好?"这样的问题.我们对软件产品或服务的质量定义看其能不能满足用户的需求 ...

  9. 代码质量在「内卷时代」的重要性

    这里是Z哥的个人公众号 每周五11:45 按时送达 当然了,也会时不时加个餐- 我的第「173」篇原创敬上 大家好,我是Z哥. 提到代码质量,不知道你的脑海中浮现出的第一个词是什么?规范?可读性?优雅 ...

最新文章

  1. vim 成“神“之路 (一)
  2. linux和unix文件没有创建时间,Linux中没有文件创建时间的概念
  3. Oracle常用操作之登录名和密码大小写问题
  4. 推荐3个快速开发平台 前后端都有 项目经验又有着落了
  5. Tips:重装系统后Anaconda目录在开始菜单消失问题
  6. 网络营销第一课:市场营销基础(2)
  7. [ZZ] 使用rsync来实现快速删除大量文件
  8. 解决 mysql 树形结构插入数据查询死循环问题
  9. 优秀渐变色彩应用PSD分层海报模板,大神都是这样玩渐变的,一看就懂
  10. sogou rank查询接口
  11. CMake macOS安装
  12. 0x80070079信号灯超时_windows server 2012 连接共享报0x80070079 信号灯超时时间已到
  13. vue 实现文字转语音
  14. 思科路由器如何导出配置文件_备份cisco路由器配置文件
  15. Objective-C学习篇03—继承
  16. 【目标检测】你想知道的手势识别都在这里 【YOLO】网络
  17. 大数据高频面试题总结
  18. vs2008简体中文正式版下载
  19. 使用Itextsharp编辑PDF
  20. js处理有序列表、js处理无序列表

热门文章

  1. 基于SVM的乳腺癌数据集分类
  2. 四川师范大学计算机学院冯林,李晓宁(四川师范大学计算机科学学院副教授)_百度百科...
  3. 记一次svga动画模板的踩坑历程
  4. QEMU零知识学习5 —— QEMU安装
  5. flash怎么去掉绿幕_AE如何抠取绿幕视频?抠取绿幕视频的方法
  6. Photoshop 快捷键
  7. 多个if与多个else if详解
  8. 《惢客创业日记》2021.01.03-04(周日)时间的自省
  9. 托管海外服务器有哪些要求?
  10. java 日历记事本_用Java实现日历记事本.doc