设计模式 —— 相见恨晚(2)
开篇请看设计模式 —— 相见恨晚(1),由于全部写在一篇会导致篇幅过长,所以另开一篇继续……
七、模版方法模式##
1、定义####
在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
看到这个模式的定义就感觉特别亲切,它的使用场景无处不在。用四个字概括就是流程封装。也就是把某个固定的流程封装到一个final函数中,并且让子类能够定制这个流程中的某些甚至所有的步骤。这就要求父类提取提取共用的代码,提升代码的复用率。
2、代码示例##
模版方法模式包含如下三个角色:
- AbsTemplate:抽象类,定义了一套算法框架
- ConcreteImplA:具体实现类A
- ConcreteImplB:具体实现类B
下面写一个示例代码,示例取自《Head First设计模式》:
public abstract class Beverage{
//泡茶或者咖啡的流程,流程一样,只是其中的具体步骤会有差异
final void prepareRecip(){
boilWater(); //第一步烧水,泡茶和泡咖啡该方法是一样的
brew(); //第二步浸泡茶或咖啡,这一步二者有区别,需要在子类重写
poriInCup(); //第三步将泡好的茶或咖啡倒入杯子中,这一步相同
addCondiments(); //最后一步加调味品,这一步有区别
}
//下面两个方法,因为泡茶和泡咖啡的操作不同,所以抽象出来,交给子类实现
abstract void brew();
abstract void addCondiments();
void boilWater(){
doSomething();
}
void pourInWater(){
doSomething();
}
}
//泡茶的类
public class Tea extends Beverage{
@Override
void brew() {
doSomething(); //执行泡茶的方法
}
@Override
void addCondiments() {
doSomething(); //执行给茶加调味品的方法
}
}
//泡咖啡的类
public class Coffee extends Beverage{
@Override
void brew() {
doSomething(); //执行泡咖啡的方法
}
@Override
void addCondiments() {
doSomething(); //执行给咖啡加调味品的方法
}
}
八、迭代器模式##
1、定义###
提供一种方法顺序访问一个容器对象中的各个元素,而又不需要暴露暴露该对象的内部表示。
根据定义,该模式的应用场景也就是用在遍历一个容器的对象时。例如遍历java中的各种集合类,如List、Map等。Android的源码中也为我们提供了迭代器遍历数据,最为典型的例子就是数据库查询使用Cursor,当我们使用SQLiteDatabase的query()方法查询数据时,会返回一个Cursor游标对象,该游标对象实质就是一个具体的迭代器,我们可以使用它来遍历数据库查询所得的结果集。
2、代码示例###
前面的几种设计模式都提供了代码示例,不过迭代器这个模式对开发者来说几乎不会自己去实现一个迭代器,面试时面试官应该也不会让你写个迭代器模式吧,所以这里就不再给出代码示例,不过UML图还是要有的,如下:
九、策略模式##
1、定义###
策略模式定义了一系列算法,并将每一个算法封装起来,而且使它们还可以互相替换。此模式让算法的变化独立于使用算法的客户。
这是该模式的官方定义,但说实话,这个定义不像其它的一些模式,有些一看就明白是怎么回事,但这个不太好懂,下面我用大白话一解释就明白了是怎么回事,其实很简答,只不过用一句话解释不清,需要啰嗦一点:
完成一项任务,往往可以有多种不同的方式,每一种方式称为一个策略,我们可以根据环境或者条件的不同选择不同的策略来完成该项任务。
例如:有许多算法可以实现某一功能,如查找、排序等,一种常用的方法是硬编码(Hard Coding)在一个类中,如需要提供多种查找算法,可以将这些算法写到一个类中,在该类中提供多个方法,每一个方法对应一个具体的查找算法;当然也可以将这些查找算法封装在一个统一的方法中,通过if…else…等条件判断语句来进行选择。这两种实现方法我们都可以称之为硬编码,如果需要增加一种新的查找算法,需要修改封装算法类的源代码;更换查找算法,也需要修改客户端调用代码。在这个算法类中封装了大量查找算法,该类代码将较复杂,维护较为困难。
为了解决这些问题,可以定义一些独立的类来封装不同的算法,每一个类封装一个具体的算法,在这里,每一个封装算法的类我们都可以称之为策略(Strategy),为了保证这些策略的一致性,一般会用一个抽象的策略类来做算法的定义,而具体每种算法则对应于一个具体策略类。
2、代码示例###
策略模式中包含如下角色:
- Strategy:策略的抽象
- ConcreteStrategy:具体的策略实现
- Context:用来操作策略的上下文环境
下面以乘坐交通工具的场景为例来写代码:我们出门选择乘坐不同各种交通工具可以作为一种策略,例如可以乘出租或者公交车,每一种交通都有不同的计费方式,选择策略不同,计费方式自然不同。我们就以此为例,看如何运用策略模式来实现:选择不同的交通工具,实现各自的计费。
//计算接口
public interface CalculateStrategy{
int calculatePrice(int km);
}
//公交车价格计算策略
public class BusStrategy implements CalculateStrategy{
@Override
public int calculatePrice(int km) {
doSomething(); //公交车的计算方式
}
}
//出租车价格计算策略
public class TaxiStrategy implements CalculateStrategy{
@Override
public int calculatePrice(int km) {
doSomething(); //出租车的计算方式
}
}
//创建Context角色,出行费用计算器
public class TranficCalculator{
CalculateStrategy strategy;
public void setStrategy(CalculateStrategy strategy){
this.strategy = strategy;
}
public int calculateStrategy(int km){
return strategy.calculatePrice(km);
}
public static void main(String[] args){
TranficCalculator tranficCalculate = new TranficCalculator();
tranficCalculate.setStrategy(new BusStrategy());
System.out.print("公交车行驶10km的计费结果为:" + tranficCalculate.calculateStrategy(10));
}
}
十、状态模式##
1、定义###
允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。
状态模式通常会拿来和策略模式比较,因为它俩的结构几乎完全一样,UML图也完全一样,区别在于它俩的“意图”。
刚开始我在学状态模式的时候,也是拿它和策略模式作比较,后来发现完全没必要这样。它俩完全是为了适应不同的运用场景,设计意图也不同,只是最后发现它俩的模式结构基本一样,除此之外没有任何关系。所以在学状态模式的时候,个人认为无需结合策略模式,理解各自的运用场景就好,下面就说说状态模式的使用场景:
在很多情况下,一个对象的行为取决于一个或多个动态变化的属性,这样的属性叫做状态,这样的对象叫做有状态的对象。当一个这样的对象与外部事件产生互动时,其内部状态就会改变,从而使得系统的行为也随之发生变化。
如果我们按照常规思路来做的话,代码中必定包含大量的 if-else 语句进行状态的判断,当有新的状态时,就得在类中加入新的 if-else ,这必定会导致这个类变得臃肿,而且不好维护,那这时就可以使用状态模式了。
状态模式将每一个条件分支放入一个独立的类中,这使得你可以根据对象自身的情况将对象的状态作为一个对象,这一对象不依赖于其它的对象而独立变化,这样就通过多态来去除过多的、重复的 if-else 等分支。
如果还不理解,看完下面的例子就应该明白了。
2、代码示例###
状态模式中包含的角色:
- Strategy:抽象状态类或接口
- ConcreteStrategy:具体状态类
- Context:用来操作状态的上下文环境
下面就以电视遥控器为例来演示状态模式的实现。我们知道电视有关机和开机的状态,在开机状态下,可以切换频道、调整音量、关机等,而在关机状态下,这些操作都是无效的,只能执行开机的操作。下面我们就来实现:
//定义状态接口(谁的状态?当然是电视机,遥控器可没有状态)
public interface TvState{
public void Channel(); //切换频道
public void volum(); //调整音量
}
//开机状态下的操作
public class PowerOnState implements TvState{
@Override
public void Channel() {
doSomething();
}
@Override
public void volum() {
doSomething();
}
}
//关机状态下的操作
public class PowerOnState implements TvState{
@Override
public void Channel() {
doNothing();
}
@Override
public void volum() {
doNothing();
}
}
public class TvController{
TvState tvState;
public void setTvState(TvState tvState){
this.tvState = tvState;
}
public void powerOn(){
setTvState(new PowerOnState());
System.out.print("开机啦");
}
public void powerOff(){
setTvState(new PowerOffState());
System.out.print("关机啦");
}
public void channel(){
tvState.channel();
}
public void volum(){
tvState.volum();
}
}
//客户端测试类
public class Client{
public static void main(String[] args){
TvController tvController = new TvController();
//设置开机状态
tvController.powerOn();
tvController.channel();//切换频道操作
//设置关机状态
tvController.powerOff();
tvController.channel();//此时切换频道不会生效
}
}
十一、代理模式##
1、定义###
为其他对象提供一个代理(类)以控制对这个对象的访问。
定义很简短也很好理解,一看就明白,没有什么需要解释的。
2、代码示例###
代理模式包含如下四个角色:
- Subject:抽象主题类
- RealSubject:真实主题类
- ProxySubject:代理类
- Client:客户类
未完……