您的当前位置:首页正文

Strategy(策略)模式

2024-11-08 来源:个人技术集锦


前言

        本周五参加了Strategy(策略)模式研讨会,本文的目的是对这个模式的总结和分享,希望对需要学习这个设计模式的同学有所帮助。本文一共分为两个部分,第一个部分是策略模式的基础知识总结;第二个部分是分享策略模式简单示例。

一、策略模式总结

        

1、模式意图

         定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。
    

2、参与者

Strategy(策略)
– 定义所有持的算法的公共接口。context使用这个接口来调用某concreteStrategy定义的算法;
ConcreteStrategy(具体策略)
– 以Strategy接口实现某具体算法;
Context(上下文)
– 用一个ConcreteStrategy对象来配置;
– 维护一个对Strategy对象的引用;
– 可定义一个接口来让Strategy访问它的数据;

    

3、结构、协作、适用性及效果

结构图:

协作:
1、Strategy和Context相互作用以实现选定的算法。当算法被调用时 , Context可以将该算法 所需要的所有数据都传递给该 Strategy。或者,Context可以将自身作为一个参数传递给 Strategy操作。这就让Strategy在需要时可以回调Context;
2、Context将它的客户的请求转发给它的Strategy。客户通常创建并传递一个ConcreteStrategy 对象给该Context;这样, 客户仅与Context交互。通常有一系列的ConcreteStrategy类可供 客户从中选择。
     适用性:
1、许多相关的类仅仅是行为有异。“策略”提供了一种用多个行为中的一个行为来配置一个类的方法。
2、需要使用一个算法的不同变体。例如,你可能会定义一些反映不同的空间/时间权衡的算法。当这些变体实现为一个算法的类层次时 ,可以使用策略模式。
3、算法使用客户不应该知道的数据。可使用策略模式以避免暴露复杂的、与算法相关的数据结构。
4、一个类定义了多种行为, 并且这些行为在这个类的操作中以多个条件语句的形式出现。将相关的条件分支移入它们各自的Strategy类中以代替这些条件语句。
     效果:
1、相关算法系列。
2、一个替代继承的方法。

继承提供了另一种支持多种算法或行为的方法。你可以直接生
成一个Context类的子类,从而给它以不同的行为。但这会将行为硬行编制到Context中,而将
算法的实现与Context的实现混合起来,从而使Context难以理解、难以维护和难以扩展,而且
还不能动态地改变算法。最后你得到一堆相关的类, 它们之间的唯一差别是它们所使用的算法
或行为。将算法封装在独立的Strategy类中使得你可以独立于其Context改变它,使它易于切换、
易于理解、易于扩展。

3、消除了一些条件语句。

Strategy模式提供了用条件语句选择所需的行为以外的另一种选
择。当不同的行为堆砌在一个类中时, 很难避免使用条件语句来选择合适的行为。将行为封装
在一个个独立的Strategy类中消除了这些条件语句。

4、实现的选择。

Strategy模式可以提供相同行为的不同实现。客户可以根据不同时间/空间
权衡取舍要求从不同策略中进行选择。

5、客户必须了解不同的Strategy。

本模式有一个潜在的缺点,就是一个客户要选择一个合适的Strategy就必须知道这些Strategy到底有何不同。此时可能不得不向客户暴露具体的实现问题。因此仅当这些不同行为变体与客户相关的行为时, 才需要使用Strategy模式。

6、Strategy和Context之间的通信开销。

无论各个ConcreteStrategy实现的算法是简单还是复
杂, 它们都共享Strategy定义的接口。因此很可能某些ConcreteStrategy不会都用到所有通过这
个接口传递给它们的信息;简单的ConcreteStrategy可能不使用其中的任何信息!这就意味着有时Context会创建和初始化一些永远不会用到的参数。如果存在这样问题, 那么将需要在Strategy和Context之间更进行紧密的耦合。


二、策略模式简单示例

1、用例描述

         策略的主要意图是定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。所以,这个模式主要是使用场景是算法。大家知道,二叉树的遍历分为前序遍历、中序遍历、后序遍历,我们把这三种算法封装起来,这样,在遍历树的时候,就可以按需要切换不同的遍历算法,这是一个典型的策略模式应用场景。      前序遍历:根节点+左子树+右子树

在遍历左子树和右子树时,仍然先访问根节点,然后遍历左子树,最后遍历右子树。

中序遍历:左子树+根节点+右子树

在遍历左右子树时,仍然先遍历左子树,再遍历根节点,后遍历右子树。

后序遍历:左子树+右子树+根节点

在遍历左右子树时,仍然先遍历左子树,在遍历右子树,后访问根节点。

    

2、示例代码

typedef struct tag_tree_node
{
    //节点的数据
    char data;
    //左子节点指针
    struct tag_tree_node* left_child;
    //右子节点指针
    struct tag_tree_node* right_child;
}tree_node_t;

class show_tree_base_t {
public:
    virtual void show_tree(tree_node_t* root) = 0;
    void visit(tree_node_t* root);
};

void show_tree_base_t::visit(tree_node_t *node){

}

class show_tree_pre_t : public  show_tree_base_t{
public:
    virtual void show_tree(tree_node_t* root);
};

void show_tree_pre_t::show_tree(tree_node_t *root){
    visit(root);
    this->show_tree(root->left_child);
    this->show_tree(root->right_child);
}


class show_tree_in_t : public  show_tree_base_t{
public:
    virtual void show_tree(tree_node_t* root);
};

void show_tree_in_t::show_tree(tree_node_t *root){
    this->show_tree(root->left_child);
    visit(root);
    this->show_tree(root->right_child);
}

class show_tree_post_t : public  show_tree_base_t{
public:
    virtual void show_tree(tree_node_t* root);
};

void show_tree_post_t::show_tree(tree_node_t *root){
    this->show_tree(root->left_child);
    this->show_tree(root->right_child);
    visit(root);
}

tree_node_t* create_tree(){

}

void delete_tree(tree_node_t* root) {

}

class context_t {
public:
    void set_show_strategy(show_tree_base_t* obj) {
        m_show_tree_obj = obj;
    }

    void show(tree_node_t *root) {
        if (m_show_tree_obj != nullptr && root != nullptr) {
            m_show_tree_obj->show_tree(root);
        }
    }

private:
    show_tree_base_t* m_show_tree_obj = nullptr;
};

int main(int argc, char *argv[])
{
    show_tree_pre_t* pre = new show_tree_pre_t;
    show_tree_in_t* in = new show_tree_in_t;
    show_tree_post_t* post = new show_tree_post_t;
    context_t* ctx = new context_t;
    tree_node_t *root = create_tree();

    //先序遍历
    ctx->set_show_strategy(pre);
    ctx->show(root);
    //中序遍历
    ctx->set_show_strategy(in);
    ctx->show(root);

    //后序遍历
    ctx->set_show_strategy(post);
    ctx->show(root);

    delete pre;
    delete in;
    delete post;
    delete_tree(root);

    return 0;
}

Top