整数区间的交集和差集样例:
[1,5] + [6,10] = [1,10]
[1,100] - [10,20] = [1,9] + [21,100]
如果让你设计一个程序,能够计算整数区间的合集、交集、差集,你会怎么设计?
我会默默想想难度,然后去github上找找有没有可复用的开源代码。
go-intervals简介
go-intervals是一个用于在一维区间(例如时间范围)上执行集合操作的库,正好能满足我的整数区间计算需求。github地址为:https://github.com/google/go-intervals/
选用原因
之所以选择go-intervals有三个方面的考虑:
- 背靠谷歌金字招牌,足够让我相信代码质量
- 完整的测试用例
- 代码开源且易懂
代码剖析
go-intervals库能对多种一维区间集合进行操作,如时间、整数等;也能支持多种开闭区间,如左开右闭、左闭右开、左闭右闭等。代码是如何进行设计的呢?难道作者在代码内部实现了所有细节?
依赖倒转原则(DIP) :高层模块不要依赖低层模块。高层模块和低层模块应该通过抽象(abstractions)来互相依赖。除此之外,抽象(abstractions)不要依赖具体实现细节(details),具体实现细节(details)依赖抽象(abstractions)。具体内容在Go设计模式(3)-设计原则有讲述。
思路:多个区间构成一个集合,区间是集合的基本单位。集合的交差并操作本质上是区间的交差并操作。
结合上面两点,作者的实现方式是将区间接口化。go-intervals只负责将在框架层面将两个集合的区间,两两进行交并差操作,交并差的实现逻辑由调用者自己实现。
1 | type Interval interface { |
实现
调用者想实现哪种区间、哪种开闭方式,只需要实现自己的Interval即可。我需要实现整数、左闭右闭区间的并集、差集操作。
左闭右开区间
在代码的测试用例中,作者给出了整数区间,左闭右开的实现:
1 | type span struct { |
左闭右闭区间
既然作者提供的不满足需求,就需要自己进行修改。参考左闭右开代码修改为左闭右闭代码,目前唯一的缺点是如果构建集合的区间有重叠会panic,重叠问题可以在业务层进行检查,倒也没什么问题。
1 | type span struct { |
测试
检验自己代码是否有问题有什么方法?至少有两点,一是代码review,二是单元测试。对于这种引入的第三方库、且自己实现部分逻辑,一定要做好单元测试,这是前人栽树后人乘凉的好事。
测试case整理如下:
[20,40]为指定区间,用其它区间和指定区间进行交、差操作。竖线表示左右区间值一样。
写了一晚上测试用例,初步测试没有问题。
1 | /** |
总结
很多时候真的不需要重复造轮子,而且也难说自己造的比别人的好,合理合法利用资源、按时完成任务有时候更加重要。写代码是谨小慎微的事情,单元测试必不可少。不但能帮助自己理清思路、发现问题,也能防止后来人改出错误。