设计模式精炼(五): 行为型01
迭代模式(Cursor)
可以顺序地访问一个聚集中的元素而不暴露聚集的内部表象(internal representation)。
|- Java中的集合框架很多都使用的迭代模式的聚集,如Vector、ArrayList、HashSet、HashMap、Hashtable等。
例:
/**
具体聚集角色类:
实现了抽象聚集角色类所要求的接口,也就是createIterator()方法。此外,还有方法getElement()向外界提供聚集元素,而方法size()向外界提供聚集的大小等。
*/
/**
抽象迭代子角色类
*/
/**
具体迭代子角色
*/
//客户端
观察者模式
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
例:
/**
抽象主题角色类:把所有对观察者对象的引用保存在一个聚集(比如ArrayList对象)里,每个主题都可以有任何数量的观察者。
抽象主题提供一个接口,可以增加和删除观察者对象,抽象主题角色又叫做抽象被观察者(Observable)角色。
*/
/**
具体角色类:将有关状态存入具体观察者对象;
在具体主题的内部状态改变时,给所有登记过的观察者发出通知。具体主题角色又叫做具体被观察者(Concrete Observable)角色。
*/
/**
抽象观察者角色类:为所有的具体观察者定义一个接口,在得到主题的通知时更新自己,这个接口叫做更新接口。
*/
/**
具体观察者角色类:存储与主题的状态自恰的状态。具体观察者角色实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态协调。
如果需要,具体观察者角色可以保持一个指向具体主题对象的引用。
*/
//客户端
运行结果如下:
Attached an observer
主题状态为:new state
状态为:new state
策略模式
针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。
|- 对算法的包装。
|- 简而言之:准备一组算法,并将每一个算法封装起来,使得它们可以互换。
例(思路模板):
例(具体事例:图书打折问题,初级会员没有折扣,中级会员9折,高级会员8折):
运行结果如下:
高级会员8折
图书最终价格为:240.0
模板方法模式
准备一个抽象类,将部分逻辑以具体方法以及具体构造函数的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。
|- 不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现。
|- 模板方法在Servlet中的应用:使用过Servlet的人都清楚,除了要在web.xml做相应的配置外,还需继承一个叫HttpServlet的抽象类。
|- ttpService类提供了一个service()方法,
|- 这个方法调用七个do方法中的一个或几个,完成对客户端调用的响应。
|- 这些do方法需要由HttpServlet的具体子类提供。
因此这是典型的模板方法模式。
|- 关键:子类可以置换掉父类的可变部分,但是子类却不可以改变模板方法所代表的顶级逻辑。
例:
public abstract class AbstractTemplate {
/**
* 模板方法
*/
public void templateMethod(){
abstractMethod();
hookMethod();
concreteMethod();
}
//基本方法(由子类实现)
protected abstract void abstractMethod();
//基本方法(空方法)
protected void hookMethod(){}
//基本方法(已经实现)
protected final void concreteMethod(){
//业务相关代码
}
}
public class ConcreteTemplate extends AbstractTemplate {
//基本方法实现
@Override
protected void abstractMethod() {
//业务相关代码
}
//重写父类方法
@Override
protected void hookMethod() {
//业务相关代码
}
}
访问者模式
封装一些施加于某种数据结构元素之上的操作。
|- 分派的概念:根据对象的类型而对方法进行的选择。
静态分派
发生在编译时期,分派根据静态类型信息发生。静态分派对于我们来说并不陌生,方法重载就是静态分派。
例(墨子骑马的故事作为例子,墨子可以骑白马或者黑马):
运行结果如下:
骑马
骑马
注:但是两次对ride()方法的调用传入的是不同的参数,也就是wh和bh。它们虽然具有不同的真实类型,但是它们的静态类型都是一样的,均是Horse类型。
|- 由此可见:重载方法的分派是根据静态类型进行的,这个分派过程在编译时期就完成了。
动态分派
发生在运行时期,动态分派动态地置换掉某个方法。
例:
运行结果如下:
黑马吃草
因此,对比可知:问题的核心就是Java编译器在编译时期并不总是知道哪些代码会被执行。
|- 编译器仅仅知道对象的静态类型,而不知道对象的真实类型。
|- 方法的调用则是根据对象的真实类型,而不是静态类型。
这样一来,上面最后一行的eat()方法调用的是BlackHorse类的eat()方法,打印的是“黑马吃草”。
本文转载自微信公众号【java学习之道】。
- 点赞
- 收藏
- 关注作者
评论(0)