《面向对象的思考过程(原书第4版)》一2.1 清楚接口和实现之间的区别

本节书摘来自华章出版社《面向对象的思考过程(原书第4版)》一书中的第2章,第2.1节,[美] 马特·魏斯费尔德(Matt Weisfeld) 著黄博文 译更多章节内容可以访问“华章计算机”公众号查看。

2.1 清楚接口和实现之间的区别

正如第1章所示,构建健壮的面向对象设计的关键之一是理解接口和实现之间的不同。因此,当设计类时,应该向用户暴露什么、隐藏什么是非常重要的。而封装与生俱来的数据隐藏机制可以对用户隐藏不必要的数据。
小心不要混淆接口与图形化用户接口(graphical user interface,GUI)这两个概念。虽然GUI名称本身包含了接口这个单词,但是我们所说的接口是一种更通用的术语,它并不局限于图形化接口。
还记得第1章中的烤面包机例子吗?烤面包机(或相似功能的设备)需要插入一个接口,即电源插座,如图2-1所示。所有需要电的设备都需要符合正确的接口,即电源插座。烤面包机不需要知道插座的任何实现,或者电是如何产生的。对所有烤面包机而言,它不关心电是燃煤电厂还是核工厂生产的,只关心具体接口可以正确、安全的工作就行。

还有一个汽车的例子。司机和汽车之间具有很多接口,比如方向盘、油门踏板、刹车和点火开关。先抛开美观问题,大多数人开车时的主要关注点是启动、加速、停止、转向等。大部分司机中极少关心那些眼睛看不到的组件(实现)。事实上,大多数人根本就无法识别出某些组件,比如催化器和垫圈。然而,任何司机必须清楚如何使用油门踏板,因为这是一个通用接口。制造厂商为车安装一个标准的油门踏板,确保市场上的目标客户能够使用这个系统。
然而,如果制造厂商决定安装一个操纵杆来代替油门踏板,大多数司机会不习惯这点,那么这个车型销量不会很广(只能博得一些喜欢打破常规的人的喜爱)。而如果制造厂商替换了汽车的引擎(改变了部分实现),只要没有改变性能和外观,大多数司机都不会注意到这点。
只要接口不变,可替换的引擎必须严格遵守接口。把四缸发动机替换为八缸发动机可能会改变接口规则,导致需要使用该引擎接口的其他组件不能正常工作。而在发电厂例子中从交流电(AC)改成直流电(DC)也会影响接口规则。
引擎属于实现,方向盘属于接口。改变实现不会对司机造成影响,而改变接口则会。司机会注意到方向盘的外观变化,即使改变可能很微小。必须强调的是,对引擎的改变不应让司机注意到,否则就会破坏接口。例如,改变引擎可能会丧失动力,这会引起驾驶者的注意,事实上是改变了接口。
用户能看到什么
接口与类直接相关。终端用户通常看不到任何类,只会看到GUI或者命令行。然而,程序员会接触类接口。重用类的前提是有人已经写了一个类。因此,程序员必须知道如何正确使用这个类。程序员需要将很多类组合成一个系统,所以需要理解类的接口。因此,本章中讨论用户时,我们主要指设计人员和开发人员,没必要引入终端用户。因此,当我们在此上下文中讨论接口时,我们在讨论类接口,而不是GUI。
正确地设计类时要注意两部分,即接口和实现。

2.1.1 接口

呈现给终端用户的服务暴露了接口。最佳实践中,只呈现给用户需要的服务。当然,不同的人对用户需要什么服务可能持有不同看法。如果你把10个人放到一个屋子让他们每个人进行独立设计,你可能会得到10份完全不同的设计,而且这些设计都没什么错。然而,作为一个通用的规则,一个类的接口应该只包含需要用户知道的东西。在烤面包机例子中,用户只需要知道烤面包机必须插到接口上(这个例子中接口就是电源插座)以及如何操作烤面包机本身。
识别用户
当设计类时最重要的考虑就是识别类的读者(或用户)。

2.1.2 实现

实现细节对于用户是隐藏的。我们必须时刻牢记关于实现的一个目标,那就是修改实现不需要变动用户代码。看起来可能有些困惑,但该目标是设计问题的核心所在。如果对接口的设计是恰当的,那么即使调整了实现,用户调用代码也无需任何改变。请记住,接口包含了调用方法及返回值的语法。如果没有改变接口,用户无需关心是否修改了实现。程序员只关心使用相同的语法能够获得相同的值即可。
我们可以拿手机来举例。打电话的接口很简单,我们只需拨一个号码或者从地址簿中选取一个联系人。如果供应商更新了软件,它不会改变你打电话的方式。无论如何修改实现,打电话的接口始终保持不变。然而,如果我的电话区号变了,供应商也有可能会修改接口。基础接口变了(比如电话区号变了),需要用户改变行为。商家希望保持这样的修改最小化,因为有些用户不喜欢这种改变,或者不想这样的麻烦。
再说烤面包机的例子。只要接口一直是电源插座,具体实现就可能会从一个燃煤电厂切换为核电站,但这不会影响烤面包机。这里有一个非常重要的规则,即煤电厂和核电厂都必须要遵循接口规格。如果煤电厂提供交流电(AC),而核电厂提供直流电(DC),就会出问题。用户和实现都必须要遵循接口规格。

