当前位置: 首页 > news >正文

C++/Java编程小论——方法设计与接口原则总结

C++/Java —— 方法设计与接口原则(详细指南)

​ 笔者的秋招应当是告一段落了,这里决定将自己的一些接口设计方法等总结一下,算是自己的Coding要求了,当然示例代码因为最近正在接触Java,这里都是用Java编写的。

设计方法之前的思考:职责与契约

​ 再我们行动起来,编写代码的时候,我们往往需要问自己一些问题。就是在我们自己的需求准备得到解决之前,我们需要确定好几个基本的问题:

​ 这个方法的单一责任是什么?它最好只做一件事(或一组高度相关的事情)。实际上在C/C++编程的时候,你也需要搞定这个。比如说——我们尽可能拆分一些短的类,他只是简单的做一个事情,我们的复杂的流程是这些简单流程的组合。

​ 我们设计的方法对调用者有什么明确承诺?输入是什么格式、返回值语义如何、什么时候抛异常、是否是线程安全的、是否可重入、是否会修改参数或全局状态?

​ 笔者一般会先搞清楚这个,然后逐一的突破和实现。

方法签名详解

​ Java和C++类似。我们先从左到右一个一个说清楚来。

返回值类型
  • 优先使用具体而明确的返回类型(例如 Optional<T>、集合接口 List<T> 而不是实现类 ArrayList<T>)。
  • 若方法可能没有结果而结果的缺失是正常情况,咱们优先采用Optional<T>而不是丢一个NULL回来,因为这侵占了可能的合法空指针。
  • 对于布尔方法,用 is, has, can 等前缀(isEmpty(), hasNext())。
Optional<User> findUserById(String id);
boolean isValidEmail(String email);
List<String> listNames();
参数设计
  • 参数数量尽量少(理想 <= 3)。若逻辑上需要多个参数,考虑将它们封装成对象(参数对象 pattern)。
  • 喜欢使用 不可变 参数(传入 Collections.unmodifiableList(...) 或传入不可变对象)以降低副作用。
  • 对于可变的集合参数,选择 defensive copy 或在 Javadoc 中清楚说明会修改。
  • 避免使用 null 表示“缺省”,考虑 Optional<T>(但不要把 Optional 用作方法参数的常规做法——多数建议是 不要Optional 用作参数类型)。

示例(参数对象):

// 不好(长参数列表)
void createUser(String username, String email, String phone, boolean sendWelcome);// 好:参数对象,便于扩展
void createUser(CreateUserRequest req);

可见性(visibility)

  • 首先把方法设为 private,然后仅在必要时提高为 protected / public。最小暴露原则(Least Privilege)。
  • 对类库的 API,使用 public 且稳定的方法签名,避免频繁改动。

throws(异常声明)

  • 明确:什么时候抛异常哪种异常(检查型 checked vs 非检查型 unchecked)。
  • 现代 Java 倾向将 编程错误(非法参数、非法状态)使用 RuntimeException(如 IllegalArgumentExceptionIllegalStateException);将可预见的外部错误(IO、网络、解析失败)使用 checked 异常或包装成 domain-specific exceptions。
  • Document exceptions in Javadoc using @throws

示例:

/*** @throws IllegalArgumentException if id is null or empty* @throws DataAccessException if repository fails*/
User getUserById(String id) throws DataAccessException;

重载 vs 泛型 vs 可变参数

  • 使用方法重载要保持语义一致,避免仅因返回类型不同而重载(Java 不允许仅靠返回类型重载)。
  • Varargs (T... args) 在 API 便利性上很好,但要注意与数组重载的歧义。
  • 若方法和类型有关,用泛型将类型信息向外暴露而非使用 Object

示例:

public <T> List<T> toList(T... elements) { ... }

方法行为与语义:纯函数、副作用、异常策略

纯函数(Pure functions)

  • 定义:相同输入永远返回相同输出、无副作用(不改变外部状态)。
  • 优点:易测试、并发友好、可缓存(memoize)。
  • 适用场景:计算、格式化、校验、映射等。

