简介:本系列将用实际业务场景带你理解设计模式,用简单粗暴的语言讲解结合实际案例来理解实际模式的思想
定义算法族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
将通用不变的方法分装成父类进行集成,将经常变化的方法搞成接口类并使用接口实现,实际生产中按
设计需求调用接口实现(我这么说你可能不太明白,下面我们用实际例子来进行讲解)
现在要开发一款冒险类游戏,游戏中有4个职业分别为国王、王后、侏儒、骑士。可以使用的武器分别为匕首、弓箭、斧头、宝剑。
每种职业一次只能使用1种武器进行战斗,但可以在游戏过程中换武器。
无意看到了产品经理后续的开发安排,未来6个月内可能还要新增多个武器,但具体是什么武器产品经理
还没想好,还有可能新增一个职业“拳击手(可能不使用武器)”这个暂时也没想好。
注意:设计模式只是一种思想,是为了解决一些不必要麻烦的方案,他可能在某些场景不是最优解,所以并不是所有的代码都要套用设计模式,如果连写个 echo "Hello World";
都要使用设计模式,那一定是中了设计模式的毒了!
在上述需求中那些是不变的东西?职业,上述4种职业是不变的。
那些是经常会变动的?上述的4种武器,根据产品开发安排后续要加入多种武器。
我们将不变的且共同拥有的进行封装成一个父类,将经常需要变化的放在一堆,过会在去处理。
//角色父类
abstract class Role{
//角色
public abstract function fight();//为啥这是抽象方法,因为要约束开发者这个方法在子类中必须实现,而且在子类中不能随意删除这个方法
}
//角色子类(王后)
class Queen extends Role {
public function fight()
{
echo '王后角色';
}
}
//角色子类(国王)
class King extends Role {
public function fight()
{
echo '国王角色';
}
}
//角色子类(侏儒)
class Troll extends Role {
public function fight()
{
echo '侏儒角色';
}
}
//角色子类(骑士)
class Knight extends Role {
public function fight()
{
echo '骑士角色';
}
}
$role = new Knight;//实例化骑士类
$role->fight;//骑士角色
这样需要那个角色就直接实例化调用那个角色方法即可,下面说说经常变化的地方
根据开发安排得知未来要新增多种“武器”和一个待定职业“拳击手(可能不使用武器)”
这个时候会有小伙伴有疑问,为什么不把武器方法也封装进抽象父类中,因为
“拳击手”职业可能不使用武器,如果封装进去那么“拳击手”如果真的不使用武器怎么办?要是后期新增
“拳击手”职业后产品经理又想给“拳击手”加上武器怎么办?这会造成代码的不易维护。
//角色父类
abstract class Role{
protected $weapon;//武器
public function setWeapon(WeaponBehavior $weapon){
$this->weapon = $weapon;
}
public function getWeapon(){
return $this->weapon->setWeapon();
}
//角色
public abstract function fight();
}
//角色子类(王后)
class Queen extends Role {
public function fight()
{
echo '王后角色';
}
}
//角色子类(国王)
class King extends Role {
public function fight()
{
echo '国王角色';
}
}
//角色子类(侏儒)
class Troll extends Role {
public function fight()
{
echo '侏儒角色';
}
}
//角色子类(骑士)
class Knight extends Role {
public function fight()
{
echo '骑士角色';
}
}
//-------------分割线--------------
//武器接口类
interface WeaponBehavior{
public function setWeapon();
}
//武器-匕首
class KnifeBehavior implements WeaponBehavior {
public function setWeapon(){
echo '实现匕首刺杀';
}
}
//武器-弓箭
class BowAndArrowBehavior implements WeaponBehavior {
public function setWeapon(){
echo '实现弓箭射击';
}
}
//武器-斧头
class AxeBehavior implements WeaponBehavior {
public function setWeapon(){
echo '实现斧头劈砍';
}
}
//武器-宝剑
class SwordBehavior implements WeaponBehavior {
public function setWeapon(){
echo '实现宝剑挥舞';
}
}
//侏儒使用匕首
$troll = new Troll;
$troll->fight();//确定角色
//给角色设置武器
$weapon = new KnifeBehavior;//实例化武器-匕首
$obj = $troll->setWeapon($weapon);//设置武器
$troll->getWeapon();//获取武器
我们新增了1个武器接口和4个武器实现类,有小伙伴可能会想为啥要是用接口类对武器实现类进行约束?
为什么不直接创建4个武器类?
答:目的是要约束武器的实现方法,如果直接创建4个武器的实现类要是实现方法的名字、参数不一致怎么办?
多人协作开发的时候每人负责1个武器类的实现,到最后可能会出现好多个不同的实现方法名称,出接口
的时候要出好少,而且参数不经约束的话,当拥有了很多武器的时候扔给你去维护你会发现那简直就是噩梦。
所以要使用接口父类进行接口实现类的方法名参数进行约束,这样接口是统一的,维护起来也会更加方便,而且
用了接口实现后接口定义好的方法在实现类中就不能被随意删除,这满足了设计模式的“不可修改只可拓展的原则”
如果没有使用设计模式也许会这样编码
abstract class Role{
public abstract function fight();
public function knifeBehavior(){
echo '实现匕首刺杀';
}
public function bowAndArrowBehavior(){
echo '实现弓箭射击';
}
public function axeBehavior(){
echo '实现斧头劈砍';
}
public function swordBehavior(){
echo '实现宝剑挥舞';
}
}
//角色子类(王后)
class Queen extends Role {
public function fight()
{
echo '王后角色';
}
}
//角色子类(国王)
class King extends Role {
public function fight()
{
echo '国王角色';
}
}
//角色子类(侏儒)
class Troll extends Role {
public function fight()
{
echo '侏儒角色';
}
}
//角色子类(骑士)
class Knight extends Role {
public function fight()
{
echo '骑士角色';
}
}
$obj = new Knight;
$obj->fight();
$obj->swordBehavior();
设想一下,某天产品经理说骑士角色使用宝剑的时候不够帅,我想让骑士使用宝剑的时候可以
挥舞两次,你会怎么办?
如果改变父类,那么其他职业使用宝剑时也会收到影响。
要是某天“拳击手”职业真的上线了,但是他只用到了父类中的角色设置方法,其他的武器
他也没用到,那他的继承父类就显得有些多余。
项目被新的产品接手了,产品觉得匕首不好,私自让开发将匕首下架了,此时父类的匕首恰好又
不是抽象方法,那么有职业类一旦切换匕首连代码都跑不起来报错说没有“匕首方法”,想改这个Bug还
不好找,写了那多代码开发也忘记都在哪里调用过“匕首方法”了,只能一个一个找,耗时又费力。
当开发者发现问题了,将父类的的武器方法都设置成抽象方法,这回所有的武器都要在每个
继承他的角色类中进行实现。这显然有点浪费时间啊,这代码都是重复的为啥不复用。
要是某天游戏做大了,新出了20个职业,这是产品说新版本的宝剑来个加成属性吧,这时候发现每个职业的
武器实现方法都是在职业类自身中,并没有放在父类中进行继承,这次每个职业的宝剑加成百分比%又
不同,可能产品眼里看就是传个参数的事没啥难度。方法多接个参数,职业类里面写
的实现方法,20几个职业类中的宝剑方法都要改一遍,想想都是麻烦,要是在把参数传错了还不
是开发的锅!
提示:下文纯属闲扯,当个乐看看就好:
期初我在看Gof23种设计模式的时候也感觉挺扯的,有种脱了裤子放屁的意思,期初也想不明白为啥要搞这设计模式,为啥会有这种感觉,因为我写的业务系统太简单了,简单到设计模式只会让我的业务系统变得更复杂!
设计模式只是一种思想,是一种结局问题的方案,他也行并不是你的最优解,所以不是写什么业务都要套用设计模式的,就比如上面的例子,要是为了长远发展应对业务的多变和不确定我才去使用设计模式。要是当时产品说就4个职业4种武器,那还用啥设计模式啊,武器一个类,职业一个类,现用现new呗,简单粗暴直接解决问题,但是产品就是这样总是在不断变化和更新的,更新的时候既要考虑对旧版本的兼容问题,又要兼顾新版本的功能实现,也许只有当业务足够复杂时才能真正体会到设计模式的好处吧。