文章

设计模式高频总结一

什么是设计模式?为什么使用设计模式?

什么是设计模式?

设计模式(Design Patterns)是软件开发中经过验证的、可重复使用的解决方案,用于解决常见的软件设计问题。它们提供了一种标准化的设计方式,可以帮助开发者在面对复杂的开发任务时,提高代码的可维护性、可扩展性和可读性。

设计模式并不是具体的代码,而是解决特定问题的模板或指南。设计模式通常可以分为三大类:

  1. 创建型模式(Creational Patterns) 解决对象创建问题,比如如何灵活、安全地创建对象。 示例:单例模式(Singleton)、工厂方法模式(Factory Method)。
  2. 结构型模式(Structural Patterns) 处理类和对象的组合,确保系统结构的灵活性和高效性。 示例:适配器模式(Adapter)、装饰器模式(Decorator)。
  3. 行为型模式(Behavioral Patterns) 关注对象之间的交互和职责划分。 示例:观察者模式(Observer)、策略模式(Strategy)。

为什么使用设计模式?

使用设计模式可以带来以下好处:

  1. 提高代码的可维护性 设计模式提供了清晰的设计思路,减少了代码的复杂性。通过合理的模块化和职责分配,使代码更易于理解和修改。
  2. 促进代码的可重用性 通过设计模式,开发者可以提取通用的解决方案,从而避免重复代码,提高开发效率。
  3. 提高代码的可扩展性 设计模式强调对修改封闭、对扩展开放(OCP,开闭原则)。这使得在增加新功能时,尽量减少对现有代码的改动。
  4. 增强团队协作效率 设计模式为开发者提供了通用的术语和设计方法,团队成员可以快速理解和沟通。
  5. 解决常见问题的最佳实践 设计模式是经过无数开发者验证的成熟方案,可以减少踩坑的可能性。
  6. 与面向对象原则契合 设计模式大多遵循面向对象设计的基本原则,比如单一职责原则(SRP)、接口隔离原则(ISP)等。

设计模式的应用场景

  1. 当需要解决特定的问题并希望重用解决方案时。
  2. 当希望改进系统的灵活性和可扩展性时。
  3. 当希望团队开发者之间快速协作并共享设计思想时。
  4. 当项目中出现复杂的代码结构并希望重构时。

总结

设计模式不仅仅是技术解决方案,更是一种设计思想的体现。通过合理地使用设计模式,可以帮助开发者构建更加健壮、灵活和高效的软件系统。在实际开发中,根据具体的需求选择合适的设计模式,能够有效提升代码质量和项目开发效率。

列举常见的设计模式分类,并简要描述每个分类中的几个具体设计模式

以下是设计模式的分类及每个分类中的几个常见设计模式的简要描述:


1. 创建型模式(Creational Patterns)

创建型模式关注对象的创建过程,旨在以灵活、高效的方式创建对象。

常见模式:

  1. 单例模式(Singleton)

    • 确保一个类只有一个实例,并提供一个全局访问点。
    • 适用于需要全局唯一对象的场景,例如配置管理器、日志管理器。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    
    class Singleton {
    public:
        static Singleton& getInstance() {
            static Singleton instance;
            return instance;
        }
    private:
        Singleton() {} // 构造函数私有化
        Singleton(const Singleton&) = delete; // 禁止拷贝
        Singleton& operator=(const Singleton&) = delete; // 禁止赋值
    };
    
  2. 工厂方法模式(Factory Method)

    • 定义一个创建对象的接口,让子类决定实例化哪个类。
    • 适用于需要动态决定对象类型的场景。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    
    class Product {
    public:
        virtual void use() = 0;
        virtual ~Product() {}
    };
       
    class ConcreteProduct : public Product {
    public:
        void use() override { std::cout << "Using ConcreteProduct\n"; }
    };
       
    class Factory {
    public:
        virtual Product* createProduct() = 0;
    };
       
    class ConcreteFactory : public Factory {
    public:
        Product* createProduct() override {
            return new ConcreteProduct();
        }
    };
    
  3. 原型模式(Prototype)

    • 使用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。
    • 适用于需要频繁创建类似对象的场景。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    
    class Prototype {
    public:
        virtual Prototype* clone() const = 0;
        virtual ~Prototype() {}
    };
       
    class ConcretePrototype : public Prototype {
    public:
        ConcretePrototype* clone() const override {
            return new ConcretePrototype(*this);
        }
    };
    

