软件开发经典设计模式:行为型

观察者模式

观察者模式(Observer Pattern)是一种行为型设计模式,它定义了一种一对多的关系,让一个对象(被观察者)状态发生改变时,其所有依赖者(观察者)都会收到通知并自动更新。这种模式用于当一个对象需要自动通知其他对象发生变化时,实现事件处理机制。

核心思想

观察者模式的核心思想是将一个系统中的多个对象彼此解耦,使对象之间的通信在遵循发布-订阅模式的基础上更加灵活。当被观察对象的状态发生变化时,所有订阅它的观察者都会得到通知并做相应的更新,这确保了数据的一致性。

组成部分

  1. 被观察者(Subject)

    • 包含对观察者对象的引用,提供注册、移除和通知观察者的方法,它维护一组观察者的集合。
  2. 观察者(Observer)

    • 定义一个更新接口,具体观察者需要实现该接口来接收更新消息。
  3. 具体被观察者(ConcreteSubject)

    • 维护有关状态的信息,当状态发生改变时,向其观察者发送通知。
  4. 具体观察者(ConcreteObserver)

    • 实现观察者接口,它维护一个指向具体被观察者的引用,以此获取必要的信息更新自身状态。

示例

下面是一个使用 C++ 实现的简单观察者模式示例,模拟气象数据更新的场景:

#include <iostream>
#include <vector>
#include <memory>

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

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

// 具体被观察者
class WeatherData : public Subject {
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() const override {
        for (const auto& observer : observers_) {
            observer->update(temperature_, humidity_, pressure_);
        }
    }

    void setMeasurements(float temperature, float humidity, float pressure) {
        temperature_ = temperature;
        humidity_ = humidity;
        pressure_ = pressure;
        notifyObservers();
    }

private:
    std::vector<std::shared_ptr<Observer>> observers_;
    float temperature_;
    float humidity_;
    float pressure_;
};

// 具体观察者
class CurrentConditionsDisplay : public Observer {
public:
    void update(float temperature, float humidity, float pressure) override {
        std::cout << "Current conditions: " << temperature << "C degrees, "
                  << humidity << "% humidity, " << pressure << "Pa pressure" << std::endl;
    }
};

// 客户端代码
int main() {
    std::shared_ptr<WeatherData> weatherData = std::make_shared<WeatherData>();
    std::shared_ptr<CurrentConditionsDisplay> currentDisplay = std::make_shared<CurrentConditionsDisplay>();

    weatherData->registerObserver(currentDisplay);

    weatherData->setMeasurements(25.0, 65.0, 1013.0);
    weatherData->setMeasurements(26.0, 70.0, 1012.0);

    return 0;
}

特点和优点

  • 解耦

    • 观察者与被观察者之间是松耦合的,对象之间无需了解彼此的具体实现。
  • 灵活性

    • 可以在运行时注册新的观察者,或移除不再需要的观察者。
  • 自动更新

    • 确保被观察者状态变化时,所有观察者都会被通知更新,保证一致性。

使用场景

  • 事件驱动系统

    • UI 框架中,观察者模式经常用于事件和回调机制。
  • 订阅发布系统

    • 类似于消息驱动的主题/频道,可以同时通知多个观察者。
  • 通知系统

    • 当需要一个事件引发多个响应时,比如应用程序中日志管理、数据更新推送等。

通过观察者模式,系统可以实现动态更新和弹性扩展,同时确保对象间低耦合性,提升了其灵活性和可维护性,在软件开发中是一种强健的组合模式。

模版模式

模板方法模式(Template Method Pattern)是一种行为型设计模式,它定义了一个算法的骨架,并允许子类在不改变其结构的情况下重写算法的某些步骤。模板方法模式在父类中定义一个操作中的算法骨架,而将某些步骤的实现延迟到子类来完成。这使得算法的步骤和具体实现之间得到解耦。

核心思想

模板方法模式的核心思想是通过在抽象基类中定义一个算法的整体结构,并将其中可变的部分的实现交给子类,以实现具体的步骤。这种模式使得算法的整体结构稳定一致,且不同的子类可以实现不同的具体行为。

组成部分

  1. 抽象类(AbstractClass)

    • 声明并实现一个模板方法,定义算法的骨架。还可以包括一个或多个抽象方法,这些方法将在子类中实现。
  2. 具体子类(ConcreteClass)

    • 实现抽象类中定义的抽象方法,从而提供算法的具体步骤。

示例

以下是一个使用 C++ 实现的简单模板方法模式示例,模拟一个制作咖啡和茶的场景:

#include <iostream>

// 抽象类:饮品制作
class CaffeineBeverage {
public:
    // 模板方法
    void prepareRecipe() {
        boilWater();
        brew();
        pourInCup();
        addCondiments();
    }

    // 固定步骤
    void boilWater() const {
        std::cout << "Boiling water" << std::endl;
    }

    void pourInCup() const {
        std::cout << "Pouring into cup" << std::endl;
    }

    // 可变步骤,具体子类需要实现
    virtual void brew() const = 0;
    virtual void addCondiments() const = 0;

    virtual ~CaffeineBeverage() = default;
};

