一、前言
在软件开发过程中,遵循良好的设计原则可以帮助我们构建出更加健壮、易于维护和扩展的系统,可能在此之前你也多少了了解过设计模式,但在实际的业务开发中使⽤用却不多,接下来我们将分别介绍这些原则及其重要性。
二、图示
三、内容
1. 单一职责原则
定义:单一职责原则
指出每个类应该只有一个职责,即一个类只负责一项功能或一类功能的逻辑。
例子: 假设你有一个Order
类,它负责处理订单信息同时也发送电子邮件通知,当需要修改电子邮件格式时,可能会影响到订单处理的逻辑,为了避免这种耦合,可以将发送邮件的功能提取到另一个类中,如EmailService
。
public class Order {
private String orderId;
private double amount;
public Order(String orderId, double amount) {
this.orderId = orderId;
this.amount = amount;
}
// ... 其他订单相关方法 ...
}
public interface EmailService {
void sendEmail(String message);
}
public class SimpleEmailService implements EmailService {
@Override
public void sendEmail(String message) {
System.out.println("Sending email: " + message);
}
}
好处:
- 更容易测试和维护。
- 降低模块间的耦合度。
2. 开放封闭原则
定义:开放封闭原则
指的是软件实体(类、模块、函数等)应该是对扩展开放的,而对修改封闭。
例子: 考虑一个支付系统,支持多种支付方式(信用卡
、PayPal
、Apple Pay
等)。可以通过继承和多态来实现新的支付方式,而不是修改现有代码。
应用:
- 使用接口或抽象类:定义一个接口或抽象类,并通过不同的实现类来扩展功能。
- 使用设计模式:策略模式、装饰器模式、工厂模式等设计模式都可以帮助实现开闭原则。
public interface PaymentMethod {
void processPayment(double amount);
}
public class CreditCardPayment implements PaymentMethod {
@Override
public void processPayment(double amount) {
System.out.println("Processing credit card payment of $" + amount);
}
}
public class PayPalPayment implements PaymentMethod {
@Override
public void processPayment(double amount) {
System.out.println("Processing PayPal payment of $" + amount);
}
}
public class PaymentProcessor {
private PaymentMethod method;
public PaymentProcessor(PaymentMethod method) {
this.method = method;
}
public void makePayment(double amount) {
method.processPayment(amount);
}
}
好处:
- 减少修改现有代码的风险。
- 提高系统的灵活性和可扩展性。
3. 里氏替换原则
定义:里氏替换原则
指在一个程序中,如果基类可以被子类替换,而不影响程序的正确性,那么这个子类是正确的,即子类对象应该能够替换基类对象而不改变程序的行为。
例子: 假设有一个Shape
类和它的子类Square
和Rectangle
。如果Square
重写了setWidth
方法,那么用Square
替换Rectangle
时可能会导致意外的行为。
情况分类:
- 如果
A
是B
的子类,则B
的对象可以替换为A
的对象,而不会破坏程序,这个是更为常见的。 - 所有引用其父类对象方法的地方,都可以透明的替换为其子类对象——下列示例将展出。
public abstract class Shape {
protected double width;
protected double height;
public Shape(double width, double height) {
this.width = width;
this.height = height;
}
public abstract double getArea();
}
public class Rectangle extends Shape {
public Rectangle(double width, double height) {
super(width, height);
}
@Override
public double getArea() {
return width * height;
}
}
public class Square extends Shape {
public Square(double side) {
super(side, side);
}
@Override
public double getArea() {
return width * width;
}
}
好处:
- 保证继承关系的正确性。
- 避免运行时错误。
4. 依赖倒置原则
定义:依赖倒置原则
强调高层模块不应该依赖于低层模块,二者都应该依赖于抽象;且抽象不应该依赖于具体实现,具体实现应该依赖于抽象。
例子: 通过接口或抽象类来定义服务接口,具体的实现类依赖于这些接口或抽象类。
应用:
依赖于抽象(接口或抽象类),而不是具体类
:通过依赖于抽象,可以在不修改高层模块的情况下更换低层模块的实现。通过依赖注入来实现依赖倒置
:使用构造器注入、方法注入或属性注入的方式,将具体实现传递给高层模块。
public interface Logger {
void log(String message);
}
public class ConsoleLogger implements Logger {
@Override
public void log(String message) {
System.out.println("Console log: " + message);
}
}
public class FileLogger implements Logger {
@Override
public void log(String message) {
System.out.println("File log: " + message);
}
}
public class Service {
private final Logger logger;
public Service(Logger logger) {
this.logger = logger;
}
public void doSomething() {
logger.log("Doing something...");
}
}
好处:
- 提高系统的松耦合性。
- 支持依赖注入等设计模式。
5. 接口隔离原则
定义:接口隔离原则
强调客户不应该被迫依赖他们不使用的方法,且多个特定客户端接口要好于一个宽泛用途的接口,即其提倡将大接口拆分为多个小接口,使得接口更具针对性和灵活性——接口尽量细化,接口中的方法尽量少
。
例子: 如果你有一个Printer
接口,其中包含打印和扫描功能,但有些打印机只支持打印,此时,可以将接口拆分为Printable
和Scanner
。
public interface Printable {
void print();
}
public interface Scanner {
void scan();
}
public class MultiFunctionDevice implements Printable, Scanner {
@Override
public void print() {
System.out.println("Printing...");
}
@Override
public void scan() {
System.out.println("Scanning...");
}
}
public class PrinterOnlyDevice implements Printable {
@Override
public void print() {
System.out.println("Printing only...");
}
}
好处:
- 减少不必要的依赖。
- 提高接口的清晰性和专一性。
6. 迪米特法则(最少知道原则)
定义:迪米特法则
提倡一个对象应该对其它对象有尽可能少的了解
,即如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用;如果其中的一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。
例子: 避免一个对象直接访问另一个对象的内部状态,而是通过方法调用来获取所需信息。
public class Student {
private String name;
private Address address;
public Student(String name, Address address) {
this.name = name;
this.address = address;
}
public String getName() {
return name;
}
public Address getAddress() {
return address;
}
public String getCity() {
return address.getCity();
}
}
public class Address {
private String city;
public Address(String city) {
this.city = city;
}
public String getCity() {
return city;
}
}
好处:
- 降低耦合度。
- 增加代码的可读性和可维护性。
四、结论
遵循这些设计原则有助于开发出更加灵活、可靠且易于维护的软件系统,虽然有时候遵循这些原则可能会带来一些额外的工作量,但从长远来看,这绝对是值得的。在实际应用中,可以根据项目的需求和特点灵活地选择和应用这些原则~~