2. 结构型模式(Structural Patterns)

结构型模式关注对象和类的组合,确保系统结构的灵活性和可扩展性。

常见模式:

  1. 适配器模式(Adapter)

    • 将一个类的接口转换为客户端希望的另一个接口。
    • 适用于接口不兼容但需要协作的场景。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    
    class Target {
    public:
        virtual void request() = 0;
    };
       
    class Adaptee {
    public:
        void specificRequest() {
            std::cout << "Specific Request\n";
        }
    };
       
    class Adapter : public Target {
        Adaptee* adaptee;
    public:
        Adapter(Adaptee* a) : adaptee(a) {}
        void request() override {
            adaptee->specificRequest();
        }
    };
    
  2. 装饰器模式(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
    
    class Component {
    public:
        virtual void operation() = 0;
        virtual ~Component() {}
    };
       
    class ConcreteComponent : public Component {
    public:
        void operation() override { std::cout << "Concrete Component\n"; }
    };
       
    class Decorator : public Component {
    protected:
        Component* component;
    public:
        Decorator(Component* c) : component(c) {}
        void operation() override {
            component->operation();
        }
    };
       
    class ConcreteDecorator : public Decorator {
    public:
        ConcreteDecorator(Component* c) : Decorator(c) {}
        void operation() override {
            Decorator::operation();
            std::cout << "Added behavior\n";
        }
    };
    

3. 行为型模式(Behavioral Patterns)

行为型模式关注对象间的交互,分配职责并确保协作。

常见模式:

  1. 观察者模式(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
    
    #include <vector>
    #include <iostream>
       
    class Observer {
    public:
        virtual void update() = 0;
    };
       
    class Subject {
        std::vector<Observer*> observers;
    public:
        void attach(Observer* observer) {
            observers.push_back(observer);
        }
        void notify() {
            for (auto obs : observers) {
                obs->update();
            }
        }
    };
       
    class ConcreteObserver : public Observer {
    public:
        void update() override {
            std::cout << "Observer updated\n";
        }
    };
    
  2. 策略模式(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
    
    class Strategy {
    public:
        virtual void execute() = 0;
    };
       
    class ConcreteStrategyA : public Strategy {
    public:
        void execute() override { std::cout << "Strategy A\n"; }
    };
       
    class ConcreteStrategyB : public Strategy {
    public:
        void execute() override { std::cout << "Strategy B\n"; }
    };
       
    class Context {
        Strategy* strategy;
    public:
        Context(Strategy* s) : strategy(s) {}
        void setStrategy(Strategy* s) { strategy = s; }
        void executeStrategy() {
            strategy->execute();
        }
    };
    
  3. 命令模式(Command)

    • 将请求封装为对象,使得可以用不同的请求参数化对象。
    • 适用于需要记录操作历史或支持撤销功能的场景。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    
    class Command {
    public:
        virtual void execute() = 0;
    };
       
    class Receiver {
    public:
        void action() { std::cout << "Action executed\n"; }
    };
       
    class ConcreteCommand : public Command {
        Receiver* receiver;
    public:
        ConcreteCommand(Receiver* r) : receiver(r) {}
        void execute() override {
            receiver->action();
        }
    };
    

总结

设计模式通过针对性解决常见问题,使代码更加高效、可维护。根据实际需求选择合适的设计模式,可以显著提升项目质量和开发效率。

解释单例模式的概念和用途,以及如何实现单例模式

单例模式的概念

单例模式(Singleton Pattern) 是一种创建型设计模式,它的核心目标是确保一个类只有一个实例,并提供全局访问点来获取该实例。通过单例模式,可以避免创建多个实例,保证系统资源的有效利用,同时确保全局状态的一致性。


单例模式的用途

单例模式通常用于以下场景:

  1. 配置管理 系统的配置信息通常需要在整个应用中共享,通过单例可以保证配置对象的唯一性。
  2. 日志记录 系统中可能需要一个全局的日志记录器,单例模式能保证日志实例的全局唯一性。
  3. 线程池 确保系统中只有一个线程池管理线程的分配和销毁。
  4. 数据库连接池 确保系统中只有一个数据库连接池实例,从而高效管理连接资源。
  5. 全局缓存 用于存储系统中需要共享的状态或数据。

单例模式的实现

单例模式的实现有多种方式,以下是一些常见的实现方法,代码均以 C++ 为例。


1. 懒汉式单例(Lazy Initialization)

懒汉式单例指的是在第一次使用时才创建实例。

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
#include <iostream>
#include <mutex>

class Singleton {
public:
    static Singleton* getInstance() {
        if (instance == nullptr) { // 第一次访问时创建实例
            instance = new Singleton();
        }
        return instance;
    }

private:
    Singleton() {} // 构造函数私有化
    ~Singleton() {}

    static Singleton* instance; // 静态指针,存储唯一实例
};

// 初始化静态成员
Singleton* Singleton::instance = nullptr;

int main() {
    Singleton* s1 = Singleton::getInstance();
    Singleton* s2 = Singleton::getInstance();
    std::cout << (s1 == s2) << std::endl; // 输出 1,表示是同一个实例
    return 0;
}

问题: 懒汉式单例在多线程环境下是不安全的,需要引入线程同步机制。


2. 线程安全的懒汉式单例

通过引入互斥锁确保线程安全。

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
#include <iostream>
#include <mutex>

class Singleton {
public:
    static Singleton* getInstance() {
        std::lock_guard<std::mutex> lock(mutex); // 加锁
        if (instance == nullptr) {
            instance = new Singleton();
        }
        return instance;
    }

private:
    Singleton() {}
    ~Singleton() {}

    static Singleton* instance;
    static std::mutex mutex; // 用于线程同步
};

// 初始化静态成员
Singleton* Singleton::instance = nullptr;
std::mutex Singleton::mutex;

int main() {
    Singleton* s1 = Singleton::getInstance();
    Singleton* s2 = Singleton::getInstance();
    std::cout << (s1 == s2) << std::endl; // 输出 1
    return 0;
}

3. 饿汉式单例(Eager Initialization)

饿汉式单例指的是在类加载时就创建实例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>

class Singleton {
public:
    static Singleton& getInstance() {
        return instance;
    }

private:
    Singleton() {}
    ~Singleton() {}

    static Singleton instance; // 静态实例
};

// 初始化静态成员
Singleton Singleton::instance;

int main() {
    Singleton& s1 = Singleton::getInstance();
    Singleton& s2 = Singleton::getInstance();
    std::cout << (&s1 == &s2) << std::endl; // 输出 1
    return 0;
}

特点

  • 实现简单,线程安全。
  • 缺点是即使没有使用实例,也会创建,占用资源。

4. 使用 C++11 的 std::call_once

C++11 引入了 std::call_oncestd::once_flag,可以高效地实现线程安全的单例。

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
#include <iostream>
#include <mutex>

class Singleton {
public:
    static Singleton& getInstance() {
        std::call_once(initFlag, []() { instance = new Singleton(); });
        return *instance;
    }

private:
    Singleton() {}
    ~Singleton() {}

    static Singleton* instance;
    static std::once_flag initFlag; // 确保只调用一次
};

// 初始化静态成员
Singleton* Singleton::instance = nullptr;
std::once_flag Singleton::initFlag;

int main() {
    Singleton& s1 = Singleton::getInstance();
    Singleton& s2 = Singleton::getInstance();
    std::cout << (&s1 == &s2) << std::endl; // 输出 1
    return 0;
}

5. C++11 的局部静态变量(推荐)

C++11 保证函数内部的局部静态变量在初始化时是线程安全的,可以非常简单地实现单例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>

class Singleton {
public:
    static Singleton& getInstance() {
        static Singleton instance; // 局部静态变量,线程安全
        return instance;
    }

private:
    Singleton() {}
    ~Singleton() {}
};

int main() {
    Singleton& s1 = Singleton::getInstance();
    Singleton& s2 = Singleton::getInstance();
    std::cout << (&s1 == &s2) << std::endl; // 输出 1
    return 0;
}

单例模式的优缺点

优点:

  1. 控制实例数量,节省系统资源。
  2. 提供全局访问点,方便统一管理。

缺点:

  1. 可能导致类过于依赖全局状态,违背面向对象的设计原则。
  2. 在多线程环境下需要额外的处理,可能增加复杂性。
  3. 可能在某些场景下导致单例生命周期难以管理(例如动态库卸载时的问题)。

总结

单例模式是一种常用的设计模式,适用于需要确保类实例唯一性的场景。推荐使用 C++11 的局部静态变量方式实现单例,因为它实现简单且线程安全。此外,在使用单例模式时应注意避免过度使用,以免导致代码难以测试和维护。

什么是工厂方法模式和抽象工厂模式?它们之间有何区别?

工厂方法模式和抽象工厂模式的概念

工厂方法模式(Factory Method Pattern)

工厂方法模式是一种创建型设计模式,它通过定义一个创建对象的接口,将对象的具体实例化推迟到子类中。每个子类负责创建一种特定类型的对象。

特点

  • 用于解决对象创建时的灵活性问题。
  • 子类可以重写工厂方法,返回不同的具体产品。

适用场景

  • 系统需要在不修改客户端代码的情况下引入新的产品。
  • 创建对象需要动态决定具体实现类。

抽象工厂模式(Abstract Factory Pattern)

抽象工厂模式是一种创建型设计模式,它提供一个创建一组相关或相互依赖对象的接口,而无需指定它们的具体类。

特点

  • 用于创建产品族。
  • 客户端通过抽象工厂与具体工厂交互,从而与具体产品解耦。

适用场景

  • 系统需要与多个产品族进行交互,而不希望依赖具体产品类。
  • 创建的一组对象之间存在依赖关系。

两者的区别

特性工厂方法模式抽象工厂模式
关注点通过子类决定创建哪种具体产品提供一个接口,用于创建一组相关联的产品
抽象程度比较简单,只有一个产品的创建方法更复杂,涉及多个产品的创建
产品种类单一产品产品族(多个相关产品)
扩展方式新增产品需要创建新的工厂子类新增产品族需要扩展工厂接口和具体实现
使用复杂度较低较高

代码示例

1. 工厂方法模式

场景:一个简单的日志系统,支持创建文件日志和控制台日志。

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
#include <iostream>
#include <memory>

// 抽象产品
class Logger {
public:
    virtual void log(const std::string& message) = 0;
    virtual ~Logger() {}
};

// 具体产品:文件日志
class FileLogger : public Logger {
public:
    void log(const std::string& message) override {
        std::cout << "FileLogger: " << message << std::endl;
    }
};

// 具体产品:控制台日志
class ConsoleLogger : public Logger {
public:
    void log(const std::string& message) override {
        std::cout << "ConsoleLogger: " << message << std::endl;
    }
};

// 抽象工厂
class LoggerFactory {
public:
    virtual std::unique_ptr<Logger> createLogger() = 0;
    virtual ~LoggerFactory() {}
};

// 具体工厂:文件日志工厂
class FileLoggerFactory : public LoggerFactory {
public:
    std::unique_ptr<Logger> createLogger() override {
        return std::make_unique<FileLogger>();
    }
};

// 具体工厂:控制台日志工厂
class ConsoleLoggerFactory : public LoggerFactory {
public:
    std::unique_ptr<Logger> createLogger() override {
        return std::make_unique<ConsoleLogger>();
    }
};

int main() {
    // 使用文件日志工厂
    std::unique_ptr<LoggerFactory> factory = std::make_unique<FileLoggerFactory>();
    std::unique_ptr<Logger> logger = factory->createLogger();
    logger->log("This is a file log message.");

    // 使用控制台日志工厂
    factory = std::make_unique<ConsoleLoggerFactory>();
    logger = factory->createLogger();
    logger->log("This is a console log message.");

    return 0;
}

2. 抽象工厂模式

场景:一个 UI 工程,支持 Windows 和 MacOS 两种平台,分别提供不同风格的按钮和文本框。

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
#include <iostream>
#include <memory>

// 抽象产品:按钮
class Button {
public:
    virtual void render() = 0;
    virtual ~Button() {}
};

// 抽象产品:文本框
class TextBox {
public:
    virtual void render() = 0;
    virtual ~TextBox() {}
};

// 具体产品:Windows 按钮
class WindowsButton : public Button {
public:
    void render() override {
        std::cout << "Rendering Windows Button" << std::endl;
    }
};

// 具体产品:Windows 文本框
class WindowsTextBox : public TextBox {
public:
    void render() override {
        std::cout << "Rendering Windows TextBox" << std::endl;
    }
};

// 具体产品:MacOS 按钮
class MacOSButton : public Button {
public:
    void render() override {
        std::cout << "Rendering MacOS Button" << std::endl;
    }
};

// 具体产品:MacOS 文本框
class MacOSTextBox : public TextBox {
public:
    void render() override {
        std::cout << "Rendering MacOS TextBox" << std::endl;
    }
};

// 抽象工厂
class UIFactory {
public:
    virtual std::unique_ptr<Button> createButton() = 0;
    virtual std::unique_ptr<TextBox> createTextBox() = 0;
    virtual ~UIFactory() {}
};

// 具体工厂:Windows 工厂
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>();
    }
};

// 具体工厂:MacOS 工厂
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>();
    }
};

