侧罗模式 android,Android开发之深入理解策略(Strategy)模式

摘要:

什么样的程序设计被称为策略模式?什么时候使用策略模式?为什么用策略模式代替抽象类或接口?这篇文章引用构建(Builder)模式的例子,将绘制钟表的过程改写成符合策略模式程序设计,程序结构如下图:

ace391a5fce751d2052960b477760a0f.png

WatchViewImpl继承View,实现自定义钟表视图

WatchViewActivity显示钟表效果

IClock定义绘制钟表的接口,impl目录的三个文件分别是接口IClock的三种实现,处理钟表的绘制逻辑,客户可以自由选择使用其中的一种策略。

一、关键代码分析

本程序如果不使用设计模式,只需要WatchViewImpl和WatchViewActivity即可实现,绘制钟表的逻辑全部写在自定义视图类内,不足的地方:如果想要修改绘制钟表的逻辑,唯一的方式就是修改WatchViewImpl,修改的同时又想保留原来的绘制逻辑,如果将第一个版本的逻辑实现在当前类封装后保留,再添加新的绘制逻辑,自定义视图类显得比较臃肿,不符合程序设计的单一设计原则

这时候开始考虑设计模式——策略(strategy)模式,看一下WatchViewActivity关键代码:

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

cn.teachcourse.strategy.WatchViewImpl watchView=new cn.teachcourse.strategy.WatchViewImpl(this);

/*第一种策略:默认算法*/

IClock defaultClock=new DefaultClockImpl(watchView);

/*第二种策略:常用算法*/

IClock normalClock=new NormalClockImpl(watchView);

/*第三种策略:设计算法*/

IClock designClock = new DesignClockImpl(watchView);

/*自由选择使用其中的一种策略,比如:normalClock*/

watchView.setIClock(normalClock);

setContentView(watchView);

}

三种算法彼此之间没有影响,如果想要添加第四种算法,不需要修改或者改动很少自定义视图类WatchViewImpl,创建第四个算法类实现接口IClock,处理绘制钟表的逻辑,然后在客户端WatchViewActivity选择实例化的对象即可,符合程序设计中的开闭原则

看一下IClock的关键代码:

/**

* Created by https://www.teachcourse.cn on 2017/7/5.

*/

public interface IClock {

void paint(Canvas canvas);

}

再来看一下默认策略算法DefaultClockImpl的实现逻辑,关键代码:

public DefaultClockImpl(WatchViewImpl watchView) {

this.watchView = watchView;

}

@Override

public void paint(Canvas canvas) {

//移动画布中心点到当前View中心

canvas.translate(getWidth() / 2, getHeight() / 2);

//绘制外圆背景颜色

paintExternalCircle(canvas);

//绘制内圆背景颜色

paintCircle(canvas);

//绘制刻度

paintScale(canvas);

//绘制指针

paintPointer(canvas);

}

/**

* 绘制钟表外部圆形背景

*/

public void paintExternalCircle(Canvas canvas) {

...

}

/**

* 绘制钟表的内部圆形背景

*

* @param canvas

*/

public void paintCircle(Canvas canvas) {

...

}

/**

* 绘制钟表刻度

*

* @param canvas

*/

private void paintScale(Canvas canvas) {

...

}

/**

* 绘制钟表上的时针、分针、秒针

*

* @param canvas

*/

private void paintPointer(Canvas canvas) {

...

}

...

}

对比DesignClockImpl设计算法实现的逻辑,关键代码:

private Handler mHandler = new Handler() {

@Override

public void handleMessage(Message msg) {

super.handleMessage(msg);

postInvalidate();

}

};

public DesignClockImpl(WatchViewImpl watchView) {

this.watchView = watchView;

mPaint=new Paint();

mPath = new Path();

}

@Override

public void paint(Canvas canvas) {

...

}

...

}

罗列关键代码的目的,想让读者可以对策略模式的设计结构一个整体的认识DefaultClockImpl类和DesignClockImpl类分别是两种不同处理绘制钟表的逻辑,两种策略的效果图分别如下:

a823b4660c8dcd4c435a521e8c291785.png

设计策略模式,实现效果图:

c1d522c77255c545a32395c58215660b.png

