❓首先什么是设计模式?
😵相信刚上大学的你和我一样,在学习这门课的时候根本不了解这些设计原则和模式有什么用处,反而不如隔壁的C++更有意思,至少还能弹出一个小黑框,给我个hello world。
✨ 如何你和我一样也是这么想,那接下来咱们以贴合生活实际的方式来看看设计模式到底有什么神奇的地方?
😄更多有趣的设计模式讲解都在设计模式👍专栏,欢迎来看看🎉🎉🎉。
【设计模式】六大原则-下
- 六大原则-下
- 单一职责
- 接口隔离原则
- 合成复用原则
- 总结
新来的小伙伴你可能会注意到,这咋直接六大原则下了,上呢?
别着急,这里有一个传送门,大家可以看看上半部分的内容。
🆒传送门🆒 【设计模式】六大原则-上
接下来咱们一起看看另外三个原则
六大原则-下
单一职责
每个类应该只有一个引起它变化的原因,即一个类只负责一项职责。
哦吼,熟悉的感觉,还是这么抽象!
害,咱们一起看看这是啥意思
相信大家读完之后就知道这条原则说的是啥,就是一个类只负责一项功能,但有一个问题,为啥一个类只负责一个功能呢?
举个例子哈,现在我们做了一个图书管理系统,然后有一个类,负责订单的处理,如下所示
public class OrderManager {
// 创建订单
public void createOrder(Order order) {
// 创建订单的逻辑
System.out.println("Order created: " + order.getId());
}
// 取消订单
public void cancelOrder(Order order) {
// 取消订单的逻辑
System.out.println("Order canceled: " + order.getId());
}
// 计算订单总价
public double calculateTotal(Order order) {
double total = 0.0;
for (OrderItem item : order.getItems()) {
total += item.getPrice() * item.getQuantity();
}
// 假设税费是总价的10%
total += total * 0.1;
return total;
}
// 生成订单报表
public void generateOrderReport(Order order) {
// 生成订单报表的逻辑
System.out.println("Order Report for Order ID: " + order.getId());
System.out.println("Total Price: " + calculateTotal(order));
}
// 持久化订单
public void saveOrder(Order order) {
// 保存订单的逻辑
System.out.println("Order saved to database: " + order.getId());
}
}
这样有啥问题吗
- 首先,这个代码太复杂了,我们现在还没有加上具体的处理流程,整个代码已经非常长了,理解起来非常困难。
- 其次,修改存在风险。我们可以看到generateOrderReport调用了calculateTotal方法,如果以后我们因为其他的需求进行了改动,例如打折促销修改了计算总价的方式,generateOrderReport极有可能出现问题。
那怎么做呢?
就可以按照单一职责原则进行修改,修改起来非常简单,就是直接为每一个方法创建一个类即可,如下
public class OrderCalculator {
public double calculateTotal(Order order) {
double total = 0.0;
for (OrderItem item : order.getItems()) {
total += item.getPrice() * item.getQuantity();
}
// 假设税费是总价的10%
total += total * 0.1;
return total;
}
}
public class OrderReportGenerator {
private final OrderCalculator calculator;
public OrderReportGenerator(OrderCalculator calculator) {
this.calculator = calculator;
}
public void generateOrderReport(Order order) {
// 生成订单报表的逻辑
System.out.println("Order Report for Order ID: " + order.getId());
System.out.println("Total Price: " + calculator.calculateTotal(order));
}
}
现在,我们可以很轻松的搞清楚这个类是干什么的,可读性增强了。
并且,上面我们提到的如果打折怎么办?
我们完全可以为OrderCalculator添加一个接口,这样如果有新的结算策略的时候,增加一个实现类就好了。
**看到这,是不是有点熟悉,这是不是就是所谓的对修改封闭,对扩展开放,开闭原则。**代码如下
public interface CalculatorInterface {
public double calculateTotal(Order order);
}
public class OrderCalculator implements CalculatorInterface {
public double calculateTotal(Order order) {
double total = 0.0;
for (OrderItem item : order.getItems()) {
total += item.getPrice() * item.getQuantity();
}
// 假设税费是总价的10%
total += total * 0.1;
return total;
}
}
public class DiscountCalculator implements CalculatorInterface {
public double calculateTotal(Order order) {
double total = 0.0;
// 打折操作
return total;
}
}
接口隔离原则
不应该强迫客户依赖它们不使用的方法。应该将臃肿的接口分解为更小、更具体的接口,这样客户只需知道它们感兴趣的方法即可。
这个是啥意思呢?
大白话翻译一下就是,我们设计的接口的功能应该尽可能细粒,不要让一个接口实现的功能太大,尽量细化一点。
例如,我们现在写一个包饺子的接口,如下所示
public interface MakeDumpling {
void cook();
}
public class MakeDumplingImpl implements MakeDumpling {
@Override
public void cook() {
System.out.println("调馅");
System.out.println("擀面皮");
System.out.println("包饺子");
System.out.println("下锅");
}
}
整体实现的还是很简单的哈,但现在有一个问题,我想要包包子,你就会发现,我们需要新定义一个接口,并且需要把调馅,擀面皮和下锅这些重复的操作代码再写一遍。
看到这,不知道你有没有意识到为什么要遵守接口隔离原则。
那我们怎么改呢
public interface interface1 {
// 擀面皮
void rollTheDough();
}
public interface interface1 {
// 调饺子馅
void mixDumplingFilling();
}
这样是不是就把功能更细化了一点,我们就可以提高代码的复用性了。
合成复用原则
尽量使用对象组合而不是继承来实现代码复用。组合可以在运行时选择或改变行为,使得系统更具灵活性。
这个比较简单一点,其实读过就明白了。很符合我们的认知哈。先看看比较官方的解释
合成复用原则是一种重要的设计原则,旨在通过组合或聚合关系来实现代码的复用,而非过度依赖继承关系。该原则强调优先使用对象组合来达到复用的目的,从而降低类与类之间的耦合度,提高系统的灵活性和可维护性
太官方了有点看不懂,咱来点通俗解释。
为啥能降低耦合呢?
咱们想一下这样一个场景,你设计的系统中大部分类都继承了一个父类。但是现在由于业务变化,父类对一个方法的参数进行了修改,那好嘛,所有子类都要改,这是不是有点难受。
那为啥能提高灵活性呢
想一下这样的场景,B类继承了A类,并使用了A的operate方法,但是现在出现了一个问题,C类也提供一个operate方法,并且我需要动态选择使用A或则C的方法,那继承很明显不行,那没办法,整不了。但如果使用组合的方式呢,我们可以动态的选择A或C的方法,灵活性就大大提升了。
总结
六大原则就全部介绍完了。