先来看问题,
【问题】有些方法,内容都差不多,只是参数的个数或者类型不一样。此时,到底是写多个方法呢,还是写一个方法涵盖所有功能(根据传入的标识去执行不同的步骤)?
举例1:
changeCode(String orgStr, String charSet);
changeCode(String orgStr); // 默认charSet为null
其实方法2,就是调用的方法1,但之所以再写一个方法2,是为了方便使用,charSet参数不是必须的,可以省略(如果省略,会用策略)。
举例2:
下面的6个方法,每个方法都有区别,要么参数个数不同,要么参数类型不同。
比如方法3和方法4,允许传入String或者byte[]类型的参数,而他们最终会调用方法1,但是方法1又最终调用的是方法2。
之所以有这种情况,是为了方便 使用方,使用方 允许传入不同类型的参数,方法内部自动进行参数转换。
Result shaWithRsa(CodeData<?> data, CodeData<?> key) { return shaWithRsa(data, key, "SHA256WithRSA"); } Result shaWithRsa(CodeData<?> data, CodeData<?> key, String algorithm) { // 省略 } Result shaWithRsa(String data, CodeData<?> key) { return shaWithRsa(new Utf8Data(data), key); } Result shaWithRsa(byte[] data, CodeData<?> key) { return shaWithRsa(new PlainData(data), key); } Result shaWithRsa(String data, byte[] key) { return shaWithRsa(new Utf8Data(data), new PlainData(key)); } Result shaWithRsa(byte[] data, String key) { return shaWithRsa(new PlainData(data), new Utf8Data(key)); }
想做一个优秀的API (接口或者工具方法)的设计,必须要严谨,因为一旦公布大家使用,后期很难再去改API了。
所以,本文专门来讨论这个问题,多参数的接口,该如何设计?
两种不同的观念
A、写多个方法,每个方法参数不同
优点:
每个方法参数清晰,见名知义,使用方便,调用也简单。
每个方法内部的处理逻辑清晰,很符合面向对象的多态思维。
缺点:
多个方法,有很多代码是冗余的,而且修改起来很麻烦。
方法太多,虽然各有各的用处,但是容易让调用者眼花缭乱。
B、写一个方法,涵盖所有参数
优点:
只需要编写和维护一套代码;
接口个数少,不会给用户造成选择困扰。
缺点:
调用时需要明确指定所有参数,即使有些参数不需要,也要传null代替。
使用时不够清晰,对于需要传哪些参数、需要传什么类型的参数,都有疑惑。
方法内部要处理多种情况,逻辑要稍微复杂,性能稍微低一点(可以忽略)。
按照这个方案,前面的案例1,只保留下面一个方法:
changeCode(String orgStr, String charSet);
案例2,只保留:
Result shaWithRsa(CodeData<?> data, CodeData<?> key, String algorithm);
很明显,方法个数变少了,但是使用难度也增加了。
如上所述,其实二者各有优劣,我都纠结了好几年,但是最近,我找到了一个完美的结合点,方法如下:
1、对于多参数的情况,且(参数个数有3种以上不同组合的情况),我建议使用上面的方案B,因为:
对于调用者而言,只有一个方法供选择,那么他必须显式的指定所有参数,但对调用者而言,使用是透明的。哪些需要,哪些不需要,调用者可以自行决定,不需要的,传入null即可。
对于写方法的人而言,注意到,他应该检查参数的合法性,不管使用者传入了什么参数,方法内部都应该做检查,某些参数本身就支持传入null。
2、对于有多类型参数的情况,且参数类型有3种以上不同组合的情况,我建议使用上面的方案A,同时将参数定义为一个Bean对象。
如下所示:
Result shaWithRsa(RsaEncryptReq req); public class RsaEncryptReq { private CodeData<?> data; private CodeData<?> key; private String algorithm; public RsaEncryptReq(byte[] data, byte[] key, String algorithm) { this.data = new PlainData(data); this.key = new PlainData(key); this.algorithm = algorithm; } public RsaEncryptReq(String data, CodeData<?> key, String algorithm) { this.data = Utf8.data(data); this.key = key; this.algorithm = algorithm; } public RsaEncryptReq(byte[] data, String key, String algorithm) { this.data = new PlainData(data); this.key = Utf8.data(key); this.algorithm = algorithm; } public RsaEncryptReq(String data, byte[] key, String algorithm) { this.data = Utf8.data(data); this.key = new PlainData(key); this.algorithm = algorithm; } public CodeData<?> getData() { return data; } public CodeData<?> getkey() { return key; } public String getAlgorithm() { return algorithm; } }
这个方案的兼具方法A和B的优点,
首先,只保留了一个方法,使得API的定义非常清晰,而且API兼容性好,定义好之后就无需更改。
其次,通过Bean的构造函数来传入不同类型的参数,非常方便扩展,也很方便使用。
3、对于参数比较少的情况(参数的组合小于等于3),并且某些参数又比较常用,则可以采用A方案。