背景
一、一个具体实现范例的逐步重构
- 补血道具(药品):a) 补血丹:补充200点生命值;b) 大还丹:补充300点生命值;c) 守护丹:补充500点生命值
- 将Fighter,F_Warrior,F_Mage 单独写在一个文件中。
Fighter.h
#pragma once
#ifndef __FIGHTER__
#define __FIGHTER__
enum ItemAddlife
{
LF_BXD, // 补血丹
LF_DHD, //大还丹
LF_SHD, //守护丹
};
class Fighter
{
public:
Fighter(int life, int magic, int attack) :m_life(life), m_magic(magic), m_attack(attack) {}
virtual ~Fighter() {}
public:
void UseItem(ItemAddlife type)
{
if (type == LF_BXD) // 补血丹
{
m_life += 200;
}
else if (type == LF_DHD) //大还丹
{
m_life += 300;
}
else if (type == LF_SHD) //守护丹
{
m_life += 500;
}
}
protected:
int m_life;
int m_magic;
int m_attack;
};
class F_Warrior :public Fighter
{
public:
F_Warrior(int life, int magic, int attack) : Fighter(life, magic, attack) {}
};
class F_Mage :public Fighter
{
public:
F_Mage(int life, int magic, int attack) : Fighter(life, magic, attack) {}
};
#endif
test.cpp
#include <iostream>
#include <list>
#include <map>
#include <string>
#include "Fighter.h"
using namespace std;
int main()
{
Fighter* prole_war = new F_Warrior(1000,0,200); //这里没有采用工厂模式,如果对象很多,可以考虑采用工厂模式创建对象
prole_war->UseItem(LF_DHD);
delete prole_war;
return 0;
}
- 在主函数中,我们是通过 prole_war->UseItem(LF_DHD); 这种方式来进行回血操作。这样的操作存在一定的问题。
- 当业务逻辑越来越复杂的时候,ItemAddlife 中会存在很多其他的药品存在,void UseItem(ItemAddlift type) 这个函数肯定越来越复杂。因为需求和业务逻辑越来越复杂。这样不符合开闭原则。
- 因此需要 策略模式(strategy pattern) 来对代码进行重构。
策略模式定义:定义一系列算法(策略类),将每个算法封装起来,让他们可以互相替换。换句话说,策略模式通常把一系列算法封装到一系列具体策略类中来作为抽象策略类的子类, 然后根据实际需要使用这些子类。
这个例子中的UML图,如上图所示。其实策略模式在实际的工程中特别常见,可以看一下下面这个实际工程中的UML图。
策略类中主要包含有三种角色。
- Context(环境类):该类中维持着一个对抽象策略类的指针或者引用。这里指Fighter类。
- Strategy(抽象策略类):定义所支持的算法的公共接口,是所有策略类的父类。这里指的是ItemStrategy类。
- ConcreteStrategy(具体策略类):抽象策略类的子类,实现抽象策略类中声明的接口。这里指的是 ItemStrategy_BXD,ItemStrategy_DHD,ItemStrategy_SHD。
- 策略类的优点:a) 以扩展的方式支持对未来的变化,符合开闭原则。b) 算法可以被复用。c) 策略模式可以看成是类继承的一种替代方案,通过为环境类对象指定不同的策略,就可以改变环境类对象的行为。
- 遇到大量不稳定的 if 条件分支 或者 switch分支,就要优先考虑是否可以通过策略模式来解决。策略模式是 if ,switch条件分支的杀手。
- 策略类的缺点:a) 导致引入许多新的策略类。b) 使用策略类时,调用者(main 主函数)必须熟知所有策略类的功能并根据实际需求自行决定使用那个策略类。
实际的大型工程中,充满了各种各样的设计模式。
Fighter.h
#pragma once
#ifndef __FIGHTER__
#define __FIGHTER__
class ItemStrategy;
/*enum ItemAddlife
{
LF_BXD, // 补血丹
LF_DHD, //大还丹
LF_SHD, //守护丹
};*/
class Fighter
{
public:
Fighter(int life, int magic, int attack) :m_life(life), m_magic(magic), m_attack(attack) {}
virtual ~Fighter() {}
public:
/*
void UseItem(ItemAddlife type)
{
if (type == LF_BXD) // 补血丹
{
m_life += 200;
}
else if (type == LF_DHD) //大还丹
{
m_life += 300;
}
else if (type == LF_SHD) //守护丹
{
m_life += 500;
}
}*/
public:
void SetItemStrategy(ItemStrategy* strategy);
void UseItem();
int GetLife();
void SetLife(int life);
protected:
int m_life;
int m_magic;
int m_attack;
ItemStrategy* itemstrategy = nullptr;
};
class F_Warrior :public Fighter
{
public:
F_Warrior(int life, int magic, int attack) : Fighter(life, magic, attack) {}
};
class F_Mage :public Fighter
{
public:
F_Mage(int life, int magic, int attack) : Fighter(life, magic, attack) {}
};
#endif
Fighter.cpp
#include "Fighter.h"
#include "ItemStrategy.h"
#include <iostream>
using namespace std;
void Fighter::SetItemStrategy(ItemStrategy* strategy)
{
itemstrategy = strategy;
}
//使用道具
void Fighter::UseItem()
{
itemstrategy->UseItem(this);
}
int Fighter::GetLife()
{
return m_life;
}
void Fighter::SetLife(int life)
{
m_life = life;
}
ItemStrategy.h
#pragma once
#ifndef __ITEMSTRATEGY__
#define __ITEMSTRATEGY__
#include "Fighter.h"
class Fighter;
class ItemStrategy
{
public:
virtual void UseItem(Fighter* obj) = 0;
virtual ~ItemStrategy() {}
};
class ItemStrategy_BXD :public ItemStrategy
{
public:
virtual void UseItem(Fighter* obj)
{
obj->SetLife(obj->GetLife()+200);
}
};
class ItemStrategy_DHD :public ItemStrategy
{
public:
virtual void UseItem(Fighter* obj)
{
obj->SetLife(obj->GetLife()+300);
}
};
class ItemStrategy_SHD :public ItemStrategy
{
public:
virtual void UseItem(Fighter* obj)
{
obj->SetLife(obj->GetLife()+500);
}
};
#endif __ITEMSTRATEGY__
test.cpp
#include <iostream>
#include <list>
#include <map>
#include <string>
#include "ItemStrategy.h"
using namespace std;
int main()
{
Fighter* prole_war = new F_Warrior(1000,0,200);
cout << prole_war->GetLife() << endl;
//吃一颗大还丹
ItemStrategy* strategyDHD = new ItemStrategy_DHD(); //创建大还丹策略
prole_war->SetItemStrategy(strategyDHD); //主角设置大还丹策略,准备吃大还丹
prole_war->UseItem(); //主角吃大还丹
cout << prole_war->GetLife() << endl;
//吃一颗补血丹
ItemStrategy* strategyBXD = new ItemStrategy_BXD(); //创建补血丹策略
prole_war->SetItemStrategy(strategyBXD); //主角设置补血丹策略,准备吃补血丹
prole_war->UseItem(); //主角吃补血丹
cout << prole_war->GetLife() << endl;
delete prole_war;
delete strategyDHD;
delete strategyBXD;
return 0;
}