这一章书中全是理论性的东西,再加上中文翻译肯定有词不达意的地方(并未有意冒犯译者,sry),初读起来并没有理解,遂仔细思考后又读了两遍才敢总结。
作者在本章提到“边界”的概念,我个人的理解是:使用外部代码(第三方库提供的API、或者其他模块的服务)与自身项目代码结合的时候,自身项目调用外来代码以及外来代码提供的功能,这两部分代码就是边界。如果干净利落的将这些代码整合,就能保持软件边界的整洁。
一、使用第三方代码(良好的实践范例)
第三方jar包和框架提供者追求通用性,这样就能在多个环境下工作,吸引更多的用户。而使用者则想要集中满足特定需求的接口,这样方便开发需求。正是因为服务提供者和使用者的目标的差异,导致了在系统边界上可能会出现问题。
举个例子,以java.util.Map为例。Map中拥有丰富的功能(这对应了上面说的第三方jar包和框架提供者追求通用性)。但是,这也会付出一些代价。比如,应用程序可能构造一个Map对象并到处传递它的引用。 假设我们原来设计的初衷是:Map对象的所有接收者都不要删除映射里的东西,但是实际情况却是任何Map的使用者都能使用clear()方法清空Map中的映射。
接下来用代码演示一下。假如你的应用程序需要一个能存储Sensor的Map,那一般可以这么写:
Map sensors = new HashMap();
当代码的其它部分需要访问这些sensor时,可以这样做:
Sensor s = (Sensor) sensors.get(sensorId);
这种获取元素的方式虽然可行,但是调用端承担了从Map对象中取得对象并将其转换成正确类型的职责,并非整洁的代码。可以通过使用泛型,让代码变整洁:
Map<Sensor> sensors = new HashMap<Sensor>();
...省略put操作
Sensor s = sensors.get(sensorId);
在系统中无节制的传递Map<Sensor>对象的引用,意味着如果Map提供的接口被修改时,有非常非常多的地方需要跟着改动。例如,Java 5加入对泛型的支持时,Map接口的确发生了变动。
所以在本例中,使用Map的更整洁的方式大致如下:
sensors的用户不关心是不是使用了泛型,把类型转换等操作提取到Sensor类中作为一个方法,这样在接口发生改变时,我们只需要修改这一个方法即可。
public class Sensors {
private Map sensors = new HashMap;
public Sensor getById(String id) {
return (Sensor) sensors.get(id);
}
}
小总结一下:并不建议总是以这种方式封装Map,建议不要将Map(或者边界上的其它接口)在系统中传递。如果你使用类似Map这样的边界接口,就把它保留在类中。避免从公共API中返回边界接口,或者将边界接口作为参数传递给公共方法。
二、使用尚不存在的代码
协同开发过程中经常会碰到代码调用的API还没有处理好的情况,在这中情况下我们定义好我们的接口和方法。一旦API被定义出来,可以使用适配器模式来进行衔接。Adapter封装了与API的互动,也提供了一个当API发生变动时唯一需要改动的地方。
读者可以自行了解一下Adapter设计模式。记住这种应用场景。
三、整洁的边界
在我们的代码中应该尽量少的了解第三方代码中的特定信息,也就需要尽量去封装好第三方边界接口。可以像封装Map那样,也可以使用适配器模式将我们的接口转换为第三方的接口。采用这两种方法,代码都能更好地与我们沟通,在边界两边推动内部一致的用法。这样当第三方代码改动的时候,我们的代码修改点也会更少。
参考
《代码整洁之道》
网友评论