// 具体子类:咖啡
class Coffee : public CaffeineBeverage {
public:
    void brew() const override {
        std::cout << "Dripping coffee through filter" << std::endl;
    }

    void addCondiments() const override {
        std::cout << "Adding sugar and milk" << std::endl;
    }
};

// 具体子类:茶
class Tea : public CaffeineBeverage {
public:
    void brew() const override {
        std::cout << "Steeping the tea" << std::endl;
    }

    void addCondiments() const override {
        std::cout << "Adding lemon" << std::endl;
    }
};

// 客户端代码
int main() {
    std::cout << "Making coffee:" << std::endl;
    Coffee coffee;
    coffee.prepareRecipe();

    std::cout << "\nMaking tea:" << std::endl;
    Tea tea;
    tea.prepareRecipe();

    return 0;
}

特点和优点

  • 代码复用

    • 共用算法的整体结构,只需在子类中实现个性化步骤,避免代码重复。
  • 可扩展性

    • 易于通过继承扩展算法的步骤,而不改变原有代码的稳定结构。
  • 控制反转

    • 基于算法即制订者的原则,模板方法模式通过抽象类控制子类的行为。

使用场景

  • 算法需要多个步骤的系统

    • 如果存在多个阶段的复杂算法,且这些阶段的方法有多个可选实现。
  • 代码复用需求

    • 减少重复代码的省事方案,可以为继承结构中的所有成员提供一套标准操作。
  • 功能扩展

    • 模板模式便于在子类中定义新的行为和细节。

模板方法模式是一种定义算法框架的有效工具,它通过约定和固定结构,在保证通用操作一致性的前提下,允许系统实现的定制化操作,特别是在业务逻辑中各个步骤可以灵活变化的场合下尤为常用。

策略模式

策略模式(Strategy Pattern)是一种行为型设计模式,它定义了一系列算法,并将每个算法封装起来,使它们可以互相替换。策略模式让算法变化,不会影响使用算法的客户。通过将算法的选择和实现委托给策略对象,客户端可以在运行时选用适合的算法实现。

核心思想

策略模式的核心思想是将算法的定义和实现分离开来。通过定义一组策略接口,在客户端和算法之间引入接口,客户端通过接口对象来访问不同算法的具体实现,无需关心具体实现细节。这不仅简化了客户端代码,也提高了算法的可扩展性和可维护性。

组成部分

  1. 策略接口(Strategy)

    • 定义算法族的公共接口,具体的策略类需要实现此接口。
  2. 具体策略(ConcreteStrategy)

    • 实现 Strategy 接口中的算法。
  3. 上下文环境(Context)

    • 维护一个对策略对象的引用,用于最终执行策略的方法。通常,它会通过接口来调用策略对象定义的算法。

示例

以下是一个使用 C++ 实现的简单策略模式示例,模拟一套不同的排序算法:

#include <iostream>
#include <vector>
#include <algorithm>
#include <memory>

// 策略接口
class SortStrategy {
public:
    virtual void sort(std::vector<int>& dataset) = 0;
    virtual ~SortStrategy() = default;
};

// 具体策略:冒泡排序
class BubbleSort : public SortStrategy {
public:
    void sort(std::vector<int>& dataset) override {
        for (size_t i = 0; i < dataset.size(); ++i) {
            for (size_t j = 0; j < dataset.size() - 1; ++j) {
                if (dataset[j] > dataset[j + 1]) {
                    std::swap(dataset[j], dataset[j + 1]);
                }
            }
        }
        std::cout << "Bubble sorted: ";
        for (int num : dataset) {
            std::cout << num << " ";
        }
        std::cout << std::endl;
    }
};

// 具体策略:快速排序
class QuickSort : public SortStrategy {
public:
    void sort(std::vector<int>& dataset) override {
        quickSort(dataset, 0, dataset.size() - 1);
        std::cout << "Quick sorted: ";
        for (int num : dataset) {
            std::cout << num << " ";
        }
        std::cout << std::endl;
    }

private:
    void quickSort(std::vector<int>& dataset, int low, int high) {
        if (low < high) {
            int pi = partition(dataset, low, high);
            quickSort(dataset, low, pi - 1);
            quickSort(dataset, pi + 1, high);
        }
    }

    int partition(std::vector<int>& dataset, int low, int high) {
        int pivot = dataset[high];
        int i = low - 1;
        for (int j = low; j <= high - 1; ++j) {
            if (dataset[j] < pivot) {
                ++i;
                std::swap(dataset[i], dataset[j]);
            }
        }
        std::swap(dataset[i + 1], dataset[high]);
        return i + 1;
    }
};

// 上下文类
class SortContext {
public:
    explicit SortContext(std::unique_ptr<SortStrategy> strategy)
        : strategy_(std::move(strategy)) {}

    void setStrategy(std::unique_ptr<SortStrategy> strategy) {
        strategy_ = std::move(strategy);
    }

    void sort(std::vector<int>& dataset) {
        strategy_->sort(dataset);
    }

private:
    std::unique_ptr<SortStrategy> strategy_;
};

