写点什么

如何设计好一个接口

用户头像
🎄新
关注
发布于: 7 小时前
如何设计好一个接口

前言

在 Java 编程语言中,接口是一个抽象类型,通常以 interface 来声明,它包含方法签名以及常量声明。在微服务架构下,接口是你展现给合作方的门面,所以接口的设计的好坏,将决定合作方对你的第一印象。

为了设计好一个接口,我们需要坚守设计原则,遵循常见的定义规范。

设计原则

六大设计原则主要是指:

  • 单一职责原则(Single Responsibility Principle);

  • 里氏替换原则(Liskov Substitution Principle);

  • 迪米特法则(Law of Demeter)”;

  • 接口隔离原则(Interface Segregation Principle);

  • 依赖倒置原则(Dependence Inversion Principle)。

  • 开闭原则(Open Closed Principle);

在接口设计的时候,我们采用的主要是单一职责原则、迪米特法则、接口隔离原则和开闭原则。下面我们开始介绍在做接口设计的时候怎么坚守这四个原则。

单一职责原则

定义:明确接口职责,一个接口、方法只做一件事。

我们以种树为例,种树一共分为 3 个过程:挖坑、撒种、埋土。三个操作过程的对象分别是:挖坑->土;撒种->种子;埋土->土;

所以我们在设计接口的时候,需要把对土对象的操作和对种子对象的操作给分割开。类图如下:



代码为:

package 种树;
public interface 土 {
void 挖坑();
void 埋土();
}
复制代码


package 种树;
public interface 种子 {
void 撒种();
}
复制代码

好处:

  • 类的复杂性降低,实现什么职责都有清晰明确的定义;

  • 提高类的可维护性和可读写性;

  • 降低变更的风险;

迪米特法则

定义:迪米特法则也称为最少知道原则,描述的是:一个对象应该对其他对象有最少的了解。

我们以活动删除为例,活动删除的时候,业务方只需要感知活动的唯一约束,类:活动 id。类图如下:



代码为:

package activity;
/** * 活动写操作服务 */public interface ActivityWriteService {
/** * 删除活动 * @param actId 活动id * @return 删除活动状态 true:成功 false:失败 */ Boolean deleteActivity(Long actId);}
复制代码

好处:

  • 降低接口的理解程度与耦合程度。

  • 数据安全。

接口隔离原则

定义:客户端不应该依赖它不需要的接口,类间的依赖关系应该建立在最小的接口上。

我们以活动操作为例,读写分离,只需要查询活动的业务方不感知活动可以编辑和删除。类图如下:


代码为:

package activity;
import java.util.List;
/** * 活动读操作服务 */public interface ActivityReadService {
/** * 查询单个活动 * * @param actId 活动id * @return 活动对象 */ Object querySingleActivity(Long actId);
/** * 查询活动列表 * * @param object 活动查询对象 * @return 活动对象列表 */ List<Object> queryActivityList(Object object);}
复制代码


package activity;
/** * 活动写操作服务 */public interface ActivityWriteService {
/** * 创建活动 * @param object 活动创建对象 * @return 活动id */ Long createActivity(Object object);
/** * 更新活动 * @param object 活动更新对象 * @return 更新活动状态 true:成功 false:失败 */ Boolean updateActivity(Object object);
/** * 删除活动 * @param actId 活动id * @return 删除活动状态 true:成功 false:失败 */ Boolean deleteActivity(Long actId);}
复制代码

好处:

  • 避免接口污染。

  • 提高灵活性。

  • 提供定制服务。

  • 实现高内聚。

开闭原则

定义:一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。

我们以领取优惠券为例,在双 11 双 12 大促的时候,业务方经常会出现同时发放淘宝、支付宝优惠券。两种类型的优惠券在领券的接口上没有大的区别。所以对外还是输出统一的领券接口,内部识别优惠券提供方拓展



代码为:

package coupon;
/** * 优惠券传播服务 */public interface CouponSpreadService {
/** * 领取优惠券 */ void applyCoupon();}
复制代码


package coupon;
/** * 领取支付宝优惠券 */public class AliPayCouponSpreadProxy implements CouponSpreadService {
@Override public void applyCoupon() {
}}
复制代码


package coupon;
/** * 领取淘宝优惠券 */public class TaobaoCouponSpreadProxy implements CouponSpreadService {
@Override public void applyCoupon() {
}}
复制代码

好处:

  • 开闭原则可以提高可维护性

  • 提高了复用性

定义规范

命名规范

接口命名原则要能清晰易懂,见名知意

强制】方法名、参数名、成员变量、局部变量都统一使用 lowerCamelCase 风格,必须遵从 驼峰形式。

正例: localValue / getHttpMessage() / inputUserId


推荐】接口类中的方法和属性不要加任何修饰符号(public 也不要加),保持代码的简洁 性,并加上有效的 Javadoc 注释。尽量不要在接口里定义变量,如果一定要定义变量,肯定是 与接口方法相关,并且是整个应用的基础常量。

正例:接口方法签名 void commit();

接口基础常量 String COMPANY = "alibaba";

反例:接口方法定义 public abstract void f();


参考】Service/DAO 层方法命名规约

1) 获取单个对象的方法用 get 做前缀。

2) 获取多个对象的方法用 list 做前缀,复数形式结尾如:listObjects。 3) 获取统计值的方法用 count 做前缀。

4) 插入的方法用 save/insert 做前缀。

5) 删除的方法用 remove/delete 做前缀。

6) 修改的方法用 update 做前缀。


参数规范

接口入参

不可以引用:

  • 枚举类型

  • 除上文提到的 Map、List、Set 之外的泛型

  • 抽象类

  • 接口类

  • 原生类型的数组

可以引用:

  • 具体的实体类,要求引用类型与实际的对象类型保持一致;不可使用父类引用类型指向子类对象。

  • 内部支持数据类, 但数组、Map、List、Set 这些集合类型不可以嵌套。

如下是错误示例:

Map<String,String[]>Map<String,List<Person>>(Person为一个具体的实体类)List<Map<String,Persion>>List<Persion[]>
复制代码

接口出参

不可以引用:

  • 枚举类型

  • 除上文提到的 Map、List、Set 之外的泛型

  • 抽象类

  • 接口类

  • 原生类型的数组

可以引用:

  • 具体的数据类,要求引用类型与实际的对象类型保持一致;不可使用父类引用类型指向子类对象 (比如,不能用 Object 引用指向其它对象)。注意:如果父类为具体类,生成工具将检查不出此类错误。

  • 内部支持数据类见文章开头定义。数组、Map、List、Set 集合类型不可以嵌套,参见上文相关示例。


发布于: 7 小时前阅读数: 14
用户头像

🎄新

关注

还未添加个人签名 2021.06.22 加入

还未添加个人简介

评论

发布
暂无评论
如何设计好一个接口