int main() {
    // 使用 Windows 工厂
    std::unique_ptr<UIFactory> factory = std::make_unique<WindowsUIFactory>();
    auto button = factory->createButton();
    auto textBox = factory->createTextBox();
    button->render();
    textBox->render();

    // 使用 MacOS 工厂
    factory = std::make_unique<MacOSUIFactory>();
    button = factory->createButton();
    textBox = factory->createTextBox();
    button->render();
    textBox->render();

    return 0;
}

总结

  • 工厂方法模式:适合单一产品类型的创建,扩展新产品时只需添加新的具体工厂。
  • 抽象工厂模式:适合多个相关产品的创建(产品族),但扩展新产品族时需要修改工厂接口。

根据具体需求选择模式可以有效提升代码的可维护性和可扩展性。

解释装饰器模式和适配器模式的概念,并举例说明它们的应用场景

装饰器模式和适配器模式的概念


装饰器模式(Decorator Pattern)

概念: 装饰器模式是一种结构型设计模式,允许动态地向对象添加额外的职责,而无需修改对象的代码。它通过创建装饰类(Decorator),在保持原有接口的同时增加功能。

特点

  • 不需要修改原始类或继承原始类,即可扩展对象的行为。
  • 装饰器类和被装饰类遵循相同的接口。