示例:

int add(int a, int b) { return a + b; } // 纯函数

有副作用的方法

  • 对外部状态有影响(修改参数、写日志、写 DB、发送网络请求)。需要在文档中明确,并尽量最小化副作用的可见面。
  • 如果可能,把副作用与纯逻辑分离(先返回结果,再有 caller 负责持久化/提交)。

异常策略总结

  • 校验失败:IllegalArgumentException 或自定义 ValidationException(unchecked);
  • 状态不允许:IllegalStateException
  • 资源/IO问题:checked exceptions 或自定义 unchecked wrapper(例如 DataAccessException);
  • 不要滥用 checked exceptions 导致调用方大量 try/catch 泄露到业务逻辑中。

性能、可测性与并发考虑

性能评估

​ 我们可以使用Profiling(笔者VS用过profile小工具),我们在搞定了性能热点路径的时候,需要避免在热路径中分配大量临时对象(例如在循环中频繁创建字符串或集合),使用 StringBuilder、对象池(审慎)或复用缓冲。

​ 若方法暴露为 API,并且会被频繁调用,文档或注释应明确性能特性(例如“此方法 O(n)”, “此方法会创建新集合”)。

可测性
  • 方法应便于单元测试:尽量少使用静态全局状态;若依赖外部系统,使用依赖注入(DI)或接口抽象来替换 mock。
  • 对副作用明确契约,单元测试时可以断言状态变化或 mock 相应依赖。
并发与线程安全
  • 首选:无状态与不可变:方法内部不使用可变静态字段或共享可变状态。
  • 如果方法需要修改共享状态:
    • 使用线程安全的并发工具(ConcurrentHashMap, Atomic* 系列)。
    • 说明是否是同步/原子的,或由调用者保证同步。
  • 对于可变返回值(如返回集合),确定是否返回防御性拷贝或不可变视图(Collections.unmodifiableList(...))。

示例(线程安全约定):

/*** Thread-safe: method is stateless and uses local variables only.*/
public int compute(int x) { ... }

方法实现细节与风格建议

命名

​ 方法名应清晰表达行为:calculateTotal, findUser, saveToDatabase。这样像读英语一样读出来我们的语义,会比其他乱七八糟的命名要舒服的多。

长度与职责

​ 建议方法行数短(例如 < 50 行),单一职责。若方法不同抽象层次混杂,咱们需要做拆分。

防御式编程
  • 对外部输入进行校验并在顶部早期返回(fail-fast)。
  • 使用 Objects.requireNonNull(arg, "msg") 做空检查。
使用注解
  • @Nullable / @NotNull(或 javax.annotation / org.jetbrains.annotations)来记录可空性。
  • @Deprecated@Deprecated(forRemoval = true, since = "1.2") 标注弃用 API。
  • @Override 总是在覆盖父方法时使用,避免拼写错误导致不覆盖。
异常处理风格
  • 捕获只有当你能处理异常或添加更有意义的上下文时才捕获(不要吞掉异常)。
  • 如果包装异常,保留原始异常为 cause:throw new MyDomainException("msg", e);

接口基础:为什么用接口?什么时候用抽象类?

接口 表示能力或契约(what),抽象类 可包含状态和默认实现(how)。现代 Java(8+)的接口更强大,但仍有判断标准:

  • 若希望定义多个不相关类能实现的能力(例如 Comparable, Iterable),使用接口。
  • 若需要共享状态(字段)或构造器逻辑,使用抽象类。
  • 若设计可演化的 API,优先接口 + 默认方法(谨慎使用),避免抽象类的单继承限制。

接口设计原则与模式

接口隔离原则(ISP)
  • 把大接口拆成小接口,让实现者只依赖其需要的方法。
  • 例如不要把 Printer 做成 print, scan, fax 三合一接口;而是拆成 Printable, Scannable, Faxable
里氏替换原则(LSP)
  • 子类型应能替换父类型而不破坏客户端行为。换言之,子实现不要改变接口声明的语义(预条件不能更强,后置条件不能更弱,不能抛出额外的未声明的检查异常等)。
