接口设计的原则:构建优雅API的完整指南

在软件开发中,接口就像建筑的地基,设计得好坏直接决定了整个系统的稳定性和可维护性。一个优秀的接口设计不仅能提升开发效率,还能降低系统复杂度,让代码更加健壮。今天我将为你详细解析接口设计的核心原则和最佳实践,让你的API设计水平上一个台阶。

一、接口设计的基础概念

什么是接口设计?

接口设计是定义系统不同组件之间交互方式的过程。它包括方法签名、参数定义、返回值、异常处理等方面的设计。好的接口设计能够隐藏实现细节,提供清晰的调用方式。

为什么接口设计如此重要?

接口一旦发布,就会被其他模块或系统依赖。如果设计不当,后续的修改会带来巨大的成本。因此,在设计阶段就要考虑周全,遵循一定的原则。

// 不好的接口设计
public class UserService {
public String processUser(String data, int type, boolean flag) {
// 参数含义不明确,难以理解和使用
return null;
}
} // 好的接口设计
public class UserService {
public UserResult createUser(CreateUserRequest request) {
// 参数明确,易于理解和扩展
return new UserResult();
} public UserResult updateUser(Long userId, UpdateUserRequest request) {
// 职责单一,参数类型明确
return new UserResult();
}
}

二、单一职责原则(SRP)

原则定义

每个接口应该只负责一个明确的功能,不应该承担多个不相关的职责。这是接口设计的基础原则。

实际应用

将复杂的功能拆分成多个简单的接口,每个接口专注于特定的业务场景。

// 违反单一职责原则
public interface UserManager {
void createUser(User user);
void deleteUser(Long userId);
void sendEmail(String email, String content);
void generateReport(Date startDate, Date endDate);
void validateUserData(User user);
} // 遵循单一职责原则
public interface UserService {
void createUser(User user);
void deleteUser(Long userId);
User getUserById(Long userId);
} public interface EmailService {
void sendEmail(String email, String content);
void sendBatchEmail(List<String> emails, String content);
} public interface ReportService {
Report generateUserReport(Date startDate, Date endDate);
Report generateActivityReport(Date startDate, Date endDate);
} public interface UserValidator {
ValidationResult validateUser(User user);
ValidationResult validateEmail(String email);
}

设计要点

  1. 功能内聚:相关的操作放在同一个接口中
  2. 职责明确:接口名称和方法名称要能清楚表达功能
  3. 易于测试:单一职责的接口更容易编写单元测试

三、开闭原则(OCP)

原则定义

接口应该对扩展开放,对修改关闭。设计时要考虑未来的扩展需求,避免频繁修改已有接口。

实现策略

通过抽象和多态来实现可扩展的接口设计。

// 基础接口设计
public interface PaymentProcessor {
PaymentResult processPayment(PaymentRequest request);
} // 不同支付方式的实现
public class AlipayProcessor implements PaymentProcessor {
@Override
public PaymentResult processPayment(PaymentRequest request) {
// 支付宝支付逻辑
return new PaymentResult();
}
} public class WechatPayProcessor implements PaymentProcessor {
@Override
public PaymentResult processPayment(PaymentRequest request) {
// 微信支付逻辑
return new PaymentResult();
}
} // 支付服务
public class PaymentService {
private Map<String, PaymentProcessor> processors; public PaymentResult pay(String paymentType, PaymentRequest request) {
PaymentProcessor processor = processors.get(paymentType);
return processor.processPayment(request);
} // 添加新的支付方式时,不需要修改现有代码
public void addPaymentProcessor(String type, PaymentProcessor processor) {
processors.put(type, processor);
}
}

扩展性设计模式

// 策略模式实现开闭原则
public interface DiscountStrategy {
BigDecimal calculateDiscount(Order order);
} public class VipDiscountStrategy implements DiscountStrategy {
@Override
public BigDecimal calculateDiscount(Order order) {
return order.getAmount().multiply(new BigDecimal("0.1"));
}
} public class CouponDiscountStrategy implements DiscountStrategy {
private String couponCode; @Override
public BigDecimal calculateDiscount(Order order) {
// 优惠券折扣逻辑
return new BigDecimal("50.00");
}
} // 价格计算服务
public class PriceCalculator {
public BigDecimal calculateFinalPrice(Order order, DiscountStrategy strategy) {
BigDecimal discount = strategy.calculateDiscount(order);
return order.getAmount().subtract(discount);
}
}

