图片来源:Chapter 8 Boundaries
有时,我们会购买第三方程序包或使用开放源代码,有时我们依靠公司其他团队打造组件或子系统。这一章讨论如何把这些外来代码干净利落地整合进自己地代码中。
一, 使用第三方代码
在接口提供者和使用者之间,存在着与生俱来的张力。
第三方程序包和框架提供者追求普适性,这样就能在多个环境中工作,吸引广泛的用户。而使用者则只想根据其特定需求而使用某些接口。
这种张力会导致系统边界上出现问题。
例如java.util.Map,Map有着广阔的接口和丰富的功能,虽然这些功能很有用,但也要付出代价。
比如,应用程序可能构造一个Map对象并传递它,其初衷是Map对象的所有接收者不要删除Map中的任何东西,但Map中正好有个clear()方法,使得任何使用者读能清除Map里的内容。
clear() void – Map containsKey(Object key) boolean – Map containsValue(Object value) boolean – Map entrySet() Set – Map equals(Object o) boolean – Map get(Object key) Object – Map getClass() Class extends Object> – Object hashCode() int – Map isEmpty() boolean – Map keySet() Set – Map notify() void – Object notifyAll() void – Object put(Object key, Object value) Object – Map putAll(Map t) void – Map remove(Object key) Object – Map size() int – Map toString() String – Object values() Collection – Map wait() void – Object wait(long timeout) void – Object wait(long timeout, int nanos) void – Object
另外,如果你的Map对象里需要保存某类对象,例如Sensor类的对象,该Map的创建如下:
Map sensors = new HashMap();
当其他代码需要访问这些Sensor对象,就会有如下代码:
Sensor s = (Sensor)sensors.get(sensorId);
这行代码一再出现。代码的调用端承担了从Map钟取得对象并转换成为正确类型的职责。这不是整洁代码。
通过对范型的使用,这段代码可读性提高很多:
Mapsensors = new HasMap (); ... Sensor s = sensors.get(sensorId);
不过,Map
另外,如果Map的接口被修改,你所有的相关代码都会被修改。
所以,使用自定义Sensors类,是一个更整洁的使用Map的方式。
public class Sensors { private Map sensors = new HashMap(); public Sensor getById(String id) { return (Sensor) sensors.get(id); } //snip }
使用Sensors类后,Sensors用户不必关心其内部是否使用了范型或Map,而且隐藏了不需要的Map接口。
并不建议总是用这种方式封装Map的使用,但如果需要将Map类对象在系统中到处传递,可以考虑这样封装。
二,使用尚不存在的代码
另外还有另一种边界。
在代码中,总有许多地方是我们的知识未及之处,例如,软件中有一个通信子系统,但该子系统的开发者还没来得及定义良好的API。
这是,我们可以通过定义自己的接口API,并把该通信子系统包装起来,使得该子系统处于我们自己的控制之下。
并且,还可以通过调用用此包装API以及模拟的信号输入,来模拟该通信子系统。
一旦通信子系统的API被定义出来,我们就编写Adapter(适配器模式)来跨接。见下图。
图片来源:Clean Code with Boundaries
三,总结
边界上的代码需要进行清晰的分割和定义测试,应该避免我们的代码过多的了解第三方代码中的特定信息。
依靠你能控制的东西,好过依靠你控制不了的东西,免得日后受它控制。
问题:
1, 你是如何管理第三方代码的?有什么样的经验教训?
Map sensors = new HasMap();
...
Sensor s = sensors.get(sensorId);
应该是
Map<Sensor> sensors = new HasMap<Sensor>();
...
Sensor s = sensors.get(sensorId);
:-)