Java基础知识整理——软件设计六大原则(单一职责原则与开闭原则)
六大设计原则是面向对象设计的基石,掌握好它,会让我们在后期的软件开发旅程中事半功倍。
一、六大设计原则概念:
1、单一职责原则(Single Responsibility Principle):一个模块、类、方法只做和它相关一系列或者一件事。
2、开闭原则(Open-Closed Principle):对扩展开放,对修改关闭。
3、里氏替换原则(Liskov Substitution Principle):继承方面,子类必须能够替换父类,凡是使用父类能完成的事,使用子类也可以同样完成,子类不能修改父类的语义。
4、接口隔离原则(Interface Segregation Principle):接口要小而专,通过组合实现更复杂的接口。
5、依赖倒置原则(Dependency Inversion Principle):高层模块不应该依赖底层模块的具体实现,而是都应依赖于抽象(接口或抽象类)。抽象不应依赖细节,而细节应依赖抽象。
6、迪米特法则(Law of Demeter):最少知识原则,将多余的操作封装起来,每次调用只涉及一个方法。
二、如果违反六大设计原则将引入哪些问题:
1、违反单一职责原则:
比如说我们有一个电商系统,有支付模块、订单模块、库存模块等等,如果我们把所有的功能放在一个类中,那么这个类就会非常臃肿而且难以维护,修改其中任何一个地方都可能影响到别的功能。我们应该按照模块先做一级分类,再根据具体业务功能做二级分类,具体到类中的方法,一个方法只完成一件事,做到三级分类,这样模块、类、方法。保持逻辑、有序的三级序列,无论是扩展还是修改,都容易清晰的定位问题。
联系到实际,我们阅读技术书籍时,比如说《Java核心技术卷Ⅰ》,如果没有目录,所有的内容都放到一章里面,那么我们学起来就很费力,这本书有654页,如果不分章节,不分小节的话,阅读起来实在太难了。而现实是,首先有目录,让我们知道有多少章,其次有小节,让我们知道每章大概有多少内容,这种结构就很清晰。我想软件开发也是这样,尽量用逻辑清晰的结构,每个模块、类、方法只负责一个职责。
(1)错误代码示例:
public class OrderService { public void createOrder(Order order) { // 创建订单 System.out.println("订单已创建,订单ID:" + order.getOrderId()); // 发送短信 System.out.println("发送短信给 " + order.getUserPhone() + ":您的订单已生成"); // 发送邮件 System.out.println("发送邮件给 " + order.getUserEmail() + ":您的订单已生成,请注意查收"); } }
(2)正确代码示例:
// 负责发送短信 public class SmsService { public void sendSms(String phoneNumber, String message) { // 发送短信逻辑 System.out.println("发送短信给 " + phoneNumber + ":" + message); } } // 负责发送邮件 public class EmailService { public void sendEmail(String email, String subject, String content) { // 发送邮件逻辑 System.out.println("发送邮件给 " + email + ":" + subject + " - " + content); } } // 负责订单创建 public class OrderService { private SmsService smsService = new SmsService(); private EmailService emailService = new EmailService(); public void createOrder(Order order) { // 创建订单的核心逻辑 System.out.println("订单已创建,订单ID:" + order.getOrderId()); // 通知用户 smsService.sendSms(order.getUserPhone(), "您的订单已生成"); emailService.sendEmail(order.getUserEmail(), "订单确认", "您的订单已生成,请注意查收"); } }
对比错误示例以及正确示例,我们发现,违反单一职责的代码只有一个类,但是却在一个方法中做了3件事,如果将来业务变化,比如短信业务调整那么就需要修改代码,由于所有业务逻辑都在一个类的方法里面,有较大可能引入风险和BUG。
而正确的示例有三个类,每个类只做一件事,程序运行时,通过组合的方式,完成业务功能。将来业务变化时,无论是新增还是已有的服务变化,都不会影响orderService的逻辑。
2、违反开闭原则:
就用电商场景举例,比如说我们下单后,会受到短信和邮件提醒,将来还会收到app提醒以及微信公众号提醒。
这次我们先看代码,在说理解。
(1)错误代码示例:
public class OrderService { public void createOrder(Order order, String notifyType) { // 创建订单 System.out.println("订单已创建,订单ID:" + order.getOrderId()); // 根据类型发送通知 if ("sms".equals(notifyType)) { sendSms(order.getUserPhone()); } else if ("email".equals(notifyType)) { sendEmail(order.getUserEmail()); } else if ("app".equals(notifyType)) { sendAppMessage(order.getUserId()); } } private void sendSms(String phone) { System.out.println("发送短信给:" + phone); } private void sendEmail(String email) { System.out.println("发送邮件给:" + email); } private void sendAppMessage(Long userId) { System.out.println("发送 App 消息给用户ID:" + userId); } }
假如现在要新增微信公众号推送,那么我们就需要修改代码,增加if逻辑,同时添加一个新方法。在修改代码逻辑的时候,就容易犯错,并且单元测试要重跑。
(2)正确代码示例:
①实体类:
public class Order { private Long orderId; private String userPhone; private String userEmail; private Long userId; // 构造方法、getter/setter 省略 public Order(Long orderId, String userPhone, String userEmail, Long userId) { this.orderId = orderId; this.userPhone = userPhone; this.userEmail = userEmail; this.userId = userId; } public Long getOrderId() { return orderId; } public String getUserPhone() { return userPhone; } public String getUserEmail() { return userEmail; } public Long getUserId() { return userId; } }
②抽象接口:
public interface NotificationService { void notifyUser(Order order); }
③通知实现类:
public class SmsNotification implements NotificationService { @Override public void notifyUser(Order order) { System.out.println("发送短信给 " + order.getUserPhone() + ":您的订单已生成"); } }
public class EmailNotification implements NotificationService { @Override public void notifyUser(Order order) { System.out.println("发送邮件给 " + order.getUserEmail() + ":您的订单已生成"); } }
public class AppNotification implements NotificationService { @Override public void notifyUser(Order order) { System.out.println("发送 App 消息给用户ID:" + order.getUserId() + ":您的订单已生成"); } }
④订单服务类:
import java.util.ArrayList; import java.util.List; public class OrderService { private List<NotificationService> notifications = new ArrayList<>(); // 添加通知方式 public void addNotification(NotificationService service) { notifications.add(service); } public void createOrder(Order order) { // 创建订单逻辑 System.out.println("订单已创建,订单ID:" + order.getOrderId()); // 调用所有通知服务 for (NotificationService notification : notifications) { notification.notifyUser(order); } } }
⑤测试类:
public class TestMain { public static void main(String[] args) { // 创建订单对象 Order order = new Order(1001L, "13800138000", "user@example.com", 9527L); // 创建订单服务 OrderService orderService = new OrderService(); // 添加多种通知方式 orderService.addNotification(new SmsNotification()); orderService.addNotification(new EmailNotification()); orderService.addNotification(new AppNotification()); // 创建订单并通知 orderService.createOrder(order); } }
当需求变化时,比如说不需要邮件提醒了,那么我们不添加邮件服务就行,比如说短信运营商修改,那么只需要改短信业务的配置,或者新增一个实现类即可。业务逻辑是不用修改的。
给自己一个正确的学习方向,以前写代码,大部分考虑的是实现,但是无形中已经违反这些原则了,后续的开发要给自己提个醒。
未完待续。。。6月19日继续补充。

浙公网安备 33010602011771号