四、里氏替换原则(LSP)

原则定义

子类对象应该能够替换父类对象,而不影响程序的正确性。接口的实现类应该完全遵循接口的契约。

设计要求

  1. 前置条件不能加强:实现类的参数要求不能比接口更严格
  2. 后置条件不能削弱:实现类的返回结果不能比接口承诺的更弱
  3. 异常处理一致:实现类抛出的异常应该是接口声明的异常的子类
// 正确的里氏替换原则应用
public interface FileStorage {
/**
* 保存文件
* @param fileName 文件名,不能为空
* @param content 文件内容,不能为空
* @return 文件保存路径
* @throws StorageException 存储异常
*/
String saveFile(String fileName, byte[] content) throws StorageException;
} // 本地文件存储实现
public class LocalFileStorage implements FileStorage {
@Override
public String saveFile(String fileName, byte[] content) throws StorageException {
// 遵循接口契约:参数检查不比接口更严格
if (fileName == null || content == null) {
throw new StorageException("参数不能为空");
} // 实现具体的本地存储逻辑
String filePath = "/local/storage/" + fileName;
// ... 保存逻辑 return filePath; // 返回值符合接口定义
}
} // 云存储实现
public class CloudFileStorage implements FileStorage {
@Override
public String saveFile(String fileName, byte[] content) throws StorageException {
// 同样遵循接口契约
if (fileName == null || content == null) {
throw new StorageException("参数不能为空");
} // 云存储逻辑
String cloudUrl = "https://cloud.storage.com/" + fileName;
// ... 上传逻辑 return cloudUrl; // 返回值符合接口定义
}
}

错误示例

// 违反里氏替换原则的错误设计
public class RestrictedFileStorage implements FileStorage {
@Override
public String saveFile(String fileName, byte[] content) throws StorageException {
// 错误1:加强了前置条件 - 接口只要求非空,但这里增加了文件大小限制
if (fileName == null || content == null) {
throw new StorageException("参数不能为空");
}
if (content.length > 1024) {
throw new StorageException("文件大小不能超过1KB"); // 这是额外的限制!
} // 错误2:削弱了后置条件 - 接口承诺返回文件路径,但这里可能返回null
if (fileName.contains("temp")) {
return null; // 违反了接口契约!接口说要返回路径,这里却返回null
} return "/restricted/storage/" + fileName;
}
} // 更明显的违反例子
public class ReadOnlyFileStorage implements FileStorage {
@Override
public String saveFile(String fileName, byte[] content) throws StorageException {
// 错误3:完全改变了方法的行为
// 接口说是"保存文件",但这个实现根本不保存,只是读取
throw new UnsupportedOperationException("只读存储不支持保存操作");
// 这样使用者调用 FileStorage.saveFile() 时就会出错
}
} // 演示里氏替换原则被违反的问题
public class FileManager {
public void uploadUserDocument(FileStorage storage, String fileName, byte[] content) {
try {
String path = storage.saveFile(fileName, content);
// 期望得到一个有效的文件路径,但可能得到null或异常
System.out.println("文件保存成功,路径: " + path);
} catch (StorageException e) {
System.out.println("保存失败: " + e.getMessage());
}
}
} // 使用时的问题
public class Demo {
public static void main(String[] args) {
FileManager manager = new FileManager();
byte[] largeFile = new byte[2048]; // 2KB文件 // 使用正常的实现 - 工作正常
FileStorage localStorage = new LocalFileStorage();
manager.uploadUserDocument(localStorage, "document.pdf", largeFile); // 成功 // 替换为违反LSP的实现 - 出现问题
FileStorage restrictedStorage = new RestrictedFileStorage();
manager.uploadUserDocument(restrictedStorage, "document.pdf", largeFile); // 失败!文件太大 FileStorage readOnlyStorage = new ReadOnlyFileStorage();
manager.uploadUserDocument(readOnlyStorage, "document.pdf", largeFile); // 抛异常! // 这就是违反里氏替换原则的问题:子类不能无缝替换父类/接口
}
}

