设计模式——行为模式

注意,设计模式的实现并不唯一。

责任链模式

将请求顺序的传递给每一个接收者,直到有一个接收者做出处理。或者一个接收者处理之后继续传递给下一个接收者。

例如 QT 的信号机制。

与装饰器模式的不同:

“责任链的管理者可以相互独立地执行一切操作, 还可以随时停止传递请求。 另一方面, 各种装饰可以在遵循基本接口的情况下扩展对象的行为。 此外, 装饰无法中断请求的传递。”

 

 “中央批下来的公款,本来应该全给到底层,但是中间每层都做一些额外的操作。”

在这个例子中,如果本来中央可以直接给到底层,但是没给,就是要让每层过一遍,这叫装饰模式。但是如果中央没办法直接给到底层,不得已必须层层传递,这叫责任链模式。

class Request;

class Layer {
    virtual bool handle(Request request) = 0;
};

class LayerImpl : public Layer {
    bool handle(Request request) override {
        if ( /* 我能处理 */ ) {
            // 处理
            return true;
        }
        for (auto layer : next_layers) {
            if (layer.handle(request)) {
                return true;
            }
        }
    }
private:
    std::vector<Layer > next_layers;
};

 命令模式

当一项业务操作代码会被复制成多份时,将其设计为一个命令。

例如,通常编辑器上有“复制”按钮,也可以通过 Ctrl+C 复制,这两项都是对剪切板的操作,可以把它们叫做 copy 命令,发送至剪切板对象。

class String;

class Command {
    virtual void execute() = 0;
};

class CopyCommand : public Command {
    void execute() override;
};

class CutCommand : public Command {
    void execute() override;
};

class Button {
    void set_on_click(Command command);

    void on_click() {
        command.execute();
    }

private:
    Command command;
};

class Shortcut {
    void set_shortcut(String key, Command command);
};

int main()
{
    CopyCommand copy_command;
    CutCommand cut_command;
    Shortcut shortcuts;

    Button copy_button;
    copy_button.set_on_click(copy_command);
    shortcuts.set_shortcut("Ctrl+C", copy_command);

    Button cut_button;
    cut_button.set_on_click(cut_command);
    shortcuts.set_shortcuts("Ctrl+X", cut_command);
}

个人理解:

把操作变成对象,用于存储、传递。

 迭代器模式

当你希望你操作的对象在不暴露数据成员的情况下提供遍历方法时,你可以要求该对象提供迭代器。

你希望在不暴露数据成员的情况下提供访问数据成员的方法时,你可以提供迭代器。

例如 std::vector,std::queue。

class Spammer;
class String

class Server {
    class SpammerIterator {
        virtual bool next() = 0;
        virtual String get_email() = 0;
    };

    void email_to(SpammerIterator spammer);
};

class WeChatContact {
    class WechatSpammerIterator : public SpammarIterator {
        bool next() override;
        String get_email() override;
    }

    SpammerIterator* create_iterator();

private:
    std::vector<Spammer> spammers;
};

class QQContact {
    class QQSpammerIterator : public SpammarIterator {
        bool next() override;
        String get_email() override;
    }

    SpammerIterator* create_iterator();

private:
    std::vector<Spammer> spammers;
};