// 客户端代码
int main() {
    std::vector<int> data = {29, 10, 14, 37, 13};

    SortContext context(std::make_unique<BubbleSort>());
    context.sort(data);

    data = {29, 10, 14, 37, 13};
    context.setStrategy(std::make_unique<QuickSort>());
    context.sort(data);

    return 0;
}

特点和优点

  • 灵活性和可扩展性

    • 可以轻松增加新的策略而无需修改上下文代码,符合开放/封闭原则。
  • 算法独立化

    • 每个算法是独立的,通过接口隔离,可以单独开发、测试和重用。
  • 运行时选择算法

    • 客户端可以在运行时选择适合的算法,而无需更改其结构。

使用场景

  • 需要动态选择算法

    • 如在运行时动态改变算法,或基于不同条件使用不同算法的情况。
  • 减少条件判断的复杂度

    • 在选择逻辑中有大量条件语句,通过策略模式可简化代码分支。
  • 有多种相关算法的情形

    • 多种算法存在相似的逻辑,可以通过策略封装避免重复代码。

策略模式是实现不同算法和操作逻辑的有效手段, 它通过封装不同的策略来减少条件判断,并提高代码灵活性和可维护性。对于需要动态选择和变化算法的场景特别有效。

职责链模式

职责链模式(Chain of Responsibility Pattern)是一种行为型设计模式,它允许您将请求沿着处理者链进行发送,直至有处理者处理这个请求。请求发送者不需要明确哪一个处理者处理请求,这提升了系统的灵活性和可扩展性。

核心思想

职责链模式的核心思想是将请求的处理分解在多个对象中进行,并将这些对象连成一条链,让请求沿着链传递,直到某个对象处理它。处理者可以动态地组合,并且可以在无需客户端知道的情况下增加新的处理者。

组成部分

  1. 抽象处理者(Handler)

    • 定义一个请求处理接口,并实现链的连接(通常通过一个指向下一个处理者的引用)。
  2. 具体处理者(ConcreteHandler)

    • 实现抽象处理者,处理请求或将请求传递给链上的下一个处理者。
  3. 客户端(Client)

    • 将请求发送到链上某个具体处理者,通常从链的开始进行调用。

示例

以下是使用 C++ 实现的简单职责链模式示例,模拟了一个简单的日志处理系统,日志信息可能被不同的日志级别处理程序处理:

#include <iostream>
#include <memory>
#include <string>

// 抽象处理者
class Logger {
public:
    virtual ~Logger() = default;

    void setNext(std::shared_ptr<Logger> nextLogger) {
        nextLogger_ = nextLogger;
    }

    void logMessage(int level, const std::string& message) {
        if (this->level_ <= level) {
            write(message);
        }
        if (nextLogger_) {
            nextLogger_->logMessage(level, message);
        }
    }

protected:
    virtual void write(const std::string& message) = 0;

    int level_;
    std::shared_ptr<Logger> nextLogger_ = nullptr;
};

// 具体处理者:控制台日志
class ConsoleLogger : public Logger {
public:
    ConsoleLogger(int level) {
        this->level_ = level;
    }

protected:
    void write(const std::string& message) override {
        std::cout << "Console Logger: " << message << std::endl;
    }
};

// 具体处理者:文件日志
class FileLogger : public Logger {
public:
    FileLogger(int level) {
        this->level_ = level;
    }

protected:
    void write(const std::string& message) override {
        std::cout << "File Logger: " << message << std::endl;
        // 在这里可以增加将日志写入文件的逻辑
    }
};

// 客户端代码
int main() {
    auto consoleLogger = std::make_shared<ConsoleLogger>(1);
    auto fileLogger = std::make_shared<FileLogger>(2);

    consoleLogger->setNext(fileLogger);

    consoleLogger->logMessage(1, "This is an info message.");
    consoleLogger->logMessage(2, "This is an warning message.");

    return 0;
}

特点和优点

  • 降低耦合度

    • 客户端无需明确指定请求的处理者,对象只需保持与链上的后继者的引用便可。
  • 动态性和灵活性

    • 可以在运行时按需增加或删除处理者,灵活定制链的结构。
  • 链式处理

    • 通过链,形成一种职责传递的关系,多处理者可以协作完成任务。

使用场景

  • 多个对象可以处理同一个请求

    • 请求在对象链中进行传递,直至处理请求的对象被发现。
  • 动态指定处理级别

    • 需要动态指定某个请求的处理者,或者在处理某个请求时,可以灵活改变请求的传递路径。
  • 无需识别具体处理者

    • 需要让多个对象都有机会处理请求,而不明确指定接收者时。

职责链模式为请求的处理提供了一种灵活多样的机制,尤其在需要多步处理的系统中,通过职责链模式可以更容易地增强系统功能和扩展性,提升系统的灵活度。

迭代器模式

迭代器模式(Iterator Pattern)是一种行为型设计模式,它提供了一种方法顺序访问一个聚合对象中的各个元素,而无需暴露其内部表示。迭代器模式将遍历聚合对象的职责从聚合对象中分离出来,成为一个对象专门负责遍历。

核心思想