五、接口隔离原则(ISP)

原则定义

不应该强迫客户依赖于它们不使用的方法。设计小而专一的接口,而不是大而全的接口。

实际应用

将大接口拆分成多个小接口,客户端只需要依赖它们实际使用的接口。

// 违反接口隔离原则的设计
public interface Worker {
void work();
void eat();
void sleep();
void code();
void design();
void test();
} // 遵循接口隔离原则的设计
public interface Workable {
void work();
} public interface Eatable {
void eat();
} public interface Sleepable {
void sleep();
} public interface Programmer extends Workable {
void code();
} public interface Designer extends Workable {
void design();
} public interface Tester extends Workable {
void test();
} // 具体实现
public class Developer implements Programmer, Eatable, Sleepable {
@Override
public void work() {
System.out.println("开发工作");
} @Override
public void code() {
System.out.println("编写代码");
} @Override
public void eat() {
System.out.println("吃饭");
} @Override
public void sleep() {
System.out.println("睡觉");
}
}

接口分离的实践

// 数据访问接口的合理分离
public interface Readable<T> {
T findById(Long id);
List<T> findAll();
List<T> findByCondition(Condition condition);
} public interface Writable<T> {
T save(T entity);
void delete(Long id);
T update(T entity);
} public interface Cacheable {
void clearCache();
void refreshCache();
} // 只读数据访问
public class ReadOnlyUserDao implements Readable<User> {
// 只实现读取操作
} // 完整数据访问
public class UserDao implements Readable<User>, Writable<User>, Cacheable {
// 实现所有操作
}

六、依赖倒置原则(DIP)

原则定义

高层模块不应该依赖低层模块,两者都应该依赖于抽象。抽象不应该依赖细节,细节应该依赖抽象。

实现方式

通过接口或抽象类定义依赖关系,而不是直接依赖具体实现。

// 违反依赖倒置原则
public class OrderService {
private MySQLOrderDao orderDao; // 直接依赖具体实现
private EmailNotifier notifier; // 直接依赖具体实现 public void createOrder(Order order) {
orderDao.save(order); // 紧耦合
notifier.sendEmail(order.getCustomerEmail(), "订单创建成功");
}
} // 遵循依赖倒置原则
public interface OrderRepository {
void save(Order order);
Order findById(Long id);
} public interface NotificationService {
void sendNotification(String recipient, String message);
} public class OrderService {
private final OrderRepository orderRepository; // 依赖抽象
private final NotificationService notificationService; // 依赖抽象 // 通过构造函数注入依赖
public OrderService(OrderRepository orderRepository,
NotificationService notificationService) {
this.orderRepository = orderRepository;
this.notificationService = notificationService;
} public void createOrder(Order order) {
orderRepository.save(order);
notificationService.sendNotification(
order.getCustomerEmail(),
"订单创建成功"
);
}
} // 具体实现
public class MySQLOrderRepository implements OrderRepository {
@Override
public void save(Order order) {
// MySQL存储逻辑
} @Override
public Order findById(Long id) {
// 查询逻辑
return null;
}
} public class EmailNotificationService implements NotificationService {
@Override
public void sendNotification(String recipient, String message) {
// 邮件发送逻辑
}
}

依赖注入实践

// 使用Spring框架的依赖注入
@Service
public class UserService {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
private final EventPublisher eventPublisher; public UserService(UserRepository userRepository,
PasswordEncoder passwordEncoder,
EventPublisher eventPublisher) {
this.userRepository = userRepository;
this.passwordEncoder = passwordEncoder;
this.eventPublisher = eventPublisher;
} public User createUser(CreateUserRequest request) {
// 业务逻辑实现
User user = new User();
user.setUsername(request.getUsername());
user.setPassword(passwordEncoder.encode(request.getPassword())); User savedUser = userRepository.save(user);
eventPublisher.publishEvent(new UserCreatedEvent(savedUser)); return savedUser;
}
}

七、接口设计的最佳实践

参数设计原则

使用明确的参数类型,避免使用基本类型和字符串传递复杂信息。

