设计模式实战策略模式:原理篇
Posted mo_weifeng
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计模式实战策略模式:原理篇相关的知识,希望对你有一定的参考价值。
前言
小明在公司负责开发网约车应用,应用可以呼叫出租车和私家车两款车。乘客打车时要显示预计价格,而价格通过里程来计算。
小明是这样写的:
public class PriceCalculator
public static final int TAXI = 1;
public static final int PRIVATE_CAR = 2;
private static float taxibusPrice(int km)
//业务逻辑
return km * 1.28f;
private static float privateCarPrice(int km)
//业务逻辑
return km * 1.23f;
public static float calculatePrice(int km, int type)
if (type == TAXI)
return taxibusPrice(km);
else if (type == PRIVATE_CAR)
return privateCarPrice(km);
return 0;
public void test()
//缺点:增加方案要改代码,不符合开闭原则。代码臃肿,耦合太高。
float taxiPrice = PriceCalculator.calculatePrice(5, PriceCalculator.TAXI);
System.out.println("出租车价格:" + taxiPrice);
float privateCarPrice = PriceCalculator.calculatePrice(5, PriceCalculator.PRIVATE_CAR);
System.out.println("私家车价格:" + privateCarPrice);
出租车价格:6.3999996
私家车价格:6.15
小明的领导一看,说这样写不行,如果要增加专车、商务车,岂不是要修改PriceCalculator类?这就违反了开闭原则,有新变化时,需要改变旧事物。随着业务逻辑的增加,PriceCalculator类也会变得越来越臃肿不堪,难以扩展。
领导建议面对这种因为使用不同方式,会改变结果的情景时,可以使用策略模式代替。
使用策略模式改造
首先我们需要把策略抽象出来,将它标准化,做成接口类。实现类实现接口时,需要实现计算价格的函数
public interface CalculateStrategy
float calculatePrice(int km);
public class TaxiStragety implements CalculateStrategy
@Override
public float calculatePrice(int km)
//业务逻辑
return km * 1.28f;
public class PrivateCarStragety implements CalculateStrategy
@Override
public float calculatePrice(int km)
//业务逻辑
return km * 1.23f;
然后我们需要一个系统环境,可以切换策略和执行策略
public class TranficCalculator
public TranficCalculator()
private CalculateStrategy mStrategy;
public void setStrategy(CalculateStrategy strategy)
mStrategy = strategy;
public float calculatePrice(int km)
if (mStrategy != null)
return mStrategy.calculatePrice(km);
return 0;
开始测试
TranficCalculator calculator = new TranficCalculator();
calculator.setStrategy(new TaxiStragety());
calculator.calculatePrice(5);
calculator.setStrategy(new PrivateCarStragety());
calculator.calculatePrice(5);
出租车策略价格:6.3999996
私家车策略价格:6.15
假如我们要添加一辆商务车,只需要增加一个商务车策略
public class BusinessStragety implements CalculateStrategy
@Override
public float calculatePrice(int km)
//业务逻辑
return km * 3.18f;
public void test3()
TranficCalculator calculator = new TranficCalculator();
calculator.setStrategy(new BusinessStragety());
System.out.println("商务车策略价格:" + calculator.calculatePrice(5));
商务车策略价格:15.900001
策略模式讲解
看一下策略模式的UML图
Strategy,抽象策略角色:抽象角色,通常使用接口或者抽象类实现。
ConcreteStrategy,具体策略角色:实现了抽象角色的类,包含具体的算法和行为。
Context,环境角色,持有抽象角色的引用,给客户端调用。
意图:封装一系列的算法,使它们可互相替换
主要解决:在相似算法情况下,可替换由if…else…带来的复杂
看到这里,去检查自己写的代码,那些有if…else…或者switch…case…的地方,并且算法类似,是不是适合用策略模式替代一下?
android动画插值器
Android给我们提供了很多动画,比如透明动画、平移动画、选择动画、缩放动画等。
比如说平移动画:
//平移动画TranslateAnimation 从(0,0)平移到(300,200)
TranslateAnimation translateAnimation = new TranslateAnimation(0,
300.f, 0, 200.f);
//动画时间为2秒
translateAnimation.setDuration(2000);
//变化率开始和结束缓慢但在中间加速
Interpolator interpolator = new AccelerateDecelerateInterpolator();
translateAnimation.setInterpolator(interpolator);
//开始动画
mImageView.startAnimation(translateAnimation);
我们可以看到Animation可以设置一个Interpolator,Interpolator是插值器的意思,可以改变动画变化的速率。并且可以有多种插值器供我们选择:
AccelerateDecelerateInterpolator :变化率开始和结束缓慢但在中间加速
AccelerateInterpolator :变化率开始缓慢然后加速
AnticipateInterpolator :变化开始向后然后向前飞行
AnticipateOvershootInterpolator :变化开始向后然后向前飞行并超过目标值,最后返回到最终值
BaseInterpolator :插值器扩展的抽象类
BounceInterpolator :更改在结束时反弹
CycleInterpolator :重复动画指定的周期数
DecelerateInterpolator :变化率开始和结束缓慢但在中间加速。
LinearInterpolator :变化率是恒定的
OvershootInterpolator :变化向前晃动并超过最后一个值然后返回
这些插值器其实就是一个个策略,一个个算法,它的抽象策略是TimeInterpolator
public interface TimeInterpolator
float getInterpolation(float var1);
具体策略实现了getInterpolation方法
@HasNativeInterpolator
public class AccelerateInterpolator extends BaseInterpolator implements NativeInterpolatorFactory
private final float mFactor;
private final double mDoubleFactor;
public AccelerateInterpolator()
mFactor = 1.0f;
mDoubleFactor = 2.0;
/**
* Constructor
*/
public AccelerateInterpolator(float factor)
mFactor = factor;
mDoubleFactor = 2 * mFactor;
public AccelerateInterpolator(Context context, AttributeSet attrs)
this(context.getResources(), context.getTheme(), attrs);
public float getInterpolation(float t)
if (mFactor == 1.0f)
return t * t;
else
return (float)Math.pow(t, mDoubleFactor);
这就是Android源码中很典型的策略模式。
还有其它常见的:
- 通过设置不同的Adapter(即不同的策略),我们就可以写出符合我们需求的ListView布局
- RecyclerView设置不同的LayoutManager,可以决定RV的显示风格。(线性布局管理器(LinearLayoutManager)、网格布局管理器(GridLayoutManager)、瀑布流布局管理器(StaggeredGridLayoutManager))
举个例子:图片加载器
加载图片有很多第三方库,如Glide、Picasso、Fresco等,我们可以根据自己的选择去切换不同的加载工具。
首先是抽象类,只有一个显示图片的接口
public interface ImageLoaderProvider
void loadImage(Context ctx, ImageConfig img);
//ImageConfig是加载的一些配置,比如有些在wifi下才加载,或者加载大图,中图,缩略图等等,简单的代码如下:
public class ImageConfig
private int type;
private String url;
private int placeHolder;
private ImageView imageView;
private int strategy;//加载策略,是否在wifi下才加载或者其他的策略,比如加载大图,中图,或者是小图等等
private ImageConfig(ImageConfig imageConfig)
this.type = imageConfig.type;
this.url = imageConfig.url;
this.placeHolder = imageConfig.placeHolder;
this.imageView = imageConfig.imageView;
this.strategy = imageConfig.strategy;
private ImageConfig()
public int getType()
return type;
public String getUrl()
return url;
public int getPlaceHolder()
return placeHolder;
public ImageView getImageView()
return imageView;
public int getStrategy()
return strategy;
public static class Builder
private ImageConfig imageConfig;
public Builder()
this.imageConfig = new ImageConfig();
public Builder type(int type)
imageConfig.type = type;
return this;
public Builder url(String url)
imageConfig.url = url;
return this;
public Builder placeHolder(int placeHolder)
imageConfig.placeHolder = placeHolder;
return this;
public Builder imgView(ImageView imgView)
imageConfig.imageView = imgView;
return this;
public Builder strategy(int strategy)
imageConfig.strategy = strategy;
return this;
public ImageConfig build()
return new ImageConfig(imageConfig);
//可以看到目前仅仅是判断是否在wifi下才加载,当然实际情况可以适当扩展,但原理不变。
Picasso策略
public class PicassoImageLoaderProvider implements ImageLoaderProvider
private ImageView imageView;
@Override
public void loadImage(Context ctx, ImageConfig config)
// 一般实现,或者根据具体的一些另外的策略加载,比如是否在wifi下自动加载等,根据业务具体决定
Picasso
.with(ctx)
.load(config.getUrl())
.tag("Picasso") //参数为 Object
.placeholder(config.getPlaceHolder())
.into(target);
imageView = config.getImageView();
private Target target = new Target()
@Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from)
//加载成功后会得到一个bitmap,可以自定义操作
imageView.setImageBitmap(bitmap);
@Override
public void onBitmapFailed(Drawable errorDrawable)
// 加载失败进行相应处理
@Override
public void onPrepareLoad(Drawable placeHolderDrawable)
;
Glide策略
public class GlideImageLoaderProvider implements ImageLoaderProvider
@Override
public void loadImage(Context ctx, ImageConfig config)
boolean flag = SettingUtils.getOnlyWifiLoadImg();
//如果不是在wifi下加载图片,直接加载
if (!flag)
loadNormal(ctx, config);
return;
int strategy = config.getStrategy();
//这里1表示的是wifi加载
if (strategy == 1)
int netType = NetUtils.getNetWorkType(ctx);
//如果是在wifi下才加载图片,并且当前网络是wifi,直接加载
if (netType == NetUtils.NETWORKTYPE_WIFI)
loadNormal(ctx, config);
else
//如果是在wifi下才加载图片,并且当前网络不是wifi,加载缓存
loadCache(ctx, config);
else
//如果不是在wifi下才加载图片
loadNormal(ctx, config);
/**
* 加载图片,这里需要注意的是默认加载的是全尺寸的,这样的话很容易导致OOM的发生,需要进行适当的压缩
*/
private void loadNormal(Context ctx, final ImageConfig config)
Glide.with(ctx)
.load(config.getUrl())
.override(ProjectConfig.IMAGE_WIDTH, ProjectConfig.IMAGE_HEIGHT)
.placeholder(config.getPlaceHolder())
.into(new SimpleTarget<GlideDrawable>()
@Override
public void onLoadFailed(Exception e, Drawable errorDrawable)
config.getImageView().setImageResource(R.drawable.ic_icon_share_sp);
@Override
public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> glideAnimation)
config.getImageView().setImageDrawable(resource);
);
/**
* 加载缓存图片
*/
private void loadCache(Context ctx, ImageConfig config)
Glide.with(ctx).using(new StreamModelLoader<String>()
@Override
public DataFetcher<InputStream> getResourceFetcher(final String model, int i, int i1)
return new DataFetcher<InputStream>()
@Override
public InputStream loadData(Priority priority) throws Exception
throw new IOException();
@Override
public void cleanup()
@Override
public String getId()
return model;
@Override
public void cancel()
;
)
.load(config.getUrl())
.override(ProjectConfig.IMAGE_WIDTH, ProjectConfig.IMAGE_HEIGHT)
.placeholder(config.getPlaceHolder())
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(config.getImageView());
Fresco策略
public class FrescoImageLoaderProvider implements ImageLoaderProvider
private static volatile FrescoImageLoaderProvider sInstance;
public static FrescoImageLoaderProvider getInstance()
if (sInstance == null)
synchronized (FrescoImageLoaderProvider.class)
if (sInstance == null)
sInstance = new FrescoImageLoaderProvider();
return sInstance;
@Override
public void loadImage(Context ctx, ImageConfig config)
//这里可以根据一些其他策略进行加载,比如wifi环境下加载或者不加载之类,具体业务决定
DraweeController controller = Fresco.newDraweeControllerBuilder()
.setUri(config.getUrl())
.setAutoPlayAnimations(true)
.setControllerListener(listener)
.build();
SimpleDraweeView simpleDraweeView = (SimpleDraweeView) config.getImageView();
simpleDrawee以上是关于设计模式实战策略模式:原理篇的主要内容,如果未能解决你的问题,请参考以下文章