欢迎来到C++面向对象设计原则讲座!
各位朋友,欢迎来到我们今天的讲座!今天我们将一起探讨C++面向对象设计中的几个核心原则:单一职责原则(SRP)、开闭原则(OCP)以及其他相关的设计思想。为了让内容更有趣,我会用轻松幽默的方式讲解,并结合代码示例和表格来帮助大家理解。
Part 1: 单一职责原则(Single Responsibility Principle, SRP)
什么是单一职责原则?
简单来说,单一职责原则就是告诉我们:一个类应该只负责一件事情。听起来很简单对吧?但实际上,很多人在写代码时会不自觉地让一个类承担过多的责任。
讲个小故事
假设你正在开发一个餐厅管理系统,其中有一个OrderManager
类,它不仅要处理订单,还要打印账单、发送邮件通知客户……这就像一个人既当厨师又当服务员还兼会计,忙得不可开交。
class OrderManager {
public:
void takeOrder() {
// 处理订单逻辑
}
void printBill() {
// 打印账单逻辑
}
void sendEmailNotification() {
// 发送邮件逻辑
}
};
问题来了:如果有一天你需要修改账单打印的格式,或者换一种邮件服务提供商,那你不得不去改动这个OrderManager
类,而这种改动可能会引入新的bug。
如何改进?
我们可以将不同的职责拆分到不同的类中,比如:
class OrderProcessor {
public:
void processOrder() {
// 专注于处理订单
}
};
class BillPrinter {
public:
void printBill() {
// 专注于打印账单
}
};
class EmailNotifier {
public:
void sendEmail() {
// 专注于发送邮件
}
};
这样每个类都只做一件事,职责清晰明了,未来维护起来也更加方便。
Part 2: 开闭原则(Open-Closed Principle, OCP)
什么是开闭原则?
开闭原则的核心思想是:软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。也就是说,当我们需要新增功能时,尽量不要修改现有的代码,而是通过扩展的方式来实现。
再讲一个小故事
假设你正在开发一个绘图程序,支持绘制不同形状的图形,比如圆形和矩形。你的初始代码可能是这样的:
enum ShapeType { CIRCLE, RECTANGLE };
class Shape {
public:
virtual void draw() = 0;
};
class Circle : public Shape {
public:
void draw() override {
std::cout << "Drawing a circle" << std::endl;
}
};
class Rectangle : public Shape {
public:
void draw() override {
std::cout << "Drawing a rectangle" << std::endl;
}
};
void drawShape(ShapeType type) {
if (type == CIRCLE) {
Circle circle;
circle.draw();
} else if (type == RECTANGLE) {
Rectangle rectangle;
rectangle.draw();
}
}
现在,如果你需要支持一个新的形状——三角形,你会怎么做?按照目前的代码结构,你需要修改drawShape
函数,添加一个新的if-else
分支。但问题是,每次新增一个形状都需要修改现有代码,违反了开闭原则。
如何改进?
我们可以使用多态和工厂模式来解决这个问题:
class Triangle : public Shape {
public:
void draw() override {
std::cout << "Drawing a triangle" << std::endl;
}
};
Shape* createShape(ShapeType type) {
switch (type) {
case CIRCLE: return new Circle();
case RECTANGLE: return new Rectangle();
case TRIANGLE: return new Triangle(); // 新增形状无需修改现有代码
default: return nullptr;
}
}
void drawShape(ShapeType type) {
Shape* shape = createShape(type);
if (shape) {
shape->draw();
delete shape; // 注意释放内存
}
}
这样,即使未来新增更多的形状,我们也不需要修改drawShape
函数,只需要在createShape
函数中添加新的分支即可。
Part 3: 其他设计原则小结
除了单一职责原则和开闭原则,还有一些其他重要的设计原则值得了解:
原则名称 | 描述 |
---|---|
里氏替换原则(LSP) | 子类必须能够完全替代父类,而不会导致程序出错。 |
接口隔离原则(ISP) | 客户端不应该依赖于它不需要的接口,接口应该尽量小而专一。 |
依赖倒置原则(DIP) | 高层模块不应该依赖于低层模块,两者都应该依赖于抽象。 |
总结
今天我们学习了两个非常重要的面向对象设计原则:单一职责原则和开闭原则。它们的核心思想是帮助我们写出更易于维护、更灵活的代码。当然,设计原则并不是死板的规则,而是指导我们思考的工具。在实际开发中,我们需要根据具体场景灵活运用。
最后,引用一句来自Robert C. Martin(Uncle Bob)的话:“Clean code is not written by following rules, it is written by thinking about your reader.”(干净的代码不是靠遵循规则写出来的,而是靠为读者着想写出来的。)
感谢大家的聆听!如果有任何问题,欢迎随时提问!