适用场景

  1. 需要为对象添加额外功能,但不想修改现有类。
  2. 动态地为对象叠加不同的功能。
  3. 希望避免因功能扩展导致的类爆炸(大量子类)。

适配器模式(Adapter Pattern)

概念: 适配器模式是一种结构型设计模式,它将一个类的接口转换为客户端期望的另一个接口。适配器使得原本由于接口不兼容而无法一起工作的类能够协同工作。

特点

  • 解决现有接口与客户端需求不匹配的问题。
  • 常用于整合第三方库或遗留代码。

适用场景

  1. 需要使用一个已经存在的类,但它的接口不符合当前需求。
  2. 需要整合不同接口的类,使其能够协同工作。

两者的区别

特性装饰器模式适配器模式
目的动态扩展对象的功能将不兼容的接口适配为兼容接口
角色装饰器为原始类增加功能适配器将已有类的接口转换为目标接口
使用时机功能增强接口不兼容
实现方式依赖于继承或组合通过组合和转换实现

代码示例

1. 装饰器模式

场景:一个简单的文本处理系统,可以为文本添加加粗、加下划线等装饰功能。

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
#include <iostream>
#include <memory>
#include <string>

// 抽象组件
class Text {
public:
    virtual std::string render() = 0;
    virtual ~Text() {}
};

