设计模式(13) - Strategy策略模式

目录

1.意图

2.UML类图

3.GOF角色说明

4.代码实现

5.实际场景一

6.实际场景二


1.意图

  策略模式定义了一系列的算法,并将每个算法封装成一个对象,使得对象之间可以相互替换。这种模式使得算法与使用它的客户分离开来,算法可以独立地进行变化。

2.UML类图

strategy_diagram

3.GOF角色说明

  Strategy(策略):定义所有支持的算法的公共接口。Context使用这个接口来调用某ConcreteStrategy定义的算法。

  ConcreteStrategy(具体策略):以Strategy接口实现某具体算法。

  Context(上下文):用一个ConcreteStrategy对象来配置;维护一个对Strategy对象的引用;可定义一个接口来让Strategy访问它的数据。

  这里的关键就是将算法的逻辑抽象接口(ContextInterface)封装到一个类中(Context), 再通过委托的方式将具体的算法实现委托给具体的Strategy类来实现(ConcreteStrategyA,B,C类)。

4.代码实现

#include<iostream>
using namespace std;

class Strategy
{
public:
	Strategy(){}
	virtual ~Strategy() {}
	virtual void AlgorithmInterface() = 0;
};

class ConcreteStrategyA:public Strategy
{
public:
	ConcreteStrategyA() {}
	virtual ~ConcreteStrategyA()
	{
		cout<<"ConcreteStrategyA::Destructor"<<endl;
	}
	void AlgorithmInterface() 
	{
		cout<<"ConcreteStrategyA::AlgorithmInterface"<<endl;
	}
};

class ConcreteStrategyB:public Strategy
{
public:
	ConcreteStrategyB() {}
	virtual ~ConcreteStrategyB() 
	{
		cout<<"ConcreteStrategyB::Destructor"<<endl;
	}
	void AlgorithmInterface()
	{
		cout<<"ConcreteStrategyB::AlgorithmInterface"<<endl;
	}
};

/*
 *Context类是Strategy模式的关键,也是Strategy模式与Template模式的根本区别。
 *Strategy通过“组合”(委托)方式实现算法(实现)的异构,而Template模式则采取的是继承的方式。
 *这两个模式的区别也是继承和组合两种实现接口重用的方式的区别。
*/
class Context
{
public:
	Context(Strategy *stg)
	{
		_stg = stg;
	}
	~Context()
	{
		if(NULL!=_stg)
			delete _stg;
	}
	void ContextInterface()
	{
		_stg->AlgorithmInterface();
	}
private:
	Strategy* _stg;
};

int main()
{
	Strategy *pStg = new ConcreteStrategyA;
	Context *pCtx = new Context(pStg);
	pCtx->ContextInterface();
	if(NULL!=pCtx)
		delete pCtx;

	pStg = new ConcreteStrategyB;
	pCtx = new Context(pStg);
	pCtx->ContextInterface();
	if(NULL!=pCtx)
		delete pCtx;

	system("pause");
	return 0;
}

运行结果为:
ConcreteStrategyA::AlgorithmInterface
ConcreteStrategyA::Destructor
ConcreteStrategyB::AlgorithmInterface
ConcreteStrategyB::Destructor
Strategy模式的代码很直观,关键是将算法的逻辑封装到一个类中。

5.实际场景一

#include<iostream>
using namespace std;

//排序行为
class SortBehavior
{
public:
	virtual void sort() const = 0;
};

//归并排序
class Merge:public SortBehavior
{
public:
	virtual void sort() const
	{
		cout<<"Merge sort()"<<endl;
	}
};

//快速排序
class Quick:public SortBehavior
{
public:
	virtual void sort() const
	{
		cout<<"Quick sort()"<<endl;
	}
};

//堆排序
class Heap:public SortBehavior
{
public:
	virtual void sort() const 
	{
		cout<<"Heap sort()"<<endl;
	}
};

//搜索行为
class SearchBehavior
{
public:
	virtual void search() const = 0;
};

//连续搜索
class Sequential:public SearchBehavior
{
public:
	virtual void search() const
	{
		cout<<"Sequential search()"<<endl;
	}
};

//二叉树搜索
class BinaryTree:public SearchBehavior
{
public:
	virtual void search() const
	{
		cout<<"BinaryTree search()"<<endl;
	}
};

//哈希表搜索
class HashTable:public SearchBehavior
{
public:
	virtual void search() const
	{
		cout<<"HashTable search()"<<endl;
	}
};