2.1.3 一个接口/实现示例

我们来创建一个简单读取数据库的类。我们将编写一些Java代码,这些代码会从数据库中获取记录。正如之前讨论的一样,进行任何设计时,识别终端用户一直是最重要的问题。你可能需要做一些场景分析,一起对终端用户做一些引导性访谈,然后会列出这个项目的需求。接下来是我们对这个数据库阅读器的需求:
必须能打开数据库的连接。
必须能关闭数据库的连接。
必须能将游标指向数据库中的第一条记录。
必须能把游标指向数据库中的最后一条记录。
必须能得到数据库中的记录条数。
必须能知道当前数据库是否仍有记录(即我们当前是否指向的是最后一条记录)。
必须能够根据键值把游标指向特定的记录。
必须能够获取指定键值的记录。
必须能基于当前游标的位置获取下一条记录。
根据以上需求,可以开始尝试设计一个读取数据库的类,为终端用户设计可能的接口。
在本例中,读取数据库的类仅提供给想使用数据库的程序员。因此接口本质上是程序员想要使用的应用程序编程接口(application-programming interface,API)。这些方法其实包装了数据库系统暴露的功能。为什么要这么做?本章后面会详细讨论该问题。简短的回答是我们需要定制数据库功能。例如,我们必须处理对象从而可以将它们写入关系型数据库中。编写这样的中间件对于设计和编码而言可能过于简单,但这是封装特性的真实示例。最重要的是,如果我们想替换数据库引擎,则无需修改大量代码。
图2-2展示了一个类图,表示了DataBaseReader类的潜在接口。
请注意,该类中的方法都是公共方法(请记住,靠近方法名的加号表示该方法是一个公共接口)。而且这里只展示了接口,没有展示任何实现。请花一分钟来确定这个类图是否能大体满足上面列出的项目需求。如果你之后发现该类图没有满足所有需求也没关系。因为面向对象设计是一个迭代的过程,所以你无须一开始就保证它绝对正确。
公共接口

请记住,如果一个方法是公共方法,那么程序员就可以访问它,因此可以认为它是类的接口。请不要混淆术语“接口”与Java和.NET中的关键字interface。稍后的章节会讨论关键字interface。
对于我们列出的每个需求,需要有对应的方法来提供对应的功能。现在需要考虑一些问题:
实际使用此类时,作为程序员的你需要了解与其有关的其他事情吗?
需要知道内部数据库代码是如何打开数据库的吗??
需要知道内部数据库代码如何对应一条具体记录的物理位置吗?
需要知道内部数据库代码如何确定是否还有剩余记录吗?
回答是都不需要!你不需要知道任何信息。只需要关心能获取到正确的值并且操作没有出错。事实上,程序员更喜欢对具体实现再做一层抽象。应用程序将使用你自定义的类来操作数据库,而这些自定义的类则负责调用相应的数据库API。
最小接口
在极限情况下,保证最小接口是刚开始不给用户提供任何公共接口。当然,这样的类是无用的。然而,这强制用户主动找你说:“我需要这个功能。”然后你们可以协商。这样保证你只在需要的情况下增加接口,绝不要假设用户需要什么东西。
创建包装对象看起来有些小题大做,但编写这样的类有很多好处。比如,当今市场上有很多中间件产品使用了包装对象的技术。考虑把对象映射到关系型数据库的问题。一些面向对象的数据库可能非常适合面向对象的应用程序。然而,一个现存的问题是大多数公司有数年的遗留数据存放在关系型数据库中。如果公司既需要保留关系型数据库中的数据,又要拥抱面向对象技术,那么如何处理这个断层?
首先,可以把所有遗留的关系型数据转换到一个全新的面向对象的数据库中。然而,任何遭受过严重的(也是长期的)数据转换之痛的人都知道不能这样做。这种转换往往会耗费大量的时间和精力,到头来系统还是不能正常工作。
其次,可以使用中间件产品把应用程序代码中的对象平滑地映射到关系型模型中。只要关系型数据库依旧盛行,这种方案相比之前就要更好些。有些人可能会认为面向对象的数据库比关系型数据库更方便持久化对象。事实上,很多开发系统都能提供这样平滑转换的服务。
对象持久化
对象持久化,意思即保存对象的状态以便稍后可以恢复和使用,因为没有持久化的对象在其生命周期之外就会被销毁掉。例如,对象的状态可以保存在数据库中。
在当前的业务环境下,关系型数据库和对象建立映射关系是一个非常好的方案。很多公司集成了这样的中间件技术。比如一个公司拥有一个网站作为前端接口,而数据存放在大型机中,这很常见。
如果创建一个完全面向对象的系统,使用面向对象的数据库是个可行的选项(也拥有更好的性能)。不过面向对象的数据库的发展历程与面向对象语言的发展历程比起来差远了。
独立应用程序
甚至从头创建一个全新的面向对象的应用程序,也很难完全避免遗留数据。新创建的面向对象的应用程序也不会是独立的应用程序,因为它很可能需要获取存储在关系型数据库(或者其他的数据存储设备)中的数据。
让我们回到数据库例子中。图2-2只展示了该类的公共接口,除此之外别无其他。当完成该类后,它可能会包含更多的方法,当然也会包含一些属性。不过作为使用该类的程序员无须知道任何私有方法和属性的相关信息。你肯定无需了解公共方法中的具体代码,只需简单知道如何与这些接口交互即可。
公共接口的实现代码会是什么样子呢(假设我们使用的是Oracle数据库)?我们来看看open()方法:

