重构对象-Extract Class提炼类三
迪丽瓦拉
2025-05-30 01:16:35
0次

重构对象-Extract Class提炼类三

1.提炼类

1.1.使用场景

某个class做了应该由两个classes做的事

你也许听过类似这样的教诲:一个class应该是一个清楚的抽象(abstract),处理一些明确的责任。
但是在实际工作中,class会不断成长扩展。你会在这儿加入一些功能,在那儿加入一些数据。
给某个class添加一项新责任时,你会觉得不值得为这项责任分离出一个单独的class。
于是,随着责任不断増加,这个class会变得过份复杂。很快,你的class就会变成一团乱麻

1.2.如何做

  • 决定如何分解类所负的责任。
  • 建立一个新类,用以表现从旧类中分离出来的责任。
  • 如果旧类剩下的责任与旧类名称不符,为旧类更名。
  • 建立“从旧类访问新类”的连接关系。
  • “有可能需要一个双向连接。但是在真正需要它之前,不要建立“从新类通往旧类”的连接。
  • 对于你想搬移的每一个字段,运用Move Field (146)搬移之。
  • 每次搬移后,编译、测试。
  • 使用Move Method (142)将必要函数搬移到新类。先搬移较低层函数(也就是“被其他函数调用”多于“调用其他函数”者),再搬移较高层函数。
  • 每次搬移之后,编译、测试。
  • 检查,精简每个类的接口。
  • 如果你建立起双向连接,检查是否可以将它改为单向连接。
  • 决定是否公开新类。如果你的确需要公开它,就要决定让它成为引用对象还是不可变的值对象。

1.3.示例

让我们从一个简单的Person class开始

 class Person...public String getName() {return _name;}public String getTelephoneNumber() {return ("(" + _officeAreaCode + ") " + _officeNumber);}String getOfficeAreaCode() {return _officeAreaCode;}void setOfficeAreaCode(String arg) {_officeAreaCode = arg;}String getOfficeNumber() {return _officeNumber;}void setOfficeNumber(String arg) {_officeNumber = arg;}private String _name;// 电话号码区号private String _officeAreaCode;// 电话号码private String _officeNumber;

在这个例子中,我可以将「与电话号码相关」的行为分离到一个独立class中。首 先我耍定义一个TelephoneNumber class来表示「电话号码」这个概念:

 class TelephoneNumber {}

然后,我要建立从Person到TelephoneNumber的连接

 class Personprivate TelephoneNumber _officeTelephone = new TelephoneNumber();

我运用Move Field 移动一个值域

// 将_areaCode属性和对应的方法移到新类class TelephoneNumber {String getAreaCode() {return _areaCode;}void setAreaCode(String arg) {_areaCode = arg;}private String _areaCode;}class Person...public String getTelephoneNumber() {return ("(" + getOfficeAreaCode() + ") " + _officeNumber);}String getOfficeAreaCode() {return _officeTelephone.getAreaCode();}void setOfficeAreaCode(String arg) {_officeTelephone.setAreaCode(arg);}

然后我可以移动其他值域,并运用Move Method 将相关函数移动到TelephoneNumber class中

 class Person...public String getName() {return _name;}public String getTelephoneNumber(){return _officeTelephone.getTelephoneNumber();}TelephoneNumber getOfficeTelephone() {return _officeTelephone;}private String _name;private TelephoneNumber _officeTelephone = new TelephoneNumber();class TelephoneNumber...public String getTelephoneNumber() {return ("(" + _areaCode + ") " + _number);}String getAreaCode() {return _areaCode;}void setAreaCode(String arg) {_areaCode = arg;}String getNumber() {return _number;}void setNumber(String arg) {_number = arg;}private String _number;private String _areaCode;

下一步要做的决定是:要不要对用户公开这个新类?我可以将Person中与电话号码相关的函数委托至TelephoneNumber,从而完全隐藏这个新类;也可以直接将它对用户公开。我还可以将它公开给部分用户(位于同一个包中的用户),而不公开给其他用户。
如果我选择公开新类,就需要考虑别名带来的危险。如果我公开了Telephone-Number,而有个用户修改了对象中的_areaCode字段值,我又怎么能知道呢?而且,做出修改的可能不是直接用户,而是用户的用户的用户。

面对这个问题,我有下列几种选择。
1.允许任何对象修改TelephoneNumber对象的任何部分。这就使得TelephoneNumber对象成为引用对象,于是我应该考虑使用Change Value to Reference (179)。这种情况下,Person应该是TelephoneNumber的访问点。
2.不许任何人不通过Person对象就修改TelephoneNumber对象。为此,我可以将TelephoneNumber设为不可修改的,或为它提供一个不可修改的接口。
3.另一个办法是:先复制一个TelephoneNumber对象,然后将复制得到的新对象传递给用户。但这可能会造成一定程度的迷惑,因为人们会认为他们可以修改TelephoneNumber对象值。此外,如果同一个TelephoneNumber对象被传递给多个用户,也可能在用户之间造成别名问题。

Extract Class (149)是改善并发程序的一种常用技术,因为它使你可以为提炼后的两个类分别加锁。如果你不需要同时锁定两个对象,就不必这样做。这方面的更多信息请看Lea[Lea]的3.3节。
这里也存在危险性。如果需要确保两个对象被同时锁定,你就面临事务问题,需要使用其他类型的共享锁。正如Lea[Lea]的8.1节所讨论的,这是一个复杂领域,比起一般情况需要更繁重的机制。事务很有实用性,但是编写事务管理程序则超出了大多数程序员的职责范围。

相关内容

热门资讯

AI做攻略、“双向奔赴”……国... 本文转自:人民网人民网记者 许维娜今年国庆假期,AI(人工智能)成了不少年轻人的“旅行搭子”。行前做...
合肥的这个“梗”, 翻篇了 社交平台的评论区,曾长期流传一句专属于合肥的黑色幽默。有人问合肥哪里好玩,本地网友半开玩笑地回复:合...
7万+旅游中高层都在的群,今天... 做旅游这行,资源卡位、渠道互通、项目落地——往往就差一个“对的机会”。我们的行业社群已聚集70,00...
一场帐篷音乐节,如何让井研从“... 帐篷音乐节现场封面新闻记者 周洪攀10月的研溪湿地,晚风里裹着柑橘的清甜与民谣的回响。夜幕降临,10...
白鹤翩翩落星愿,上海迪士尼度假... 10月9日,世界候鸟日前夕,上海迪士尼度假区星愿公园的湖风里多了一分白鹤的灵动。三座白鹤雕塑正式揭幕...
成都锦江边赏初秋枫叶飘飘 目前,成都锦江边国华街枫叶树红了。近年来,国华街附近的几条街都栽种了红枫树,是城区较集中看枫叶的点位...
GBTA:商务旅行正在复苏,但... 亚太正在成为全球商务旅行增长的重要引擎。在经历疫情后的快速反弹之后,全球商务旅行正在进入一个新的增长...
从景区焕新到节赛引流 邹城双节... 齐鲁网·闪电新闻10月9日讯 中秋、国庆“双节”假期,邹城市文化和旅游局提前谋划、周密部署,立足自身...
江山如此多娇 且看石楼今朝 金秋十月,黄河岸畔秋意盎然,丰收盛会喜迎国庆。本次石楼国庆文旅活动以“江山如此多娇,且看石楼今朝”为...
海南国庆假期接待游客475.4... 2026年国庆假期,海南接待游客475.40万人次,较2025年增长13.1%,较2024年增长15...