5. 重新组织对象(数据)

本文介绍了几种改进数据组织的方法,包括自封装字段、以对象取代数据值、将值对象改为引用对象、以对象取代数组、复制被监视数据、以子类取代类型码以及以State/Strategy取代类型码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

程序中处理的所有对象都是数据,所有抽象、算法都是为了表示数据、处理数据。下面我们就看看如何更好的组织数据。

[b]5.1 Self Encapsulate Field (自封装字段)[/b]
在同一个类中如果直接访问这个类的一个字段,则与字段之间的[color=red]耦合[/color]关系很深,为这个字段建立取值/设值函数,并且只以这些函数来访问字段。就如有一个原则“尽可能让所有的字段都是private的”,其实可变字段即使只通过getter/setter字段访问,也会有安全隐患,除非使用[b]保护性拷贝[/b]等手段。
[img]http://dl2.iteye.com/upload/attachment/0107/2746/d266d5c7-9bff-3f61-8653-fcb3e808ce81.png[/img]
在“[color=red]字段访问方式[/color]”这个问题上,存在两种截然不同的观点:
(1)在该变量定义所在的类中,可以自由访问它。
(2)即使在这个类中也应该只使用访问函数间接访问。
个人支持第一种,除非对于一个变量每次使用的都是经过一定[color=red]算法[/color]计算后的值,那么可以把这个算法包装起来。当然,智者见智。

[b]5.2 Replace Data Value with Object (以对象取代数据值)[/b]
有一个数据项,需要与其他数据和行为一起使用才有意义,就可以将数据项变成对象,封装数据和行为。
[img]http://dl2.iteye.com/upload/attachment/0107/2748/e93ca703-2e0c-33b9-82ac-7b62e3959766.png[/img]
开发初期,我们往往决定以[b]简单的数据项[/b]表示简单的情况。但是,随着开发的进行,这些简单数据项不再那么简单了。比如[b]一开始[/b]可能用一个String来表示“电话号码”,但[b]后来[/b]发现,电话号码需要“格式化”,“提取区号”之类的特殊行为。这样Duplicate Code和Feature Envy味道很快就会从代码中散发出来。当这些坏味道开始出现,就应该讲数据值变成对象了。这是一个演变的过程,随着需求的变化,组织的变化,我们代码的处理方式也要随之变化。

[b]5.3 Change Value to Reference (将值对象改为引用对象)[/b]
当从一个类衍生出许多彼此相等的实例,我们希望将他们替换为同一个对象。
[img]http://dl2.iteye.com/upload/attachment/0107/2750/ba36c1cc-9287-3b66-8564-74bf95d2c085.png[/img]
[color=red]也就是尽可能少的创建对象。[/color]如果多个对象里包含的是同一个对象(比如Date),那么就可以采用引用而不需要为每个对象创建一个对象。这可以通过使用静态工厂方法或单例模式等来实现。

[b]5.4 Replace Array with Object (以对象取代数组)[/b]
如果有一个数组,其中的元素各自代表不同的东西。那么可以以对象来替代数组。对于数组中的每个元素变成对象的一个字段。
[img]http://dl2.iteye.com/upload/attachment/0107/2752/aa64ab7d-b922-36dd-b182-57fc706057c0.jpg[/img]
相信很少有人会犯这种错误。[color=red]一个数组的元素一般都是同一个范畴的数据,否则结构会很乱[/color]。

[b]5.5 Duplicate Observed Data (复制“被监视数据”)[/b]
比如有一些领域数据置身与GUI控件中,可以把这些领域数据单独组成一个对象,利用观察者模式。这样可以解耦数据与展示,对相同的数据可以有不同的展示方式。这也是基于良好的分层结构,将业务处理逻辑与界面展示层分开。
[img]http://dl2.iteye.com/upload/attachment/0107/2754/aa892ef3-4d36-3f82-b989-ab84a3cc17ba.png[/img]

5.6 Replace type code with subclasses (以子类取代类型码)
如果有一个[color=red]不可变的类型码,而且它会影响类的行为(根据不同的值执行不同的动作)[/color]。可以以子类取代这个类型码。
[img]http://dl2.iteye.com/upload/attachment/0107/2758/379f0ec9-8bcc-3ac3-82bb-d528f1f6a9e1.png[/img]

一般来说,[b]这种情况的标志就是像switch这样的条件表达式[/b]。可能有两种表现形式:switch语句或if-else结构。不论哪种形式,都是检查类型码值,并[color=red]根据不同的值执行不同的动作[/color],这时可以以replace conditional with polymorphism进行重构。
为了能顺利进行重构,首先应该将类型码替换为可拥有多态行为的[b]继承体系[/b]。这样的一个继承体系应该以类型码的宿主类为基类,并针对每一种类型码各建立一个子类。然后把不同的行为分别放到对应的子类中,选择不同的子类型执行不同的行为来代替以前的switch判断。
但有以下两种情况[color=red]不能[/color]这么做:
(1)类型码值在对象建立之后发生了改变。
(2)由于其他原因,类型码宿主类已经有了子类。这就需要使用replace type code with State/Strategy。这其实是“继承和组合”之间的抉择,[b]当然还是优先推荐组合。[/b]
[color=red][b]
特别注意:[/b][/color]并不是所有的只要出现switch就需要替换掉类型码。只是当类型码[color=red]重复出现[/color]决定类的行为,重复出现根据type code来做不同的行为时,才需要动手重构。毕竟需要判断的地方是不能省的。

[b]5.7 replace type code with State/Strategy (以State/Strategy取代类型码)[/b]
有一个类型码,它会影响类的行为,但无法通过继承手法消除它,还可以使用状态对象取代类型码。
[img]http://dl2.iteye.com/upload/attachment/0107/2760/38f38c99-1860-3c20-a350-3cfc4491ca30.png[/img]

State模式和Strategy模式非常相似,因此无论选择其中哪个,重构过程都是相同的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值