在这个例子中,如果你是程序员,会发现open方法需要String类型作为参数。Name代表了需要传入的数据库文件,但我们并不关心它如何映射到一个具体的数据库。使用接口只需知道如何使用这个方法,不用关心方法细节,这正是接口的美好之处!
为了迷惑用户,我们修改数据库的实现。昨晚我们把Oracle数据库中的全部数据迁移到了一个SQLAnywhere数据库中(我们忍耐了这巨大而漫长的痛苦)。虽然总共花费了好几个小时,但最终我们做到了。
现在代码如下所示:

今天早上竟然没有听到任何用户的抱怨。这是因为虽然改变了实现,但并未改变接口!用户关心的调用接口仍然是相同的。修改实现代码可能需要大量的工作(即使只改了一行代码,整个模块都需要重新编译),但使用了Da-taBaseReader类的应用程序代码则无需任何修改。
代码重新编译
动态加载的类是在运行时加载的,并不是静态链接到一个可执行文件。当使用动态加载的类(比如Java和.NET)时,无需重新编译使用者的类。在静态链接语言(比如C++)中,引入新类需要一个链接。
分离用户接口与实现,能省却大量头痛的事。在图2-3中,数据库的具体实现对终端用户来说是透明的,终端用户只能看到接口。

时间: 2024-07-29 04:48:35

《面向对象的思考过程(原书第4版)》一2.1 清楚接口和实现之间的区别的相关文章

《社交网站界面设计(原书第2版)》——1.9 为设备之间的空间进行设计

1.9 为设备之间的空间进行设计 移动设备带来机遇,也带来危机,迫使设计师找到新的方式在一个很有挑战的屏幕大小范围内呈现信息.通过响应式网页设计和应用程序开发,我们为每种设备都做了很多设计工作.然而,对于我们在技术方面的进步来说,一种新的设计危机已经形成.这个危机更多是围绕着我们所使用设备的数量,而不是种类. 随着我们在日常生活中所使用设备的增加,我们常常会被困在这些设备中.随着设备的增加,新的机会不再是关于设计每个屏幕,而是关于设计这些设备之间的交互--通常根本不会使用屏幕.这些设备的间隙中存

《面向对象的思考过程(原书第4版)》一1.1 基本概念

本节书摘来自华章出版社<面向对象的思考过程(原书第4版)>一书中的第1章,第1.1节,[美] 马特·魏斯费尔德(Matt Weisfeld) 著 1.1 基本概念 本书主要目标是让你学会思考如何将面向对象概念应用于面向对象的系统设计中.历史上定义面向对象的语言拥有以下特点:封装(encapsulation).继承(inheritance)和多态(polymorphism).因此,如果设计一门语言时没有完全实现以上特性,那么通常我们认为该语言不是完全面向对象的.即使实现了这三点,我也往往会加入组

《面向对象的思考过程(原书第4版)》一3.5 多重继承

本节书摘来自华章出版社<面向对象的思考过程(原书第4版)>一书中的第3章,第3.5节,[美] 马特·魏斯费尔德(Matt Weisfeld) 著黄博文 译更多章节内容可以访问"华章计算机"公众号查看. 3.5 多重继承 第7章会讲述有关继承的更多细节.然而,这里很适合讨论多重继承.多重继承是类设计中最强大也最具挑战的一个方面. 顾名思义,多重继承允许一个类继承自多个类.实际看起来这是个好主意.对象都应该模拟现实世界,不是吗?现实世界中存在很多多重继承的例子.父母是多重继承的

《面向对象的思考过程(原书第4版)》一 第3章 高级的面向对象概念