小明同学可能喜欢绘制挂在墙上的钟表,类似于DefaultClockImpl效果;小红同学想要绘制带在手上的电子表,比较时尚,类似于DesignClockImpl效果;如果小李还想绘制一款与小红、小明不一样的钟表,那也不是不可能,小李构思一下绘制的逻辑,重新实现IClock接口即可。

二、策略模式分析

在钊林的另一篇介绍UML的文章里说过,学习设计模式,UML图谱可以简单清晰表示类之间的关系,可以整体理解设计模式的思路,不懂UML图谱的同学先了解一下。

绘制上述Demo的UML图谱如下:

c7669a5d86b59a5b6b9346e9e42050ef.png

UML类图的含义:WatchViewActivity依赖于WatchViewImpl,同时WatchViewImpl依赖于IClock接口;DefaultClockImpl、NormalClockImpl和DesignClockImpl实现接口,为了方便部分同学理解,总结一下UML图的基本语法:

类的UML图分为抽象类和非抽象类,抽象类名使用斜体字形;

接口的UML图,接口名使用斜体字形,同时使用《Interface》符号修饰;

泛化关系的UML图使用实线空心箭头表示;

关联关系的UML图使用实线箭头表示;

依赖关系的UML图使用虚线箭头表示;

实现关系的UML图使用虚线空心箭头表示

策略模式:定义了算法族,分别封装起来,让它们之间可以相互代替,此模式让算法的变化独立于使用算法的客户。

通用的UML类图:

3dc7aaf027dcf23017d2720c9a8c242b.png

学习设计模式的过程,比较简单的方法,记住每种设计模式通用的UML类图,依样画葫芦,创建同样结构的类关系,达到使用设计模式的目的,比如上文绘制钟表的例子,IClock对应Strategy,WatchViewImpl对应Context等

三、为什么用策略模式代替抽象类或接口?

在绘制钟表的例子中,另外的两种处理方法:

第一种:可以尝试将自定义视图类WatchViewImpl,声明为一个抽象类,声明抽象方法void paint(Canvas canvas),让子类实现绘制逻辑,不同的子类实现不同的效果,这样也是可以实现自定义视图的多样化的。

抽象类的UML类图:

2eaa87bccdd674ea3671e5d9e3b9f742.png

不足地方:

如果存在另一个WatchView,比如:WatchViewCopy,该类想要使用DefaultWatchViewImpl绘制钟表的逻辑代码,通常的做法复制一份到WatchViewCopy的子类中,结果出现比较多的重复代码;类似的,如果想要使用另外两个子类的绘制逻辑,依然继续复制;

这个时候,策略模式的优势就体现出来了,处理绘制钟表代码的逻辑与WatchViewImpl没有联系,其他需要IClock接口的类,可以简单注入对应的实现类,可以实现代码的复用。

上面的说法如果不容易理解,那么再举一个例子,WatchView作为一个自定义视图类,通常可以在布局文件中使用,当用户选择使用DefaultWatchViewImpl,必然布局文件、Activity类关联的是DefaultWatchViewImpl的引用,如果这时候更换NormalWatchViewImpl,需要改动的地方势必比策略模式要多。

当然,还有别的原因,这里列举的两个不足,想要说明可以使用抽象类的地方可以使用策略模式代替,反过来则不行。

第二种:如果使用接口,定义接口IClock,定义方法void paint(Canvas canvas),让WatchView继承View的同时实现接口方法,处理绘制钟表的逻辑全部写在了自定义视图类内,这个时候的接口相比抽象类,扩展性能更不好,然后就可以因此引生出策略模式啦。

接口的UML类图:

6482acbed49e3cef01f66e9e85e587f5.png

四、总结

全文从引入绘制钟表的例子作为入口,让读者对策略模式的设计过程一个整体的认识;第二部分引入策略模式的定义、模板,让读者可以快速掌握该模式的使用;第三部分,从例子的角度出发,说明策略模式比抽象类或接口的优势,目的想要阐明:多用组合,少用继承的观点,对对象的行为可能需要复用的情况,选择使用策略模式代替抽象类,抽象类和接口同样有存在的优势,具体看开发的需要。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值