Java中的七大设计原则
简介
Java 中的七大设计原则,也称为 SOLID 原则,是一组指导面向对象设计的基本规则。这些原则帮助开发者构建具有高内聚、低耦合的系统,从而提高代码的重用性、可读性、可扩展性和可靠性。
目的
- 代码可重用性:避免重复编写相同功能的代码。
- 可读性:编程规范化,便于他人理解和维护。
- 可扩展性:新增功能时只需扩展而不修改现有代码。
- 可靠性:新增功能不影响原有功能的正确性。
- 高内聚低耦合:使系统模块化,减少模块间的依赖。
基本原则与示例
下面分别介绍七大设计原则,并通过具体示例帮助理解。
1. 单一职责原则(SRP)
定义 :
一个类应只负责一项职责。若一个类承担多个职责,当其中某一部分发生变化时,可能会影响其他部分,导致维护困难。
示例 :
假设有一个订单处理类,同时负责订单校验和保存操作,违反了单一职责原则:
java
public class OrderProcessor {
public void validate(Order order) {
// 校验订单逻辑
}
public void save(Order order) {
// 保存订单到数据库
}
public void process(Order order) {
validate(order);
save(order);
}
}
优化后,将校验和保存拆分为两个独立类:
java
public class OrderValidator {
public boolean validate(Order order) {
// 仅负责订单校验
return order != null && order.getItems() != null;
}
}
public class OrderRepository {
public void save(Order order) {
// 仅负责将订单保存到数据库
}
}
public class OrderProcessor {
private OrderValidator validator = new OrderValidator();
private OrderRepository repository = new OrderRepository();
public void process(Order order) {
if (validator.validate(order)) {
repository.save(order);
} else {
throw new IllegalArgumentException("Invalid order");
}
}
}
好处:降低单个类的复杂性,提高可读性和维护性,降低变更风险。
2. 开闭原则(OCP)
定义 :
软件实体(类、模块、函数等)应对扩展开放,对修改关闭。即在不修改现有代码的前提下,通过扩展实现新功能。
示例 :
假设有一个图形绘制程序,最初只绘制圆形和矩形,代码如下:
java
public class GraphicEditor {
public void drawShape(Shape shape) {
if (shape instanceof Rectangle) {
drawRectangle((Rectangle) shape);
} else if (shape instanceof Circle) {
drawCircle((Circle) shape);
}
}
private void drawRectangle(Rectangle r) {
System.out.println("Drawing a rectangle");
}
private void drawCircle(Circle c) {
System.out.println("Drawing a circle");
}
}
如果现在需要增加绘制三角形,则必须修改 drawShape
方法,违反了开闭原则。
优化后,利用多态扩展功能:
java
// 定义抽象的Shape接口
public interface Shape {
void draw();
}
public class Rectangle implements Shape {
public void draw() {
System.out.println("Drawing a rectangle");
}
}
public class Circle implements Shape {
public void draw() {
System.out.println("Drawing a circle");
}
}
public class Triangle implements Shape {
public void draw() {
System.out.println("Drawing a triangle");
}
}
public class GraphicEditor {
public void drawShape(Shape shape) {
shape.draw(); // 直接调用多态方法,不需要修改已有代码
}
}
好处:添加新图形只需增加新的实现类,而无需修改现有代码。
3. 里氏替换原则(LSP)
定义 :
子类对象应可以替换父类对象,并且程序功能不受影响。
示例 :
假设有一个父类 Bird
,其中有方法 fly()
,如果派生出鸵鸟类(不能飞)则会违反 LSP:
java
public class Bird {
public void fly() {
System.out.println("Bird is flying");
}
}
public class Sparrow extends Bird {
// 正常飞行
}
public class Ostrich extends Bird {
// 鸵鸟不会飞,但继承了 fly() 方法
@Override
public void fly() {
throw new UnsupportedOperationException("Ostrich can't fly");
}
}
优化建议 :
将鸟分为会飞和不会飞的类别:
java
public abstract class Bird {
// 公共属性和方法
}
public interface Flyable {
void fly();
}
public class Sparrow extends Bird implements Flyable {
public void fly() {
System.out.println("Sparrow is flying");
}
}
public class Ostrich extends Bird {
// 不实现飞行接口
}
好处:客户端可以安全地替换对象而不会出现异常。
4. 接口隔离原则(ISP)
定义 :
客户端不应被迫依赖它们不使用的接口。大型接口应拆分成更小的、专门化的接口。
示例 :
假设有一个大型接口 MultiFunctionDevice
,包含打印、扫描、传真等功能:
java
public interface MultiFunctionDevice {
void print();
void scan();
void fax();
}
如果某些设备只需要打印功能,就不应依赖其他无用的方法。
优化后,拆分为更小的接口:
java
public interface Printer {
void print();
}
public interface Scanner {
void scan();
}
public interface Fax {
void fax();
}
public class SimplePrinter implements Printer {
public void print() {
System.out.println("Printing document");
}
}
好处:客户端只需依赖自己需要的功能接口,降低不必要的依赖。
5. 依赖反转原则(DIP)
定义 :
高层模块不应依赖低层模块,两者都应该依赖于抽象。抽象不应依赖于细节,细节应该依赖于抽象。
示例 :
假设有一个消息发送模块直接依赖于具体的邮件发送类:
java
public class EmailSender {
public void sendEmail(String message) {
System.out.println("Sending email: " + message);
}
}
public class Notification {
private EmailSender sender = new EmailSender();
public void notify(String message) {
sender.sendEmail(message);
}
}
如果需要增加短信通知,就必须修改 Notification 类。
优化后,通过依赖抽象实现扩展:
java
// 抽象接口
public interface MessageSender {
void send(String message);
}
public class EmailSender implements MessageSender {
public void send(String message) {
System.out.println("Sending email: " + message);
}
}
public class SMSSender implements MessageSender {
public void send(String message) {
System.out.println("Sending SMS: " + message);
}
}
public class Notification {
private MessageSender sender;
public Notification(MessageSender sender) {
this.sender = sender;
}
public void notify(String message) {
sender.send(message);
}
}
使用时可以传入 EmailSender 或 SMSSender,从而实现高层模块不依赖于具体实现。
6. 迪米特法则(LoD)
定义 :
一个对象应尽量少地了解其他对象的内部结构,只与直接的朋友(直接持有引用的对象)通信。
示例 :
假设有一个汽车类,直接访问发动机的细节,违反了迪米特法则:
java
public class Engine {
public void start() {
System.out.println("Engine starting");
}
}
public class Car {
private Engine engine = new Engine();
public void startCar() {
// 直接调用 engine 的方法
engine.start();
}
}
优化后,通过提供接口隐藏内部细节:
java
public interface EngineController {
void startEngine();
}
public class Engine implements EngineController {
public void start() {
System.out.println("Engine starting");
}
@Override
public void startEngine() {
start();
}
}
public class Car {
private EngineController engineController;
public Car(EngineController engineController) {
this.engineController = engineController;
}
public void startCar() {
engineController.startEngine(); // 只与直接接口交互
}
}
好处:降低模块间的耦合度,提高系统的灵活性和可维护性。
7. 合成复用原则
定义 :
优先使用组合(组合对象)而非继承来构建复杂对象。组合使得类之间的耦合度更低,便于灵活扩展。
示例 :
假设有一个显示文本的类,如果直接通过继承扩展其他功能,可能导致类层次过深。采用组合可以更灵活地添加功能:
java
// 定义文本显示接口
public interface Displayable {
void display();
}
public class TextDisplay implements Displayable {
private String text;
public TextDisplay(String text) {
this.text = text;
}
public void display() {
System.out.println("Text: " + text);
}
}
// 通过组合扩展功能
public class DecoratedDisplay implements Displayable {
private Displayable displayable;
public DecoratedDisplay(Displayable displayable) {
this.displayable = displayable;
}
@Override
public void display() {
System.out.println("Decorator: Begin");
displayable.display();
System.out.println("Decorator: End");
}
}
public class Main {
public static void main(String[] args) {
Displayable simpleText = new TextDisplay("Hello, World!");
Displayable decorated = new DecoratedDisplay(simpleText);
decorated.display();
}
}
输出:
vbnet
Decorator: Begin
Text: Hello, World!
Decorator: End
好处:通过组合可以在不继承原有类的情况下增加额外功能,提高灵活性和复用性。
核心思想
- 独立变化:找出系统中容易变化的部分,将它们独立出来,避免和不变化的代码混在一起。
- 面向接口编程:针对接口编程,而不是针对实现编程,从而实现模块间的松耦合。
- 降低耦合:遵循迪米特法则和合成复用原则,确保模块只与直接相关的对象交互。
总结
Java 的七大设计原则(单一职责、开闭、里氏替换、接口隔离、依赖反转、迪米特法则和合成复用)为软件开发提供了宝贵的指导思想。遵循这些原则有助于构建高内聚、低耦合、可扩展且易于维护的系统。通过本文提供的定义、详细说明和示例代码,相信你对这些原则有了更深入的理解,并能在实际项目中灵活应用这些设计思想,构建更加优秀的软件系统。