// 不好的设计
public interface OrderService {
String createOrder(String customerInfo, String itemsInfo, String addressInfo);
} // 好的设计
public interface OrderService {
OrderResult createOrder(CreateOrderRequest request);
} public class CreateOrderRequest {
private Long customerId;
private List<OrderItem> items;
private Address shippingAddress;
private PaymentMethod paymentMethod; // getters and setters
} public class OrderResult {
private Long orderId;
private OrderStatus status;
private BigDecimal totalAmount;
private Date createdTime; // getters and setters
}

返回值设计

统一返回值格式,提供丰富的状态信息。

// 统一的API响应格式
public class ApiResponse<T> {
private boolean success;
private String message;
private String errorCode;
private T data;
private Long timestamp; public static <T> ApiResponse<T> success(T data) {
ApiResponse<T> response = new ApiResponse<>();
response.setSuccess(true);
response.setData(data);
response.setTimestamp(System.currentTimeMillis());
return response;
} public static <T> ApiResponse<T> error(String errorCode, String message) {
ApiResponse<T> response = new ApiResponse<>();
response.setSuccess(false);
response.setErrorCode(errorCode);
response.setMessage(message);
response.setTimestamp(System.currentTimeMillis());
return response;
}
} // 使用统一返回格式的接口
public interface UserController {
ApiResponse<User> getUserById(Long id);
ApiResponse<List<User>> getUsers(PageRequest pageRequest);
ApiResponse<Void> deleteUser(Long id);
}

异常处理设计

定义清晰的异常层次结构,提供有意义的错误信息。

// 基础业务异常
public abstract class BusinessException extends Exception {
private final String errorCode;
private final String errorMessage; public BusinessException(String errorCode, String errorMessage) {
super(errorMessage);
this.errorCode = errorCode;
this.errorMessage = errorMessage;
} // getters
} // 具体业务异常
public class UserNotFoundException extends BusinessException {
public UserNotFoundException(Long userId) {
super("USER_NOT_FOUND", "用户不存在: " + userId);
}
} public class InvalidPasswordException extends BusinessException {
public InvalidPasswordException() {
super("INVALID_PASSWORD", "密码格式不正确");
}
} // 接口中的异常声明
public interface UserService {
User getUserById(Long id) throws UserNotFoundException;
User login(String username, String password)
throws UserNotFoundException, InvalidPasswordException;
}

版本控制策略

为接口设计版本控制机制,支持向后兼容的演进。

// 版本化接口设计
public interface UserServiceV1 {
User createUser(String username, String email);
} public interface UserServiceV2 {
User createUser(CreateUserRequest request);
User createUserWithProfile(CreateUserWithProfileRequest request);
} // 向后兼容的实现
@Service
public class UserServiceImpl implements UserServiceV1, UserServiceV2 { @Override
public User createUser(String username, String email) {
// 将V1接口转换为V2接口调用
CreateUserRequest request = new CreateUserRequest();
request.setUsername(username);
request.setEmail(email);
return createUser(request);
} @Override
public User createUser(CreateUserRequest request) {
// V2接口的实现
return null;
} @Override
public User createUserWithProfile(CreateUserWithProfileRequest request) {
// 新功能实现
return null;
}
}

八、接口文档和契约

接口文档的重要性

完善的接口文档是团队协作的基础。文档应该包括:

  1. 接口目的和功能说明
  2. 参数详细描述
  3. 返回值格式说明
  4. 异常情况处理
  5. 使用示例
/**
* 用户管理服务接口
*
* @author 开发团队
* @version 2.0
* @since 2024-01-01
*/
public interface UserService { /**
* 根据用户ID获取用户信息
*
* @param userId 用户ID,必须大于0
* @return 用户信息,如果用户不存在返回null
* @throws IllegalArgumentException 当userId小于等于0时抛出
* @throws ServiceException 当系统异常时抛出
*
* @example
* <pre>
* UserService userService = ...;
* User user = userService.getUserById(123L);
* if (user != null) {
* System.out.println("用户名: " + user.getUsername());
* }
* </pre>
*/
User getUserById(Long userId) throws ServiceException; /**
* 创建新用户
*
* @param request 创建用户请求,不能为null
* - username: 用户名,长度3-20字符,不能为空
* - email: 邮箱地址,必须符合邮箱格式
* - password: 密码,长度6-20字符
* @return 创建成功的用户信息,包含系统生成的用户ID
* @throws ValidationException 当请求参数验证失败时抛出
* @throws DuplicateUserException 当用户名或邮箱已存在时抛出
* @throws ServiceException 当系统异常时抛出
*/
User createUser(CreateUserRequest request)
throws ValidationException, DuplicateUserException, ServiceException;
}