依赖倒置原则(DIP)
  • 高层模块不应该依赖低层模块,两者都应该依赖抽象(接口)。通过接口注入依赖,便于测试和替换实现。
设计契约(Design by Contract)
  • 在接口上清晰写出前置条件、后置条件、不变式(使用 Javadoc 的 @throws@implSpec@implNote 来说明实现者或调用者责任)。
常见模式
  • 策略模式(Strategy):使用接口定义可替换算法;调用端注入具体实现。
  • 适配器模式(Adapter):接口适配不同实现以统一调用。
  • 装饰器模式(Decorator):接口+组合用于动态增强行为。

Java 接口的现代用法(Java 8+)

默认方法(default)

允许在接口中提供方法实现,从而实现向后兼容地扩展接口。使用场景:

  • 为现有接口添加新行为而不破坏旧实现。
  • 提供便捷的组合方法。

注意:默认方法不应该含有大量状态逻辑,且可能造成菱形继承冲突(类实现多个接口时需要显式解决冲突)。

示例:

public interface Logger {void log(String msg);default void logInfo(String msg) { log("[INFO] " + msg); }
}

私有方法(private) — Java 9+

可在接口内部抽取默认方法的公共实现逻辑,而不暴露给实现者。

函数式接口(Functional Interface)

带有单个抽象方法(SAM)的接口,可用作 Lambda 的目标,适用于策略或回调:

@FunctionalInterface
public interface Transformer<T,R> {R apply(T t);
}

java.util.function 包中的标准函数式接口(Function, Consumer, Supplier 等)配合使用能提升互操作性。

标记接口(Marker Interface)

Serializable,表示类型具备某种能力。现代替代方式可是注解(更清晰)。


API 演化、版本兼容与退役策略

  • 向后兼容(backward compatible):新增 public 方法或重载一般是安全的;修改已有方法签名会破坏兼容。
  • 接口扩展:首选用 default 方法来添加新方法(注意行为一致性与可能冲突)。
  • 弃用(deprecation):用 @Deprecated 标注并在 Javadoc 说明替换方案与移除时间窗口。
  • 语义变更:即使签名不变,改变返回值语义或异常行为也会破坏客户端。尽量避免。

接口实现的测试与文档

Javadoc 要点

  • 每个公共接口与方法都应有 Javadoc:说明职责、参数意义、返回值语义、抛出的异常、线程安全约束、性能特性、可变性(是否修改参数或返回可修改集合)。
  • 使用标签:@param, @return, @throws, @implSpec, @implNote, @since

示例 Javadoc:

/*** Finds user by id.** @param id non-null id of the user* @return Optional containing user if found, otherwise Optional.empty()* @throws IllegalArgumentException if id is null or empty* @implSpec Implementations should perform a lookup in the underlying store.*/
Optional<User> findUserById(String id);

单元测试

  • 测试契约而不是实现细节:为接口编写测试用例(抽象测试类)并让每个实现继承这些测试,确保所有实现满足同一契约。
  • 测试线程安全约定(并发单元测试)在有并发保证时很重要。

设计检查清单 + 常见反模式

设计检查清单(发布前)

  • 方法名字是否清晰表达行为?
  • 参数是否最小且不可避免?是否需要参数对象?
  • 有无明确的空值策略(允许 null 吗)?是否在 Javadoc 指定?
  • 是否是纯函数?是否有副作用?是否文档化副作用?
  • 返回值是否应该为 Optional/Collection/Stream?是否应返回防御性拷贝或不可变集合?
  • 异常使用是否合理?是否避免过度使用 checked 异常?
  • 是否线程安全?谁负责同步?
  • 是否易于测试?是否可以 mock/replace?
  • 是否易于向后兼容演化?