// 具体组件
class PlainText : public Text {
    std::string content;
public:
    PlainText(const std::string& text) : content(text) {}
    std::string render() override {
        return content;
    }
};

// 抽象装饰器
class TextDecorator : public Text {
protected:
    std::unique_ptr<Text> text;
public:
    TextDecorator(std::unique_ptr<Text> t) : text(std::move(t)) {}
};

// 具体装饰器:加粗
class BoldDecorator : public TextDecorator {
public:
    BoldDecorator(std::unique_ptr<Text> t) : TextDecorator(std::move(t)) {}
    std::string render() override {
        return "<b>" + text->render() + "</b>";
    }
};

// 具体装饰器:加下划线
class UnderlineDecorator : public TextDecorator {
public:
    UnderlineDecorator(std::unique_ptr<Text> t) : TextDecorator(std::move(t)) {}
    std::string render() override {
        return "<u>" + text->render() + "</u>";
    }
};

int main() {
    std::unique_ptr<Text> text = std::make_unique<PlainText>("Hello, World!");
    text = std::make_unique<BoldDecorator>(std::move(text));  // 加粗
    text = std::make_unique<UnderlineDecorator>(std::move(text));  // 加下划线
    std::cout << text->render() << std::endl;  // 输出:<u><b>Hello, World!</b></u>
    return 0;
}