契约测试

使用契约测试确保接口实现符合设计

@ExtendWith(MockitoExtension.class)
class UserServiceContractTest { @Mock
private UserRepository userRepository; @InjectMocks
private UserServiceImpl userService; @Test
@DisplayName("根据ID获取用户 - 用户存在时应返回用户信息")
void getUserById_WhenUserExists_ShouldReturnUser() throws ServiceException {
// Given
Long userId = 1L;
User expectedUser = new User(userId, "testuser", "test@example.com");
when(userRepository.findById(userId)).thenReturn(Optional.of(expectedUser)); // When
User actualUser = userService.getUserById(userId); // Then
assertThat(actualUser).isNotNull();
assertThat(actualUser.getId()).isEqualTo(userId);
assertThat(actualUser.getUsername()).isEqualTo("testuser");
} @Test
@DisplayName("根据ID获取用户 - 用户不存在时应返回null")
void getUserById_WhenUserNotExists_ShouldReturnNull() throws ServiceException {
// Given
Long userId = 999L;
when(userRepository.findById(userId)).thenReturn(Optional.empty()); // When
User actualUser = userService.getUserById(userId); // Then
assertThat(actualUser).isNull();
} @Test
@DisplayName("根据ID获取用户 - 无效ID应抛出异常")
void getUserById_WhenInvalidId_ShouldThrowException() {
// Given
Long invalidId = -1L; // When & Then
assertThatThrownBy(() -> userService.getUserById(invalidId))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("用户ID必须大于0");
}
}

九、性能和安全考虑

接口性能优化

设计时要考虑性能影响,避免接口调用成为系统瓶颈。

// 批量操作接口
public interface UserService {
// 单个操作
User getUserById(Long id); // 批量操作,提升性能
List<User> getUsersByIds(List<Long> ids); // 分页查询,避免一次性加载大量数据
PageResult<User> getUsers(PageRequest pageRequest); // 异步操作接口
CompletableFuture<User> getUserByIdAsync(Long id);
} // 分页结果封装
public class PageResult<T> {
private List<T> content;
private long totalElements;
private int totalPages;
private int currentPage;
private int pageSize; // constructors, getters and setters
} // 分页请求参数
public class PageRequest {
private int page = 0;
private int size = 20;
private String sortBy;
private String sortDirection = "ASC"; // getters and setters
}

接口安全设计

在接口层面考虑安全防护,防止恶意调用和数据泄露。

// 安全的接口设计
public interface SecureUserService { /**
* 获取用户信息(敏感信息脱敏)
*/
UserDTO getUserById(Long id, SecurityContext context); /**
* 更新用户信息(需要权限验证)
*/
@RequiresPermission("USER_UPDATE")
UserDTO updateUser(Long id, UpdateUserRequest request, SecurityContext context); /**
* 删除用户(需要高级权限)
*/
@RequiresRole("ADMIN")
void deleteUser(Long id, SecurityContext context);
} // 安全上下文
public class SecurityContext {
private Long currentUserId;
private Set<String> roles;
private Set<String> permissions;
private String sessionId; // 权限检查方法
public boolean hasPermission(String permission) {
return permissions.contains(permission);
} public boolean hasRole(String role) {
return roles.contains(role);
}
} // 数据传输对象(DTO)- 隐藏敏感信息
public class UserDTO {
private Long id;
private String username;
private String email; // 可能需要脱敏
private Date createdTime;
// 不包含密码等敏感信息 // 邮箱脱敏方法
public String getMaskedEmail() {
if (email != null && email.contains("@")) {
String[] parts = email.split("@");
return parts[0].substring(0, 2) + "***@" + parts[1];
}
return email;
}
}