//Context
class Context
{
private:
	SortBehavior *_sort;
	SearchBehavior *_search;
public:
	Context(){}
	void set_sort(SortBehavior *s)
	{
		_sort = s;
	}
	void set_search(SearchBehavior *s)
	{
		_search = s;
	}
	void sort() const
	{
		_sort->sort();
	}
	void search() const
	{
		_search->search();
	}
};

int main(int argc, char *argv[])
{
	Merge merge;
	Quick quick;
	Heap heap;

	Sequential sqn;
	BinaryTree bt;
	HashTable ht;

	Context ctx;
	ctx.set_sort(&merge);
	ctx.sort();

	ctx.set_search(&bt);
	ctx.search();

	system("pause");
	return 0;
}

运行结果为:
Merge sort()
BinaryTree search()

基于以上结果,可以得到下面的总结:
1).存在两个接口: SortBehavior以及SearchBehavior,连同实现了每个具体行为的相关的类。
2).依靠这种设计,其他类型的对象可以重用这里的搜索以及排序行为,因为这些行为不再被隐藏于我们的聚合类中。
3).并且,无需修改已存在行为类的任何数据,就可以添加新的行为进来。
4).定义了下面的对象指针,它们在运行时期间指向特定的行为类。
    class Context {
    private:
        SortBehavior *_sort;
        SearchBehavior *_search;
    }
5).依靠这些对象指针,我们可以实现每种行为
    void sort() const
    {
        _sort->sort();
    }
    void search() const
    {
        _search->search();
    }
6).Context对象不会自己去处理排序行为,而是将此行为委托给对象指针_sort去处理。
7).我们无需关心Context对象类型是什么,只需关心它是否知道如何去排序(如何调用sort()函数)。
8).实际上,上面代码中我们也使用了组合来提供更多的灵活性。它不仅使得我们可以把算法系列封装在它们自己各自的类中,还可以让我们在运行时期间改变行为,只要组合的这个对象实现了正确的行为接口(例如,_sort实现了sort(), _search实现了search())。

6.实际场景二

  在这个例子中,有两种方法来记录联系人信息:stream & database. 对应的两个类(StreamRecord以及DatabaseRecord),通过基类函数store(),共享了相同的接口。

class Record
{
public:
	virtual void start_record() = 0;
	virtual void store_field(const string &name, const string &value) = 0;
	virtual void finish_record() = 0;
	virtual ~Record() {}
};

struct ContactData
{
	string first_name, last_name, phone, email;
};

class ContactRecorder
{
public:
	ContactRecorder(Record *a) : _record(a)
	{
		assert(NULL!=a);
	}
	void store(const ContactData &data)
	{
		assert(NULL!=_record);
		_record->start_record();
		_record->store_field("first name", data.first_name);
		_record->store_field("last name", data.last_name);
		_record->store_field("phone", data.phone);
		_record->store_field("email", data.email);
		_record->finish_record();
	}
private:
	Record *_record;
};

class StreamRecorder:public Record
{
public:
	StreamRecorder(ostream &s, const string &record_name=string()):_ostream(s), \
                              _record_name(record_name)
	{
	}
	void start_record()
	{
		_ostream<<_record_name<<"(";
	}
	void store_field(const string &name, const string &value)
	{
		_ostream<<name<<": "<<value<<"; ";
	}
	void finish_record()
	{
		_ostream<<") "<<endl;
	}
	void set_record_name(const string &name)
	{
		_record_name = name;
	}
private:
	ostream &_ostream;
	string _record_name;
};

class MySql {};

class DatabaseRecorder:public Record
{
public:
	DatabaseRecorder():_dbConnection(new MySql) {}
	void start_record() 
	{
		cout<<"start tranaction"<<endl;
	}
	void store_field(const string&name, const string &value)
	{
		cout<<"insert into table"<<endl;
	}
	void finish_record()
	{
		cout<<"finish transaction"<<endl;
	}
private:
	MySql *_dbConnection;
};

int main()
{
	ContactData data = {"Phill", "Collyns", "123-456-789", "pc@email.com"};
	StreamRecorder smrd(std::cout);
	ContactRecorder contact(&smrd);
	contact.store(data);

	DatabaseRecorder dbRecord;
	ContactRecorder contact2(&dbRecord);
	contact2.store(data);

	system("pause");
	return 0;
}

运行结果为:
(first name: Phill; last name: Collyns; phone: 123-456-789; email: pc@email.com; )
start tranaction
insert into table
insert into table
insert into table
insert into table
finish transaction

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值