1.7 当代码锁定在类中时,类层次结构很复杂
避免在OO中两次编写相同代码的一种方法涉及类继承。实际上,当预先知道系统的每个需求时,您可以这样设计类层次结构,即具有共同行为的类从基类派生。
此模式的一个示例如图1.15所示,重点放在我们的类图中处理Member和Librarian的部分。Member和Librarian都需要登录能力,他们从User类继承了这一能力。
到目前一切尚好。
![](https://img-blog.csdnimg.cn/20210619122124821.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTM3MTY4NTk=,size_16,color_FFFFFF,t_70)
但是,当在系统实现之后添加对系统的需求时,情况就完全不同了。
现在是星期一上午11点,离截止日期还有两天(周三午夜),南希给你打了一个紧急电话。
你不确定这是梦境还是现实。你捏一下自己,就会感觉到震动。这绝对是现实!
南希:“项目进行得怎么样了?”
你:“很好,南希。我们如期赶上了最后期限。运行我们上一轮的回归测试。”
南希:“太棒了!这意味着我们有时间在系统中添加一个小功能。对吗?”
你:“那要看你说的“小”是什么意思了。”
南希:“我们需要把VIP会员加入系统。”
你:“你说的VIP会员是什么意思?”
南希:“VIP会员是指允许自己向图书馆添加图书的会员。”
你:“嗯…”
南希:“什么?”
你:“这可不是个小变化!”
南希:“为什么?”
我也在问你南希曾经问过的问题:为什么把VIP会员加入你的系统不是一件小事?
毕竟,您已经编写了允许图书管理员向图书馆添加图书项目的代码:它位于Librarian::addBookItem()中。
是什么阻止您对VIP会员重复使用此代码?
原因是在面向对象中,代码作为方法锁定在类中。
TIP 在面向对象中,代码被锁定在类中。.
让我们看看您会如何处理客户在最后一刻提出的这一请求。
VIP会员是允许自己向图书馆添加图书项目的会员。
让我们将客户需求分解为两部分:
- VIP会员是会员
- 允许VIP会员自行向图书馆添加图书
当然,您需要一个新的类VIPMember。
对于需求#1,让VIPMember派生自Member听起来很合理
然而,处理要求#2更为复杂。我们不能使VIPMember派生自Librarian,因为VIPMember和Librarian之间的关系不是线性的:
- 一方面,VIP会员就像图书管理员,他们被允许添加图书项目
- 另一方面,VIP会员不像图书馆管理员,因为他们不被允许阻止会员或列出会员的图书借阅情况
问题是添加图书项目的代码锁定在Librarian类中。VIPMember类无法使用此代码
一种可能的解决方案是使Librarian::addBookItem()的代码对Librarian和VIPMember都可用,如图1.16所示。以下是对前一个类关系图的更改:
- 扩展User的UserWithBookItemRight基类
- 将addBookItem()从Librarian移到UserWithBookItemRight
- VIPMember和Library均使用BookItemRight扩展UserWithBookItemRight
![](https://img-blog.csdnimg.cn/20210619124728368.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTM3MTY4NTk=,size_16,color_FFFFFF,t_70)
这很艰难,但你能够按时处理(这要归功于你笔记本电脑前的一个白色夜晚)。您甚至可以在系统中包含新的测试,并再次运行回归测试。
您太兴奋了,以至于没有注意到类关系图中引入的菱形VIPMember(VIPMember用BookItemRight扩展了Member和UserWithBookItemRight,它们都扩展了User)
我们是周三上午10点,离截止日期还有14个小时,你打电话给南希,告诉她这个好消息:
你:“南希,我们可以按时把贵宾会员加入系统。”
南希:“太棒了!我告诉过你这只是个很小的特写”
你:“嗯…”
南希:“听着,反正我也要给你打电话的。我刚刚和我的商业伙伴开完会,我们意识到在发布之前我们还需要一个小功能。你能在截止日期前处理好吗?”
你:“再一次,这取决于你所说的“小”是什么意思。”
南希:“我们需要在系统中加入超级会员。”
你:“你说的超级会员是什么意思?”
南希:超级会员是指允许屏蔽和解锁会员的会员
你:“嗯…”
南希:“什么?”
你:“这可不是个小变化!”
南希:“为什么?”
与VIP会员一样,向系统添加超级成员需要更改类层次结构。图1.17显示了一种可能的解决方案。
![](https://img-blog.csdnimg.cn/20210619125544245.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTM3MTY4NTk=,size_16,color_FFFFFF,t_70)
超级会员的加入使系统变得过于复杂。你突然发现你的职业图中有3颗钻石:不是宝石,而是3颗致命的死亡钻石!
您试图通过将User类转换为接口并使用组合重于继承设计模式来避免菱形。
但是,随着最后期限的到来,你无法使用你大脑中的所有细胞。
事实上,这种复杂性使您无法在最后期限之前交付系统。您告诉自己应该使用组合而不是类继承。但现在已经太晚了。
TIP 在面向对象中,更喜欢组合而不是类继承。
你打电话给南希,让她在晚上10点,也就是截止日期前两个小时向她解释情况:
你:“听着,南希,我们真的已经尽力了,但是我们不能在截止日期前把超级会员加入系统”
南希:“别担心,我和我的生意伙伴决定推迟发布会。”
你:呼!
南希:“你觉得如果我们以后再增加其他小功能,你能按时处理吗?”
你:“是的。”
南希:“怎么可能呢?”
你:“我们将把系统从面向对象重构为面向数据”
南希:“什么是面向数据的?”
你:"它是一种神奇的调料,允许开发人员编写代码以更快地更改需求! "
TIP DO是一种神奇的调料,它允许开发人员编写代码以更快地更改需求!
读完这本书后,你将属于快乐的开发者社区,他们知道DO魔法酱的食谱。
1.8 总结
在本章中,我们探讨了OO增加系统复杂性的趋势,即OO系统往往难以理解。复杂性增加的根本原因与将代码和数据混合到对象中有关。
我们说明了OO的一些基本方面是如何增加OO系统的复杂性的。
面向对象编程的各个方面及其对增加系统复杂性的影响
方面 | 对复杂性增加的影响 |
---|---|
代码和数据混在一起 | 类往往涉及许多关系。 |
对象是可变的 | 阅读代码时的额外思考 |
对象是可变的 | 多线程环境中的显式同步 |
数据锁定在对象中 | 数据序列化是不容易的 |
代码锁定在类中 | 类层次结构很复杂 |
可以使用智能设计模式和该语言的高级功能来处理这种复杂性。这本书建议通过坚持面向数据的编程来处理这种复杂性,这是一种可以在OO和FP中实现的范例。