设计模式的原则
设计模式的原则是面向对象设计的指导思想,它们为我们提供了设计高质量、可维护、可扩展软件系统的基本准则。这些原则是设计模式背后的理论基础。
SOLID 原则
SOLID 是五个重要设计原则的首字母缩写,由 Robert C. Martin 提出。
1. 单一职责原则 (Single Responsibility Principle - SRP)
定义:一个类应该只有一个引起变化的原因。
解释:
- 每个类应该只负责一个特定的功能或职责
- 避免创建”万能类”,这样的类难以维护和测试
- 将不同的职责分离到不同的类中
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| class Employee { public: void calculateSalary() { } void saveToDatabase() { } void generateReport() { } };
class Employee { public: void calculateSalary() { } };
class EmployeeRepository { public: void save(Employee& employee) { } };
class ReportGenerator { public: void generate(Employee& employee) { } };
|
2. 开闭原则 (Open-Closed Principle - OCP)
定义:软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。
解释:
- 可以通过添加新代码来扩展功能,而不是修改现有代码
- 使用抽象和多态来实现这一原则
- 减少修改现有代码带来的风险
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| class Shape { };
class AreaCalculator { public: double calculateArea(Shape* shape) { if (auto circle = dynamic_cast<Circle*>(shape)) { return 3.14 * circle->radius * circle->radius; } else if (auto rectangle = dynamic_cast<Rectangle*>(shape)) { return rectangle->width * rectangle->height; } } };
class Shape { public: virtual double calculateArea() const = 0; virtual ~Shape() {} };
class Circle : public Shape { public: double calculateArea() const override { return 3.14 * radius * radius; } private: double radius; };
class Rectangle : public Shape { public: double calculateArea() const override { return width * height; } private: double width, height; };
class AreaCalculator { public: double calculateArea(const Shape& shape) { return shape.calculateArea(); } };
|
3. 里氏替换原则 (Liskov Substitution Principle - LSP)
定义:子类型必须能够替换它们的基类型。
解释:
- 派生类应该能够完全替代基类,而不影响程序的正确性
- 子类不应该加强前置条件或削弱后置条件
- 子类不应该改变基类的行为
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| class Rectangle { protected: double width, height; public: virtual void setWidth(double w) { width = w; } virtual void setHeight(double h) { height = h; } double getArea() const { return width * height; } };
class Square : public Rectangle { public: void setWidth(double w) override { width = height = w; } void setHeight(double h) override { width = height = h; } };
void testRectangle(Rectangle& rect) { rect.setWidth(5); rect.setHeight(4); assert(rect.getArea() == 20); }
class Shape { public: virtual double getArea() const = 0; virtual ~Shape() {} };
class Rectangle : public Shape { public: Rectangle(double w, double h) : width(w), height(h) {} double getArea() const override { return width * height; } private: double width, height; };
class Square : public Shape { public: Square(double side) : side(side) {} double getArea() const override { return side * side; } private: double side; };
|
4. 接口隔离原则 (Interface Segregation Principle - ISP)
定义:客户端不应该被迫依赖它们不使用的接口。
解释:
- 将庞大的接口拆分为更小、更具体的接口
- 客户端只应该知道它们实际使用的方法
- 避免”胖接口”和不需要的依赖
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| class Worker { public: virtual void work() = 0; virtual void eat() = 0; virtual void sleep() = 0; };
class Robot : public Worker { public: void work() override { } void eat() override { throw std::runtime_error("机器人不需要吃饭"); } void sleep() override { throw std::runtime_error("机器人不需要睡觉"); } };
class Workable { public: virtual void work() = 0; };
class Eatable { public: virtual void eat() = 0; };
class Sleepable { public: virtual void sleep() = 0; };
class Human : public Workable, public Eatable, public Sleepable { public: void work() override { } void eat() override { } void sleep() override { } };
class Robot : public Workable { public: void work() override { } };
|
5. 依赖倒置原则 (Dependency Inversion Principle - DIP)
定义:
- 高层模块不应该依赖低层模块,两者都应该依赖抽象
- 抽象不应该依赖细节,细节应该依赖抽象
解释:
- 减少模块间的耦合度
- 提高代码的可测试性和灵活性
- 通过依赖注入实现
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| class MySQLDatabase { public: void saveData(const std::string& data) { } };
class DataProcessor { private: MySQLDatabase database; public: void processData(const std::string& data) { database.saveData(data); } };
class Database { public: virtual void saveData(const std::string& data) = 0; virtual ~Database() {} };
class MySQLDatabase : public Database { public: void saveData(const std::string& data) override { } };
class PostgreSQLDatabase : public Database { public: void saveData(const std::string& data) override { } };
class DataProcessor { private: Database& database; public: DataProcessor(Database& db) : database(db) {} void processData(const std::string& data) { database.saveData(data); } };
|
其他重要原则
6. 迪米特法则 (Law of Demeter - LoD) 或最少知识原则
定义:一个对象应该对其他对象有最少的了解。
解释:
- 只与直接的朋友通信
- 减少类之间的耦合
- 避免链式调用:
a.getB().getC().doSomething()
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| class House { public: Kitchen& getKitchen() { return kitchen; } private: Kitchen kitchen; };
class Person { public: void cook(House& house) { house.getKitchen().getStove().turnOn(); } };
class House { public: void cookMeal() { kitchen.prepareMeal(); } private: Kitchen kitchen; };
class Person { public: void cook(House& house) { house.cookMeal(); } };
|
7. 组合/聚合复用原则 (Composite/Aggregate Reuse Principle - CARP)
定义:优先使用对象组合/聚合,而不是类继承。
解释:
- 组合比继承更灵活
- 减少继承层次的深度
- 提高代码的复用性和灵活性
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| class Bird { public: virtual void fly() = 0; };
class Sparrow : public Bird { public: void fly() override { } };
class Penguin : public Bird { public: void fly() override { throw std::runtime_error("企鹅不会飞"); } };
class Flyable { public: virtual void fly() = 0; };
class FlyWithWings : public Flyable { public: void fly() override { } };
class NoFly : public Flyable { public: void fly() override { } };
class Bird { protected: std::unique_ptr<Flyable> flyBehavior; public: virtual void fly() { if (flyBehavior) flyBehavior->fly(); } virtual ~Bird() {} };
class Sparrow : public Bird { public: Sparrow() { flyBehavior = std::make_unique<FlyWithWings>(); } };
class Penguin : public Bird { public: Penguin() { flyBehavior = std::make_unique<NoFly>(); } };
|
原则之间的关系和应用
这些设计原则不是孤立的,它们相互关联、相互支持:
- SRP 是基础:单一职责是其他原则的基础
- OCP 是目标:开闭原则是我们追求的目标
- LSP 和 ISP 是规范:里氏替换和接口隔离是实现OCP的规范
- DIP 是手段:依赖倒置是实现其他原则的重要手段
实际应用建议
- 不要过度设计:原则是指导,不是教条。根据实际需求平衡
- 循序渐进:开始时可以简单实现,随着需求变化逐步重构
- 关注可读性:过于复杂的设计可能降低代码可读性
- 测试驱动:良好的测试可以帮助验证设计是否合理
总结
设计模式的原则为我们提供了创建高质量软件的指导思想。理解和应用这些原则可以帮助我们:
- 编写更灵活、可维护的代码
- 减少代码之间的耦合度
- 提高代码的复用性和可测试性
- 更好地应对需求变化
这些原则需要在实际项目中不断实践和体会,才能真正掌握其精髓。记住,原则是工具,而不是目的,最终目标是创建高质量的软件系统。