int main()
{
    WeChatContact wechat_contact;
    QQContact qq_contact;

    Server server;
    SpammerIterator* spammer_iterator;
    if (/*using WeChat*/) {
        spammer_iterator = wechat_contact.create_iterator();
    }
    else if (/*using QQ*/) {
        spammer_iterator = qq_contact.create_iterator();
    }
     
    while (spammer_iterator.next()) {
        server.email_to(spammer_iterator->get_email();
    }
}

你也可以提供一个 for_each 方法。

中介者模式

多个对象之间的关系错综复杂,把这些关系抽取为单独的一个类,这个类是中介者。

与其他模式的区别

  • 责任链模式:一个请求——多个接收者
  • 命令模式:多个对象发出同一个请求
  • 中介者模式:抽象出请求者与接收者之间的联系
  • 观察者:接受者选择请求者

 例如在 A,B,C 三个对象中,每个对象负责一项业务。你希望 A 业务完成后,要修改 B 对象的状态。B 业务完成后,修改 C 对象的状态;C 业务完成后,修改 A 对象的状态。抽取出一个类,持有对三个对象的引用,当 A,B,C 任一业务完成后通知此对象,此对象根据事件通知执行后续。

enum class Events;

class Mediator {
    void notify(Events event) {
        if (/* event 为 A */) {
            b.set_data();
        }
        else if (/* event 为 B */) {
            c.set_data();
        }
        else if (/* event 为 C */) {
            a.set_data();
        }
    }
    

private:
    A& a;
    B& b;
    C& c;
};

class A {
    void server() {
        // 业务 A
        mediator.notify(Events::A);
    }

    void set_data();

private:
    Mediator& mediator;
};

class B {
    void server() {
        // 业务 B
        mediator.notify(Events::B);
    }

    void set_data();

private:
    Mediator& mediator;
};

class C {
    void server() {
        // 业务 C
        mediator.notify(Events::C);
    }

    void set_data();

private:
    Mediator& mediator;
};

备忘录模式

当你需要为对象创建快照和从快照中还原对象时,这项工作可以委托给一个快照对象,从而避免暴露对象的数据成员。

class Data; // 二进制数据

class Object {
public:
    // 作为嵌套类可以访问 Object 的成员
    class SnapShot {
        Data create();

        void restore(Data data);    
    
    private:
        Object& object;
    };
private:
    int a;
    double b;
    std::string c;
};

观察者模式

又称订阅者模式。

把请求者发送请求到接收者 改为 接受者订阅请求者的请求。

例如 Kafka,逛不同网站时订阅该网站的商品邮件。

class Request;
class Endpoint; // 这个名称通常表示 IP:PORT,例如 "127.0.0.1:8080"

class Server {
    void on_request(Request request) {
        for (auto subscriber : subscribers) {
            send(subscriber, request);
        }
    }

    void register(Endpoint endpoint) {
        subscribers.push_back(endpoint);
    }

private:
    std::vector<Endpoint> subscribers;
};

class Client {
    Endpoint get_endpoint();

    void receive();

    void on_request(Request request) {
        // 处理请求
    }
};

状态模式

相同的接口在不同状态下表现出不同的行为。

例如视频暂停时、播放时,双击屏幕的行为不同。

class State {
    virtual void on_double_click() = 0;
};

class PauseState {
    void on_double_click() override {
        // 隐藏暂停图标,继续播放
    }
};

class PlayingState {
    void on_double_click() override {
        // 显示暂停图标,暂停播放
    }
};

class VideoPlayer {
    void change_state(State state);

    void on_double_click() {
        state.on_double_click();
    }
};

策略模式

一个需要替换不同算法的场景中,我们可以将算法抽象出来。

例如地图app中,从一个位置到另一个位置,因为交通方式不同,有不同的路线和算法。

class Strategy {
    virtual int execute(int a, int b) = 0;
};

class AddStrategy {
    int execute(int a, int b) override {
        return a + b;
    }
};

class SubStrategy {
    int execute(int a, int b) override {
        return a - b;
    }
};

class MulStrategy {
    int execute(int a, int b) override {
        return a * b;
    }
};

int main()
{
    int a,b;
    Strategy strategy;

    if (/*add*/) {
        strategy = AddStrategy();
    }
    else if (/*sub*/) {
        strategy = SubStrategy();
    }
    else if (/*mul*/) {
        strategy = MulStrategy();
    }
    strategy.execute();
}

在某些语言中,你可以使用函数对象代替多态。

与其它模式的关系:

  • 状态模式包含了具体了业务。策略模式期望算法无需关心业务。
  • 命令模式包装操作。策略模式抽象算法。
  • 装饰模式更改外表。策略模式替换内核。

 模板方法模式

将一系列步骤抽象,让客户端可以替换每一个步骤的工作。

  • 抽象步骤必须由各个子类来实现
  • 可选步骤已有一些默认实现, 但仍可在需要时进行重写

“还有另一种名为钩子的步骤。 钩子是内容为空的可选步骤。 即使不重写钩子, 模板方法也能工作。 钩子通常放置在算法重要步骤的前后, 为子类提供额外的算法扩展点。”

// 使用函数对象代替多态
class Function;

class Work {
    void set_step1(Function step1);
    void set_hook(Function hook);
    void set_step2(Function step2);
    void set_step3(Function step3);
    void set_step4(Function step4);

    void work() {
        step1();
        hook();
        step2();
        step3();
        step4();
    }

private:
    Function step1;
    Function hook;
    Function step2;
    Function step3;
    Function step4;
};

class BaseWork {
    virtual void step1() = 0;
    virtual void hook(); // 默认实现
    virtual void step2() = 0;
    virtual void step3() = 0;
    virtual void step4() = 0;

    void work() {
        step1();
        hook();
        step2();
        step3();
        step4();
    }
};

class Work : public BaseWork {
    void step1() override;
    void step2() override;
    void step3() override;
    void step4() override;
};

访问者模式

访问者模式是一种行为设计模式, 它能将算法与其所作用的对象隔离开来。

 对于继承自 X 的 A,B,C 三个派生类,它们重载的同一个算法函数实现不同,如何实现?简单,override 即可。如果甲方要求修改算法,但又不希望调整已经稳定的 A,B,C 的代码,该如何呢?

简单的重载无法满足该需求。访问者模式将算法实现在作用对象外部,从而不影响作用对象的代码。

class Parameters;

class Visitor {
    void for_A(Parameters param) {
        // 对 A 的算法
    }
    void for_B(Parameters param) {
        // 对 B 的算法
    }
    void for_C(Parameters param) {
        // 对 C 的算法
    }
};

class Base {
    virtual accept(Visitor visitor) = 0;
};

class A : public Base {
    void accept(Visitor visitor) {
        visitor.for_A(Parameters());
    }
};

class B : public Base {
    void accept(Visitor visitor) {
        visitor.for_B(Parameters());
    }
};

class C : public Base {
    void accept(Visitor visitor) {
        visitor.for_C(Parameters());
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值