十、总结

核心要点回顾

接口设计的五大核心原则:

  1. 单一职责原则(SRP):每个接口只负责一个明确的功能
  2. 开闭原则(OCP):对扩展开放,对修改关闭
  3. 里氏替换原则(LSP):实现类要完全遵循接口契约
  4. 接口隔离原则(ISP):设计小而专一的接口
  5. 依赖倒置原则(DIP):依赖抽象而不是具体实现

设计最佳实践

参数和返回值设计:

  • 使用明确的参数类型,避免基本类型传递复杂信息
  • 统一返回值格式,提供丰富的状态信息
  • 设计清晰的异常层次结构

版本和文档管理:

  • 为接口设计版本控制机制
  • 编写完善的接口文档和使用示例
  • 使用契约测试确保实现正确性

性能和安全考虑:

  • 提供批量操作和分页查询接口
  • 在接口层面实现安全防护
  • 对敏感数据进行脱敏处理

实际应用建议

设计阶段:

  • 充分理解业务需求,明确接口职责
  • 考虑未来的扩展需求,设计灵活的接口
  • 与团队成员充分沟通,确保设计共识

实现阶段:

  • 严格按照接口契约实现
  • 编写完整的单元测试和集成测试
  • 持续重构,优化接口设计

维护阶段:

  • 谨慎修改已发布的接口
  • 通过版本控制支持接口演进
  • 及时更新文档和示例代码

常见问题避免

设计陷阱:

  • 避免设计过于复杂的接口
  • 不要在接口中暴露实现细节
  • 避免频繁修改已发布的接口

性能陷阱:

  • 避免设计导致N+1查询的接口
  • 不要忽视批量操作的需求
  • 避免返回过大的数据集

掌握了这些接口设计原则和最佳实践,你就能设计出既优雅又实用的API。好的接口设计不仅能提升开发效率,还能让系统更加稳定和可维护。记住,接口设计是一个需要不断学习和实践的过程,随着经验的积累,你的设计水平会不断提升。


想要学习更多软件架构和设计模式的实战技巧?欢迎关注我的微信公众号【一只划水的程序猿】,这里有最前沿的技术分享和最实用的编程经验,让你的代码设计能力快速提升!记得点赞收藏,与更多开发者分享这些宝贵的设计原则!

