在软件开发的世界里,设计模式有如一本精妙的工程艺术指导准则,为我们提供了解决常见问题的优雅实现方案。然而,有些程序员可能会认为设计模式太过繁琐,一个简单的 if/else 语句就能解决问题,何必费心去学习这些看似复杂的概念呢?在这个系列的文章里,我和大家一起探讨为什么设计模式是值得的,以及如何在实际开发中去融入设计模式的思想。
拥有设计模式的思维方式,意味着你不仅仅是在写能够工作的代码,更是在构建具有良好结构的、易于理解和维护的代码。设计模式是过去经验的总结,它们提供了在各种场景下验证过的最佳实践,有助于避免常见的陷阱和错误。
需求总是在变化的,永远不可能存在一层不变的需求,而设计模式可以为你的代码提供更好的扩展性。通过采用开放封闭等设计原则,你的代码可以更容易地适应新的功能需求,而不需要对原有的代码进行大规模修改。
设计模式是一种通用的编程语言,它提供了一种共享的术语和理解方式,有助于团队成员更容易理解和协作。当所有人都熟悉常见的设计模式时,交流就会变得更加高效,合作更加顺畅。
在日常编码中刻意使用设计模式是学习的最佳途径。不要觉得使用设计模式会让代码变得复杂和难以理解,相反,它会使你的代码更加清晰、模块化,易于维护。逐渐将设计模式的思想融入到自己的编程风格中,形成个性化的代码风格。
学以致用是掌握设计模式的关键。在实际项目中应用设计模式,从而更好地理解其实际应用场景。实战中的经验往往比理论知识更加深刻和有说服力。
不要急于一时,逐步演进是掌握设计模式的关键。从一段已有的代码开始,尝试用设计模式和SOLID原则进行重构。通过对比前后的代码,你将会发现自己在设计和编写代码方面的进步。
设计模式的基石是设计原则,例如开放封闭原则、单一职责原则等。深入理解这些原则,能够更好地指导你在实际项目中的设计和编码过程。设计原则是设计模式的根基,也是培养良好代码习惯的关键。
一般的书籍和文章讲到设计原则,都讲的是 SOLID 原则,而我这里要说的是七原则:SOLID + CARP + LoD
SRP 是一项简单易懂且重要的原则,但是在实际过程中往往最容易忽略的,它强调在应用中,一个类应该只有一个引起变化的原因,只有一个职责。
SRP 是基于康威定律的推导结论:软件系统的最佳结构高度依赖于开发这个系统的组织的内部结构,每个软件模块都有且只有一个需要被改变的理由。
多职责的类设计
public class UserService {
/*** 运营部员工绩效计算 */
public BigDecimal calculateKPIResultForOperation() { }
/*** 商务员工绩效计算 */
public BigDecimal calculateKPIResultForBusiness() { }
/*** 产品部员工绩效计算 */
public BigDecimal calculateKPIResultForProduct() { }
/*** 技术部员工绩效计算 */
public BigDecimal calculateKPIResultForTechnology() { }
}
SRP 后的类设计
public interface UserService {
public BigDecimal calculateKPIResult();
}
// 不同部门实现接口
public class OperationUserService implement UserService {}
public class BusinessUserService implement UserService {}
public class ProductUserService implement UserService {}
public class TechnologyUserService implement UserService {}
OCP 的核心思想是通过扩展已有的代码,增加新的行为和功能,而不是修改已有的代码。
无 OCP 的代码
public class Calculator {
public int add(int a, int b) {
return a + b;
}
public int subtract(int a, int b) {
return a - b;
}
public int multiply(int a, int b) {
return a * b;
}
public int divide(int a, int b) {
return a / b;
}
}
有 OCP 的代码
public interface Operation {
int calculate(int a, int b);
}
public class AddOperation implements Operation {
public int calculate(int a, int b) {
return a + b;
}
}
public class SubtractOperation implements Operation {
public int calculate(int a, int b) {
return a - b;
}
}
public class MultiplyOperation implements Operation {
public int calculate(int a, int b) {
return a * b;
}
}
public class DivideOperation implements Operation {
public int calculate(int a, int b) {
return a / b;
}
}
LSP 强调在软件中,子类必须能够替换其父类,即子类应该具有与父类相同的行为和功能,而不仅仅是继承父类的属性和方法。
无 LSP 代码
class Shape {
void draw() {
System.out.println("Drawing a shape");
}
}
class Circle extends Shape {
void draw() {
System.out.println("Drawing a circle");
}
}
有 LSP 代码
interface Drawable {
void draw();
}
class Shape implements Drawable {
public void draw() {
System.out.println( "Drawing a shape");
}
}
class Circle extends Shape {
public void draw() {
System.out.println("Drawing a circle");
}
}
ISP 强调在应用中使用多个特定的接口,而不是一个单一的总接口,从而避免端侧就不需要被强制依赖他们不需要的接口。
无 ISP 代码
interface ShoppingCart {
void addItem(Product product, int quantity);
void removeItem(Product product);
void updateQuantity(Product product, int quantity);
}
class ShoppingCartImpl implements ShoppingCart {
private Map items = new HashMap<>();
@Override
public void addItem(Product product, int quantity) {
items.put(product, quantity);
}
@Override
public void removeItem(Product product) {
items.remove(product);
}
@Override
public void updateQuantity(Product product, int quantity) {
int currentQuantity = items.get(product);
items.put(product, currentQuantity + quantity);
}
}
有 ISP 代码
interface AddToCart {
void addItem(Product product, int quantity);
}
interface RemoveFromCart {
void removeItem(Product product);
}
interface UpdateQuantity {
void updateQuantity(Product product, int quantity);
}
class ShoppingCartImpl implements AddToCart, RemoveFromCart, UpdateQuantity {
private Map items = new HashMap<>();
@Override
public void addItem(Product product, int quantity) {
items.put(product, quantity);
}
@Override
public void removeItem(Product product) {
items.remove(product);
}
@Override
public void updateQuantity(Product product, int quantity) {
int currentQuantity = items.get(product);
items.put(product, currentQuantity + quantity);
}
}
DIP 强调在应用中,高层模块不应该依赖于底层模块,它们应该依赖于抽象。
无 DIP 代码
class UserService {
private UserDao userDao;
public UserService(UserDao userDao) {
this.userDao = userDao;
}
public User getUserById(int userId) {
return userDao.getUserById(userId);
}
}
class UserDao {
public User getUserById(int userId) {
// 具体实现逻辑,如从数据库中获取用户信息
return new User(userId, "John Doe");
}
}
有 DIP 代码
interface UserDataAccess {
User getUserById(int userId);
}
class UserDao implements UserDataAccess {
@Override
public User getUserById(int userId) {
// 具体实现逻辑,如从数据库中获取用户信息
return new User(userId, "John Doe");
}
}
class UserService {
private UserDataAccess userDataAccess;
public UserService(UserDataAccess userDataAccess) {
this.userDataAccess = userDataAccess;
}
public User getUserById(int userId) {
return userDataAccess.getUserById(userId);
}
}
CARP 强调在应用设计过程中优先使用合成/聚合的关系,而不是继承的关系来实现复用。
无 CARP 代码
class Car {
private Engine engine;
private Transmission transmission;
private Wheel wheel;
private Door door;
public Car(Engine engine, Transmission transmission, Wheel wheel, Door door) {
this.engine = engine;
this.transmission = transmission;
this.wheel = wheel;
this.door = door;
}
public void start() {
engine.start();
}
public void shift(int gear) {
transmission.shift(gear);
}
public void turn(int degrees) {
wheel.turn(degrees);
}
public void open() {
door.open();
}
}
有 CARP 代码
interface Engine {
void start();
}
interface Transmission {
void shift(int gear);
}
interface Wheel {
// 可以添加一些方法,例如 rotate() 和 brake() 等
}
interface Door {
void open();
}
class Car {
private Engine engine;
private Transmission transmission;
private Wheel wheel;
private Door door;
public Car(Engine engine, Transmission transmission, Wheel wheel, Door door) {
this.engine = engine;
this.transmission = transmission;
this.wheel = wheel;
this.door = door;
}
}
LoD 强调在应用中应该尽量减少对象之间的直接依赖关系,降低耦合度,提高可维护性和可重用性。
核心思想是一个对象对其他对象保持最少的了解,并且只和那些和自己最有直接关系的对象进行交互。
一个对象只暴露必要的接口给其他对象,并且应该通过这些接口与其他对象进行交互。
无 LoD 代码
class Account {
private User user;
private List transactions;
public Account(User user, List transactions) {
this.user = user;
this.transactions = transactions;
}
public double getBalance() {
double balance = 0.0;
for (Transaction transaction : transactions) {
balance += transaction.getAmount();
}
return balance;
}
public void debit(double amount) {
user.setBalance(user.getBalance() - amount);
}
}
有 LoD 代码
interface UserService {
double getBalance(User user);
}
interface TransactionService {
void debit(User user, double amount);
}
class Account {
private UserService userService;
private TransactionService transactionService;
public Account(UserService userService, TransactionService transactionService) {
this.userService = userService;
this.transactionService = transactionService;
}
public double getBalance() {
return userService.getBalance(userService.getUser());
}
public void debit(double amount) {
transactionService.debit(userService.getUser(), amount);
}
}
接下来会逐一介绍各个设计模式。
我在介绍每一个设计模式的时候都会采用统一的框架,如下:
1、什么是 XXX 设计模式?
2、为什么使用 XXX 设计模式?
3、如何实现 XXX 设计模式?
4、是否存在缺陷和不足?
5、如何缓解缺陷和不足?