本节书摘来自华章出版社<面向对象的思考过程(原书第4版)>一书中的第3章,第3.1节,[美] 马特·魏斯费尔德(Matt Weisfeld) 著黄博文 译更多章节内容可以访问"华章计算机"公众号查看. 第3章 高级的面向对象概念 第1章和第2章讲述了面向对象的基本概念.在开始学习关于构建面向对象系统的一些具体设计问题之前,我们需要更进一步了解面向对象的一些概念,比如构造函数.操作符重载以及多重继承.我们也会讲述错误处理技术以及面向对象的设计中作用域的重要性.其中一些概念可能

《面向对象的思考过程(原书第4版)》一1.3 过程式编程与面向对象编程

本节书摘来自华章出版社<面向对象的思考过程(原书第4版)>一书中的第1章,第1.3节,[美] 马特·魏斯费尔德(Matt Weisfeld) 著黄博文 译更多章节内容可以访问"华章计算机"公众号查看. 1.3 过程式编程与面向对象编程 在我们深入了解面向对象开发的优势之前,先考虑一个更基本的问题:究竟什么是对象?这既是一个复杂的问题,也是一个简单的问题.它复杂是因为学习任何一种软件开发方法论都非易事.它简单是因为人们已经在按对象的方式进行思考.例如,当你看到一个人,你会把他

《面向对象的思考过程(原书第4版)》一1.6 究竟什么是类

本节书摘来自华章出版社<面向对象的思考过程(原书第4版)>一书中的第1章,第1.6节,[美] 马特·魏斯费尔德(Matt Weisfeld) 著黄博文 译更多章节内容可以访问"华章计算机"公众号查看. 1.6 究竟什么是类 简单地说,类是对象的蓝图.当你实例化一个对象时,你基于类来构建这个对象.事实上,尝试解释类和对象实际上是一个先有鸡还是先有蛋的问题.描述类时不使用术语对象是非常困难的,反之亦然.例如,个人自行车是一个对象.然而,有人已经创建了蓝图(就是类)来建造这个自行

《面向对象的思考过程(原书第4版)》一1.10 多态

本节书摘来自华章出版社<面向对象的思考过程(原书第4版)>一书中的第1章,第1.10节,[美] 马特·魏斯费尔德(Matt Weisfeld) 著黄博文 译更多章节内容可以访问"华章计算机"公众号查看. 1.10 多态 多态是一个希腊词,字面上理解为许多形状.尽管多态与继承是紧耦合的关系,但它通常单独作为面向对象技术中最强大的优点之一.当向一个对象发送一个消息时,该对象必须定义一个方法来响应这个消息.在继承体系图中,所有的子类从它们的超类中继承接口.然而,由于每个子类是单独

《面向对象的思考过程(原书第4版)》一第1章 面向对象的概念简介

本节书摘来自华章出版社<面向对象的思考过程(原书第4版)>一书中的第1章,[美] 马特·魏斯费尔德(Matt Weisfeld) 著 第1章 面向对象的概念简介 很多程序员其实并不知道在20世纪60年代就已出现面向对象的软件开发方式.尽管受欢迎的面向对象的编程语言(例如Smalltalk和C++)已被广泛使用,但直到90年代中后期面向对象范式才开始快速发展.面向对象方法论的兴起恰逢互联网作为商业及娱乐平台之际.总之,对象借助网络能良好协作.后来显然互联网存活下来,而面向对象的技术已经在开发新的

《面向对象的思考过程(原书第4版)》一1.8.1 接口

本节书摘来自华章出版社<面向对象的思考过程(原书第4版)>一书中的第1章,第1.8节,[美] 马特·魏斯费尔德(Matt Weisfeld) 著黄博文 译更多章节内容可以访问"华章计算机"公众号查看. 1.8.1 接口 我们可以看到接口定义了对象之间通信的基本手段.每个类设计接口规格来保证对象能被正确实例化和操作.必须向对象提供的接口发送消息来使用对象暴露的任何行为.接口需要完整描述类与类之间的交互.在大多数面向对象的语言中,访问修饰符指定为public的方法属于接口.私有

《面向对象的思考过程(原书第4版)》一1.9 继承

本节书摘来自华章出版社<面向对象的思考过程(原书第4版)>一书中的第1章,第1.9节,[美] 马特·魏斯费尔德(Matt Weisfeld) 著黄博文 译更多章节内容可以访问"华章计算机"公众号查看. 1.9 继承 面向对象程序设计的最强大的功能之一就是代码重用.结构化设计提供的代码重用非常受限.你可以编写一个功能块,然后多次重用它.但是面向对象的设计更进一步,允许你定义类之间的关系,通过组织和识别不同类之间的共性,不仅可以实现代码重用,也可以指导设计.继承是实现该功能的主