第13章 类与封装 面向对象设计概述 面向对象设计(Object-Oriented Design,OOD)是一种基于对象概念的软件开发方法,它通过抽象、封装、继承和多态等核心机制,将系统分解为相互协作的对象集合,每个对象封装了特定的职责和行为。
面向对象设计的核心原则 抽象(Abstraction) :从具体实现中提取本质特征,形成抽象接口,隐藏实现细节封装(Encapsulation) :将数据和操作数据的方法绑定在一起,通过访问控制实现信息隐藏继承(Inheritance) :通过层次结构实现代码重用和类型扩展,建立”is-a”关系多态(Polymorphism) :通过统一接口支持不同实现,提高代码的灵活性和可扩展性组合(Composition) :通过对象组合实现更灵活的代码重用,建立”has-a”关系面向对象设计的价值 可维护性 :模块化设计和信息隐藏使得代码更易于理解和修改可扩展性 :通过继承和多态,系统可以在不修改现有代码的情况下进行扩展可重用性 :封装和抽象使得组件可以在不同场景中重复使用可测试性 :模块化设计使得单元测试更加容易实现代码清晰度 :面向对象的设计更符合人类的思维模式,提高代码的可读性类设计与封装原则 SOLID设计原则深度解析 SOLID是由Robert C. Martin提出的一组面向对象设计基本原则,它们为创建高质量、可维护的软件系统提供了坚实的理论基础。
单一职责原则(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 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 56 57 class User {public : void saveToDatabase () { } User loadFromDatabase (int id) { } bool validate () { } void sendVerificationEmail () { } }; class User {public : std::string getName () const { return name; } void setName (const std::string& name) { this ->name = name; } private : std::string name; std::string email; }; class UserRepository {public : void save (const User& user) { std::cout << "Saving user " << user.getName () << " to database" << std::endl; } User findById (int id) { std::cout << "Finding user with id " << id << " from database" << std::endl; return User (); } }; class UserValidator {public : bool isValid (const User& user) { return !user.getName ().empty () && !user.getEmail ().empty (); } }; class EmailService {public : void sendVerificationEmail (const User& user) { std::cout << "Sending verification email to " << user.getEmail () << std::endl; } };
优势 :
提高代码的可维护性和可测试性 减少类之间的耦合 便于团队协作和代码复用 降低修改代码时引入错误的风险 开放封闭原则(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 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 class Shape {public : enum Type { CIRCLE, RECTANGLE, TRIANGLE }; Type type; double radius; double width; double height; double base; }; class AreaCalculator {public : double calculateArea (const Shape& shape) { switch (shape.type) { case Shape::CIRCLE: return M_PI * shape.radius * shape.radius; case Shape::RECTANGLE: return shape.width * shape.height; case Shape::TRIANGLE: return 0.5 * shape.base * shape.height; default : return 0 ; } } }; class Shape {public : virtual ~Shape () = default ; virtual double calculateArea () const = 0 ; virtual std::string getName () const = 0 ; }; class Circle : public Shape {public : explicit Circle (double radius) : radius(radius) { } double calculateArea () const override { return M_PI * radius * radius; } std::string getName () const override { return "Circle" ; } double getRadius () const { return radius; } private : double radius; }; class Rectangle : public Shape {public : Rectangle (double width, double height) : width (width), height (height) {} double calculateArea () const override { return width * height; } std::string getName () const override { return "Rectangle" ; } double getWidth () const { return width; } double getHeight () const { return height; } private : double width, height; }; class Triangle : public Shape {public : Triangle (double base, double height) : base (base), height (height) {} double calculateArea () const override { return 0.5 * base * height; } std::string getName () const override { return "Triangle" ; } private : double base, height; }; class AreaCalculator {public : double calculateArea (const Shape& shape) { return shape.calculateArea (); } void printArea (const Shape& shape) { std::cout << "Area of " << shape.getName () << ": " << calculateArea (shape) << std::endl; } }; class Square : public Shape {public : explicit Square (double side) : side(side) { } double calculateArea () const override { return side * side; } std::string getName () const override { return "Square" ; } private : double side; };
优势 :
提高代码的可扩展性和可维护性 减少修改现有代码时引入错误的风险 支持增量开发和团队协作 符合软件设计的”开闭”哲学 实现技巧 :
使用抽象基类定义稳定的接口 利用多态实现行为的动态绑定 应用依赖倒置原则,依赖抽象而非具体实现 结合工厂模式创建对象,隐藏具体实现细节 里氏替换原则(Liskov Substitution Principle - LSP) 核心思想 :子类应该可以替换其父类,而不会影响程序的正确性。也就是说,使用基类指针或引用的代码,在替换为派生类对象时,程序的行为应该保持不变。
技术实现 :
子类必须完全遵守基类的接口约定 子类不能强化前置条件(preconditions) 子类不能弱化后置条件(postconditions) 子类必须保持基类的不变量(invariants) 子类方法的异常类型不能比基类更宽 实际应用场景 :
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 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 class Rectangle {protected : double width; double height; public : Rectangle (double w, double h) : width (w), height (h) {} virtual void setWidth (double w) { width = w; } virtual void setHeight (double h) { height = h; } double getWidth () const { return width; } double getHeight () const { return height; } double calculateArea () const { return width * height; } }; class Square : public Rectangle {public : explicit Square (double side) : Rectangle(side, side) { } void setWidth (double w) override { width = w; height = w; } void setHeight (double h) override { height = h; width = h; } }; void processRectangle (Rectangle& rect) { double originalWidth = rect.getWidth (); rect.setHeight (10 ); assert (rect.getWidth () == originalWidth); } class Shape {public : virtual ~Shape () = default ; virtual double calculateArea () const = 0 ; virtual std::string getName () const = 0 ; }; class Rectangle : public Shape {private : double width; double height; public : Rectangle (double w, double h) : width (w), height (h) {} void setWidth (double w) { width = w; } void setHeight (double h) { height = h; } double getWidth () const { return width; } double getHeight () const { return height; } double calculateArea () const override { return width * height; } std::string getName () const override { return "Rectangle" ; } }; class Square : public Shape {private : double side; public : explicit Square (double side) : side(side) { } void setSide (double s) { side = s; } double getSide () const { return side; } double calculateArea () const override { return side * side; } std::string getName () const override { return "Square" ; } }; void processShape (Shape& shape) { std::cout << "Area of " << shape.getName () << ": " << shape.calculateArea () << std::endl; } class Bird {public : virtual ~Bird () = default ; virtual void makeSound () = 0 ; virtual void eat () = 0 ; }; class FlyingBird : public Bird {public : virtual void fly () = 0 ; }; class Sparrow : public FlyingBird {public : void makeSound () override { std::cout << "Sparrow: Chirp chirp!" << std::endl; } void eat () override { std::cout << "Sparrow: Eating seeds" << std::endl; } void fly () override { std::cout << "Sparrow: Flying high" << std::endl; } }; class Penguin : public Bird {public : void makeSound () override { std::cout << "Penguin: Honk honk!" << std::endl; } void eat () override { std::cout << "Penguin: Eating fish" << std::endl; } void swim () { std::cout << "Penguin: Swimming in the ocean" << std::endl; } };
优势 :
确保继承层次的正确性和一致性 提高代码的可维护性和可预测性 支持多态的正确使用 减少运行时错误的可能性 常见违反LSP的情况 :
子类抛出基类不抛出的异常 子类改变基类方法的返回值语义 子类违反基类的方法约定 子类修改基类的私有状态(通过不安全的强制转换) 接口隔离原则(Interface Segregation Principle - ISP) 核心思想 :客户端不应该依赖它不使用的接口。一个类对另一个类的依赖应该建立在最小的接口上。
技术实现 :
将大接口拆分为多个小接口 每个接口专注于特定的功能组 客户端只实现它们需要的接口 避免”胖接口”(fat interfaces) 使用接口组合而非单一继承 实际应用场景 :
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 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 class Employee {public : virtual void work () = 0 ; virtual void takeBreak () = 0 ; virtual void attendMeeting () = 0 ; virtual void submitReport () = 0 ; virtual void manageTeam () = 0 ; }; class Developer : public Employee {public : void work () override { } void takeBreak () override { } void attendMeeting () override { } void submitReport () override { } void manageTeam () override { throw std::runtime_error ("Developers don't manage teams" ); } }; class Manager : public Employee {public : void work () override { } void takeBreak () override { } void attendMeeting () override { } void submitReport () override { } void manageTeam () override { } }; class Workable {public : virtual ~Workable () = default ; virtual void work () = 0 ; }; class Breakable {public : virtual ~Breakable () = default ; virtual void takeBreak () = 0 ; }; class MeetingAttendable {public : virtual ~MeetingAttendable () = default ; virtual void attendMeeting () = 0 ; }; class Reportable {public : virtual ~Reportable () = default ; virtual void submitReport () = 0 ; }; class TeamManageable {public : virtual ~TeamManageable () = default ; virtual void manageTeam () = 0 ; }; class Developer : public Workable, public Breakable, public MeetingAttendable, public Reportable {public : void work () override { std::cout << "Developer: Writing code" << std::endl; } void takeBreak () override { std::cout << "Developer: Taking a break" << std::endl; } void attendMeeting () override { std::cout << "Developer: Attending a meeting" << std::endl; } void submitReport () override { std::cout << "Developer: Submitting a report" << std::endl; } }; class Manager : public Workable, public Breakable, public MeetingAttendable, public Reportable, public TeamManageable {public : void work () override { std::cout << "Manager: Managing work" << std::endl; } void takeBreak () override { std::cout << "Manager: Taking a break" << std::endl; } void attendMeeting () override { std::cout << "Manager: Attending a meeting" << std::endl; } void submitReport () override { std::cout << "Manager: Submitting a report" << std::endl; } void manageTeam () override { std::cout << "Manager: Managing the team" << std::endl; } }; class Robot : public Workable {public : void work () override { std::cout << "Robot: Performing tasks" << std::endl; } };
优势 :
减少接口污染,提高接口的内聚性 避免不必要的方法实现 降低类之间的耦合度 提高代码的可维护性和可测试性 支持更灵活的组合方式 现代C++中的接口隔离 :
使用纯抽象类定义接口 利用C++20的概念(Concepts)进一步约束接口 使用lambda表达式和函数对象实现更灵活的行为注入 结合依赖注入框架实现松耦合设计 依赖倒置原则(Dependency Inversion Principle - DIP) 核心思想 :高层模块不应该依赖低层模块,两者都应该依赖抽象。抽象不应该依赖细节,细节应该依赖抽象。
技术实现 :
定义抽象接口作为模块间的契约 使用依赖注入(构造函数注入、 setter 注入、接口注入) 避免硬编码依赖关系 使用工厂模式或依赖注入容器管理依赖 采用控制反转(IoC)思想 实际应用场景 :
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 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 class MySQLDatabase {public : void connect () { } void executeQuery (const std::string& query) { } }; class UserService {public : UserService () : db (MySQLDatabase ()) {} void createUser (const std::string& name, const std::string& email) { db.connect (); std::string query = "INSERT INTO users (name, email) VALUES ('" + name + "', '" + email + "')" ; db.executeQuery (query); } private : MySQLDatabase db; }; class Database {public : virtual ~Database () = default ; virtual void connect () = 0 ; virtual void executeQuery (const std::string& query) = 0 ; }; class MySQLDatabase : public Database {public : void connect () override { std::cout << "Connecting to MySQL database" << std::endl; } void executeQuery (const std::string& query) override { std::cout << "Executing MySQL query: " << query << std::endl; } }; class PostgreSQLDatabase : public Database {public : void connect () override { std::cout << "Connecting to PostgreSQL database" << std::endl; } void executeQuery (const std::string& query) override { std::cout << "Executing PostgreSQL query: " << query << std::endl; } }; class UserService {public : explicit UserService (std::unique_ptr<Database> db) : db(std::move(db)) { } void createUser (const std::string& name, const std::string& email) { db->connect (); std::string query = "INSERT INTO users (name, email) VALUES ('" + name + "', '" + email + "')" ; db->executeQuery (query); } private : std::unique_ptr<Database> db; }; int main () { auto mysqlDb = std::make_unique <MySQLDatabase>(); UserService userServiceWithMySQL (std::move(mysqlDb)) ; userServiceWithMySQL.createUser ("Alice" , "alice@example.com" ); auto postgresDb = std::make_unique <PostgreSQLDatabase>(); UserService userServiceWithPostgres (std::move(postgresDb)) ; userServiceWithPostgres.createUser ("Bob" , "bob@example.com" ); return 0 ; } class DependencyContainer {public : template <typename T, typename ... Args> void registerType (Args&&... args) { } template <typename T> std::unique_ptr<T> resolve () { return nullptr ; } };
优势 :
提高代码的可测试性(便于使用模拟对象) 增强系统的可扩展性(易于添加新的实现) 降低模块间的耦合度 提高代码的可维护性和可读性 支持并行开发(团队可以独立开发不同模块) 依赖注入的实现方式 :
构造函数注入 :通过构造函数传递依赖(最常用,推荐)Setter注入 :通过setter方法设置依赖接口注入 :通过实现特定接口来注入依赖依赖注入容器 :使用专门的容器管理依赖关系与其他原则的关系 :
DIP是实现OCP的关键(通过抽象支持扩展) DIP支持ISP(通过抽象接口隔离) DIP有助于实现SRP(通过依赖管理简化职责) 高级设计模式与实践 设计模式是在软件设计中反复出现的问题的可重用解决方案,它们是经过验证的最佳实践,可以帮助开发者更有效地解决常见的设计问题。
创建型设计模式深度解析 创建型模式专注于对象的创建过程,提供了灵活的对象创建机制,隐藏了创建细节,提高了代码的可维护性和可扩展性。
单例模式(Singleton) 核心思想 :确保一个类只有一个实例,并提供一个全局访问点。
技术实现 :
私有构造函数防止外部实例化 静态方法提供全局访问点 禁止拷贝和移动操作 线程安全的实现(C++11后使用局部静态变量) 懒加载或饿加载策略 实际应用场景 :
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 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 class Singleton {public : static Singleton& getInstance () { static Singleton instance; return instance; } Singleton (const Singleton&) = delete ; Singleton& operator =(const Singleton&) = delete ; Singleton (Singleton&&) = delete ; Singleton& operator =(Singleton&&) = delete ; void initialize (const std::string& config) { if (!initialized) { this ->config = config; initialized = true ; std::cout << "Singleton initialized with config: " << config << std::endl; } } void doSomething () { if (!initialized) { throw std::runtime_error ("Singleton not initialized" ); } std::cout << "Singleton doing something with config: " << config << std::endl; } bool isInitialized () const { return initialized; } private : Singleton () : initialized (false ) { std::cout << "Singleton instance created" << std::endl; } bool initialized; std::string config; }; class ConfigManager : public Singleton {public : static ConfigManager& getInstance () { static ConfigManager instance; return instance; } void loadConfig (const std::string& filePath) { std::cout << "Loading config from " << filePath << std::endl; configs["database" ] = "localhost:3306" ; configs["api_key" ] = "secret_key_123" ; } std::string getConfig (const std::string& key) { auto it = configs.find (key); if (it != configs.end ()) { return it->second; } return "" ; } private : ConfigManager () = default ; std::unordered_map<std::string, std::string> configs; }; void useSingletonPattern () { auto & singleton = Singleton::getInstance (); singleton.initialize ("production" ); singleton.doSomething (); auto & anotherReference = Singleton::getInstance (); std::cout << "Same instance: " << (&singleton == &anotherReference) << std::endl; auto & configManager = ConfigManager::getInstance (); configManager.loadConfig ("config.json" ); std::cout << "Database config: " << configManager.getConfig ("database" ) << std::endl; }
优点 :
确保唯一实例,避免资源浪费 提供全局访问点,方便使用 懒加载,按需创建实例 线程安全(C++11后) 缺点 :
违反单一职责原则 增加代码耦合度 测试困难(难以模拟) 可能导致内存泄漏(如果实例持有资源) 适用场景 :
工厂方法模式(Factory Method) 核心思想 :定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。
技术实现 :
抽象产品类定义产品接口 具体产品类实现产品接口 抽象创建者类定义工厂方法 具体创建者类实现工厂方法,创建具体产品 使用依赖倒置原则,依赖抽象而非具体实现 实际应用场景 :
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 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 class Logger {public : virtual ~Logger () = default ; virtual void log (const std::string& message) = 0 ; virtual void logError (const std::string& message) = 0 ; virtual void logWarning (const std::string& message) = 0 ; }; class ConsoleLogger : public Logger {public : void log (const std::string& message) override { std::cout << "[INFO] " << message << std::endl; } void logError (const std::string& message) override { std::cerr << "[ERROR] " << message << std::endl; } void logWarning (const std::string& message) override { std::cout << "[WARNING] " << message << std::endl; } }; class FileLogger : public Logger {private : std::ofstream file; public : explicit FileLogger (const std::string& filename) { file.open (filename, std::ios::app); if (!file.is_open ()) { throw std::runtime_error ("Failed to open log file" ); } } ~FileLogger () { if (file.is_open ()) { file.close (); } } void log (const std::string& message) override { if (file.is_open ()) { file << "[INFO] " << getCurrentTime () << " " << message << std::endl; } } void logError (const std::string& message) override { if (file.is_open ()) { file << "[ERROR] " << getCurrentTime () << " " << message << std::endl; } } void logWarning (const std::string& message) override { if (file.is_open ()) { file << "[WARNING] " << getCurrentTime () << " " << message << std::endl; } } private : std::string getCurrentTime () { auto now = std::chrono::system_clock::now (); auto time = std::chrono::system_clock::to_time_t (now); std::stringstream ss; ss << std::put_time (std::localtime (&time), "%Y-%m-%d %H:%M:%S" ); return ss.str (); } }; class LoggerFactory {public : virtual ~LoggerFactory () = default ; virtual std::unique_ptr<Logger> createLogger () = 0 ; void logMessage (const std::string& message) { auto logger = createLogger (); logger->log (message); } }; class ConsoleLoggerFactory : public LoggerFactory {public : std::unique_ptr<Logger> createLogger () override { return std::make_unique <ConsoleLogger>(); } }; class FileLoggerFactory : public LoggerFactory {private : std::string filename; public : explicit FileLoggerFactory (std::string filename) : filename(std::move(filename)) { } std::unique_ptr<Logger> createLogger () override { return std::make_unique <FileLogger>(filename); } }; void useFactoryMethodPattern () { ConsoleLoggerFactory consoleFactory; consoleFactory.logMessage ("Application started" ); FileLoggerFactory fileFactory ("app.log" ) ; fileFactory.logMessage ("Application started" ); auto consoleLogger = consoleFactory.createLogger (); consoleLogger->logError ("An error occurred" ); auto fileLogger = fileFactory.createLogger (); fileLogger->logWarning ("A warning message" ); }
优点 :
遵循开放封闭原则,添加新产品时无需修改现有代码 遵循单一职责原则,每个工厂只负责创建一种产品 隐藏产品创建细节,客户端只关心产品接口 提高代码的可测试性,便于模拟产品 缺点 :
增加了类的数量,系统变得更复杂 对于简单的对象创建,可能过于繁琐 适用场景 :
产品类型不确定,需要在运行时动态创建 产品创建过程复杂,需要封装创建逻辑 系统需要遵循开放封闭原则,支持产品扩展 客户端不关心产品的具体实现,只使用产品接口 抽象工厂模式(Abstract Factory) 核心思想 :提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。抽象工厂模式是工厂方法模式的扩展,用于创建产品族。
技术实现 :
抽象产品类定义产品接口 具体产品类实现产品接口 抽象工厂类定义创建产品族的接口 具体工厂类实现创建产品族的方法 产品族中的产品相互配合使用 实际应用场景 :
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 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 class Button {public : virtual ~Button () = default ; virtual void render () = 0 ; virtual void onClick () = 0 ; }; class WindowsButton : public Button {public : void render () override { std::cout << "Rendering Windows button" << std::endl; } void onClick () override { std::cout << "Windows button clicked" << std::endl; } }; class MacOSButton : public Button {public : void render () override { std::cout << "Rendering macOS button" << std::endl; } void onClick () override { std::cout << "macOS button clicked" << std::endl; } }; class TextBox {public : virtual ~TextBox () = default ; virtual void render () = 0 ; virtual void setText (const std::string& text) = 0 ; virtual std::string getText () = 0 ; }; class WindowsTextBox : public TextBox {private : std::string text; public : void render () override { std::cout << "Rendering Windows text box with text: '" << text << "'" << std::endl; } void setText (const std::string& text) override { this ->text = text; } std::string getText () override { return text; } }; class MacOSTextBox : public TextBox {private : std::string text; public : void render () override { std::cout << "Rendering macOS text box with text: '" << text << "'" << std::endl; } void setText (const std::string& text) override { this ->text = text; } std::string getText () override { return text; } }; class UIFactory {public : virtual ~UIFactory () = default ; virtual std::unique_ptr<Button> createButton () = 0 ; virtual std::unique_ptr<TextBox> createTextBox () = 0 ; }; class WindowsUIFactory : public UIFactory {public : std::unique_ptr<Button> createButton () override { return std::make_unique <WindowsButton>(); } std::unique_ptr<TextBox> createTextBox () override { return std::make_unique <WindowsTextBox>(); } }; class MacOSUIFactory : public UIFactory {public : std::unique_ptr<Button> createButton () override { return std::make_unique <MacOSButton>(); } std::unique_ptr<TextBox> createTextBox () override { return std::make_unique <MacOSTextBox>(); } }; class Application {private : std::unique_ptr<UIFactory> factory; std::unique_ptr<Button> button; std::unique_ptr<TextBox> textBox; public : Application (std::unique_ptr<UIFactory> factory) : factory (std::move (factory)) { button = this ->factory->createButton (); textBox = this ->factory->createTextBox (); } void initialize () { textBox->setText ("Hello, World!" ); } void render () { button->render (); textBox->render (); } void handleClick () { button->onClick (); } }; void useAbstractFactoryPattern () { std::cout << "=== Windows Application ===" << std::endl; auto windowsFactory = std::make_unique <WindowsUIFactory>(); Application windowsApp (std::move(windowsFactory)) ; windowsApp.initialize (); windowsApp.render (); windowsApp.handleClick (); std::cout << "\n=== macOS Application ===" << std::endl; auto macFactory = std::make_unique <MacOSUIFactory>(); Application macApp (std::move(macFactory)) ; macApp.initialize (); macApp.render (); macApp.handleClick (); }
优点 :
保证产品族的一致性,相关产品一起创建 遵循开放封闭原则,添加新产品族时无需修改现有代码 隐藏产品创建细节,客户端只关心产品接口 提高代码的可维护性和可扩展性 缺点 :
增加了类的数量,系统变得更复杂 添加新产品时需要修改抽象工厂和所有具体工厂 对于简单的对象创建,可能过于繁琐 适用场景 :
需要创建产品族,且产品族中的产品相互配合使用 系统需要独立于产品的创建、组合和表示 系统需要配置为使用多个产品族中的一个 产品族的一致性很重要,需要确保使用同一产品族的产品 结构型设计模式深度解析 结构型模式专注于类和对象的组合,通过合理的结构设计来实现更灵活、更有效的代码组织。
适配器模式(Adapter) 核心思想 :将一个类的接口转换成客户希望的另一个接口。适配器模式使原本由于接口不兼容而不能一起工作的类可以协同工作。
技术实现 :
类适配器:通过多重继承实现 对象适配器:通过组合实现(更常用) 接口适配:转换接口方法签名 数据适配:转换数据格式 实际应用场景 :
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 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 class PaymentProcessor {public : virtual ~PaymentProcessor () = default ; virtual void processPayment (double amount) = 0 ; virtual void refundPayment (double amount) = 0 ; virtual bool verifyPayment () = 0 ; }; class LegacyPaymentSystem {public : void makePayment (double amount) { std::cout << "Legacy system: Processing payment of $" << amount << std::endl; } void issueRefund (double amount) { std::cout << "Legacy system: Issuing refund of $" << amount << std::endl; } bool validateTransaction () { std::cout << "Legacy system: Validating transaction" << std::endl; return true ; } }; class ThirdPartyPaymentAPI {public : void submitPayment (double amount, const std::string& currency) { std::cout << "Third-party API: Submitting payment of " << currency << " " << amount << std::endl; } void requestRefund (double amount, const std::string& currency, const std::string& transactionId) { std::cout << "Third-party API: Requesting refund of " << currency << " " << amount << " for transaction " << transactionId << std::endl; } bool authenticate () { std::cout << "Third-party API: Authenticating" << std::endl; return true ; } }; class LegacyPaymentAdapter : public PaymentProcessor {private : LegacyPaymentSystem& legacySystem; public : explicit LegacyPaymentAdapter (LegacyPaymentSystem& system) : legacySystem(system) { } void processPayment (double amount) override { legacySystem.makePayment (amount); } void refundPayment (double amount) override { legacySystem.issueRefund (amount); } bool verifyPayment () override { return legacySystem.validateTransaction (); } }; class ThirdPartyPaymentAdapter : public PaymentProcessor {private : ThirdPartyPaymentAPI& thirdPartyAPI; std::string currency; std::string transactionId; public : ThirdPartyPaymentAdapter (ThirdPartyPaymentAPI& api, std::string currency) : thirdPartyAPI (api), currency (std::move (currency)), transactionId ("TXN" + std::to_string (rand ())) {} void processPayment (double amount) override { if (verifyPayment ()) { thirdPartyAPI.submitPayment (amount, currency); } } void refundPayment (double amount) override { thirdPartyAPI.requestRefund (amount, currency, transactionId); } bool verifyPayment () override { return thirdPartyAPI.authenticate (); } }; class ECommerceSystem {private : std::unique_ptr<PaymentProcessor> paymentProcessor; public : ECommerceSystem (std::unique_ptr<PaymentProcessor> processor) : paymentProcessor (std::move (processor)) {} void checkout (double totalAmount) { std::cout << "Processing checkout for $" << totalAmount << std::endl; if (paymentProcessor->verifyPayment ()) { paymentProcessor->processPayment (totalAmount); std::cout << "Checkout completed successfully" << std::endl; } else { std::cout << "Payment verification failed" << std::endl; } } void processRefund (double refundAmount) { std::cout << "Processing refund for $" << refundAmount << std::endl; paymentProcessor->refundPayment (refundAmount); std::cout << "Refund processed successfully" << std::endl; } }; void useAdapterPattern () { std::cout << "=== Using Legacy Payment System ===" << std::endl; LegacyPaymentSystem legacySystem; auto legacyAdapter = std::make_unique <LegacyPaymentAdapter>(legacySystem); ECommerceSystem legacyStore (std::move(legacyAdapter)) ; legacyStore.checkout (199.99 ); legacyStore.processRefund (50.00 ); std::cout << "\n=== Using Third-Party Payment API ===" << std::endl; ThirdPartyPaymentAPI thirdPartyAPI; auto thirdPartyAdapter = std::make_unique <ThirdPartyPaymentAdapter>(thirdPartyAPI, "USD" ); ECommerceSystem modernStore (std::move(thirdPartyAdapter)) ; modernStore.checkout (299.99 ); modernStore.processRefund (75.00 ); }
优点 :
复用现有代码,无需修改适配者 客户端代码统一,无需关心具体实现 遵循开放封闭原则,易于扩展 降低系统间的耦合度 缺点 :
增加了系统的复杂性 可能引入性能开销(适配层) 对于复杂接口,适配工作可能繁琐 适用场景 :
集成 legacy 系统到新系统 使用第三方库,但其接口不符合需求 统一多个相似但接口不同的系统 系统演化过程中,接口需要兼容 装饰器模式(Decorator) 核心思想 :动态地给一个对象添加一些额外的职责。装饰器模式提供了一种比继承更灵活的方式来扩展对象的功能。
技术实现 :
组件接口定义核心功能 具体组件实现核心功能 装饰器抽象类继承组件接口并持有组件引用 具体装饰器实现额外功能 支持多层装饰,形成装饰链 实际应用场景 :
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 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 class DataProcessor {public : virtual ~DataProcessor () = default ; virtual std::string process (const std::string& data) = 0 ; virtual std::string getName () const = 0 ; }; class BaseDataProcessor : public DataProcessor {public : std::string process (const std::string& data) override { std::cout << "Base processor: Processing raw data" << std::endl; return data; } std::string getName () const override { return "BaseProcessor" ; } }; class DataProcessorDecorator : public DataProcessor {protected : std::unique_ptr<DataProcessor> processor; public : DataProcessorDecorator (std::unique_ptr<DataProcessor> processor) : processor (std::move (processor)) {} std::string process (const std::string& data) override { return processor->process (data); } std::string getName () const override { return processor->getName (); } }; class EncryptionDecorator : public DataProcessorDecorator {private : std::string encrypt (const std::string& data) { std::string encrypted = data; for (char & c : encrypted) { c += 1 ; } return encrypted; } public : using DataProcessorDecorator::DataProcessorDecorator; std::string process (const std::string& data) override { std::string processed = DataProcessorDecorator::process (data); std::string encrypted = encrypt (processed); std::cout << "Encryption decorator: Encrypting data" << std::endl; return encrypted; } std::string getName () const override { return processor->getName () + "+Encryption" ; } }; class CompressionDecorator : public DataProcessorDecorator {private : std::string compress (const std::string& data) { std::cout << "Compression decorator: Compressing data (original size: " << data.size () << ")" << std::endl; return "[COMPRESSED]" + data.substr (0 , std::min (10 , static_cast <int >(data.size ()))) + "..." ; } public : using DataProcessorDecorator::DataProcessorDecorator; std::string process (const std::string& data) override { std::string processed = DataProcessorDecorator::process (data); std::string compressed = compress (processed); return compressed; } std::string getName () const override { return processor->getName () + "+Compression" ; } }; class ValidationDecorator : public DataProcessorDecorator {private : bool validate (const std::string& data) { std::cout << "Validation decorator: Validating data" << std::endl; return !data.empty (); } public : using DataProcessorDecorator::DataProcessorDecorator; std::string process (const std::string& data) override { if (!validate (data)) { throw std::runtime_error ("Data validation failed" ); } return DataProcessorDecorator::process (data); } std::string getName () const override { return processor->getName () + "+Validation" ; } }; void useDecoratorPattern () { std::cout << "=== Base Processor ===" << std::endl; auto baseProcessor = std::make_unique <BaseDataProcessor>(); std::string result1 = baseProcessor->process ("Hello, World!" ); std::cout << "Result: " << result1 << std::endl; std::cout << "Processor name: " << baseProcessor->getName () << std::endl; std::cout << "\n=== Encryption Processor ===" << std::endl; auto encryptionProcessor = std::make_unique <EncryptionDecorator>(std::make_unique <BaseDataProcessor>()); std::string result2 = encryptionProcessor->process ("Hello, World!" ); std::cout << "Result: " << result2 << std::endl; std::cout << "Processor name: " << encryptionProcessor->getName () << std::endl; std::cout << "\n=== Encryption + Compression Processor ===" << std::endl; auto encryptedCompressedProcessor = std::make_unique <CompressionDecorator>( std::make_unique <EncryptionDecorator>( std::make_unique <BaseDataProcessor>() ) ); std::string result3 = encryptedCompressedProcessor->process ("Hello, World!" ); std::cout << "Result: " << result3 << std::endl; std::cout << "Processor name: " << encryptedCompressedProcessor->getName () << std::endl; std::cout << "\n=== Validation + Encryption + Compression Processor ===" << std::endl; auto fullyDecoratedProcessor = std::make_unique <ValidationDecorator>( std::make_unique <CompressionDecorator>( std::make_unique <EncryptionDecorator>( std::make_unique <BaseDataProcessor>() ) ) ); std::string result4 = fullyDecoratedProcessor->process ("Hello, World!" ); std::cout << "Result: " << result4 << std::endl; std::cout << "Processor name: " << fullyDecoratedProcessor->getName () << std::endl; try { fullyDecoratedProcessor->process ("" ); } catch (const std::exception& e) { std::cout << "Expected error: " << e.what () << std::endl; } }
优点 :
遵循开放封闭原则,通过组合扩展功能 避免使用继承导致的类爆炸 支持动态添加和移除功能 可以组合多个装饰器,实现复杂功能 装饰器和组件解耦,提高代码可维护性 缺点 :
增加了类的数量 装饰链过长时,调试和理解会变得困难 对于不透明的装饰器,可能会隐藏组件的真实类型 适用场景 :
需要动态、透明地给对象添加功能 功能可以独立扩展,且可以组合使用 避免使用继承导致的类层次结构过于复杂 单一职责原则的体现,每个装饰器只负责一个功能 组合模式(Composite) 核心思想 :将对象组合成树形结构以表示”部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
技术实现 :
组件接口定义叶节点和组合对象的共同操作 叶节点实现基本操作,不包含子节点 组合对象实现操作,并管理子节点集合 支持递归组合,形成树形结构 统一的接口使得客户端可以透明地处理单个对象和组合对象 实际应用场景 :
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 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 class FileSystemNode {public : virtual ~FileSystemNode () = default ; virtual void display (int indent = 0 ) = 0 ; virtual int getSize () const = 0 ; virtual std::string getName () const = 0 ; virtual void add (std::unique_ptr<FileSystemNode> node) {} virtual void remove (const std::string& name) {} virtual FileSystemNode* find (const std::string& name) { return nullptr ; } }; class File : public FileSystemNode {private : std::string name; int size; std::string content; public : File (std::string name, int size, std::string content = "" ) : name (std::move (name)), size (size), content (std::move (content)) {} void display (int indent = 0 ) override { std::cout << std::string (indent, ' ' ) << "- " << name << " (" << size << " bytes)" << std::endl; } int getSize () const override { return size; } std::string getName () const override { return name; } void add (std::unique_ptr<FileSystemNode> node) override { throw std::runtime_error ("Cannot add to a file" ); } void remove (const std::string& name) override { throw std::runtime_error ("Cannot remove from a file" ); } }; class Directory : public FileSystemNode {private : std::string name; std::vector<std::unique_ptr<FileSystemNode>> children; public : explicit Directory (std::string name) : name(std::move(name)) { } void display (int indent = 0 ) override { std::cout << std::string (indent, ' ' ) << "+ " << name << " (directory)" << std::endl; for (const auto & child : children) { child->display (indent + 2 ); } } int getSize () const override { int totalSize = 0 ; for (const auto & child : children) { totalSize += child->getSize (); } return totalSize; } std::string getName () const override { return name; } void add (std::unique_ptr<FileSystemNode> node) override { children.push_back (std::move (node)); } void remove (const std::string& name) override { auto it = std::remove_if (children.begin (), children.end (), [&name](const std::unique_ptr<FileSystemNode>& node) { return node->getName () == name; }); if (it != children.end ()) { children.erase (it, children.end ()); std::cout << "Removed " << name << " from " << this ->name << std::endl; } } FileSystemNode* find (const std::string& name) override { if (this ->name == name) { return this ; } for (const auto & child : children) { FileSystemNode* result = child->find (name); if (result) { return result; } } return nullptr ; } std::vector<File*> getFiles () { std::vector<File*> files; collectFiles (files); return files; } private : void collectFiles (std::vector<File*>& files) { for (const auto & child : children) { if (auto * file = dynamic_cast <File*>(child.get ())) { files.push_back (file); } else if (auto * dir = dynamic_cast <Directory*>(child.get ())) { dir->collectFiles (files); } } } }; void useCompositePattern () { auto root = std::make_unique <Directory>("root" ); auto etc = std::make_unique <Directory>("etc" ); etc->add (std::make_unique <File>("hosts" , 1024 , "127.0.0.1 localhost" )); etc->add (std::make_unique <File>("passwd" , 2048 )); auto home = std::make_unique <Directory>("home" ); auto user = std::make_unique <Directory>("user" ); user->add (std::make_unique <File>("document.txt" , 5120 , "Hello, World!" )); user->add (std::make_unique <File>("image.jpg" , 102400 )); auto pictures = std::make_unique <Directory>("pictures" ); pictures->add (std::make_unique <File>("vacation.jpg" , 204800 )); pictures->add (std::make_unique <File>("family.jpg" , 153600 )); user->add (std::move (pictures)); home->add (std::move (user)); root->add (std::move (etc)); root->add (std::move (home)); root->add (std::make_unique <File>("README.md" , 1536 , "Project documentation" )); std::cout << "=== File System Structure ===" << std::endl; root->display (); std::cout << "\n=== Total Size ===" << std::endl; std::cout << "Total size of root directory: " << root->getSize () << " bytes" << std::endl; std::cout << "\n=== Find Operation ===" << std::endl; if (auto * found = root->find ("hosts" )) { std::cout << "Found: " << found->getName () << " (" << found->getSize () << " bytes)" << std::endl; } if (auto * found = root->find ("vacation.jpg" )) { std::cout << "Found: " << found->getName () << " (" << found->getSize () << " bytes)" << std::endl; } std::cout << "\n=== Remove Operation ===" << std::endl; if (auto * homeDir = dynamic_cast <Directory*>(root->find ("home" ))) { homeDir->remove ("README.md" ); if (auto * userDir = dynamic_cast <Directory*>(homeDir->find ("user" ))) { userDir->remove ("document.txt" ); } } std::cout << "\n=== Updated File System Structure ===" << std::endl; root->display (); std::cout << "\n=== Updated Total Size ===" << std::endl; std::cout << "Total size of root directory: " << root->getSize () << " bytes" << std::endl; }
优点 :
统一处理单个对象和组合对象,简化客户端代码 易于添加新的组件类型,符合开放封闭原则 递归组合形成复杂的树形结构 清晰的层次结构,便于理解和维护 缺点 :
可能使得设计过于一般化,某些操作对叶节点无意义 对于需要区分叶节点和组合对象的场景,可能需要类型检查 递归操作可能导致性能问题(对于非常深的树) 适用场景 :
表示部分-整体层次结构(如文件系统、组织架构) 客户端需要统一处理单个对象和组合对象 需要递归组合形成复杂结构的场景 组件的添加和移除需要灵活处理的场景 行为型模式 行为型模式专注于对象之间的通信。
观察者模式(Observer) 定义对象间的一种一对多依赖关系,当一个对象状态发生变化时,所有依赖它的对象都得到通知并被自动更新。
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 56 57 class Observer {public : virtual ~Observer () = default ; virtual void update () = 0 ; }; class Subject {public : virtual ~Subject () = default ; virtual void attach (std::shared_ptr<Observer> observer) { observers.push_back (observer); } virtual void detach (Observer* observer) { } virtual void notify () { for (const auto & observer : observers) { observer->update (); } } private : std::vector<std::shared_ptr<Observer>> observers; }; class ConcreteSubject : public Subject {public : void setState (int state) { this ->state = state; notify (); } int getState () const { return state; } private : int state; }; class ConcreteObserver : public Observer {public : ConcreteObserver (ConcreteSubject& subject) : subject (subject) { this ->subject.attach (std::shared_ptr <Observer>(this )); } void update () override { state = subject.getState (); } private : ConcreteSubject& subject; int state; };
策略模式(Strategy) 定义一系列算法,把它们封装起来,并且使它们可相互替换。
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 class Strategy {public : virtual ~Strategy () = default ; virtual void algorithm () = 0 ; }; class ConcreteStrategyA : public Strategy {public : void algorithm () override { } }; class ConcreteStrategyB : public Strategy {public : void algorithm () override { } }; class Context {public : void setStrategy (std::unique_ptr<Strategy> strategy) { this ->strategy = std::move (strategy); } void executeAlgorithm () { strategy->algorithm (); } private : std::unique_ptr<Strategy> strategy; };
命令模式(Command) 将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化。
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 class Command {public : virtual ~Command () = default ; virtual void execute () = 0 ; virtual void undo () = 0 ; }; class Receiver {public : void action () { } void reverseAction () { } }; class ConcreteCommand : public Command {public : ConcreteCommand (Receiver& receiver) : receiver (receiver) {} void execute () override { receiver.action (); } void undo () override { receiver.reverseAction (); } private : Receiver& receiver; }; class Invoker {public : void setCommand (std::unique_ptr<Command> command) { this ->command = std::move (command); } void executeCommand () { command->execute (); } void undoCommand () { command->undo (); } private : std::unique_ptr<Command> command; };
面向对象设计最佳实践 1. 合理使用继承 继承是一种”is-a”关系 :派生类应该是基类的一种特殊类型避免过深的继承层次 :继承层次应该控制在合理的范围内优先使用组合而非继承 :当派生类和基类之间不是”is-a”关系时,应该使用组合2. 设计良好的类 单一职责 :每个类应该只有一个职责高内聚 :类的成员之间应该高度相关低耦合 :类之间的依赖应该尽可能少接口简洁 :只暴露必要的接口3. 合理使用设计模式 理解设计模式的适用场景 :不同的设计模式适用于不同的场景不要过度使用设计模式 :简单的问题应该用简单的解决方案灵活应用设计模式 :根据具体情况调整设计模式4. 代码组织 模块化 :将代码组织成逻辑模块命名规范 :使用一致的命名规范注释 :添加适当的注释代码风格 :保持一致的代码风格面向对象设计的挑战 1. 复杂性管理 抽象层次 :合理的抽象层次有助于管理复杂性模块化 :将系统分解为模块化的组件文档 :良好的文档有助于理解系统设计2. 性能考虑 继承与多态的开销 :虚函数调用比普通函数调用慢内存开销 :继承层次会增加对象的大小设计权衡 :在设计和性能之间找到平衡点3. 可测试性 依赖注入 :使用依赖注入提高可测试性接口隔离 :使用接口隔离原则提高可测试性模拟对象 :使用模拟对象进行单元测试示例:面向对象设计的综合应用 图形编辑器 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 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 class Shape {public : virtual ~Shape () = default ; virtual void draw () = 0 ; virtual void move (int x, int y) = 0 ; virtual std::unique_ptr<Shape> clone () = 0 ; }; class Circle : public Shape {public : Circle (int x, int y, int radius) : x (x), y (y), radius (radius) {} void draw () override { std::cout << "Drawing circle at (" << x << ", " << y << ") with radius " << radius << std::endl; } void move (int dx, int dy) override { x += dx; y += dy; } std::unique_ptr<Shape> clone () override { return std::make_unique <Circle>(x, y, radius); } private : int x, y, radius; }; class Rectangle : public Shape {public : Rectangle (int x, int y, int width, int height) : x (x), y (y), width (width), height (height) {} void draw () override { std::cout << "Drawing rectangle at (" << x << ", " << y << ") with width " << width << " and height " << height << std::endl; } void move (int dx, int dy) override { x += dx; y += dy; } std::unique_ptr<Shape> clone () override { return std::make_unique <Rectangle>(x, y, width, height); } private : int x, y, width, height; }; class ShapeFactory {public : static std::unique_ptr<Shape> createShape (const std::string& type, int x, int y, int ... params) { if (type == "circle" ) { return std::make_unique <Circle>(x, y, params[0 ]); } else if (type == "rectangle" ) { return std::make_unique <Rectangle>(x, y, params[0 ], params[1 ]); } return nullptr ; } }; class ShapeComposite : public Shape {public : void addShape (std::unique_ptr<Shape> shape) { shapes.push_back (std::move (shape)); } void removeShape (Shape* shape) { } void draw () override { for (const auto & shape : shapes) { shape->draw (); } } void move (int dx, int dy) override { for (auto & shape : shapes) { shape->move (dx, dy); } } std::unique_ptr<Shape> clone () override { auto composite = std::make_unique <ShapeComposite>(); for (const auto & shape : shapes) { composite->addShape (shape->clone ()); } return composite; } private : std::vector<std::unique_ptr<Shape>> shapes; }; class GraphicsEditor {public : void addShape (std::unique_ptr<Shape> shape) { shapes.push_back (std::move (shape)); } void drawAll () { for (const auto & shape : shapes) { shape->draw (); } } void moveAll (int dx, int dy) { for (auto & shape : shapes) { shape->move (dx, dy); } } private : std::vector<std::unique_ptr<Shape>> shapes; }; void useGraphicsEditor () { GraphicsEditor editor; editor.addShape (ShapeFactory::createShape ("circle" , 10 , 10 , 5 )); editor.addShape (ShapeFactory::createShape ("rectangle" , 20 , 20 , 10 , 8 )); editor.drawAll (); editor.moveAll (5 , 5 ); editor.drawAll (); }
总结 面向对象设计是一种强大的软件开发方法,它通过封装、继承、多态和抽象等概念,帮助开发者创建可维护、可扩展的软件系统。
SOLID原则是面向对象设计的基本原则,它们为创建高质量的软件提供了指导。设计模式是在软件设计中反复出现的问题的解决方案,它们可以帮助开发者更有效地解决常见的设计问题。
在实际开发中,应该根据具体情况灵活应用面向对象设计的原则和模式,在设计和性能之间找到平衡点,创建既优雅又高效的软件系统。