`
z75148885
  • 浏览: 191528 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

refactoring--除去代码异味(bad smell)(2)

 
阅读更多

让我们来看一下另外一个例子 ,在当前的系统中 ,有三种用户 :常规用户 ,管理员和游客 。
常规用户必须每隔90 天修改一次密码 (更频繁也行 ),管理员必须每30 天修改一次密码 ,游客就不需要修改了,常规用户跟管理员可以打印报表 。
先看一下当前的代码 :

class UserAccount {
final static int USERTYPE_NORMAL = 0;
final static int USERTYPE_ADMIN = 1;
final static int USERTYPE_GUEST = 2;
int userType;
String id;
String name;
String password;
Date dateOfLastPasswdChange;
public boolean checkPassword(String password) {
...
}
}


class InventoryApp {
void login(UserAccount userLoggingIn, String password) {
if (userLoggingIn.checkPassword(password)) {
GregorianCalendar today = new GregorianCalendar();
GregorianCalendar expiryDate = getAccountExpiryDate(userLoggingIn);
if (today.after(expiryDate)) {
//提示用户修改密码
...
}
}
}

GregorianCalendar getAccountExpiryDate(UserAccount account) {
int passwordMaxAgeInDays = getPasswordMaxAgeInDays(account);
GregorianCalendar expiryDate = new GregorianCalendar();
expiryDate.setTime(account.dateOfLastPasswdChange);
expiryDate.add(Calendar.DAY_OF_MONTH, passwordMaxAgeInDays);
return expiryDate;

}

int getPasswordMaxAgeInDays(UserAccount account) {
switch (account.getType()) {
case UserAccount.USERTYPE_NORMAL:
return 90;
case UserAccount.USERTYPE_ADMIN:
return 30;
case UserAccount.USERTYPE_GUEST:
return Integer.MAX_VALUE;
}
}

void printReport(UserAccount currentUser) {
boolean canPrint;
switch (currentUser.getType()) {
case UserAccount.USERTYPE_NORMAL:
canPrint = true;
break;
case UserAccount.USERTYPE_ADMIN:
canPrint = true;
break;
case UserAccount.USERTYPE_GUEST:
canPrint = false;
}
if (!canPrint) {
throw new SecurityException("You have no right");
}
//打印报表
}
}


用一个对象代替一种类别 (注意 ,之前是一个类代替一种类别 )
根据之前讲的解决方法 ,要去掉类别代码 ,我们只需要为每种类别创建一个子类 ,比如:
abstract class UserAccount {
String id;
String name;
String password;
Date dateOfLastPasswdChange;
abstract int getPasswordMaxAgeInDays();

abstract boolean canPrintReport();
}


class NormalUserAccount extends UserAccount {
int getPasswordMaxAgeInDays() {
return 90;
}

boolean canPrintReport() {
return true;
}
}


class AdminUserAccount extends UserAccount {
int getPasswordMaxAgeInDays() {
return 30;
}

boolean canPrintReport() {
return true;
}
}


class GuestUserAccount extends UserAccount {
int getPasswordMaxAgeInDays() {
return Integer.MAX_VALUE;
}

boolean canPrintReport() {
return false;
}
}


但问题是 ,三种子类的行为 (里面的代码 )都差不多一样,getPasswordMaxAgeInDays 这个方法就一个数值不同 (30,90或者Integer.MAX_VALUE)。canPrintReport 这个方法也不同在一个数值 (true 或false )。这三种用户类型只需要用三个对象代替就行了 ,无须特地新建三个子类了 :
class UserAccount {
UserType userType;
String id;
String name;
String password;
Date dateOfLastPasswdChange;
UserType getType() {
return userType;
}
}


class UserType {
int passwordMaxAgeInDays;
boolean allowedToPrintReport;
UserType(int passwordMaxAgeInDays, boolean allowedToPrintReport) {
this.passwordMaxAgeInDays = passwordMaxAgeInDays;
this.allowedToPrintReport = allowedToPrintReport;
}

int getPasswordMaxAgeInDays() {
return passwordMaxAgeInDays;
}

boolean canPrintReport() {
return allowedToPrintReport;
}

static UserType normalUserType = new UserType(90, true);
static UserType adminUserType = new UserType(30, true);
static UserType guestUserType = new UserType(Integer.MAX_VALUE, false);
}


class InventoryApp {
void login(UserAccount userLoggingIn, String password) {
if (userLoggingIn.checkPassword(password)) {
GregorianCalendar today = new GregorianCalendar();
GregorianCalendar expiryDate = getAccountExpiryDate(userLoggingIn);
if (today.after(expiryDate)) {
//提示用户修改密码
...
}
}
}

GregorianCalendar getAccountExpiryDate(UserAccount account) {
int passwordMaxAgeInDays = getPasswordMaxAgeInDays(account);
GregorianCalendar expiryDate = new GregorianCalendar();
expiryDate.setTime(account.dateOfLastPasswdChange);
expiryDate.add(Calendar.DAY_OF_MONTH, passwordMaxAgeInDays);
return expiryDate;
}

int getPasswordMaxAgeInDays(UserAccount account) {
return account.getType().getPasswordMaxAgeInDays();
}

void printReport(UserAccount currentUser) {
boolean canPrint;
canPrint = currentUser.getType().canPrintReport();
if (!canPrint) {
throw new SecurityException("You have no right");
}
//打印报表.
}
}

注意到了吧,用一个对象代替类别,同样可以移除switch或者if-then-else-if。

总结一下类别代码的移除

要移动一些类别代码和switch表达式,有两种方法:
1.用基于同一父类的不同子类来代替不同的类别。
2.用一个类的不同对象来代替不同的类别。
当不同的类别具有比较多不同的行为时,用第一种方法。当这些类别的行为非常相似,或者只是差别在一些值上面的时候,用第二个方法。

普遍的代码异味
类别代码和switch表达式是比较普遍的代码异味。此外,还有其他的代码异味也很普遍。
下面是大概的异味列表:
代码重复
太多的注释
类别代码(type code)

switch或者一大串if-then-else-if
想给一个变量,方法或者类名取个好名字时,也怎么也取不好
用类似XXXUtil, XXXManager, XXXController 和其他的一些命名
在变量,方法或类名中使用这些单词“And”,“Or”等等
一些实例中的变量有时有用,有时没用
一个方法的代码太多,或者说方法太长
一个类的代码太多,或者说类太长
一个方法有太多参数
两个类都引用了彼此(依赖于彼此)

分享到:
评论

相关推荐

    Refactoring-to-pattern

    《Refactoring-to-Pattern》这本书探讨了重构(refactoring)与模式(pattern)之间的结合应用,旨在帮助软件开发者改进现有代码的设计,并通过模式来解决常见的设计问题。本书不仅介绍了重构的基本概念和技术,还...

    Refactoring-重构_改善既有代码的设计

    Refactoring-重构_改善既有代码的设计

    Python库 | JoeLiu_RF_Refactoring-1.0.2-py3-none-any.whl

    标题中的"Python库 | JoeLiu_RF_Refactoring-1.0.2-py3-none-any.whl"指的是一款名为JoeLiu_RF_Refactoring的Python库,版本为1.0.2。在Python生态系统中,库是可重复使用的代码模块,它们提供了各种功能,帮助...

    Professional-Refactoring-Workbook

    《Professional Refactoring Workbook》是一本专注于软件重构的专业书籍,旨在帮助开发者提升代码质量和可维护性。重构是软件开发过程中的一个重要环节,它涉及到在不改变软件外在行为的前提下,改进其内部结构,...

    refactoring-sql-applications

    2. **增强可读性和可维护性**:良好的数据库设计不仅能够提升查询效率,还能让代码更加清晰易懂,便于团队之间的协作。 3. **性能优化**:通过对查询逻辑和数据库结构的优化,可以显著提升系统的整体性能。 4. **...

    重构,改善既有代码的设计(中英双版)Martin Fowler 著)Addison+Wesley+-+Refactoring-Improving+the+Design+of+Existing+Code

    重构,改善既有代码的设计(中文版,Martin Fowler 著).pdf Addison+Wesley+-+Refactoring-Improving+the+Design+of+Existing+Code.pdf(英文版) 连个pdf ,分布为中,j英版的。

    Refactoring-Presentation-from-JavaOne

    ### Refactoring:改进现有代码的设计 #### 知识点概览 - **重构定义与目的** - **重构的背景** - **重构实例演示** - **重构步骤** - **重构工具介绍** - **重构最佳实践** #### 重构定义与目的 重构...

    vim-php-refactoring-toolbox:VIM Php重构工具箱

    VIMPHP重构工具箱重命名局部变量重命名类变量重命名方法提取用途提取常量提取类属性提取方法建立财产检测未使用的使用声明对齐分配创建setter和getter 记录所有代码安装 : Plug 'adoy/vim-php-refactoring-...

    DSP-refactoring-前端项目-vueflask.zip

    DSP_refactoring_前端项目_vueflask.zip DSP_refactoring_前端项目_vueflask.zip DSP_refactoring_前端项目_vueflask.zipDSP_refactoring_前端项目_vueflask.zip DSP_refactoring_前端项目_vueflask.zip

    Refactoring--Improving the Design of Existing Code(重构,英文清晰版,带完整书签)

    2. 重构的时机和重要性:重构不是一开始就进行的工作,而是在软件开发过程中的任何时间点,当发现代码变得难以理解和维护时,都可以采取重构的方式进行优化。 3. 重构的策略:重构通常按照一系列的小步骤进行,每个...

    refactoring - improving your exist coding

    2. **代码坏味道**:书中列举了许多常见的代码“坏味道”,如冗余代码、复杂的条件表达式、过长函数等,这些都是需要进行重构的标志。 3. **重构步骤**:详细解释了进行重构的一般流程,包括识别问题、设计改进方案...

    Refactoring - Improving the Design of Existing Code

    《重构:改善既有代码的设计》是马丁·福勒(Martin Fowler)的一本经典著作,它在IT领域中具有深远的影响,特别是在软件开发和维护方面。这本书详细阐述了如何通过重构来提升现有代码的设计质量,从而使得软件系统...

    refactoring-improving the design of existing code.pdf

    2. **重构模式**:Martin Fowler 提供了一系列的重构模式,每个模式都是一个微小的代码改进步骤,如“提取方法”、“替换类型检查为虚函数”等。这些模式旨在减少坏味道,改善代码结构。 3. **单元测试**:重构过程...

    gildedrose-ref-kata-ts:用Jest在TS中的GildedRose-Refactoring-Kata

    gildedrose-ref-kata-ts 与Jest合作的TS中的GildedRose-Refactoring-Kata。运行测试npm test 运行Node.js应用npx ts-node app.ts 3 2

    Refactoring2-free-chapter_cn.pdf

    Refactoring2-free-chapter_cn

    Refactoring-Improving the Design of Existing Code.pdf

    "Refactoring" was conceived in Smalltalk circles, but it wasn't long before it found its way into other programming language camps. Because refactoring is integral to framework development, the ...

    Refactoring-Improving the Design of Existing Code

    重构能够帮助开发者识别并消除代码中的“异味”(即那些不易理解或维护的代码),从而简化程序结构,使得代码更加清晰、易于扩展和修改。这对于长期维护大型软件项目尤为重要。 #### 三、重构技巧与案例 本书通过...

    重构-改善既有代码的设计 英文原版

    ### 重构:改善既有代码的设计 #### 知识点概览 1. **重构的概念与重要性** 2. **重构的作者与贡献者** 3. **重构的目的与目标受众** 4. **重构的方法与技巧** 5. **重构与单元测试的关系** 6. **重构中的设计模式...

Global site tag (gtag.js) - Google Analytics