迭代器模式通过提供一个统一的接口来访问不同类型的集合,抽象了集合内部结构的不同,实现了聚合与遍历的分离。这样一来,客户端就不需关心具体集合的实现细节,可以灵活替换和扩展集合。

组成部分

  1. 迭代器接口(Iterator)

    • 定义获取集合元素的方法(例如,nexthasNext),而不暴露集合的具体实现。
  2. 具体迭代器(ConcreteIterator)

    • 实现迭代器接口,维护当前位置以便顺序访问集合中的元素。
  3. 聚合接口(Aggregate)

    • 定义创建迭代器的接口。
  4. 具体聚合(ConcreteAggregate)

    • 实现聚合接口,返回一个特定的迭代器实例。

示例

以下是一个使用 C++ 实现的简单迭代器模式示例,模拟一个整数集合的遍历:

#include <iostream>
#include <vector>
#include <memory>

// 迭代器接口
template <typename T>
class Iterator {
public:
    virtual ~Iterator() = default;
    virtual T next() = 0;
    virtual bool hasNext() const = 0;
};

// 聚合接口
template <typename T>
class Aggregate {
public:
    virtual ~Aggregate() = default;
    virtual std::unique_ptr<Iterator<T>> createIterator() const = 0;
};

// 具体迭代器
template <typename T>
class ConcreteIterator : public Iterator<T> {
public:
    ConcreteIterator(const std::vector<T>& items) : items_(items), current_(0) {}

    T next() override {
        return items_[current_++];
    }

    bool hasNext() const override {
        return current_ < items_.size();
    }

private:
    std::vector<T> items_;
    size_t current_;
};

// 具体聚合
template <typename T>
class ConcreteAggregate : public Aggregate<T> {
public:
    void addItem(const T& item) {
        items_.push_back(item);
    }

    std::unique_ptr<Iterator<T>> createIterator() const override {
        return std::make_unique<ConcreteIterator<T>>(items_);
    }

private:
    std::vector<T> items_;
};

// 客户端代码
int main() {
    ConcreteAggregate<int> numbers;
    numbers.addItem(1);
    numbers.addItem(2);
    numbers.addItem(3);

    auto iterator = numbers.createIterator();
    while (iterator->hasNext()) {
        std::cout << iterator->next() << " ";
    }
    std::cout << std::endl;

    return 0;
}

特点和优点

  • 单一责任原则

    • 单独的迭代器对象负责集合的遍历,这简化了聚合类,并减少了聚合和遍历操作之间的耦合。
  • 开放/闭合原则

    • 可以向系统中添加新的聚合和迭代器,而无需对客户端和其他部分进行修改。
  • 统一接口

    • 提供一致的接口以遍历不同类型的集合。

使用场景

  • 遍历集合的多样性

    • 当你需要在集合上执行多种不同的遍历操作时,比如正序和倒序。
  • 隐藏集合的细节

    • 不希望让客户端知道具体集合的复杂结构时,维护聚合对象的同时可以灵活访问其元素。
  • 多种集合支持

    • 如果需要遍历不同的集合结构(数组、列表、树等),可使用迭代器模式灵活适配。

迭代器模式使得客户端能够使用统一的接口遍历不同类型的集合,它为我们提供了一种访问聚合对象内容的机制,同时隐藏了内部的复杂性,实现了系统中遍历逻辑与集合对象的解耦。

状态模式

状态模式(State Pattern)是一种行为型设计模式,它允许对象在其内部状态改变时改变它的行为。状态模式的核心思想是通过将状态逻辑封装到独立的状态类中,使得对象在状态变化时能够动态改变行为,而不是在对象内部添加复杂的条件判断。

核心思想

状态模式通过创建一组状态类来表示对象的不同状态,并将行为的实现委派给这些状态类。这样,当对象内部状态发生变化时,通过切换状态类来改变对象行为。这使得代码更加清晰,避免了复杂的条件语句,并且与状态相关的变更更为可控。

组成部分

  1. 状态接口(State)

    • 定义一个状态基础接口,具体状态需要实现该接口以封装不同状态下的行为。
  2. 具体状态(ConcreteState)

    • 实现状态接口,具体定义在该状态下对象的行为。
  3. 上下文(Context)

    • 维护一个状态对象的引用,通过状态对象来完成行为的变化。

示例

以下是一个使用 C++ 实现的状态模式的简单示例,模拟了一个简单的电灯开关状态转换:

#include <iostream>
#include <memory>

// 前置声明:上下文类
class Context;

// 状态基类
class State {
public:
    virtual ~State() = default;
    virtual void handle(Context& context) = 0;
};

// 上下文类
class Context {
public:
    Context(std::unique_ptr<State> state) : state_(std::move(state)) {}

    void setState(std::unique_ptr<State> state) {
        state_ = std::move(state);
    }

    void request() {
        state_->handle(*this);
    }

private:
    std::unique_ptr<State> state_;
};

// 具体状态:打开状态
class OnState : public State {
public:
    void handle(Context& context) override {
        std::cout << "Light is turned on, turning it off..." << std::endl;
        context.setState(std::make_unique<OffState>());
    }
};

