一.软件设计的六大原则,通常被称为SOLID原则,
是面向对象设计(OOD)中最重要的指导方针之一。这些原则旨在提高软件的可维护性、可扩展性和可读性。以下是SOLID原则的详细解释:
1. 单一职责原则 (Single Responsibility Principle, SRP)
单一职责原则指出,一个类应该只有一个引起它变化的原因,即一个类应该只有一个职责。这样可以减少类的复杂性,增加代码的可维护性。
示例:
class User {
private String name;
private String email;
// getters and setters
}
class UserService {
public void createUser(User user) {
// 业务逻辑
}
}
class UserRepository {
public void saveUser(User user) {
// 数据库操作
}
}
2. 开放/封闭原则 (Open/Closed Principle, OCP)
开放/封闭原则指出,软件实体(类、模块、函数等)应该对扩展开放,对修改封闭。这意味着应通过扩展类的行为来实现功能的变化,而不是修改现有的代码。
示例:
abstract class Shape {
abstract void draw();
}
class Circle extends Shape {
void draw() {
// 画圆
}
}
class Rectangle extends Shape {
void draw() {
// 画矩形
}
}
3. 里氏替换原则 (Liskov Substitution Principle, LSP)
里氏替换原则指出,子类必须能够替换掉它们的父类。这意味着子类应该在任何父类能够出现的地方都能使用,并且行为不会改变。
示例:
class Rectangle {
private int width;
private int height;
public void setWidth(int width) {
this.width = width;
}
public void setHeight(int height) {
this.height = height;
}
}
class Square extends Rectangle {
// 重写方法确保正方形的特性
@Override
public void setWidth(int width) {
super.setWidth(width);
super.setHeight(width);
}
@Override
public void setHeight(int height) {
super.setWidth(height);
super.setHeight(height);
}
}
4. 接口隔离原则 (Interface Segregation Principle, ISP)
接口隔离原则指出,客户端不应该被迫依赖它们不需要的接口。应将接口划分为多个小接口,使每个接口只包含客户端感兴趣的方法。
示例:
interface Worker {
void work();
}
interface Eater {
void eat();
}
class Robot implements Worker {
public void work() {
// 机器人工作
}
}
class Human implements Worker, Eater {
public void work() {
// 人类工作
}
public void eat() {
// 人类吃饭
}
}
5. 依赖倒置原则 (Dependency Inversion Principle, DIP)
依赖倒置原则指出,高层模块不应该依赖低层模块,二者都应该依赖其抽象。具体实现应该依赖抽象,而不是抽象依赖具体实现。
示例:
interface MessageService {
void sendMessage(String message);
}
class EmailService implements MessageService {
public void sendMessage(String message) {
// 发送电子邮件
}
}
class SMSService implements MessageService {
public void sendMessage(String message) {
// 发送短信
}
}
class Notification {
private MessageService messageService;
public Notification(MessageService messageService) {
this.messageService = messageService;
}
public void notify(String message) {
messageService.sendMessage(message);
}
}
6. 迪米特法则 (Law of Demeter, LoD)
迪米特法则,也称为最少知识原则,指出一个对象应该对其他对象有尽可能少的了解。每个单元只应与它的直接朋友通信,而不是与陌生人通信。
示例:
class Engine {
public void start() {
// 启动引擎
}
}
class Car {
private Engine engine;
public Car() {
this.engine = new Engine();
}
public void startCar() {
engine.start();
}
}
总结
- 单一职责原则(SRP):每个类只有一个职责。
- 开放/封闭原则(OCP):对扩展开放,对修改封闭。
- 里氏替换原则(LSP):子类必须能够替换掉它们的父类。
- 接口隔离原则(ISP):使用多个专门的接口,而不是一个总接口。
- 依赖倒置原则(DIP):依赖于抽象,而不是具体实现。
- 迪米特法则(LoD):一个对象应对其他对象有尽可能少的了解。
这些原则共同帮助软件设计者创建灵活、可维护、可扩展的系统。
二. 那么还是以5.依赖倒置原则这个例子为基础,违反依赖倒置的例子是什么样子?
违反依赖倒置原则(Dependency Inversion Principle, DIP)的例子通常是高层模块直接依赖于低层模块的实现细节,而不是依赖于抽象。这会导致代码的耦合度增加,难以维护和扩展。
违反依赖倒置原则的例子
问题描述
假设我们有一个通知系统(Notification System),它发送消息(Message)给用户。我们有一个具体的邮件服务(EmailService)来发送电子邮件。高层模块(Notification类)直接依赖于具体的邮件服务(EmailService)。
代码示例
// 具体的邮件服务类
class EmailService {
public void sendEmail(String message) {
// 发送电子邮件的具体实现
System.out.println("Sending email: " + message);
}
}
// 高层模块,通知类
class Notification {
private EmailService emailService;
public Notification() {
this.emailService = new EmailService();
}
public void notify(String message) {
emailService.sendEmail(message);
}
}
public class Main {
public static void main(String[] args) {
Notification notification = new Notification();
notification.notify("Hello, this is a notification!");
}
}
问题分析
- 高耦合:
Notification
类直接依赖于具体的EmailService
类。这使得Notification
类与EmailService
类紧密耦合。 - 难以扩展:如果我们想要增加一个新的消息服务(如
SMS
服务),我们需要修改Notification
类的代码。这违反了开放/封闭原则(OCP),使得系统难以扩展。 - 难以测试:在单元测试中,我们无法轻易替换
EmailService
,因为Notification
类直接创建了EmailService
的实例。
解决方案:依赖倒置原则
通过依赖倒置原则,我们可以重构代码,使高层模块依赖于抽象,而不是具体实现。
重构后的代码示例
// 抽象的消息服务接口
interface MessageService {
void sendMessage(String message);
}
// 具体的邮件服务类实现接口
class EmailService implements MessageService {
public void sendMessage(String message) {
// 发送电子邮件的具体实现
System.out.println("Sending email: " + message);
}
}
// 高层模块,通知类依赖于抽象接口
class Notification {
private MessageService messageService;
public Notification(MessageService messageService) {
this.messageService = messageService;
}
public void notify(String message) {
messageService.sendMessage(message);
}
}
public class Main {
public static void main(String[] args) {
// 依赖注入具体实现
MessageService emailService = new EmailService();
Notification notification = new Notification(emailService);
notification.notify("Hello, this is a notification!");
}
}
重构后的优点
- 低耦合:
Notification
类不再直接依赖于具体的EmailService
类,而是依赖于MessageService
接口。这降低了类之间的耦合度。 - 易于扩展:要增加新的消息服务(如
SMS
服务),只需实现MessageService
接口,并在创建Notification
实例时传递新的实现即可,无需修改现有的Notification
类代码。 - 易于测试:在单元测试中,可以轻松地替换
MessageService
接口的实现,使用模拟(mock)对象来测试Notification
类。
这里,违反依赖倒置原则的代码示例展示了高层模块直接依赖低层模块的实现细节,从而导致高耦合和低扩展性。通过引入抽象接口并依赖于这些接口,可以遵循依赖倒置原则,提高代码的灵活性、可维护性和可测试性。
三.那么还是以6. 迪米特法则中这个例子为基础,违反迪米特法则的例子是什么样子?
违反依赖倒置原则(Dependency Inversion Principle, DIP)的例子通常是高层模块直接依赖于低层模块的实现细节,而不是依赖于抽象。这会导致代码的耦合度增加,难以维护和扩展。
违反依赖倒置原则的例子
问题描述
假设我们有一个通知系统(Notification System),它发送消息(Message)给用户。我们有一个具体的邮件服务(EmailService)来发送电子邮件。高层模块(Notification类)直接依赖于具体的邮件服务(EmailService)。
代码示例
// 具体的邮件服务类
class EmailService {
public void sendEmail(String message) {
// 发送电子邮件的具体实现
System.out.println("Sending email: " + message);
}
}
// 高层模块,通知类
class Notification {
private EmailService emailService;
public Notification() {
this.emailService = new EmailService();
}
public void notify(String message) {
emailService.sendEmail(message);
}
}
public class Main {
public static void main(String[] args) {
Notification notification = new Notification();
notification.notify("Hello, this is a notification!");
}
}
问题分析
- 高耦合:
Notification
类直接依赖于具体的EmailService
类。这使得Notification
类与EmailService
类紧密耦合。 - 难以扩展:如果我们想要增加一个新的消息服务(如
SMS
服务),我们需要修改Notification
类的代码。这违反了开放/封闭原则(OCP),使得系统难以扩展。 - 难以测试:在单元测试中,我们无法轻易替换
EmailService
,因为Notification
类直接创建了EmailService
的实例。
解决方案:依赖倒置原则
通过依赖倒置原则,我们可以重构代码,使高层模块依赖于抽象,而不是具体实现。
重构后的代码示例
// 抽象的消息服务接口
interface MessageService {
void sendMessage(String message);
}
// 具体的邮件服务类实现接口
class EmailService implements MessageService {
public void sendMessage(String message) {
// 发送电子邮件的具体实现
System.out.println("Sending email: " + message);
}
}
// 高层模块,通知类依赖于抽象接口
class Notification {
private MessageService messageService;
public Notification(MessageService messageService) {
this.messageService = messageService;
}
public void notify(String message) {
messageService.sendMessage(message);
}
}
public class Main {
public static void main(String[] args) {
// 依赖注入具体实现
MessageService emailService = new EmailService();
Notification notification = new Notification(emailService);
notification.notify("Hello, this is a notification!");
}
}
重构后的优点
- 低耦合:
Notification
类不再直接依赖于具体的EmailService
类,而是依赖于MessageService
接口。这降低了类之间的耦合度。 - 易于扩展:要增加新的消息服务(如
SMS
服务),只需实现MessageService
接口,并在创建Notification
实例时传递新的实现即可,无需修改现有的Notification
类代码。 - 易于测试:在单元测试中,可以轻松地替换
MessageService
接口的实现,使用模拟(mock)对象来测试Notification
类。
违反依赖倒置原则的代码示例展示了高层模块直接依赖低层模块的实现细节,从而导致高耦合和低扩展性。通过引入抽象接口并依赖于这些接口,可以遵循依赖倒置原则,提高代码的灵活性、可维护性和可测试性。
标签:原则,Notification,void,class,面向对象,EmailService,设计,message,public From: https://www.cnblogs.com/gongchengship/p/18284823