您的位置:首页 > 编程语言 > Java开发

Java基础再回首之设计模式系列①-----StrategyPattern 策略者模式(案列教程,附带demo)

2017-05-12 16:48 686 查看

一、前言

自己学java学的也熟悉了。踏入工作的不归路之后,身为一个Android开发工程师,不仅要Java基础扎实,还要对其的设计模式有所掌握,最近在群听到《Head First》这本书的设计模式很好,也很适合我现在的情况,所以决定全心全意学好Java的设计模式。于是,在此写一系列设计模式的博文,如有兴趣请借鉴。

我的座右铭: 成功的反义词不是失败,而是什么都不去做。

希望这句话同样的激励着正在读大学或者已经工作的你和我。共勉,哈哈~

二、案列。

现在你要为某公司的游戏设计一类武器,客户要求要有斧头、剑、火炮,目前就三种武器。

要求一:每种武器一定要有其的使用方法、外表特点、攻击特效。

要求二:某些武器有使用权限,比如只能限定RMB玩家,而不允许非RMB玩家使用。

2.1 普通方案:

把武器设计为超类,在其里面把必须有的方法设计为抽象方法:使用方法、外表特点、攻击特效。这样就可以让子类来实现了其具体的内容了。

使用权限问题的就让不同的子类做不同的处理,没有权限的就覆盖不做任何事情,而拥有权限的武器子类就可以做自己的事情。

这方案看起来,好像很有成就感咯。哈哈~

于是乎,我们就有了下面的思维导图:



于是乎,就有了以下代码:

Weapon.class 父类

package Weapon;/*
* 项目名:StrategyPattern-Sample
* 文件名:Weapon
* 创建时间:2017/5/12下午 2:46
* 创建者 xuhong
* 描述:武器 ,父类
*/

public abstract class Weapon {

// 抽象方法:显示武器样式
public abstract void display();

//抽象方法:显示特效
public abstract void specialEffects();

//抽象方法:使用方法
public abstract void usage();

//抽象方法: 使用权限
public abstract void peimission();
}


axe.class 斧头子类

package Weapon;

/*
* 项目名:StrategyPattern-Sample
* 包名:Weapon
* 文件名:Axe
* 创建时间:2017/5/12  下午 2:50
* 创建者 xuhong
* 描述:斧头子类
*/

public class Axe extends Weapon {

@Override
public void display() {
System.out.print("斧头样式。");
}

@Override
public void specialEffects() {
System.out.print("斧头特效。");
}

@Override
public void usage() {
System.out.print("斧头的使用方法。");
}

@Override
public void peimission() {
System.out.print("斧头不需要RMB购买。");
}
}


使用:

import Weapon.Axe;

public class Main {

public static void main(String[] args) {
Axe axe = new Axe();
axe.display();
axe.specialEffects();
axe.usage();
axe.peimission();
}
}


输出如下:



至今到此,哇!好像好简单的样子,得到老板的赏心悦目。加工资~So happy~

过了一个月,客户又有了新的要求:要求新增多种武器,但是这几种武器很特别,在使用这些武器有不同的金币加成。 解决方法:我们又在父类写了几个抽象方法,把之前的几个子类覆盖这方法而不做任何事情。

又过了几个月,又新增几种武器,但是这几种武器使用不能提供加金币…. 后期维护代码我们发现很麻烦,每次都要在父类加抽象方法,而且还有在每个子类覆盖。

问题来了:

1.前面仅仅新增几种武器的话,还可以接受。但是后期,稍微一新增某种特别的属性,会牵一发动全身,造成其他子类武器不想要的改变。

2.代码在多个子类重复。而且覆盖没有使用。

3.很难知道某种武器的全部属性,因为某些属性只是覆盖什么事情都没有做。

4.运行代码时候,不容易动态改变武器的属性。

策略者模式来了:

分开变化和不变化的部分:

从哪里开始呢?就我们目前所知,除了display()、usage()、specialEffects()方法之外,permission()方法这类方法经常改变。

现在为了要分开“变化与不变化的部分”,我们准备建立两组类(完全远离 Weapon),一个是与权限permission() 相关的,一个是与 金币加成的 goldAdd() 相关的,每一组将实现各自的动作。比如说,这种武器加成每次加100金币,这种每次加200金币,还有一种加300金币。

设计武器的金币加成为例:

我们所希望一切具有弹性。毕竟,正是因为一开始武器的各种属性没有弹性,才让我们走上这条道路。比如,我们设计一种武器实例,希望指定的每次使用加成100金币的属性给予他。而且我们在使用时候,可以在某些条件不给予它这个属性。这样就可以动态的为这个武器设计属性了。

于是乎,我们利用接口代表每个属性,比方说:permission和goldAdd,而具体的使用权限和金币加成多少都用一个类来实现这个接口,在这个类的重写方法具体的做自己的事情。这些类就叫“属性类”。由属性类而不是Weapon类来实现属性接口。

这样的做法迥异于以往,之前的做法是:属性由子类类来实现,这种做法都是依赖于“实现”,我们被绑得死死的,很难动态改变其属性。

三、代码分析。

3.1、我们先看工程架构:



3.2、父类 WeaponSuper.class:

分析:

1.首先在全局变量声明为public的2个接口(不加public默认就是当前类的访问权限),其作用是交给子类去实例化该接口对象,子类需要什么属性,让他们自己选择,这样就不用在父类操作子类的事情,也不必关心子类去做了什么,重要的是子类可以自由选择组合。