// 具体状态:关闭状态
class OffState : public State {
public:
    void handle(Context& context) override {
        std::cout << "Light is turned off, turning it on..." << std::endl;
        context.setState(std::make_unique<OnState>());
    }
};

// 客户端代码
int main() {
    Context lightSwitch(std::make_unique<OffState>());

    lightSwitch.request();  // Turning ON
    lightSwitch.request();  // Turning OFF

    return 0;
}

特点和优点

  • 状态行为封装

    • 每个具体状态类封装了与该状态相关的行为,简化了上下文对象的逻辑。
  • 易于扩展

    • 新的状态可以通过添加新的状态类来实现,符合开放/封闭原则。
  • 消除条件语句

    • 常见的 switch或if语句被状态类替代,提升了代码的灵活性和可维护性。

使用场景

  • 对象需要根据状态改变行为

    • 当一个对象的行为随状态变化而变化时。
  • 状态数量较多时

    • 使用状态模式可以避免大量复杂的条件分支。
  • 希望封装与状态切换相关的行为

    • 将与状态切换相关的逻辑集中在各个状态类中,避免巨大的单一类。

状态模式可以让对象在状态变化时灵活改变行为,而不必在代码中大量使用条件语句。通过状态模式,使状态转换变得清晰,并能轻松扩展对象的行为,无需对客户端做额外修改。

访问者模式

访问者模式(Visitor Pattern)是一种行为型设计模式,它允许你在不修改对象结构的情况下增加新的操作。访问者模式通过将操作逻辑从对象结构中分离出来,使得新的操作可以添加到现有类层次结构中。

核心思想

访问者模式的核心思想是用访问者类封装一些操作,这些操作作用于对象结构中的各个元素。在对象结构内部创建一个对访问者的接口,以便访问者能来访问和执行操作。这样,当需要在对象结构上定义新的操作时,可以通过增加新的访问者类来实现,而不必改变对象结构本身。

组成部分

  1. 访问者接口(Visitor)

    • 为对象结构中每个具体类声明一个访问方法。
  2. 具体访问者(ConcreteVisitor)

    • 实现每个访问方法,以定义作用于具体元素的操作。
  3. 元素接口(Element)

    • 定义一个方法,接受访问者访问。
  4. 具体元素(ConcreteElement)

    • 实现 Element 接口,定义接受访问者访问的方法。
  5. 对象结构(ObjectStructure)

    • 能够枚举它的元素,可以提供一个高层接口以允许访问者访问其内部元素。

示例

以下是一个使用 C++ 实现的访问者模式的简单示例,模拟了一个计算机的部件结构,各部件可以接受不同的访问者以实现不同的功能:

#include <iostream>
#include <vector>
#include <memory>

// 前置声明访问者接口
class Visitor;

// 元素接口
class ComputerPart {
public:
    virtual ~ComputerPart() = default;
    virtual void accept(std::shared_ptr<Visitor> visitor) = 0;
};

// 访问者接口
class Visitor {
public:
    virtual ~Visitor() = default;
    virtual void visit(class Keyboard& keyboard) = 0;
    virtual void visit(class Monitor& monitor) = 0;
    virtual void visit(class Mouse& mouse) = 0;
};

// 具体元素:键盘
class Keyboard : public ComputerPart {
public:
    void accept(std::shared_ptr<Visitor> visitor) override {
        visitor->visit(*this);
    }
};

// 具体元素:显示器
class Monitor : public ComputerPart {
public:
    void accept(std::shared_ptr<Visitor> visitor) override {
        visitor->visit(*this);
    }
};

// 具体元素:鼠标
class Mouse : public ComputerPart {
public:
    void accept(std::shared_ptr<Visitor> visitor) override {
        visitor->visit(*this);
    }
};

// 具体访问者
class DisplayVisitor : public Visitor {
public:
    void visit(Keyboard& keyboard) override {
        std::cout << "Displaying Keyboard." << std::endl;
    }

    void visit(Monitor& monitor) override {
        std::cout << "Displaying Monitor." << std::endl;
    }

    void visit(Mouse& mouse) override {
        std::cout << "Displaying Mouse." << std::endl;
    }
};

// 客户端代码
int main() {
    std::vector<std::shared_ptr<ComputerPart>> parts;
    parts.push_back(std::make_shared<Keyboard>());
    parts.push_back(std::make_shared<Monitor>());
    parts.push_back(std::make_shared<Mouse>());

    std::shared_ptr<Visitor> displayVisitor = std::make_shared<DisplayVisitor>();

    for (auto& part : parts) {
        part->accept(displayVisitor);
    }

    return 0;
}

特点和优点

  • 添加新行为

    • 可以在不修改元素类的情况下对对象结构添加新的操作,符合开放/封闭原则。
  • 集中相关行为

    • 将复杂的相关行为在访问者类中实现,而不是分散到各个元素类中,有助于提高代码的集中性。
  • 简化元素类

    • 元素类不负责处理复杂的操作,将这些操作委托给访问者对象。

