迪米特法则(Law of Demeter, LoD),又称 “最少知识原则(Least Knowledge Principle)”,是面向对象设计的经典指导原则之一。其核心思想是:一个对象应当尽可能少地与其他对象发生相互作用,只与 “直接的朋友” 通信,避免与 “陌生人” 产生直接交互。
直接的朋友:指与当前对象有直接关联的类,包括:
陌生人:与当前对象无直接关联的类。例如,若类 A 通过类 B 的成员变量访问类 C,则类 C 是类 A 的 “陌生人”。
用户提到的语句:“如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。” 这直接点明了迪米特法则的核心设计逻辑:
两个类若无需直接通信(即无业务逻辑上的直接关联),则不应建立直接依赖。直接交互会导致类之间的耦合度升高,当其中一个类的实现变化时,可能引发连锁反应(如修改类 C 会影响类 A)。
若必须调用另一个类的方法,应通过一个中间类(第三者)间接转发。中间类作为 “桥梁”,将两个原本无关的类解耦,使它们仅依赖中间类,而非彼此。
以 “客户 - 员工 - 项目” 管理系统为例:
// 客户类(直接与项目类交互,违反LoD)
class Client {
public:
void checkProjectProgress(Employee* employee) {
// 客户直接访问员工的项目对象(陌生人)
Project* project = employee->getProject();
project->showProgress(); // Client与Project无直接关联,属于陌生人
}
};
// 员工类
class Employee {
private:
Project* project; // 员工的项目(直接朋友)
public:
Project* getProject() { return project; }
};
// 项目类
class Project {
public:
void showProgress() { std::cout << "项目进度:80%" << std::endl; }
};
问题:
Client
类直接调用Project
类的方法,但Client
与Project
无直接关联(Project
是Client
的 “陌生人”)。Project
类修改showProgress()
方法(如重命名为displayProgress()
),Client
类需同步修改,耦合度高。// 客户类(仅与直接朋友Employee交互)
class Client {
public:
void checkProjectProgress(Employee* employee) {
employee->reportProjectProgress(); // 通过Employee转发调用(第三者)
}
};
// 员工类(作为第三者,封装Project的访问)
class Employee {
private:
Project* project; // 直接朋友
public:
// 员工负责转发项目进度查询(避免Client直接访问Project)
void reportProjectProgress() {
project->showProgress(); // 员工与Project是直接朋友
}
};
// 项目类(无需暴露给Client)
class Project {
public:
void showProgress() { std::cout << "项目进度:80%" << std::endl; }
};
Client
的核心职责是查询项目进度,Employee
作为项目的负责人,是合理的 “直接朋友”。IProjectReporter
)定义转发方法,避免中间类与具体实现强耦合。迪米特法则的本质是通过限制对象间的交互范围,降低系统耦合度。用户提到的语句精准概括了这一原则:
在实际开发中,遵循迪米特法则能显著提升代码的可维护性和扩展性,尤其在大型系统中(如微服务架构、模块化设计),是降低模块间耦合的关键指导原则。
依赖倒转原则是面向对象设计的重要原则,由 Robert C. Martin( Uncle Bob)提出,是 SOLID 原则的一部分。其核心思想是:
简而言之,依赖关系应通过抽象(接口 / 抽象类)建立,而非具体实现类。这使系统更灵活、可扩展,符合 “开闭原则”。
// 低层模块:MySQL数据库实现
class MySQLDatabase {
public:
void saveOrder(const std::string& orderData) {
std::cout << "将订单数据 [" << orderData << "] 存入MySQL数据库" << std::endl;
}
};
// 高层模块:订单服务(直接依赖具体数据库实现)
class OrderService {
private:
MySQLDatabase database; // 直接依赖具体类
public:
void createOrder(const std::string& orderData) {
// 业务逻辑...
database.saveOrder(orderData); // 直接调用具体实现
}
};
问题:
OrderService
(高层)直接依赖MySQLDatabase
(低层),若需切换数据库(如改用 Redis),必须修改OrderService
代码,违反开闭原则。// 抽象接口:定义数据库操作契约
class Database {
public:
virtual void saveOrder(const std::string& orderData) = 0;
virtual ~Database() = default;
};
// 低层模块:MySQL实现(依赖抽象)
class MySQLDatabase : public Database {
public:
void saveOrder(const std::string& orderData) override {
std::cout << "将订单数据 [" << orderData << "] 存入MySQL数据库" << std::endl;
}
};
// 低层模块:Redis实现(依赖抽象)
class RedisDatabase : public Database {
public:
void saveOrder(const std::string& orderData) override {
std::cout << "将订单数据 [" << orderData << "] 缓存到Redis" << std::endl;
}
};
// 高层模块:订单服务(依赖抽象)
class OrderService {
private:
Database* database; // 依赖抽象接口,而非具体类
public:
// 通过构造函数注入依赖(依赖注入)
explicit OrderService(Database* db) : database(db) {}
void createOrder(const std::string& orderData) {
// 业务逻辑...
database->saveOrder(orderData); // 通过接口调用,不关心具体实现
}
};
// 客户端代码:控制反转(IoC)
int main() {
// 选择具体实现并注入到高层模块
Database* mysqlDb = new MySQLDatabase();
OrderService service(mysqlDb);
service.createOrder("订单#123"); // 输出:存入MySQL数据库
// 动态切换实现(无需修改OrderService)
delete mysqlDb;
Database* redisDb = new RedisDatabase();
OrderService service2(redisDb);
service2.createOrder("订单#456"); // 输出:缓存到Redis
delete redisDb;
return 0;
}
依赖注入(Dependency Injection, DI):
OrderService
通过构造函数接收Database
接口实现。控制反转(Inversion of Control, IoC):
main()
函数负责创建具体数据库实例并注入到OrderService
。优势 | 说明 |
---|---|
可维护性 | 修改低层实现(如数据库)时,无需改动高层模块(如业务逻辑)。 |
可扩展性 | 新增低层实现(如添加 MongoDB 支持)时,只需实现接口,高层模块无感知。 |
可测试性 | 测试时可注入模拟对象(如MockDatabase ),隔离外部依赖,便于单元测试。 |
松耦合 | 高层与低层仅通过抽象接口交互,耦合度降至最低。 |
抽象粒度:
saveOrder()
),而非实现细节。避免循环依赖:
结合工厂模式:
依赖倒转原则的核心是通过抽象隔离高层与低层模块,使系统更灵活、可维护。关键点:
在大型系统(如微服务架构)中,DIP 是实现 “高内聚、低耦合” 的基石,广泛应用于框架设计(如 Spring)和模块化开发中。
外观模式是一种结构型设计模式,其核心思想是为复杂的子系统提供一个统一的简单接口,隐藏子系统的复杂性,使客户端只需通过这个接口与子系统交互,而无需直接调用多个子系统的复杂方法。
外观类(Facade):
子系统类(Subsystem Classes):
客户端(Client):
场景:计算机启动过程涉及 CPU、内存、硬盘等多个子系统的复杂初始化,通过外观模式简化启动流程。
步骤 1:定义子系统类
// 子系统:CPU
class CPU {
public:
void startup() {
std::cout << "CPU 启动..." << std::endl;
}
void shutdown() {
std::cout << "CPU 关闭..." << std::endl;
}
};
// 子系统:内存
class Memory {
public:
void startup() {
std::cout << "内存自检并加载..." << std::endl;
}
void shutdown() {
std::cout << "内存清理并释放..." << std::endl;
}
};
// 子系统:硬盘
class HardDrive {
public:
void startup() {
std::cout << "硬盘初始化..." << std::endl;
}
void shutdown() {
std::cout << "硬盘停止读写..." << std::endl;
}
};
步骤 2:创建外观类
// 外观类:封装计算机启动和关闭的复杂流程
class ComputerFacade {
private:
CPU cpu;
Memory memory;
HardDrive hardDrive;
public:
// 简化的启动接口
void start() {
std::cout << "=== 计算机启动流程 ===" << std::endl;
cpu.startup();
memory.startup();
hardDrive.startup();
std::cout << "=== 计算机启动完成 ===" << std::endl;
}
// 简化的关闭接口
void shutdown() {
std::cout << "=== 计算机关闭流程 ===" << std::endl;
cpu.shutdown();
memory.shutdown();
hardDrive.shutdown();
std::cout << "=== 计算机已关闭 ===" << std::endl;
}
};
步骤 3:客户端代码
int main() {
// 创建外观对象
ComputerFacade computer;
// 客户端只需调用简单接口,无需关心内部子系统的复杂交互
std::cout << "用户按下电源键..." << std::endl;
computer.start(); // 调用简化的启动接口
std::cout << "\n用户按下关机键..." << std::endl;
computer.shutdown(); // 调用简化的关闭接口
return 0;
}
优势 | 说明 |
---|---|
简化接口 | 隐藏子系统的复杂性,提供统一的简单接口,降低客户端使用难度。 |
松耦合 | 客户端与子系统解耦,子系统的修改不影响客户端,符合开闭原则。 |
提高安全性 | 限制客户端直接访问子系统,减少误操作风险。 |
可维护性增强 | 子系统的内部变化只需在外观类中调整,不影响其他部分。 |
复杂子系统的简化访问:
分层架构的边界定义:
遗留系统的适配:
避免过度抽象:
保留直接访问的可能性:
与中介者模式的区别:
外观模式的核心价值在于通过一个统一的接口封装复杂子系统,使客户端无需了解内部细节,降低系统的使用门槛和耦合度。它是 “封装变化” 和 “依赖倒转” 原则的典型应用,广泛用于框架设计(如 Java 的java.net.URL
类封装网络请求)、游戏引擎(如 Unity 的 API 封装底层渲染逻辑)和企业系统(如 ERP 系统的业务门面层)。
建造者模式是一种创建型设计模式,其核心思想是将一个复杂对象的构建与表示分离,使同样的构建过程可以创建不同的表示。它允许用户通过指定复杂对象的类型和内容,一步一步创建出一个完整的对象,而无需关心对象内部的具体构造细节。
产品类(Product):
抽象建造者(Builder):
buildPartA()
、buildPartB()
)。getProduct()
)。具体建造者(Concrete Builder):
指挥者(Director):
场景:构建电脑(Computer),包含 CPU、内存、硬盘等组件,通过建造者模式实现不同配置的电脑组装。
// 产品类:电脑
class Computer {
private:
std::string cpu; // CPU型号
std::string memory; // 内存容量
std::string hardDisk;// 硬盘容量
std::string graphics;// 显卡型号(可选)
public:
void setCPU(const std::string& cpu) {
this->cpu = cpu;
}
void setMemory(const std::string& memory) {
this->memory = memory;
}
void setHardDisk(const std::string& hardDisk) {
this->hardDisk = hardDisk;
}
void setGraphics(const std::string& graphics) {
this->graphics = graphics;
}
void showInfo() const {
std::cout << "电脑配置:" << std::endl
<< "CPU: " << cpu << std::endl
<< "内存: " << memory << std::endl
<< "硬盘: " << hardDisk << std::endl;
if (!graphics.empty()) {
std::cout << "显卡: " << graphics << std::endl;
}
}
};
// 抽象建造者:定义构建电脑各部分的接口
class ComputerBuilder {
protected:
Computer computer;
public:
virtual void buildCPU() = 0;
virtual void buildMemory() = 0;
virtual void buildHardDisk() = 0;
virtual void buildGraphics() = 0; // 可选组件
// 返回构建好的电脑
Computer getComputer() {
return computer;
}
virtual ~ComputerBuilder() = default;
};
// 具体建造者:标准办公电脑
class OfficeComputerBuilder : public ComputerBuilder {
public:
void buildCPU() override {
computer.setCPU("Intel i5-12400");
}
void buildMemory() override {
computer.setMemory("16GB DDR4");
}
void buildHardDisk() override {
computer.setHardDisk("512GB SSD");
}
void buildGraphics() override {
// 办公电脑不配置独立显卡
computer.setGraphics("集成显卡");
}
};
// 具体建造者:游戏电脑
class GamingComputerBuilder : public ComputerBuilder {
public:
void buildCPU() override {
computer.setCPU("AMD Ryzen 7 5800X");
}
void buildMemory() override {
computer.setMemory("32GB DDR5");
}
void buildHardDisk() override {
computer.setHardDisk("1TB NVMe SSD");
}
void buildGraphics() override {
computer.setGraphics("NVIDIA RTX 4070");
}
};
// 指挥者:控制建造过程的顺序
class ComputerDirector {
private:
ComputerBuilder* builder;
public:
explicit ComputerDirector(ComputerBuilder* builder) : builder(builder) {}
// 构建电脑的流程
void constructComputer() {
builder->buildCPU();
builder->buildMemory();
builder->buildHardDisk();
builder->buildGraphics();
}
};
int main() {
// 构建办公电脑
OfficeComputerBuilder officeBuilder;
ComputerDirector officeDirector(&officeBuilder);
officeDirector.constructComputer();
Computer officeComputer = officeBuilder.getComputer();
std::cout << "办公电脑配置:" << std::endl;
officeComputer.showInfo();
// 构建游戏电脑
GamingComputerBuilder gamingBuilder;
ComputerDirector gamingDirector(&gamingBuilder);
gamingDirector.constructComputer();
Computer gamingComputer = gamingBuilder.getComputer();
std::cout << "\n游戏电脑配置:" << std::endl;
gamingComputer.showInfo();
return 0;
}
优势 | 说明 |
---|---|
分离构建与表示 | 客户端无需知道产品内部细节,只需指定类型和内容,构建过程由指挥者控制。 |
分步构建复杂对象 | 产品的构建过程被分解为多个步骤,允许更精细的控制和更灵活的扩展。 |
支持多种配置 | 同一构建过程可通过不同的具体建造者创建不同配置的产品。 |
符合开闭原则 | 新增具体建造者无需修改现有代码,扩展性好。 |
复杂对象的构建:
需要多种配置:
构建步骤稳定但具体实现可变:
维度 | 建造者模式 | 工厂模式 |
---|---|---|
创建复杂度 | 适合构建复杂对象(多步骤) | 适合创建简单对象(单一操作) |
关注点 | 强调分步构建和配置的灵活性 | 强调对象创建的统一接口 |
返回结果 | 通常通过指挥者分步构建,最终返回完整对象 | 直接返回产品实例 |
应用场景 | 产品有多种配置组合(如电脑组装) | 产品类型明确,创建逻辑集中管理 |
在某些场景下,可省略指挥者角色,直接通过建造者链式调用构建方法。例如:
// 简化版建造者(省略指挥者)
class ComputerBuilder {
private:
Computer computer;
public:
ComputerBuilder& setCPU(const std::string& cpu) {
computer.setCPU(cpu);
return *this;
}
ComputerBuilder& setMemory(const std::string& memory) {
computer.setMemory(memory);
return *this;
}
// 其他setter方法...
Computer build() {
return computer;
}
};
// 客户端使用
Computer customComputer = ComputerBuilder()
.setCPU("AMD Ryzen 9")
.setMemory("64GB")
.build();
建造者模式的核心价值在于将复杂对象的构建过程与表示分离,通过分步构建和多态实现,使同一构建流程可创建不同配置的产品。它在以下场景特别有用:
该模式广泛应用于框架设计(如 Java 的StringBuilder
、Android 的AlertDialog.Builder
)、游戏开发(角色创建系统)和企业系统(报表生成器)。