开发的时候可能我们会用到第三方包和使用开源代码或者是其他团队的开发的组件系统,不管是那种情况我们都得干净整洁的整合经自己的代码中。
所谓边界就是指外来代码和自己写的代码之间进行整合的连接区域。
1.使用第三方代码
第三方程序包和框架提供者追求普适性,这样能在多个环境中工作,吸引广泛的用户。而使用者则想要集中满足特定需求的接口。这样的情况可能会导致系统边界出现问题。
例子:
//如果你的应用程序需要一个包容Sensor类对象的Map映射图,大概会是这样:
Map sensors = new HashMap();
//当代码的其他部分需要访问这些sensor,就会有这行代码:
Sensor s = (Sensor)sensors.get(sensorId );
//使用范型,提高代码可读性
Map<Sensor> sensors = new HashMap<Sensor>();
Sensor s = sensors.get(sensorId );
//在系统中不受限制地传递Map<Sensor>的实体,意味着当到Map的接口被修改时,有许多地方都要跟着改。
使用map更整洁的方式:
public class Sensors {
private Map sensors = new HashMap();
public Sensor getById(String id) {
return (Sensor) sensors.get(id);
}
}
书中作者建议:“我们建议不要将Map(或在边界上的其他接口)在系统中传递。如果你使用类似Map这样的边界接口,就把它保留在类或近亲类中。避免从公共API中返回边界接口,或将边界接口作为参数传递给公共API。”
3.学习log4j
编写测试用例来学习第三方程序的API,再根据自己的需要进行封装,将应用程序的其他部分与log4j的边界接口隔离开来。
4.学习行测试的好处不只是免费
学习性测试的好处不光免费,还在投资上有正面回报,当第三方程序包发布了新版本,我们可以运行学习性测试,看看程序包的行为有没有改变。
5.使用尚不存在的代码
使用不存在的代码时,可以先定义自己需要的接口,然后编写类调用接口。当第三方程序或者开源代码暂时还没有提供我们所需的API时,可以暂时根据具体业务,定义我们所需的API,并使用。等真实的API被开发出来,再接入相应的接口。这样的好处是,有助于保持客户代码更可读,且集中于它该完成的工作。
6.整洁的边界
边界上肯跟会发生的事有很多,改动是其中之一。有良好的软件设计,无需巨大投入和重写即可进行修改。在使用我们控制不了的代码时,必须加倍小心保护投资,确保未来的修改不至于代价太大。
封装Map:
public class XflowDefinationConfServiceImpl implements XflowDefinationConfService {
private static final Map<LegalEntity, XflowDefinationConfRepository> REPOSITORIES = new HashMap<>();
public XflowDefinationConfServiceImpl() {
init();
}
private void init() {
LegalEntityContext.doWithSupportLegalEntityList(legalEntity -> {
XflowDefinationConfLoader extLoader = new XflowDefinationConfLoader();
Collection<XflowDefinationConf> extConfs = extLoader.load(legalEntity);
XflowDefinationConfRepository repository = new XflowDefinationConfRepositoryImpl(extConfs);
REPOSITORIES.put(legalEntity, repository);
});
}
@Override
public CompositeXflow find(PaymentChannelConfigView serviceChannel, String transactionType, AbstractInitContext context) {
return REPOSITORIES.get(LegalEntityContext.getCurrent()).find(serviceChannel, transactionType, context);
}
@Override
public CompositeXflow find(String serviceProvider, String transactionType, String symbolicName) {
return REPOSITORIES.get(LegalEntityContext.getCurrent()).find(serviceProvider, transactionType, symbolicName);
}
@Override
public Set<RecoverableAtomInfo> getRecoverableAtomInfos() {
return REPOSITORIES.get(LegalEntityContext.getCurrent()).getRecoverableAtomInfos();
}
}
适配器:
public class NextDirective extends AbstractXflowDirective implements Cloneable{
private IAtom atom;
private final Set<AtomResultCode> atomResultCodes;
private final Closure<Boolean> dynamicRule;
public NextDirective(ICompositeStatus sourceStatus,
ICompositeStatus targetStatus,
List<XflowAction> actions,
List<NextDirective> nextDirectives,
IAtom atom,
Set<AtomResultCode> atomResultCodes,
Closure<Boolean> dynamicRule) {
super(sourceStatus, targetStatus, actions, nextDirectives);
this.atom = atom;
this.atomResultCodes = atomResultCodes;
this.dynamicRule = dynamicRule;
}
public IAtom getAtom() {
return atom;
}
@Override
public String getKey() {
return null;
}
@Override
public IAtom getAtomOfNext() {
if (CollectionUtils.isEmpty(this.actions)) {
return null;
}
Optional<XflowAction> requestAction = this.actions.stream().filter(o -> o.getType().equals(XflowActionType.REQUEST)).findFirst();
return requestAction.map(action -> ((RequestAction)action).getAtom()).orElse(null);
}
采用这两种方式,代码都能更好地与我们沟通,在边界两边推动内部一致的用法,当第三方代码有改动时修改点也会更少。
7.总结
保持边界整洁,可以避免我们的代码过多依赖第三方程序的内部实现细节,减少第三方程序对我们代码的入侵程度,尽可能降低因第三方程序修改而对我们代码产生的影响。