2. 适配器模式

场景:一个客户端期望使用统一的 Target 接口,但现有 Adaptee 的接口不兼容,需要使用适配器进行转换。

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
#include <iostream>
#include <memory>

// 目标接口
class Target {
public:
    virtual void request() = 0;
    virtual ~Target() {}
};

// 已有类(适配者)
class Adaptee {
public:
    void specificRequest() {
        std::cout << "Specific request from Adaptee\n";
    }
};

// 适配器
class Adapter : public Target {
    std::unique_ptr<Adaptee> adaptee;
public:
    Adapter(std::unique_ptr<Adaptee> a) : adaptee(std::move(a)) {}
    void request() override {
        adaptee->specificRequest();  // 转换为目标接口调用
    }
};

int main() {
    std::unique_ptr<Target> target = std::make_unique<Adapter>(std::make_unique<Adaptee>());
    target->request();  // 输出:Specific request from Adaptee
    return 0;
}

装饰器模式和适配器模式的应用场景

装饰器模式的应用场景

  1. 图形界面库 添加功能(如边框、阴影)到图形组件(如按钮、文本框)而不修改原始组件代码。
  2. 日志系统 动态地为日志添加功能(如日志级别、时间戳、格式化等)。

适配器模式的应用场景

  1. 遗留系统整合 将新开发系统与旧系统兼容,使其接口统一。
  2. 第三方库封装 封装第三方库,使其接口符合当前项目的需求。

总结

  • 装饰器模式:侧重增强对象功能,适用于功能动态叠加的场景。
  • 适配器模式:侧重接口转换,适用于接口不兼容的场景。

根据具体需求选择模式,可以有效提升代码的灵活性和可维护性。

什么是观察者模式?如何实现观察者模式?

观察者模式的概念

观察者模式(Observer Pattern) 是一种行为型设计模式,它定义了一种 一对多 的依赖关系。当被观察对象(Subject)状态发生变化时,所有依赖它的观察者(Observer)都会被自动通知。


观察者模式的特点

  1. 解耦:观察者模式将观察者与被观察者解耦,使得它们可以独立地改变和扩展。
  2. 动态关系:观察者可以在运行时动态添加或移除。
  3. 灵活性:支持广播通信,一个主题可以通知多个观察者。

观察者模式的组成

  1. Subject(主题/被观察者)
    • 维护观察者列表。
    • 提供方法用于注册、移除观察者。
    • 通知观察者状态变化。
  2. Observer(观察者)
    • 定义接口,用于接收来自主题的通知。
  3. ConcreteSubject(具体主题)
    • 存储主题的具体状态。
    • 实现状态变化后通知所有观察者。
  4. ConcreteObserver(具体观察者)
    • 维护对具体主题的引用。
    • 实现更新接口以响应主题的通知。

观察者模式的实现

以下是 C++ 实现观察者模式的一个简单示例。

代码示例:天气预报系统

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
#include <iostream>
#include <vector>
#include <memory>
#include <string>

// 观察者接口
class Observer {
public:
    virtual void update(float temperature, float humidity, float pressure) = 0;
    virtual ~Observer() {}
};

