Android开发中常见的设计模式(四)——策略模式

Posted on 2018-07-30 08:45  xl_phoenix  阅读(143)  评论(0编辑  收藏  举报

策略模式定义了一些列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变换。

假设我们要出去旅游,而去旅游出行的方式有很多,有步行,有坐火车,有坐飞机等等。而如果不使用任何模式,我们的代码可能就是这样子的。

public class TravelStrategy {
	enum Strategy{
		WALK,PLANE,SUBWAY
	}
	private Strategy strategy;
	public TravelStrategy(Strategy strategy){
		this.strategy=strategy;
	}
	
	public void travel(){
		if(strategy==Strategy.WALK){
			print("walk");
		}else if(strategy==Strategy.PLANE){
			print("plane");
		}else if(strategy==Strategy.SUBWAY){
			print("subway");
		}
	}
	
	public void print(String str){
		System.out.println("出行旅游的方式为:"+str);
	}
	
	public static void main(String[] args) {
		TravelStrategy walk=new TravelStrategy(Strategy.WALK);
		walk.travel();
		
		TravelStrategy plane=new TravelStrategy(Strategy.PLANE);
		plane.travel();
		
		TravelStrategy subway=new TravelStrategy(Strategy.SUBWAY);
		subway.travel();
	}
}

这样做有一个致命的缺点,一旦出行的方式要增加,我们就不得不增加新的else if语句,而这违反了面向对象的原则之一,对修改封闭。而这时候,策略模式则可以完美的解决这一切。

首先,需要定义一个策略接口。

public interface Strategy {
	void travel();
}

然后根据不同的出行方式实行对应的接口

public class WalkStrategy implements Strategy{

	@Override
	public void travel() {
		System.out.println("walk");
	}

}
public class PlaneStrategy implements Strategy{

	@Override
	public void travel() {
		System.out.println("plane");
	}

}
public class SubwayStrategy implements Strategy{

	@Override
	public void travel() {
		System.out.println("subway");
	}

}

此外还需要一个包装策略的类,并调用策略接口中的方法

public class TravelContext {
	Strategy strategy;

	public Strategy getStrategy() {
		return strategy;
	}

	public void setStrategy(Strategy strategy) {
		this.strategy = strategy;
	}

	public void travel() {
		if (strategy != null) {
			strategy.travel();
		}
	}
}

测试一下代码

public class Main {
	public static void main(String[] args) {
		TravelContext travelContext=new TravelContext();
		travelContext.setStrategy(new PlaneStrategy());
		travelContext.travel();
		travelContext.setStrategy(new WalkStrategy());
		travelContext.travel();
		travelContext.setStrategy(new SubwayStrategy());
		travelContext.travel();
	}
}

输出结果如下

plane
walk
subway

可以看到,应用了策略模式后,如果我们想增加新的出行方式,完全不必要修改现有的类,我们只需要实现策略接口即可,这就是面向对象中的对扩展开放准则。假设现在我们增加了一种自行车出行的方式。只需新增一个类即可。

public class BikeStrategy implements Strategy{

	@Override
	public void travel() {
		System.out.println("bike");
	}

}

之后设置策略即可

public class Main {
	public static void main(String[] args) {
		TravelContext travelContext=new TravelContext();
		travelContext.setStrategy(new BikeStrategy());
		travelContext.travel();
	}
}

而在Android的系统源码中,策略模式也是应用的相当广泛的.最典型的就是属性动画中的应用.

我们知道,在属性动画中,有一个东西叫做插值器,它的作用就是根据时间流逝的百分比来来计算出当前属性值改变的百分比.

我们使用属性动画的时候,可以通过set方法对插值器进行设置.可以看到内部维持了一个时间插值器的引用,并设置了getter和setter方法,默认情况下是先加速后减速的插值器,set方法如果传入的是null,则是线性插值器。而时间插值器TimeInterpolator是个接口,有一个接口继承了该接口,就是Interpolator这个接口,其作用是为了保持兼容

private static final TimeInterpolator sDefaultInterpolator =
		new AccelerateDecelerateInterpolator();  
private TimeInterpolator mInterpolator = sDefaultInterpolator; 
@Override
public void setInterpolator(TimeInterpolator value) {
	if (value != null) {
		mInterpolator = value;
	} else {
		mInterpolator = new LinearInterpolator();
	}
}

@Override
public TimeInterpolator getInterpolator() {
	return mInterpolator;
}
public interface Interpolator extends TimeInterpolator {
    // A new interface, TimeInterpolator, was introduced for the new android.animation
    // package. This older Interpolator interface extends TimeInterpolator so that users of
    // the new Animator-based animations can use either the old Interpolator implementations or
    // new classes that implement TimeInterpolator directly.
}

此外还有一个BaseInterpolator插值器实现了Interpolator接口,并且是一个抽象类

abstract public class BaseInterpolator implements Interpolator {
    private int mChangingConfiguration;
    /**
     * @hide
     */
    public int getChangingConfiguration() {
        return mChangingConfiguration;
    }

    /**
     * @hide
     */
    void setChangingConfiguration(int changingConfiguration) {
        mChangingConfiguration = changingConfiguration;
    }
}

平时我们使用的时候,通过设置不同的插值器,实现不同的动画速率变换效果,比如线性变换,回弹,自由落体等等。这些都是插值器接口的具体实现,也就是具体的插值器策略。我们略微来看几个策略。

public class LinearInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {

    public LinearInterpolator() {
    }

    public LinearInterpolator(Context context, AttributeSet attrs) {
    }

    public float getInterpolation(float input) {
        return input;
    }

    /** @hide */
    @Override
    public long createNativeInterpolator() {
        return NativeInterpolatorFactoryHelper.createLinearInterpolator();
    }
}
public class AccelerateDecelerateInterpolator extends BaseInterpolator
        implements NativeInterpolatorFactory {
    public AccelerateDecelerateInterpolator() {
    }

    @SuppressWarnings({"UnusedDeclaration"})
    public AccelerateDecelerateInterpolator(Context context, AttributeSet attrs) {
    }

    public float getInterpolation(float input) {
        return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
    }

    /** @hide */
    @Override
    public long createNativeInterpolator() {
        return NativeInterpolatorFactoryHelper.createAccelerateDecelerateInterpolator();
    }
}

内部使用的时候直接调用getInterpolation方法就可以返回对应的值了,也就是属性值改变的百分比。

Copyright © 2024 xl_phoenix
Powered by .NET 8.0 on Kubernetes