2.上面说到变与不变,这里不变的属性方法我们提取出来,在父类里面写成抽象方法,让子类去做自己的事情。那些易变化的属性我们改为接口,用组合去实现,并不是用继承。

package Weapon;/*
* 项目名:StrategyPattern-Sample
* 包名:WeaponSuper
* 文件名:WeaponSuper
* 创建时间:2017/5/12下午 2:46
* 创建者 xuhong
* 描述:武器 ,父类
*/

import Interface_WeaponProperty.IGoldAdd;
import Interface_WeaponProperty.IPermission;

public abstract class WeaponSuper {

IGoldAdd iGoldAdd;
IPermission iPermission;

//显示武器的加成
public void showGoldAdd(){
int goldAdd = iGoldAdd.GoldAdd();
System.out.print("这武器新增"+goldAdd+"金币哦!");
}

//显示武器的权限
public void showPeimission(){
iPermission.permission();
}

/**
* 设置武器加成的方法
* @param iGoldAdd 接口
*/
public void setiGoldAdd(IGoldAdd iGoldAdd){
this.iGoldAdd=iGoldAdd;
}

// 抽象方法:显示武器样式
public abstract void display();

//抽象方法:显示特效
public abstract void showSpecialEffects();

//抽象方法:使用方法
public abstract void showUsage();

}


3.3、接口与其实现类分析。

3.3.1 .金币加成的接口 IGoldAdd 。这里的代码就没什么好分析了,就一个抽象方法。

package Interface_WeaponProperty;

public interface IGoldAdd {
int GoldAdd();
}


3.3.2 权限使用的接口 Interface_WeaponProperty

package Interface_WeaponProperty;

public interface IPermission {
void permission();
}


3.3.3 .重头戏来了,看看我们实现类的代码。这是我们自由想要做的事情,比如我写了三个实现类,分别做了加300、200、100金币的加成,后续你也可以添加一些不同金币加成类。

import Interface_WeaponProperty.IGoldAdd;

//增加300金币
public class GoldAdd_300 implements IGoldAdd {
@Override
public int GoldAdd() {
return 300;
}
}


import Interface_WeaponProperty.IGoldAdd;

//增加200金币
public class GoldAdd_200 implements IGoldAdd{
@Override
public int GoldAdd() {
return 200;
}
}


import Interface_WeaponProperty.IGoldAdd;

//增加100金币
public class GoldAdd_100 implements IGoldAdd {
@Override
public int GoldAdd() {
return 100;
}
}


3.3.4 相信你看到了上面的实现类,你也知道权限接口的实现类怎么写了吧、无非也是写不同的类做不同方法。好吧。直接撸代码。

import Interface_WeaponProperty.IPermission;

public class NeedPermission implements IPermission {
@Override
public void permission() {
System.out.print("需要权限的武器!");
}
}


import Interface_WeaponProperty.IPermission;

public class NoNeedPermission implements IPermission {
@Override
public void permission() {
System.out.print("不需要权限的武器!");
}
}


4.核心代码来了,看看我们的子类该怎么做?

分析:我们这里写了一个子类,在构造方法实例化父类声明的对象。而且不变共有的属性,我们都继承了并且做自己的事情。

import GoldAdd.GoldAdd_100;
import Permission.NoNeedPermission;

public class Axe extends WeaponSuper {

//父类声明的对象在子类的构造方法实例化
public Axe() {
iGoldAdd = new GoldAdd_100();
iPermission = new NoNeedPermission();
}
@Override
public void display() {
System.out.print("我只是一个斧头!");
}

@Override
public void showSpecialEffects() {
System.out.print("斧头没有特效");
}

@Override
public void showUsage() {
System.out.print("斧头的使用说明!");
}
}


5. 看看我们怎么组合,怎么使用。

分析:

1.这里我只是声明一个父类的对象,去new一个子类,当然了。你也可以去声明对象为自己,有何不同?声明为父类对象去实例化子类时候,该对象只能调用被继承下来的方法,不能调用子类的其他的方法。

现在我们可以动态的在代码改变武器的某些属性了,下面代码我改为了300金币的加成呢。

2.你还可以去子类里面不用实例化某个接口对象,这里你就随心所欲的想要自己的属性了,跟父类很大的脱离关系。

import GoldAdd.GoldAdd_300;
import Weapon.Axe;
import Weapon.WeaponSuper;

public class Main {
public static void main(String[] args) {

WeaponSuper axe = new Axe();
axe.showGoldAdd();
axe.display();
axe.showPeimission();
axe.showUsage();

//动态的给予这个武器300金币的加成
axe.setiGoldAdd(new GoldAdd_300());
axe.showGoldAdd();
}
}


四、归纳。

1.设计原则:针对接口编程,而不是针对实现的编程。

2.设计原则:找到应用中可能需要变化之处,把他们独立出来,不要和那些不需要变化的代码混在一起。

3.多用组合,少用继承。

4.主要是用来分离算法,在相同的行为抽象下有不同的具体实现策略。这个模式很好的演示了开闭原则:定义抽象,增加新的策略只需要增加新的类,然后在运行中动态更换即可,没有影响到原来的逻辑,从而达到了很好的可扩展性。

5.在Android中使用策略者模式的多之又多,典型的是动画类Animation ,其父类里定义 Interpolator 插值器对象,用来在执行动画的时候达到所需要的速度变化效果。这样就也可在代码中动态的更新不同的动画效果了。这和我们上面的思想一样。

转载请注明原创:http://blog.csdn.net/xh870189248/article/details/71747282

源码:http://download.csdn.net/detail/xh870189248/9840941
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