程序设计的所有原则和方法论都是追求一件事——简单——功能简单、依赖简单、修改简单、理解简单。因为只有简单才好用,简单才好维护。因此,不应该以评论艺术品的眼光来评价程序设计是否优秀,程序设计的艺术不在于有多复杂多深沉,而在于能用多简单的方式实现多复杂的业务需求;不在于只有少数人能理解,而在于能让更多人理解。
系列文章
程序设计——方法论总结
程序设计——领域驱动设计
前言
本篇希望可以为你解决如下问题提供思路
- 代码很难阅读:命名不规范、不地道,方法和类太臃肿,类之间调用关系绕来绕去
- 类分类不清楚,不知道一个类应该放哪个包下
- 分层不清楚,不知道哪层应该放什么样的代码
- 业务代码比较分散
- 扩展性差,牵一发动全身
- 做需求分析、系统设计、甚至是与团队沟通方案时不知道使用什么图做描述,或者使用的图不够地道
本篇是程序设计方法论的总结,其中有些不详细的地方请谅解,还望诸君查阅更多资料。如果有表达不正确的地方,也请指出。
1. 设计原则
1.1 开闭原则(Open Closed Principle)
描述
Software entities like classes, modules and functions should be open for extension but closed for modification
一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。
适用场景: 模块、类、接口、方法
反例表现: 任何改动都只能在原来代码上删除和增加代码,牵一发而动全身,破坏原有代码的完整性,原来的功能都需要重新回归测试,才能保证功能没被影响。
好处: 扩展性好,代码变动风险低
如何做: 分离不变部分和易变部分,对不变部分做封装,对易变部分提供接口或者方法做扩展。
思考: 开闭原则是一切原则的总结,所有的原则都是为了实现开闭原则。
示例
先给出实现搜索功能的一个反例,如下
public class SearchService {
public Result search(Params params) {
// 关键词搜索
if ("keyword".equals(params.getType())) {
return searchByKeyword(params);
}
// 类目搜索
if ("category".equals(params.getType())) {
return searchByCategory(params);
}
// 促销搜索
if ("promotion".equals(params.getType())) {
return searchByPromotion(params);
}
return null;
}
}
修改后的代码,如下
// 定义一个搜索接口
public class ISearch{
Result search(Params params);
}
public class SearchService {
// 使用一个容器来管理每个搜索类型的实现
Map<String, ISearch> context = Map.of(
"keyword", new KeywordSearch(),
"category", new CategorySearch(),
"promotion", new PromotionSearch());
public Result search(Params params) {
// 从容器中获取对应的搜索对象
ISearch search = context.get(params.getEntryType());
if (search == null) {
return null;
}
return search.search(params);
}
}
1.2 单一职责原则(Single Responsibility Principle)
描述
There should never be more than one reason for a class to change
适用场景: 模块、类、接口、方法
反例表现: 代码结构不清,代码量大呈现臃肿,重复代码。后果可读性差,不可维护,牵一发而动全身。
好处: 代码粒度小,可读性好。
如何做: 通过横向和纵向划分职责,组织代码结构,可使代码层级结构更清晰
示例
下面代码违法单一职责
public interface UserService {
void register(String telephone, String password);
void login(String telephone, String password);
void sendEmail(String email);
}
修改后代码如下
public interface UserService {
void register(String telephone, String password);
void login(String telephone, String password);
}
public interface EmailService {
void sendEmail(String email);
}
1.3 依赖倒置原则(Dependence Inversion Principle)
描述
High level modules should not depend upon low level modules. Both should depend upon abstractions.
Abstractions should not depend upon details. Details should depend upon abstractions.
- 上层模块不应该依赖底层模块,它们都应该依赖于抽象。
- 抽象不应该依赖于细节,细节应该依赖于抽象。
作用
3. 避免类之间耦合
4. 提高系统稳定性
如何做
面向接口编程、IOC(spring ioc)、SPI
示例
以用户注册成功后发布成功消息为例子
// 1. 定义一个用户注册完成消息发布接口
public interface IUserRegisteredPublisher{
void publish(User user);
}
// 2. 实现用户注册完成消息发布接口,把消息发布到kafka
public class UserRegisteredKafkaPublisher implements IUserRegisteredPublisher{
@Override
public void publish(User user) {
System.out.println("send info to kafka");
}
}
// 3. 定义用户注册服务
public class UserRegisterService{
IUserRegisteredPublisher publisher;
UserRegisterService(UserRegisterService publisher) {
this.publisher = publisher;
}
public void register(