使用场景

  • 对象结构稳定

    • 对象类很少改变,但经常需要增加新的操作。
  • 需要实施中央访问控制

    • 可以在访问者类中集中控制对各类的访问行为。
  • 跨多个类的操作

    • 当需要跨多个类的操作,并希望让该操作独立于类而变化时。

访问者模式适用于在不改变对象结构的情况下,需要增加新的操作或功能的场合。通过分离访问行为与对象结构,可以实现高扩展性,并使得功能变更独立于对象结构。

备忘录模式

备忘录模式(Memento Pattern)是一种行为型设计模式,它允许你在不暴露对象实现细节的情况下保存和恢复对象的状态。该模式为对象的临时状态存储提供了一种机制,当用户需要撤销某些操作或回滚状态时,可以从保存在外部的备忘录中恢复先前的状态。

核心思想

备忘录模式的基本思想是提供一种能够捕获和存储对象内部状态的方式,并能在需要时恢复该状态。该模式通常涉及三个角色:原发器(Originator)、备忘录(Memento)和负责人(Caretaker)。

组成部分

  1. 原发器(Originator)

    • 创建一个备忘录以捕获内部状态,并可通过该备忘录恢复状态。
  2. 备忘录(Memento)

    • 存储原发器的内部状态,保护其不被访问,即不提供存取方法,只是原发器的快照。
  3. 负责人(Caretaker)

    • 负责保存备忘录,但不能操作或检查备忘录的内容。

示例

下面是一个使用 C++ 实现的简化备忘录模式,模拟了一个文本编辑器的撤销功能:

#include <iostream>
#include <string>
#include <memory>
#include <stack>

// 原发器类
class Editor {
public:
    void setText(const std::string& text) {
        text_ = text;
    }

    std::string getText() const {
        return text_;
    }

    class Memento {
    public:
        explicit Memento(const std::string& state) : state_(state) {}
        std::string getState() const {
            return state_;
        }
    private:
        const std::string state_;
    };

    std::unique_ptr<Memento> save() const {
        return std::make_unique<Memento>(text_);
    }

    void restore(const Memento& memento) {
        text_ = memento.getState();
    }

private:
    std::string text_;
};

// 负责人类
class Caretaker {
public:
    void backup(Editor& editor) {
        history_.push(editor.save());
    }

    void undo(Editor& editor) {
        if (!history_.empty()) {
            editor.restore(*history_.top());
            history_.pop();
        }
    }

private:
    std::stack<std::unique_ptr<Editor::Memento>> history_;
};

// 客户端代码
int main() {
    Editor editor;
    Caretaker caretaker;

    editor.setText("Hello, World!");
    std::cout << "Current Text: " << editor.getText() << std::endl;
    caretaker.backup(editor);

    editor.setText("Hello, Design Patterns!");
    std::cout << "Changed Text: " << editor.getText() << std::endl;

    caretaker.undo(editor);
    std::cout << "After Undo: " << editor.getText() << std::endl;

    return 0;
}

特点和优点

  • 状态恢复

    • 能有效恢复对象到之前的状态,使得应用实现撤销(undo)操作。
  • 封装性

    • 备忘录存储状态,外部不能直接读取,它封装了对象的状态存取。
  • 简化原发器管理

    • 将过去状态管理封装在负责人的职责之内,简化对象管理。

使用场景

  • 需要保存对象快照

    • 如编辑器中的撤销操作、事务管理中的快照等。
  • 不希望外界直接访问内部状态

    • 状态数据不能直接暴露和操作。
  • 必须使用复杂对象的状态

    • 包含许多独立变量的复杂对象需要对此进行保存和恢复。

备忘录模式在需要对象状态恢复的系统中有明显的优势,它有效分离了状态存取和管理的职责,同时保护了对象内部结构。这使得备忘录模式适合用于需要撤销和恢复功能的应用中,如文本编辑器、游戏和数据库事务管理等场景中。

命令模式

命令模式(Command Pattern)是一种行为型设计模式,它将请求封装为对象,从而使你可以用不同的请求对客户端进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。命令模式是一种将动作(或事件)和他们的发起者分离出来的设计模式。

核心思想

命令模式的核心思想是将命令的调用者与命令的实现者分开。通过定义命令对象,可以将动作及其参数化,请求者只需调用命令的接口,而不需要关心命令的具体实现。

组成部分

  1. 命令接口(Command)

    • 声明一个执行操作的命令方法,如 execute()
  2. 具体命令(ConcreteCommand)

    • 实现命令接口,绑定接收者对象,并在 execute() 中定义请求的执行细节。
  3. 接收者(Receiver)

    • 知道如何实施与执行命令相关的操作。
  4. 调用者(Invoker)

    • 持有命令对象并在某个时间点执行命令。
  5. 客户端(Client)

    • 创建具体命令对象并设置其接收者。

示例

下面是一个使用 C++ 实现的简单命令模式示例,模拟遥控器与电灯之间的交互:

#include <iostream>
#include <memory>
#include <vector>

// 接收者类
class Light {
public:
    void on() {
        std::cout << "Light is ON" << std::endl;
    }
    void off() {
        std::cout << "Light is OFF" << std::endl;
    }
};

