重构对象-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节所讨论的,这是一个复杂领域,比起一般情况需要更繁重的机制。事务很有实用性,但是编写事务管理程序则超出了大多数程序员的职责范围。

相关内容

热门资讯

linux入门---制作进度条 了解缓冲区 我们首先来看看下面的操作: 我们首先创建了一个文件并在这个文件里面添加了...
C++ 机房预约系统(六):学... 8、 学生模块 8.1 学生子菜单、登录和注销 实现步骤: 在Student.cpp的...
A.机器学习入门算法(三):基... 机器学习算法(三):K近邻(k-nearest neigh...
数字温湿度传感器DHT11模块... 模块实例https://blog.csdn.net/qq_38393591/article/deta...
有限元三角形单元的等效节点力 文章目录前言一、重新复习一下有限元三角形单元的理论1、三角形单元的形函数(Nÿ...
Redis 所有支持的数据结构... Redis 是一种开源的基于键值对存储的 NoSQL 数据库,支持多种数据结构。以下是...
win下pytorch安装—c... 安装目录一、cuda安装1.1、cuda版本选择1.2、下载安装二、cudnn安装三、pytorch...
MySQL基础-多表查询 文章目录MySQL基础-多表查询一、案例及引入1、基础概念2、笛卡尔积的理解二、多表查询的分类1、等...
keil调试专题篇 调试的前提是需要连接调试器比如STLINK。 然后点击菜单或者快捷图标均可进入调试模式。 如果前面...
MATLAB | 全网最详细网... 一篇超超超长,超超超全面网络图绘制教程,本篇基本能讲清楚所有绘制要点&#...
IHome主页 - 让你的浏览... 随着互联网的发展,人们越来越离不开浏览器了。每天上班、学习、娱乐,浏览器...
TCP 协议 一、TCP 协议概念 TCP即传输控制协议(Transmission Control ...
营业执照的经营范围有哪些 营业执照的经营范围有哪些 经营范围是指企业可以从事的生产经营与服务项目,是进行公司注册...
C++ 可变体(variant... 一、可变体(variant) 基础用法 Union的问题: 无法知道当前使用的类型是什...
血压计语音芯片,电子医疗设备声... 语音电子血压计是带有语音提示功能的电子血压计,测量前至测量结果全程语音播报࿰...
MySQL OCP888题解0... 文章目录1、原题1.1、英文原题1.2、答案2、题目解析2.1、题干解析2.2、选项解析3、知识点3...
【2023-Pytorch-检... (肆十二想说的一些话)Yolo这个系列我们已经更新了大概一年的时间,现在基本的流程也走走通了,包含数...
实战项目:保险行业用户分类 这里写目录标题1、项目介绍1.1 行业背景1.2 数据介绍2、代码实现导入数据探索数据处理列标签名异...
记录--我在前端干工地(thr... 这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 前段时间接触了Th...
43 openEuler搭建A... 文章目录43 openEuler搭建Apache服务器-配置文件说明和管理模块43.1 配置文件说明...