常见反模式

  • 上帝方法(God Method):方法做太多事情,难以复用和测试。
  • 大而笨重的接口(Fat Interface):接口中包含大量不相关方法,违反 ISP。
  • 返回 null 作为常态:会增加 NPE 风险;Prefer Optional 或空集合。
  • 隐藏副作用:方法修改全局状态但文档中没有说明。
  • 在接口中放状态:接口本应是契约,包含状态会导致实现耦合(除非是默认方法辅助的不可变映射)。


文章转载自:

http://ulXWFsk2.mkrqh.cn
http://C5c2JZWB.mkrqh.cn
http://vYE0W9NJ.mkrqh.cn
http://m6iJHlJU.mkrqh.cn
http://zjHXGoKC.mkrqh.cn
http://jDJY4f7K.mkrqh.cn
http://Q2MAFwS7.mkrqh.cn
http://3AwNpJOU.mkrqh.cn
http://aeEgGbdx.mkrqh.cn
http://ypGYDplG.mkrqh.cn
http://3kWVtZzc.mkrqh.cn
http://nsiYDOxv.mkrqh.cn
http://QBedw8qY.mkrqh.cn
http://l6f4Xgho.mkrqh.cn
http://zoNeVxpo.mkrqh.cn
http://YEmGzPau.mkrqh.cn
http://PpPi2Fbx.mkrqh.cn
http://DqXc8SXk.mkrqh.cn
http://TUJrd93S.mkrqh.cn
http://inHzgYP6.mkrqh.cn
http://su4n9ljE.mkrqh.cn
http://rp7eXTl5.mkrqh.cn
http://JCPlSIV7.mkrqh.cn
http://aTrY9MZY.mkrqh.cn
http://0J9aJmtj.mkrqh.cn
http://sfXVw0r7.mkrqh.cn
http://ntlYHDuy.mkrqh.cn
http://mlFrXEgA.mkrqh.cn
http://PIrhi80o.mkrqh.cn
http://tdp6cD4i.mkrqh.cn
http://www.dtcms.com/a/376564.html

相关文章:

  • Java-Spring入门指南(四)深入IOC本质与依赖注入(DI)实战
  • 线扫相机采集图像起始位置不正确原因总结
  • JVM 对象创建的核心流程!
  • 秋日私语:一片落叶,一个智能的温暖陪伴
  • springCloud之配置/注册中心及服务发现Nacos
  • 第1讲 机器学习(ML)教程
  • Ubuntu 系统 YOLOv8 部署教程(GPU CPU 一键安装)
  • 【C++】string 的使用(初步会用 string,看这一篇文章就够了)
  • 基于 lua_shared_dict 的本地内存限流实现
  • 基于场景的自动驾驶汽车技术安全需求制定方法
  • 【lucene】pointDimensionCount` vs `pointIndexDimensionCount`:
  • 大语言模型入门指南:从原理到实践应用
  • 旧设备新智慧:耐达讯自动化RS232转Profibus连接流量泵工业4.0通关秘籍
  • 扭蛋机小程序有哪些好玩的创新功能?
  • 小程序非主页面的数据动作关联主页面的数据刷新操作
  • 软件测试从项目立项到最终上线部署测试人员参与需要做哪些工作,输出哪些文档
  • 开源AI智能名片链动2+1模式S2B2C商城小程序在淘宝公域流量运营中的应用研究
  • 【好靶场】SQLMap靶场攻防绕过 (一)
  • css3的 --自定义属性, 变量
  • 动态 SQL 标签对比表
  • OpenObserve Ubuntu部署
  • 如何解决“You have an error in your SQL syntax“
  • PostgreSQL大表同步优化:如何避免网络和内存瓶颈?
  • vue3 的痛点
  • 在 Ubuntu 22.04 系统(CUDA 12.9)中,通过本地DEB 包安装 cuDNN 9.13.0 的方法步骤
  • MySQL整理【03】事务隔离级别和MVCC
  • 信息检索2
  • Unity2019用vscode的问题
  • iOS 文件管理与能耗调试结合实战 如何查看缓存文件、优化电池消耗、分析App使用记录(uni-app开发与性能优化必备指南)
  • 【华为OD】完美走位