// 命令接口
class Command {
public:
    virtual ~Command() = default;
    virtual void execute() = 0;
    virtual void undo() = 0;
};

// 具体命令:打开灯
class LightOnCommand : public Command {
public:
    explicit LightOnCommand(Light* light) : light_(light) {}
    void execute() override {
        light_->on();
    }
    void undo() override {
        light_->off();
    }
private:
    Light* light_;
};

// 具体命令:关闭灯
class LightOffCommand : public Command {
public:
    explicit LightOffCommand(Light* light) : light_(light) {}
    void execute() override {
        light_->off();
    }
    void undo() override {
        light_->on();
    }
private:
    Light* light_;
};

// 调用者
class RemoteControl {
public:
    void setCommand(std::shared_ptr<Command> command) {
        command_ = command;
    }
    void pressButton() {
        if (command_) {
            command_->execute();
            history_.push_back(command_);
        }
    }
    void pressUndo() {
        if (!history_.empty()) {
            history_.back()->undo();
            history_.pop_back();
        }
    }
private:
    std::shared_ptr<Command> command_;
    std::vector<std::shared_ptr<Command>> history_;
};

// 客户端代码
int main() {
    Light light;
    auto lightOnCommand = std::make_shared<LightOnCommand>(&light);
    auto lightOffCommand = std::make_shared<LightOffCommand>(&light);

    RemoteControl remote;
    remote.setCommand(lightOnCommand);
    remote.pressButton();  // Light is ON
    remote.setCommand(lightOffCommand);
    remote.pressButton();  // Light is OFF
    
    remote.pressUndo();    // Light is ON

    return 0;
}

特点和优点

  • 解耦命令请求与实现

    • 调用者与实现者完全解耦,使得请求者不知道命令是如何被执行的。
  • 支持撤销/恢复操作

    • 可以在命令中实现 undo() 方法,支持撤销和恢复功能。
  • 易于扩展

    • 可以在不修改调用者代码的情况下增加新的命令类。
  • 组合命令

    • 可以将多个命令组合成一个宏命令来简单执行一系列操作。

使用场景

  • 需要参数化对象

    • 需要用不同请求对对象进行参数化时。
  • 事务性管理

    • 支持编排和回滚事务,以应对失败的操作。
  • 队列请求

    • 需要将请求排队时,如任务调度。

命令模式是处理各种请求和操作的一种灵活方式,使得系统可以在运行时改变命令,同时通过将命令独立于其接收者的调用实现了解耦。这种模式特别适合于需要执行日志记录、撤销操作、事务管理的应用程序。

解释器模式

解释器模式(Interpreter Pattern)是一种行为型设计模式,用于实现一个语言的解释器。解释器模式定义了一种语言的文法,并用它来表示和解释程序中该语言的句子。此模式适合用于具有简单文法的特定领域语言(DSL),它可以通过定义一系列的规则来解析和执行语言中的命令。

核心思想

解释器模式通过定义语法树的结构,利用基础节点类(通常称为抽象表达式类)和具体节点类(具体表达式类)对语言的每个规则进行建模。它利用各个专门的节点类来解析表达式树,以实现对语言的解释。

组成部分

  1. 抽象表达式(AbstractExpression)

    • 声明一个接口,用于实现具体的表达式的解释操作。
  2. 终结符表达式(TerminalExpression)

    • 实现与语言的终结符相关的解释操作,通常在文法图中出现的叶节点。
  3. 非终结符表达式(NonterminalExpression)

    • 为文法中的非终结符实现解释操作,通常它包含多个其他表达式来构成一个复杂表达式。
  4. 上下文(Context)

    • 包含解释器之外的全局信息,一般会存储用来解释的基本环境信息。
  5. 客户端(Client)

    • 根据语言的文法创建解析的表达式树,然后调用解释方法。

示例

这是一个简单的 C++ 示例,演示了使用解释器模式来解析和求值基本的算术表达式:

#include <iostream>
#include <string>
#include <map>
#include <memory>

// 上下文类
class Context {
public:
    void setVariable(const std::string& name, int value) {
        variables_[name] = value;
    }

    int getVariable(const std::string& name) const {
        return variables_.at(name);
    }

private:
    std::map<std::string, int> variables_;
};

// 抽象表达式类
class Expression {
public:
    virtual ~Expression() = default;
    virtual int interpret(const Context& context) const = 0;
};

// 终结符表达式
class Number : public Expression {
public:
    explicit Number(int value) : value_(value) {}

    int interpret(const Context&) const override {
        return value_;
    }

private:
    int value_;
};

// 终结符表达式:变量
class Variable : public Expression {
public:
    explicit Variable(const std::string& name) : name_(name) {}

    int interpret(const Context& context) const override {
        return context.getVariable(name_);
    }

private:
    std::string name_;
};

// 非终结符表达式:加法
class Addition : public Expression {
public:
    Addition(std::unique_ptr<Expression> left, std::unique_ptr<Expression> right)
        : left_(std::move(left)), right_(std::move(right)) {}

    int interpret(const Context& context) const override {
        return left_->interpret(context) + right_->interpret(context);
    }

private:
    std::unique_ptr<Expression> left_;
    std::unique_ptr<Expression> right_;
};

