天梯设计模式——策略模式


写在前面的声明

首先有一点要说明我不是什么大牛,小牛都不是,我只是一个学生。
其次 最近在公司实习  感觉项目的代码很杂乱 。同一个java文件很多人都改过 于是我会碰到各种变量命名方式 各种重复的方法 毫无注释。代码写出来只有自己能看懂 这样的结果就是当我第n手需要修改代码时 觉得与其抓破头的去揣测每个方法的作用以及代码的实现 还不如重新写过。于是代码就越来越 多冗余 高耦合。写代码时经常需要去查看一下源代码 但发现很多时候看不太懂。“为什么这里点进去是一个抽象方法  那里点进去又只是个接口 这样绕啊啊意义何在 为什么不直接实现” 设计模式就是这些问题的答案。
我要写的东西只是我在设计模式学习过程中的笔记和感悟 认识也还很粗糙,如果能帮到你 我很高兴 如果有问题 欢迎指正。



PS:自我总结 全手打 转载行个好 注明一下啦!

什么是设计模式?

设计模式这个词 作为程序员一定不陌生 作为学生 我也前前后后几次尝试去了解 直到最近这一次算是有了新的认识。那我们到底该如何定义设计模式呢?
设计模式: 是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。(摘自度娘)

还是不好懂?那来看看我的理解:
反复使用、多数人知晓的“  这句话表明 设计模式是经受住了无数实际项目考验的。反复使用 设计模式就好比我们小时候玩的积木 要撘出心里想象的房子 我们只要把一块一块的积木搭起来就好 喜欢用哪块就用哪块 随时可以换掉。这突出了设计模式的一个特点 松散的耦合 在实际的代码中就是 不会因为一点修改 牵一发而动全身。
经过分类编目的“。每种设计模式针对某一类特定的问题能提供很好的解决办法这说明为什么设计模式有那么几十种。
代码设计经验的总结“ 注意 是经验的总结。告诉我们的是如何写代码 而不是具体写什么样的代码。如果你很了解设计模式了 就能写出有很好的拓展性 鲁棒性的代码。
以往我们在写代码的过程中会把经常要用的方法写在一个文件里 然后打包以方便以后调用。而设计模式是需要装在脑子里 如果你跟我一样还是个没有太多实际项目经验的学生 那就到已有的应用中去寻找它们的影子 万一哪天你想来设计一个自己的项目 再努力的尝试去运用它们。”以往是代码复用,现在该是经验复用了“ 这就是我理解的设计模式。

说好的策略模式呢?

主菜来了。
让我们从一个实际问题出发:
  现在有一个项目模块需要你来设计 这是一个动作冒险类的游戏 游戏当中会有很多角色 武器。当然 一个角色每次只能用一种武器啊 不然当装饰么 很重啊!
你想。。。。。
  。。。。
。。。。
。。。。。。
。。。。。。。
告诉你我是怎么想的:
作为一个学生(不知道有什么好傲娇的- -!) 我的想法当然很简单了。老师说了 要采用面向对象的设计思想 那我当然要用继承啊。于是 我写了一个所有角色的父类(Fulei.java)在父类中定义了一个使用武器的方法(ShiyongWuqi()),每个具体的角色去继承这个父类 ”英勇的战士啊 开始奇怪的大冒险吧“ 就是这么简单。我已经迫不及待了。。。
慢着 万一有的角色没有武器或者就不该有武器怎么办? 比如以后要加各种NPC 难道赤手空拳的我还要被手握凶器的NPC追砍么。不行 得想个办法。其实我可以在子类中用空方法覆盖ShiyongWuqi()这个方法不就行了 但细想一会 我发现如果以后要加别的东西 (比如装备特效什么的) 有的角色有有的没有 我一个个覆盖会烦死的啊。这时候该停一停了 我总结了下用继承来实现容易出现的问题:
 1> 很难一次性掌握角色所有可能的行为或状态
2> 仅仅靠覆盖父类方法会在多个子类中出现很多无效和重复的代码
3> 在父类中定义所有角色的状态或行为太死板了 不够灵活
4> 今后如果对父类修改会对所有子类造成影响 甚至是令人发狂的影响
哪该怎么办啊 好麻烦啊!
老师又来了 接口教给你是干嘛用的?对啊 如果我把武器单独剥离出来 用一个接口来封装表示它 那么有武器的角色就去实现这个接口 没有的就不实现。不得不说 我真是聪明绝了顶。于是 我满怀信心的交出了我最后设计出来的类图结构:


采用了Android(我是做android的- -)最经典的模式啊 xxx extends xxx implement xxx 好高大上有没有 But。。。 老师又来了:
你解决了单使用继承的一小部分问题 但是根本上还是没什么区别 还是会有大量代码重复行不行? 
当然完全不信啊 这可是我的杰作啊。
那你试想一下:如果100个角色 其中10个使用武器的方法是A种 20个使用方法是B种 30个是C种。。。。 虽然他们的使用方法大同小异 小到只要改一行代码 但你就还非得对每种不同重复写那几十或几百行一样代码 只因为有那一行不一样。去看看 策略模式 吧。
我靠 好可怕 还会出现这种情况啊。于是我老老实实的按老师的提示去研究策略模式了,下面不是老师说的 是设计模式告诉我的:
 先说一条 OO设计中的一条设计原则:

找出应用中可能需要变化的部分之处,把它们独立出来,不要和那些不需要变化的代码混在一起。

”封装变化的部分 说起来好抽象啊 到底该如何实现啊?“ 客官别急 小二这就道来
之所以我的方案 也就是利用继承或接口 不能很好解决问题的原因是 角色的使用武器的方式会在子类中不断变化 并且让所有子类都去实现这个接口也是不恰当的。接口并没有实现的代码 所以无法达到代码复用的目的。然而 仔细分析 我们不难发现 如果把父类中会变化的部分(也就是使用武器的方法)取出 并”封装“起来 这样其他的部分不就不会受到影响了么
下面就要谈谈用什么“封装”
“封装?不就是用类封装么” 至少当时我的第一反应是如此。这里用的是接口来封装 但是用法跟我的用法又不太一样。首先 我是将所有的武器使用方法作为一个接口 然后具体的某一个子类去实现它 至于实现的逻辑是有多么的与其他的子类的将要实现的逻辑类似(我又提到了这一点。。。但是真的很重要啊 ) 这个接口它 dont care 反正你实现我一次你就要重头到尾 完完整整的实现一遍。这就是针对实现的编程。
然而 设计模式大神的方法是。
任然是用接口封装武器使用这一行为 不同的是 这个接口的不同实现不再交给某个具体的角色子类 而是用一个新的类去实现这个接口。这样 虽然都是使用武器(WeaponBehavior接口) 但是细节上的不同却被具体的某一使用武器类封装了(UseKnife类)。这就是针对接口编程。
“针对接口编程”这六个字想必作为CS的学生都是如雷贯耳 有必要单独拎出来说说:
严格意义来说 这句话应该是 针对超类型编程。超类型包括我们所说的接口 还有抽象类。抽象类和接口在很多方面具有相同的特点 它们是java实现多态的关键。这句话说的再明确一点就是 变量的声明类型应该是超类型 通常是一个抽象类或者一个接口。如此 只要是具体实现此超类型的类所产生的对象 都可以指定给这个变量。 这意味着 声明类时不用理会以后执行是的真正对象类型!看个例子:
一个抽象类Animal 有两个具体实现 Dog 和 Cat 继承Animal
“针对实现编程”
Dog d = new Dog();  声明变量 d 为Dog类型(Animal的具体实现),会造成我们必须针对具体实现编程
d.bark();
<span style="font-size:18px;">“针对超类型编程”</span>
Animal animal = new Dog();     已经知道该对象是狗,但我们用的是Dog的超类型 animal进行调用
   	   animal.makeSound();
这里的多态调用我们肯定经常看到 说起来是很简单 但是在实际运用中的奥秘只能慢慢体会了 我只是个学生。。。

到此为止 角色的武器使用方法跟角色已经完全无关了 它现在是单独一套体系 虽然结构比较简洁(一个接口 和 它的不同实现)。 也就是说 如果我愿意 我可以把它用在任何我想用的地方 它只是一块任我摆布的积木。下面我们来看看 设计模式大神的解决方案吧 没代码我说个XX:

角色父类:
public class Character{
     WeaponBehavior weapon;
   
  public void setWeapon(WeaponBehavior  w){//实现了各种武器的可插拔哦
this.weapon = w;
}
public void fight(){
weapon.useWeapon();
}
}
在父类Character中加入了一个武器接口的字段 这样当角色子类调用从父类继承来的fight()方法时 实际是委托给了某一具体武器类的useWeapon()方法来执行

武器接口:
public interface WeaponBehavior{
public void useWeapon();
}


武器类:
public class Knife implements WeaponBehavior{
@Override
public void useWeapon(){
//cut cut cut
}
}
public class Bar implements WeaponBehavior{
@Override
public void useWeapon(){
//hit hit hit
}
}

比如我要创建一个拿着大刀的角色:
public class Jxnu707WithBigKnife extends Character{
Kinfe bigKnife = new Kinfe();
setWeapon(bigKnife);// 这里的多态调用还记得么 传进去的是Knife类型 而方法声明时的参数是 WeaponBehavior 我管你是Knife还是Bar 只要是实现了WeaponBehav ior的类型我都认
@override
public void fight(){
sysout("不好意思 我要出来申明一下 我用的是屠龙刀!");
super.fight();
}
}
怎么样?大神的代码是不是写的很漂亮。这样的实现 是把角色和武器完全剥离开的 就算以后要加一万种武器 你只要分别实现一下 WeaponBehavior接口 然后把这种武器的使用逻辑写好就行 完全不用去想跟角色类之间的关系 写完新武器立马就能用 只要在你的Character子类上set一下就好 比如 jxnu707.setWeapon(倚天剑); 真正做到屠龙宝刀 点击就送!!!
下面是图:


呐 这就是传说中的策略模式:定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
这里的 算法族就是右边的那一半 互相替换就是接口的多态引用 应该很明晰的了吧。
最后问一下 这个博客中的代码要怎么写啊 为什么我看别人的都有行号 像我这样跟在记事本里敲代码的应该是我打开的方式不对吧。
最后 感谢自己完成了第一篇博客(写好长时间啊) 当然 我只是个学生 更多的是来求指导的 欢迎指正!

PS:自我总结 全手打 转载行个好 注明一下啦!




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值