接口设计的原则:构建优雅API的完整指南的更多相关文章

  1. 使用ASP.NET Core构建RESTful API的技术指南

    译者荐语:利用周末的时间,本人拜读了长沙.NET技术社区翻译的技术标准<微软RESTFul API指南>,打算按照步骤写一个完整的教程,后来无意中看到了这篇文章,与我要写的主题有不少相似之 ...

  2. php后台对接ios,安卓,API接口设计和实践完全攻略,涨薪必备技能

    2016年12月29日13:45:27    关于接口设计要说的东西很多,可能写一个系列都可以,vsd图都得画很多张,但是由于个人时间和精力有限,所有有些东西后面再补充   说道接口设计第一反应就是r ...

  3. 0226 rest接口设计

                背景 为了更方便的书写和阐述问题,文章中按照第一人称的角度书写.作为一个以java为主要开发语言的工程师,我所描述的都是java相关的编码和设计. 工程师的静态输出就是代码和文 ...

  4. 优秀的API接口设计原则及方法(转)

    一旦API发生变化,就可能对相关的调用者带来巨大的代价,用户需要排查所有调用的代码,需要调整所有与之相关的部分,这些工作对他们来说都是额外的.如果辛辛苦苦完成这些以后,还发现了相关的bug,那对用户的 ...

  5. Web API接口设计经验总结

    在Web API接口的开发过程中,我们可能会碰到各种各样的问题,我在前面两篇随笔<Web API应用架构在Winform混合框架中的应用(1)>.<Web API应用架构在Winfo ...

  6. atitit.基于http json api 接口设计 最佳实践 总结o7

    atitit.基于http  json  api 接口设计 最佳实践 总结o7 1. 需求:::服务器and android 端接口通讯 2 2. 接口开发的要点 2 2.1. 普通参数 meth,p ...

  7. Spring+SpringMVC+MyBatis+easyUI整合进阶篇(二)RESTful API实战笔记(接口设计及Java后端实现)

    写在前面的话 原计划这部分代码的更新也是上传到ssm-demo仓库中,因为如下原因并没有这么做: 有些使用了该项目的朋友建议重新创建一个仓库,因为原来仓库中的项目太多,结构多少有些乱糟糟的. 而且这次 ...

  8. RESTful API实战笔记(接口设计及Java后端实现)

    写在前面的话 原计划这部分代码的更新也是上传到ssm-demo仓库中,因为如下原因并没有这么做: 有些使用了该项目的朋友建议重新创建一个仓库,因为原来仓库中的项目太多,结构多少有些乱糟糟的. 而且这次 ...

  9. 总结常见的违背Rest原则的接口设计做法

    此文已由作者郑华斌授权网易云社区发布. REST这词我们常常挂在嘴边,比如"开发一个rest接口",又比如Spring项目的代码: @RestControllerpublic cl ...

  10. Web API接口设计(学习)

    1.在接口定义中确定MVC的GET或者POST方式 由于我们整个Web API平台是基于MVC的基础上进行的API开发,因此整个Web API的接口,在定义的时候,一般需要显示来声明接口是[HttpG ...

随机推荐

  1. [源码系列:手写spring] IOC第二节:BeanDefinition和BeanDefinitionRegistry

    主要内容 BeanDefinition:顾名思义,就是类定义信息,包含类的class类型.属性值.方法等信息. BeanDefinitionRegistry:添加BeanDefinitionRegis ...

  2. Arrays工具类--java进阶day06

    1.Arrays工具类 这些方法都是针对数组,并且都被static修饰,可以直接使用类名进行调用 1.toString 将数组拼接成带有相对应格式的字符串,可用于展示数组 2.equals 比较两个数 ...

  3. IE 条件注释

    参考文档 IE6 IE7 IE8 IE9 IE10 Css hack及IE条件注释法 IE的有条件注释判定IE版本详解(附实例代码)

  4. 强化学习(on-policy)同步并行采样(on-line)的并行化效率分析

    在强化学习中(on-line)的算法如果是on-policy的算法都是需要较大的采样样本的,因此采样的效率往往对整个算法运行效率有着自关重要的影响,在deepmind(Google)公司的强化学习的并 ...

  5. 如何在 Vim 里直接完成 Git 操作?

    Vim 是 Linux 下一款很常用的文本编辑器,虽然它对初学者而言并不友好,但通过一些插件的配合,它可以被打造成一款很强大的 IDE .良许曾经介绍过三款很常用的插件,可点击以下链接查看: Vim ...

  6. Windows端口号操作

    1.查看所有端口号:netstat -ano 2.查看端口号:netstat -ano | findstr "16888" 3.杀死进程:taskkill /f /pid 8524

  7. (原创)[开源][.Net Framework 4.0] SimpleLiveDataFeed(极易使用的采集数据滚动显示,基于MSChart)更新 v1.0,增加NuGet包

    一.前言 不管还有没有用的,即然是自己原创,就开源了发出来吧,也算是在这互联网上留下了些许记忆. 碎碎念:很早之前的 Windows Mobile 6.5/6.5.3 相关开发记录因为懒惰,现在想回忆 ...

  8. macOS终端修改DNS

    以WiFi为例 networksetup -listallnetworkservices networksetup -setdnsservers Wi-Fi 8.8.8.8 networksetup ...

  9. 全局搜索——Lucene.Net与盘古分词的实现思路

    一.Lucene.Net 1.Lucene.Net介绍: Lucene.Net是一个C#开发的开源全文索引库(自带的有索引管理.分词.查询) Lucene.Net.Index 提供索引管理,词组排序. ...

  10. Apache Druid RCE漏洞复现及修复(CVE-2023-25194)

    Apache Druid RCE漏洞复现及修复(CVE-2023-25194) 2023-03-16 声明:本文分享的安全工具和项目均来源于网络,漏洞环境本地搭建,仅供安全研究与学习,如用于其他用途, ...