// 客户端代码
int main() {
    // 设置上下文
    Context context;
    context.setVariable("x", 5);

    // 解析表达式: (x + 10)
    std::unique_ptr<Expression> expression = 
        std::make_unique<Addition>(
            std::make_unique<Variable>("x"),
            std::make_unique<Number>(10)
        );

    // 解释结果
    std::cout << "Result: " << expression->interpret(context) << std::endl;

    return 0;
}

特点和优点

  • 易于扩展

    • 通过增加新类,支持新的表达式或语法规则有助于扩展功能。
  • 直观的语言建模

    • 可以直观地实现和扩展特定领域的语言,确保代码的可读性和逻辑清晰。
  • 适用于简单语法

    • 尤其适合有简单语法的任务,让代码便于理解和管理。

使用场景

  • 特定领域语言

    • 应用于简单的语法解析,例如小型数据库查询语言或脚本语言。
  • 规则和配置解析

    • 用于解释配置文件、规则引擎和状态机中的规则。
  • 编译器、解释器

    • 适用于注重代码解析和语法分析的场合。

解释器模式适合用于带有简单语法的特定领域语言场景中,虽然它的主要缺点是在适用于复杂语法时会变得不够高效,但在简单语言解析和执行的场合中依然能提供清晰易维护的代码结构。

中介模式

中介者模式(Mediator Pattern)是一种行为型设计模式,它通过一个中介者对象来封装对象之间的交互,声明了一个对象负责处理并协调其他对象之间的通信和协作,使不同对象之间的依赖关系消除,从而实现松耦合。

核心思想

中介者模式的核心思想是通过引入一个中介者对象,该对象实现各个对象之间的通信和协作,而不是让对象彼此直接交互。这样可以有效减少对象间的依赖和耦合,使得系统更加灵活和易于维护。

组成部分

  1. 中介者接口(Mediator)

    • 定义一个接口,用于发送请求给各个同事对象。
  2. 具体中介者(ConcreteMediator)

    • 实现中介者接口,协调各个同事对象之间的交互。
  3. 同事对象抽象类(Colleague)

    • 定义一个引用指向中介者对象,并可以与中介者进行交互。
  4. 具体同事类(ConcreteColleague)

    • 实现同事对象的行为,通过中介者与其他对象合作。

示例

以下是一个使用 C++ 实现的简单中介者模式示例,模拟一个简单的聊天系统:

#include <iostream>
#include <string>
#include <vector>
#include <memory>

// 前置声明
class Colleague;

// 中介者接口
class Mediator {
public:
    virtual void sendMessage(const std::string& message, Colleague* colleague) = 0;
};

// 同事对象抽象类
class Colleague {
public:
    explicit Colleague(Mediator* mediator) : mediator_(mediator) {}
    virtual ~Colleague() = default;
    virtual void receiveMessage(const std::string& message) = 0;

protected:
    Mediator* mediator_;
};

// 具体中介者
class ConcreteMediator : public Mediator {
public:
    void addColleague(Colleague* colleague) {
        colleagues_.push_back(colleague);
    }

    void sendMessage(const std::string& message, Colleague* sender) override {
        for (auto colleague : colleagues_) {
            if (colleague != sender) {
                colleague->receiveMessage(message);
            }
        }
    }

private:
    std::vector<Colleague*> colleagues_;
};

// 具体同事类
class ConcreteColleague : public Colleague {
public:
    explicit ConcreteColleague(Mediator* mediator, const std::string& name)
        : Colleague(mediator), name_(name) {}

    void send(const std::string& message) {
        std::cout << name_ << " sends: " << message << std::endl;
        mediator_->sendMessage(message, this);
    }

    void receiveMessage(const std::string& message) override {
        std::cout << name_ << " received: " << message << std::endl;
    }

private:
    std::string name_;
};

// 客户端代码
int main() {
    ConcreteMediator mediator;

    ConcreteColleague alice(&mediator, "Alice");
    ConcreteColleague bob(&mediator, "Bob");

    mediator.addColleague(&alice);
    mediator.addColleague(&bob);

    alice.send("Hi Bob!");
    bob.send("Hello Alice!");

    return 0;
}

特点和优点

  • 解耦对象间的交互

    • 中介者对象将对象间的通信职责封装,使得对象通信的复杂性降低,各对象只需与中介者通信。
  • 简化对象协议

    • 通过统一中介者接口管理,减少了对象之间相互连接的数量。
  • 易于扩展

    • 添加新的同事类不会影响其他同事类,符合开放/闭合原则。

使用场景

  • 复杂对象交互

    • 对象之间的交互复杂且多时,采用中介者模式简化通信。
  • 对象消息处理逻辑

    • 需要统一控制和协调多个对象的消息处理流程。
  • 解耦状态变化的依赖

    • 将交互和控制逻辑从对象中抽取到独立中介者进行集中管理。

中介者模式通过将对象交互逻辑抽离到独立的中介者对象中,极大地减少了系统的复杂度和类之间的直接耦合,使系统更易于维护和扩展,是管理复杂对象交互的有效工具。

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值