// 被观察者接口
class Subject {
public:
    virtual void registerObserver(std::shared_ptr<Observer> observer) = 0;
    virtual void removeObserver(std::shared_ptr<Observer> observer) = 0;
    virtual void notifyObservers() = 0;
    virtual ~Subject() {}
};

// 具体主题(天气数据)
class WeatherData : public Subject {
    std::vector<std::shared_ptr<Observer>> observers;
    float temperature;
    float humidity;
    float pressure;

public:
    void registerObserver(std::shared_ptr<Observer> observer) override {
        observers.push_back(observer);
    }

    void removeObserver(std::shared_ptr<Observer> observer) override {
        observers.erase(std::remove(observers.begin(), observers.end(), observer), observers.end());
    }

    void notifyObservers() override {
        for (auto& observer : observers) {
            observer->update(temperature, humidity, pressure);
        }
    }

    // 模拟天气数据变化
    void setMeasurements(float temp, float hum, float pres) {
        temperature = temp;
        humidity = hum;
        pressure = pres;
        notifyObservers(); // 通知观察者
    }
};

// 具体观察者:当前天气显示
class CurrentConditionsDisplay : public Observer {
    float temperature;
    float humidity;

public:
    void update(float temp, float hum, float /*pressure*/) override {
        temperature = temp;
        humidity = hum;
        display();
    }

    void display() {
        std::cout << "Current conditions: " << temperature << "°C and " 
                  << humidity << "% humidity" << std::endl;
    }
};

// 具体观察者:统计天气数据
class StatisticsDisplay : public Observer {
    float maxTemperature = 0.0f;
    float minTemperature = 100.0f;
    float temperatureSum = 0.0f;
    int numReadings = 0;

public:
    void update(float temp, float /*humidity*/, float /*pressure*/) override {
        temperatureSum += temp;
        numReadings++;

        if (temp > maxTemperature) maxTemperature = temp;
        if (temp < minTemperature) minTemperature = temp;

        display();
    }

    void display() {
        std::cout << "Avg/Max/Min temperature = " 
                  << (temperatureSum / numReadings) << "/" 
                  << maxTemperature << "/" 
                  << minTemperature << "°C" << std::endl;
    }
};

int main() {
    // 创建主题(天气数据)
    auto weatherData = std::make_shared<WeatherData>();

    // 创建观察者
    auto currentDisplay = std::make_shared<CurrentConditionsDisplay>();
    auto statisticsDisplay = std::make_shared<StatisticsDisplay>();

    // 注册观察者
    weatherData->registerObserver(currentDisplay);
    weatherData->registerObserver(statisticsDisplay);

    // 模拟天气数据更新
    weatherData->setMeasurements(25.0f, 65.0f, 1013.0f);
    weatherData->setMeasurements(28.0f, 70.0f, 1012.0f);
    weatherData->setMeasurements(22.0f, 90.0f, 1011.0f);

    return 0;
}

输出结果

1
2
3
4
5
6
Current conditions: 25°C and 65% humidity
Avg/Max/Min temperature = 25/25/25°C
Current conditions: 28°C and 70% humidity
Avg/Max/Min temperature = 26.5/28/25°C
Current conditions: 22°C and 90% humidity
Avg/Max/Min temperature = 25/28/22°C

观察者模式的应用场景

  1. 事件监听系统
    • GUI 框架中的事件模型(例如按钮点击监听)。
  2. 发布订阅系统
    • 消息队列中的发布/订阅机制(如 MQTT)。
  3. 数据绑定
    • 前端框架中的双向数据绑定(如 Vue.js 和 Angular)。
  4. 监控系统
    • 系统中的实时状态监控,通知多个观察者(如日志、报警)。

观察者模式的优缺点

优点

  1. 实现了对象之间的松耦合。
  2. 动态地添加、删除观察者,灵活性强。
  3. 符合开闭原则,易于扩展。

缺点

  1. 如果观察者过多,通知所有观察者会影响性能。
  2. 可能出现循环依赖的问题(需额外处理)。
  3. 需要确保观察者更新逻辑的正确性,否则可能引发问题。

总结

观察者模式是一种非常实用的设计模式,特别适合需要动态更新和通知的场景。在实际开发中,合理运用观察者模式可以提高系统的扩展性和可维护性。

本文由作者按照 CC BY 4.0 进行授权