6

博客新址,这里更有趣

目录

  1. 策略模式

  2. 为什么使用策略模式?

  3. 策略模式应用实例

策略模式

策略模式:建立行为族,将不同的行为分别封装,同时彼此可相互替代,算法的变化可以独立于使用者。
优点:提高了可复用性,将行为和使用者解耦出来
缺点:增加了编写代码工作量

为什么使用策略模式

通过一个游戏的例子我们可以更好的说明这个问题,我们的游戏中有很多的人物,这个时候,我们定义一个基础游戏角色类,然后不同的角色将会有不同的技能,所以其攻击行为和防御行为在不同的角色中是不同的,其单位攻击所造成的伤害和攻击的特效也是不同的,如果按照传统的方式,我们首先想到的写法可能是这样子的。

//基类,具有攻击和防御行为,所有游戏角色继承自这个基类
class People{
    People(){

    }
//攻击
    public void attck(){

    }
//防御
    public void defense(){

    }
}
//法师角色

class Master extends People{
    Master(){

    }
    public void attck(){
        造成魔法伤害
    }
    public void defense(){
        抵御魔法攻击
    }

}
//剑客角色
class Swordsman extends People{
    Swordsman(){

    }
    public void attck(){
        造成物理伤害
    }
    public void defense(){
        抵御物理攻击
    }
}

这样子来看完全可行,我们需要什么角色实例的时候只需要new一个即可,但是我们的游戏中的角色往往是有很多的,而不只是简单这几种的。当我们为了改善游戏体验进一步吸引用户,所以需要对游戏中的角色的攻击增加特效或者是对防御增加某种特殊防御,或者是该角色的攻击方式和防御方式都发生了变化,这个时候我们就需要对每一个角色类进行查找,找到那些角色有这种攻击方式和防御方式,然后打开对其进行修改,工作将变得很繁琐,同时对原有的类进行修改也进一步增加了我们犯错的几率。如何改善这种状况呢?这个时候我们需要考虑,这其中那些东西是变的,而那些东西是固定的,然后封装变化的。这也正是设计的一个原则找出那些变化的将他们独立出来,不要和那些不需要变化的混在一起

所以我们想到将这些变化的行为,攻击和防御进行封装起来,将一些不会变化的比如,姓名,性别设置,更新方法与之孤立起来。将行为进行封装,便于我们后期的需求变更时,对项目进行更新,那么我们想到的就是设置不同的行为类,然后通过委托的方式,在角色的内部发生其真实行为。那么我们可以写出下面的代码。

abstract class People{
    Attackable attckable;
    Denfenseable denfenseable;
    public void setAttckMethond(Attackable attckable){
        this.attckable = attckable;
    }
    public void setDefenseMethond(Denfenseable denfenseable){
        this.denfenseable = denfenseable;
    }
    public void attck(){
        attckable.invoke();

    }
    public void defense(){
        denfenseable.invoke();
    }
    public abstract void haveSex();
    More Methond.....
}
//法师
class Master extends People{
    Master(){

    }
    public void haveSex(){
        System.out.println("Come on");
    }

}
//剑客
class Swordsman extends People{
     Swordsman(){

    }
    public void haveSex(){
        System.out.println("ya mie die")
    }
}
//定义攻击接口
public interface Attackable{
    public void invoke();
}
// 定义防御接口
public interface Denfenseable{
    public void invoke();
}
//实现了攻击接口的秒杀全场攻击
public class KillAll implements Attackable{
    public void invoke(){
        System.out.println("Kill All");
    }
}
//实现了防御接口的抵御任何攻击接口
public class DenfenseAnyAttack implements Denfenseable{
    public void invoke(){
        System.out.println("Denfense All");
    }
}

将变化的攻击和防御行为单独拿出来作为接口,然后在基类中通过委托的形式,借助多态,来实现相应的攻击,防御方式,同时设置了set方法,使得其扩展性进一步增强。当我们修改英雄的攻击行为和防御行为只需要通过set方法即可,如果对攻击和防御行为的表现形式进行修改,通过找到相应的行为类修改即可,借助委托,多态,实现了彻底的将对象和行为进行解耦。

依赖注入:通常我们可以通过set方法,构造函数来将我们所依赖的对象,通过接口或者是父类的形式注入进去。内部借助委托机制实现相应的功能。

策略模式应用实例

在我们开法,我们所使用的类库中,有哪些是通过策略模式来实现的呢?
在Android的动画中,Animation有一个方法为setInterpolator() 熟悉Android开发的一定知道其作用,用来设置一个插值器,其作用是用来控制动画开始结束等一些特效,而其实现就是使用了策略模式。

package android.animation;
 
public interface Interpolator {

    float getInterpolation(float input);
}

有一个插值器的接口,然后各种不同类型的插值器通过实现接口来实现不同的动画特效。实现Interpolator接口的类有

AccelerateDecelerateInterpolator 在动画开始与介绍的地方速率改变比较慢,在中间的时候加速

AccelerateInterpolator 在动画开始的地方速率改变比较慢,然后开始加速

AnticipateInterpolator 开始的时候向后然后向前甩

AnticipateOvershootInterpolator 开始的时候向后然后向前甩一定值后返回最后的值

BounceInterpolator 动画结束的时候弹起

CycleInterpolator 动画循环播放特定的次数,速率改变沿着正弦曲线

DecelerateInterpolator 在动画开始的地方快然后慢

LinearInterpolator 以常量速率改变

OvershootInterpolator 向前甩一定值后再回到原来位置

举一个例子

package android.view.animation;
 
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
 
public class AccelerateInterpolator implements Interpolator {
    private final float mFactor;
    private final double mDoubleFactor;
 
    public AccelerateInterpolator() {
        mFactor = 1.0f;
        mDoubleFactor = 2.0;
    }
 
    public AccelerateInterpolator(float factor) {
        mFactor = factor;
        mDoubleFactor = 2 * mFactor;
    }
 
    public AccelerateInterpolator(Context context, AttributeSet attrs) {
        TypedArray a =
            context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.AccelerateInterpolator);
 
        mFactor = a.getFloat(com.android.internal.R.styleable.AccelerateInterpolator_factor, 1.0f);
        mDoubleFactor = 2 * mFactor;
 
        a.recycle();
    }
 
    public float getInterpolation(float input) {
        if (mFactor == 1.0f) {
            return input * input;
        } else {
            return (float)Math.pow(input, mDoubleFactor);
        }
    }
}

Animation源码

public abstract class Animation implements Cloneable {
    Interpolator mInterpolator;
    
    public void setInterpolator(Interpolator i) {
         mInterpolator = i;
     }
 
    public boolean getTransformation(long currentTime, Transformation outTransformation) {
        final float interpolatedTime = mInterpolator.getInterpolation(normalizedTime);
        applyTransformation(interpolatedTime, outTransformation);
       // ... ...
    }
      protected void ensureInterpolator() {
         if (mInterpolator == null) {
             mInterpolator = new AccelerateDecelerateInterpolator();
         }
     }
 
}

总结:通过对于源码的分析,不难发现其和游戏角色设定的相似之处,实现一个接口,然后不同行为实现该接口,使用者通过set方法,以接口的形式,实现具体实例的依赖注入。从而实现相应的功能。


Jensen95
2.9k 声望534 粉丝

连续创业者。


引用和评论

0 条评论