重构改善既有代码的设计-学习(一):封装

1、封装记录(Encapsulate Record)

一些记录性结构(例如hash、map、hashmap、dictionary等),一条记录上持有什么字段往往不够直观。如果其使用范围比较宽,这个问题往往会造成许多困扰。所以,记录性结构应该被封装成为一个类。

例如:

javascript 复制代码
organization = {name: "Acme Gooseberries", country: "GB"};

应该被重构为:

javascript 复制代码
class Organization {
    constructor(data) {
        this._name = data.name;
        this._country = data.country;
    }
    get name() {return this._name;}
    set name(arg) {this._name = arg;}
    get country() {return this._country;}
    set country(arg) {this._country = arg;}
}

2、封装集合(Encapsulate Collection)

我们通常鼓励封装,但封装时人们常常犯一个 错误:只对集合变量的访问进行了封装,但依然让取值函数 返回集合本身。这使得集合的成员变量可以直接被修改,而封装它的类则全然不知,无法介入。

修改的方式是,在类上提供一些修改集合的方法(通常是"添加"、"删除"等),这样就可以使对集合的修改必须经过类。

例如:

javascript 复制代码
class Person {
    get courses() {
        //这里直接把对象本身返回了,在哪里偷偷摸摸被改了都不知道
        return this._courses;
    }  
    set courses(aList) {this._courses = aList;} 
}

应该改为:

javascript 复制代码
class Person {
    get courses() {return this._courses.slice();}

    // 提供修改对象的方法,想改,就必须用类的方法
    addCourse(aCourse) { ... }
    removeCourse(aCourse) { ... }
}

3、以对象取代基本类型(Replace Primitive with Object)

不要执着于用基本类型,应该把一些数据封装成对象。

例如,本身"图案"这个字段可能只是一个字符串,用来存储一个链接,但后来随着业务的逐渐复杂,开始有尺寸、文案等等,那他们就应该被抽取出来,放在一个类里面。

4、以查询取代临时变量(Replace Temp with Query)

如果一个变量只声明一次之后不再被改变,那就别再声明变量了,而是直接用一个查询操作取代他。比如:

javascript 复制代码
// 这里的变量只用了一次
const basePrice = this._quantity * this._itemPrice;

if (basePrice > 1000)
    return basePrice * 0.95;
else
    return basePrice * 0.98;

应该变为 :

javascript 复制代码
// 构建一个查询用来替代那个变量
get basePrice() {this._quantity * this._itemPrice;}

...

if (this.basePrice > 1000)
    return this.basePrice * 0.95;
else
    return this.basePrice * 0.98;

5、提炼类(Extract Class)

在实际工作中, 类会不断成长扩展。设想你有一个维护大量函数和数据的类。这样的类往往 因为太大而不易理解。此时你需要考虑哪些部分可以分离出 去,并将它们分离到一个独立的类中。如果某些数据和某些 函数总是一起出现,某些数据经常同时变化甚至彼此相依, 这就表示你应该将它们分离出去。

6、内联类(Inline Class)

当一个类因为某种原因开始萎缩(可能是因为一些重构动作移走了这个类的责任),不再承担足够责任,那么他不再有单独存在的理由。应该将"萎缩类"塞进另一个类中。"另一个类"应该是和"萎缩类"关联最紧密的那个类。

7、隐藏委托关系(Hide Delegate)

如果某些客户端先通过服务对象的字段得到另一个对象(受托类),然后调用后者的函数,那么客户就必须知晓这一层委托关系。万一受托类修改了接口,变化会波及通过服务对象使用它的所有客户端。我可以在服务对象上放置一个简单的委托函数,将委托关系隐藏起来,从而去除这种依赖。这么一来,即使将来委托关系发生变化,变化也只会影响服务对象,而不会直接波及所有客户端。

例如:现在有两个类,代表"人"的Person和代表"部门"的Department。有些客户端希望知道某人的经理是谁,为此,它必须先取得Department对象。

javascript 复制代码
// 客户端必须知道 要调部门 才能知道经理
manager = aPerson.department.manager;

这样的编码就对客户端揭露了Department的工作原理,于是客户知道:Department负责追踪"经理"这条信息。如果对客户隐藏Department,可以减少耦合。为了这一目的,我在Person中建立一个简单的委托函数。

javascript 复制代码
class Person {
    get manager() {
        // 将部门这层封装起来,使客户端不感知
        return this.department.manager;
    }
}

manager = aPerson.manager;

8、移除中间人(Remove Middle Man)

刚刚谈到了"封装受托对象"的好处。但是这层封装也是有代价的。每当客户端要使用受托类的新特性时,你就必须在服务端添加一个简单委托函数。随着受托类的特性(功能)越来越多,更多的转发函数就会使人烦躁。服务类完全变成了一个中间人,此时就应该让客户直接调用受托类。

很难说什么程度的隐藏才是合适的。6个月前恰如其分的封装,现今可能就显得笨拙。重构的意义就在于:你永远不必说对不起------只要把出问题的地方修补好就行了。

做法就是把7中的例子反过来......

9、替换算法(Substitute Algorithm)

改进算法。

相关推荐
文火冰糖的硅基工坊6 小时前
[嵌入式系统-83]:算力芯片的类型与主流架构
人工智能·重构·架构
文火冰糖的硅基工坊15 小时前
《投资-111》价值投资者的认知升级与交易规则重构 - 价值投资的思维模式:穿越表象,回归本质
重构·架构·投资·投机
文火冰糖的硅基工坊1 天前
《投资-114》价值投资者的认知升级与交易规则重构 - 从大规模分工的角度看,如何理解“做正确的事”,即满足下游正确的需求
重构·投资·投机
文火冰糖的硅基工坊2 天前
《投资-107》价值投资者的认知升级与交易规则重构 - 上市公司的估值,估的不是当前的净资产的价值,而是未来持续赚钱的能力,估的是公司未来所有赚到钱的价值。
重构·架构·投资·投机
文火冰糖的硅基工坊2 天前
《投资-99》价值投资者的认知升级与交易规则重构 - 什么是周期性股票?有哪些周期性股票?不同周期性股票的周期多少?周期性股票的买入和卖出的特点?
大数据·人工智能·重构·架构·投资·投机
文火冰糖的硅基工坊2 天前
《投资-93》价值投资者的认知升级与交易规则重构 - 衡量公司价值的本质是在公司的整个存续期间能够创造多少自由现金流,而不是当下有多少现金流。
重构·架构·产业链
lpfasd1232 天前
从OpenAI发布会看AI未来:中国就业市场的重构与突围
人工智能·重构
yueyuebaobaoxinx3 天前
2025 AI 落地元年:从技术突破到行业重构的实践图景
人工智能·重构
文火冰糖的硅基工坊3 天前
《投资-88》价值投资者的认知升级与交易规则重构 - 第三层:估值安全边际,“再好的公司,如果买贵了,也会变成一笔糟糕的投资。”
安全·重构
文火冰糖的硅基工坊3 天前
《投资-78》价值投资者的认知升级与交易规则重构 - 架构
大数据·人工智能·重构