设计模式中的第三类是行为型模式,共11种,分别为:
策略、模板方法、观察者、责任链、 迭代子、备忘录、状态、命令、解释器、访问者、调停者
本篇介绍其前4种
策略、模板方法、观察者、责任链。
Strategy
策略模式
1 策略模式属于对象的行为模式。其用意是针对一组算法,将每一个算法封装到具有公共接口的独立类中,使得它们可以相互替换。策略模式可以在不影响客户端的情况下独立 地发生变化。在客户端代码中只需要做出策略的选择即可实现不同的算法,而无须做算法的实现。这样就可以将策略的选择逻辑与策略的实现逻辑分离开来,使二者可以独立 地变化。
2 策略模式中的角色用客户端(上下文、环境)角色、抽象策略角色、具体策略角色。
环境角色(Context):持有一个strategy类的引用
抽象策略角色(Strategy):通常用一个接口或抽象类表示,给出所有具体策略类需要实现的方法。
具体策略角色(ConcreteStrategy):继承/实现抽象策略角色,包装了算法或行为 。
3 java 语言内部的用到的策略模式 举例:
LayoutManager 布局管理器是抽象接口,抽象了awt容器中如何排列其GUI组件的方法。其具体的 排列行为/算法 由子类FlowLayout 、BorderLayout、CardLayout、
GridLayout、GridBagLayout 等去实现。
swing中的Border类也是同理、
在任何一个swing构件上都可以画上边框(Border),比如panel、button等。而swing库提供了很多的边框类型,包括bevel、line、titled、CompoundBorder等。
swing构件的基类是JComponent,JComponent类实现了paintBorder()方法,并持有一个私有的边框对象的引用。由于Border是一个接口而非实现类,因此这个引用可
以指向任何实现了Border接口的边框对象。
一个图书折扣的例子:
在一个图书的电子商城系统中,需要计算图书折扣后的价格,并在商品列表和展示页面显示其原价和折扣价。而整个商城中各类图书的折扣规则并不一致:有些图书没有折扣(不打折),有的图书提供一个百分比折扣。有的图书固定减少1元作为折扣 等等。由于折扣的算法五花八门,我们很难在一个折扣类中实现所有的折扣算法。这时就可以使用策略模式,将折扣类单独抽象出来(里面含有计算折扣的抽象方法),由具体的子折扣类去实现不同的折扣方法。
实现代码如下
CalcDiscount 计算折扣的基接口
package com.jelly.mypattern.strategy;
/**
* 计算折扣 基接口
* @author jelly
*
*/
public interface CalcDiscount {
/**
* 折扣 抽象方法
* @param price 原始价格
* @return 折扣后价格
*/
public abstract double discount(double price);
}
NoDiscouont 不打折的折扣类
package com.jelly.mypattern.strategy;
/**
* 无折扣
* @author jelly
*
*/
public class NoDiscount implements CalcDiscount {
/**
*
* 折扣价 与原价相等
*/
@Override
public double discount(double price) {
return price;
}
}
FixedDiscount 打固定折扣 1 package com.jelly.mypattern.strategy;
/**
* 固定折扣 1
* @author jelly
*
*/
public class FixedDiscount implements CalcDiscount{
@Override
public double discount(double price) {
return price-1;
}
}
PercentDiscount 按百分比(90%) 折扣
package com.jelly.mypattern.strategy;
public class PercentDiscount implements CalcDiscount{
@Override
public double discount(double price) {
return price *0.9;
}
}
测试代码
package com.jelly.mypattern.strategy;
/**
* 客户端 上下文
* @author jelly
*
*/
public class StrategyTest {
public static void main(String[] args) {
double goodsPrice=2000;
CalcDiscount calcDiscount=new PercentDiscount();
double discountPrice=calcDiscount.discount(goodsPrice);
System.out.println("商品原价为:"+goodsPrice);
System.out.println("商品折扣价为:"+discountPrice);
}
}
控制台输出
商品原价为:2000.0
商品折扣价为:1800.0
策略模式中抽象策略只有一个而具体策略类往往有多个,它们形成一个算法簇。算法簇中算法类并不包括个性化数据,仅仅是对算法的封装,因而是可以单例和享元的。
在实际设计中,我们可以结合使用策略模式和享元模式,使用享元工厂创建策略对象,让每个具体策略类在整个系统中都只有唯一的一个实例,被所有的客户端代码共享。
注:享元工厂往往也是为单例的或静态的,称为单例享元工厂和静态享元工厂。单例与静态的区别在于,单例类可以被继承以重写其方法,因而是便于扩展的,而静态方法因为无法被子类重写,故静态类不便于扩展。
下面是使用策略+享元工厂的实现。
package com.jelly.mypattern.strategy;
import java.util.HashMap;
import java.util.Map;
/**
* 折扣享元工厂 类
* @author jelly
*
*/
public class CalcDiscountFactory {
private static CalcDiscountFactory instance=new CalcDiscountFactory();
private static final String NO_DISCOUNT ="noDiscount";
private static final String FIXED_DISCOUNT="fixedDiscount";
private static final String PERCENT_DISCOUNT="percentDiscount";
private Map<String,CalcDiscount> calcDiscountMap=new HashMap<String,CalcDiscount>();
public static CalcDiscountFactory getInstance(){
return instance;
}
public synchronized CalcDiscount getNoDiscount(){
CalcDiscount calcDiscount= calcDiscountMap.get(NO_DISCOUNT);
if(calcDiscount==null){
calcDiscount=new NoDiscount();
calcDiscountMap.put(NO_DISCOUNT, calcDiscount);
}
return calcDiscount;
}
public synchronized CalcDiscount getFixedDiscount(){
CalcDiscount calcDiscount= calcDiscountMap.get(FIXED_DISCOUNT);
if(calcDiscount==null){
calcDiscount=new FixedDiscount();
calcDiscountMap.put(FIXED_DISCOUNT, calcDiscount);
}
return calcDiscount;
}
public synchronized CalcDiscount getPercentDiscount(){
CalcDiscount calcDiscount= calcDiscountMap.get(PERCENT_DISCOUNT);
if(calcDiscount==null){
calcDiscount=new PercentDiscount();
calcDiscountMap.put(PERCENT_DISCOUNT, calcDiscount);
}
return calcDiscount;
}
}
测试代码
public class StrategyTest {
/*public static void main(String[] args) {
double goodsPrice=2000;
CalcDiscount calcDiscount=new PercentDiscount();
double discountPrice=calcDiscount.discount(goodsPrice);
System.out.println("商品原价为:"+goodsPrice);
System.out.println("商品折扣价为:"+discountPrice);
}*/
public static void main(String[] args) {
double goodsPrice=2000;
CalcDiscountFactory factory= CalcDiscountFactory.getInstance();
double discountPrice=factory.getFixedDiscount().discount(goodsPrice);
System.out.println("商品原价为:"+goodsPrice);
System.out.println("商品折扣价为:"+discountPrice);
}
}
控制台输出 :
商品原价为:2000.0
商品折扣价为:1999.0
TemplateMethod
模板方法模式
1 模板方法模式是类的行为模式,它是基于类的继承来实现代码复用的基本技术。 模板方法抽象需要设计师给出一个算法的轮廓或骨架(也叫顶层逻辑)。
另一些设计师给出这个算法就具体实现逻辑步骤。这些具体的逻辑步骤方法称为基本方法,而将这些基本方法汇总起来的方法叫做模板方法。
2 模板方法通常采用类继承来实现。此模式中包含以下角色:
抽象模板角色(Abstract Template):
(1) 定义了一个或多个抽象操作方法,以便让子类去实现。这些操作称为基本操作,它们是一个顶层逻辑的组成步骤。
(2) 定义并实现类一个模板方法(顶层逻辑),这个模板方法一般是具体的,它是一个顶层逻辑或算法骨架,而具体的实现步骤在其定义的抽象方法中,
推迟到子类中去实现。顶层逻辑也可以调用一些具体的方法。
具体模板角色(Contrete Template)
(1) 实现父类中所定义的一个或多个抽象方法,它们是顶层逻辑的组成步骤。
(2) 每一个抽象模板橘色都可以有多个具体模板角色与之对应,而每个具体模板角色可以给出父类方法的不同实现,从而使得顶层逻辑的实现各不相同。
示例java代码:
AbstractTemplate 模板方法基类
package com.jelly.mypattern.templateMethod;
/**
* 抽象模板 基类
* @author jelly
*
*/
public abstract class AbstractTemplate {
/**
* 模板方法 定义顶层逻辑。
* 这个操作方法虽然是具体方法
* 但其中operation2 operation3 operation4 都是
* 抽象的,需要推迟到子类中完成。
*/
public void operation(){
operation1();
int a=10;int b=20;
if(a<b){
operation2();
}
operation3();
doHookMethod();
}
public void operation1(){
System.out.println("aaaaa");
}
public abstract void operation2();
public abstract void operation3();
public abstract void operation4();
/**
* 钩子方法 一般以do开头,在java中是一种标准的做法。
* 在模板方法模式中,钩子方法一般是具体的,且提供空实现。这样可选择性地让子类去重写。
* 在子类中,你可以选择重写,或者保持默认(使用父类中的钩子方法)均可。
*/
protected void doHookMethod(){
}
}
TemplateC 是具体的 ,实现了父类中所有抽象方法
package com.jelly.mypattern.templateMethod;
/**
* TemplateC 继承AbstractTemplate
* 并实现了父类中所有抽象方法,所以TemplateC 直接就是具体类,
* 可以被实例化。
* @author jelly
*
*/
public class TemplateC extends AbstractTemplate {
@Override
public void operation2() {
System.out.println("2222");
}
@Override
public void operation3() {
System.out.println("33333");
}
@Override
public void operation4() {
System.out.println("44444");
}
}
TemplateA 抽象类
package com.jelly.mypattern.templateMethod;
/**
* 抽象模板方法的实现类,
* 由于只实现了operation2 operation3 方法,operation4 还不知道如何实现
* 故此类也是抽象类,其operation4 方法等待自己的子类去实现。
* @author jelly
*
*/
public abstract class TemplateA extends AbstractTemplate{
@Override
public void operation2() {
System.out.println("执行 operation2...");
}
@Override
public void operation3() {
System.out.println("执行 operation3...");
}
public abstract void operation4() ;
@Override
protected void doHookMethod() {
System.out.println("do someThing... ");
}
}
TemplateB 具体类
package com.jelly.mypattern.templateMethod;
/**
* TemplateB 继承TemplateA 实现了父类的operation4 抽象方法
* 并选择性地重写了父类中doHookMethod 抽象方法。
* 这样TemplateB才成为一个具体的类
* @author jelly
*
*/
public class TemplateB extends TemplateA {
@Override
public void operation4() {
System.out.println("xxxxxx");
}
@Override
protected void doHookMethod() {
super.doHookMethod();
System.out.println("do something2...");
}
}
测试代码
package com.jelly.mypattern.templateMethod;
public class TemplateMethodTest {
public static void main(String[] args) {
AbstractTemplate temMethod=new TemplateC();
temMethod.operation();
System.out.println("------------");
AbstractTemplate temMethod2=new TemplateB();
temMethod2.operation();
}
}
控制台输出
aaaaa
2222
33333
------------
aaaaa
执行 operation2...
执行 operation3...
do someThing...
do something2...
3 需要指出的是继承不应该被滥用。
很多面向对象设计专家从1986年就开始警告继承关系被滥用的可能。有一些面向对象的编程语言,如self,甚至将类的继承关系从语言功能中取消掉,改为完全地使用委派。
其他设计师虽然不提倡彻底取消继承,但无一例外地在鼓励设计中尽可能多的使用委派关系代替继承关系。
GOF95 书中状态、策略、装饰、桥梁、抽象工厂模式均是将依赖继承的实现转换为基于对象的组合和聚合的实现,这些模式的要点就是使用委派关系代替继承关系。
不应被滥用并不表示根本不该使用,模板方式模式正是在恰当地鼓励使用继承。此模式可以用来改写一些拥有相同功能的相关类,将可复用的一般性行为代码移动到基类中,
而将特殊化的行为代码移到子类中。
4 模板方法模式在java web开发中的应用。
javax.servlet.Servlet 是一个基接口,提供了init()、destory()和service()三个决定servlet声明周期的方法。GenericServlet是一个抽象类,
它提供了Servlet接口的默认实现,却留下了一个service方法,这个方法仍然是抽象的,需要在子类中提供实现。我们熟知的HttpServlet类是继承自GenericServlet,
虽然它本身也是抽象的,但是它的所有行为包括sevice方法和7个do方法都提供了默认实现。HttpServlet中的servie方法就是一个模板方法,这个方法中会调用7个do方法
(也称为钩子方法)中的一个或多个,完成对客户端的响应。这些do方法的实现显然也需要由HttpServlet的具体子类中提供,这就是典型的模板方法模式。
Observer
观察者模式
1 观察者模式是对象的行为模式,也叫发布-订阅模式,源-监听模式。 观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。
当这个主题对象的状态发生变化时,会自动通知所有的观察者对象, 使他它们能够同步变化。
2 观察者模式中的角色
主题角色(Subject):
主题角色把所有观察者对象的引用保存在一个聚集(集合)中,每个主题都可以有多个观察者对象。 抽象主题提供一个接口,可以维护观察者的聚集(添加/删除等)。主题角色又叫做被观察者角色
(Observable),一般用一个抽象类或接口实现
抽象观察者(Observer)角色:
为所有的具体观察者定义一个接口,在得到主题状态变更通知时更新自己。这个接口叫做变更接口。抽象观察者角色一般用一个抽象类或接口来实现。
具体主题角色(Concrete Subject): 实现抽象主题接口。
具体观察者角色(Concrete Observer):实现观察者接口。
java示例代码:
Subject 抽象主题
package com.jelly.mypattern.observer;
import java.util.ArrayList;
/**
* 抽象主题角色
* 定义管理观察者聚集的方法
*
* @author jelly
*
*/
public abstract class Subject {
/**
* 维护 观察者聚集 集合
*/
private ArrayList<Observer> observerList=new ArrayList<Observer>();
/**
* 添加 注册观察者
* @param observer
*/
public void attach(Observer observer) {
if(observer==null){
throw new NullPointerException();
}
if(!observerList.contains(observer)){
observerList.add(observer);
}
}
/**
* 删除 取消注册观察者
* @param observer
*/
public void detach(Observer observer) {
observerList.remove(observer);
}
/**
* 主题状态发生变更,通知所有已注册的观察者
*/
public void notifyObservers() {
for (Observer ob :observerList){
ob.update();
}
}
/**
* 查询 返回主题中已注册的观察者,
* @return
*/
@SuppressWarnings("unchecked")
public ArrayList<Observer> observers(){
return (ArrayList<Observer>) observerList.clone();
}
}
具体主题角色 ConcreteSubject
package com.jelly.mypattern.observer;
/**
* 具体主题角色
* @author jelly
*
*/
public class ConcreteSubject extends Subject{
private String state;
public ConcreteSubject() {
}
public ConcreteSubject(String state) {
this.state=state;
}
public void change(String newState){
state=newState;
this.notifyObservers();
}
}
观察者角色 Observer 接口
package com.jelly.mypattern.observer;
/**
* 观察者角色
* @author jelly
*
*/
public interface Observer {
/**
* 接收到主题的变更通知,更新自身
*/
public void update();
}
具体观察者角色 ConcreteObserver
package com.jelly.mypattern.observer;
/**
* 具体观察者角色
* @author jelly
*
*/
public class ConcreteObserver implements Observer{
@Override
public void update() {
System.out.println("观察者观察到主题状态发生了变更,开始更新自身...");
}
}
测试代码
package com.jelly.mypattern.observer;
/**
* 测试 代码
* @author jelly
*
*/
public class ObserverTest {
public static void main(String[] args) {
ConcreteSubject subject=new ConcreteSubject();
Observer ob1= new ConcreteObserver();
Observer ob2= new ConcreteObserver();
subject.attach(ob1);
subject.attach(ob2);
subject.change("b");
}
}
控制台输出
观察者观察到主题状态发生了变更,开始更新自身...
观察者观察到主题状态发生了变更,开始更新自身...
3 java语言中使用观察者模式
JDK1.0中提供了 java.util.Observer接口 和java.util.Observable 类 分别表示观察者角色和主题角色。
Observer 接口中定义了唯一的一个方法update(Observable o,Object arg)
下面是sun JDK 中的观察者模式 实现代码
public interface Observer {
void update(Observable o, Object arg);
}
public class Observalbe{
private boolean changed=false;
private Vector obs;
public Observable(){
obs=new Vector();
}
public synchronized void addObserver( Observer o){
if (o==null){
throw new NullPointerException();
}
if(!obs.contains(o)){
obs.addElement(o);
}
}
public synchronized void deleteObserver(Observer o) {
obs.removeElement(o);
}
public void notifyObservers() {
notifyObservers(null);
}
public void notifyObservers(Object arg) {
Object[] arrLocal;
synchronized (this) {
if (!changed)
return;
arrLocal = obs.toArray();
clearChanged();
}
for (int i = arrLocal.length-1; i>=0; i--)
((Observer)arrLocal[i]).update(this, arg);
}
public synchronized void deleteObservers() {
obs.removeAllElements();
}
protected synchronized void setChanged() {
changed = true;
}
protected synchronized void clearChanged() {
changed = false;
}
public synchronized boolean hasChanged() {
return changed;
}
public synchronized int countObservers() {
return obs.size();
}
}
4 观察者模式还有很多应用场合 。swing 中事件委托 ,awt事件监听都使用到了观察者模式。
Chain Of Responsibility
责任链/职责链模式
1 责任链是一种对象的行为模式,在责任链中,每个对象都含有一个下家对象的引用,链中的最后一环的下家对象为null。请求在这个对象链中传递,直到有一个对象决定处理这个请求,
发出请求的客户端并不知道链中的哪个对象处理了这个请求 。
2 责任链中的角色
处理者(Handler) : 定义处理请求的接口,接受客户的请求。
具体处理者(Concrete Handler):接受的请求后,可以选择处理掉或将请求传递给下家。由于具体处理者持有一个下家的引用,因此如果需要,具体处理者可以访问下家。
3 纯与非纯的责任链。
一个纯的责任链模式要求一个具体的处理者对象要么处理请求,要么将责任推给下家。不允许出现某个处理对象在承担了部分职责后有把责任推传个下方。
显然这样的模式在现实中并不常见,常见的情况是链中的处理对象对这个请求承担部分职责,然后将请求传给下方,整个链中每对象都可以对请求承担部分职责,
所有对象加起来就完成了客户端请求的处理。 显然非纯的责任链模式在现实开发与应用更为常见。
在java web开发中,使用非纯责任链模式的例子实在太多:
我们熟知的servlet Filter过滤器, struct2 拦截器、 springMVC 拦截器、springMVC url映射转发器 、hibernate 拦截器、mybatis拦截器、spring aop切面、
spring 事务管理器......无论是过滤器、拦截器、切面还是事务管理器 都用到了责任链模式,它们都是责任链条中一部分,对请求承担着部分职责(或者说参与了请求的部分业务 处理),并且它们都可以配置多个形成一个链条。
就拿struts2来说, 在struts2中可以针对某个/某些action配置多个拦截器(struts2自身就是基于拦截器设计的),在到达action之前,request请求会经过一层层的拦截器处理。
这些拦截器就构成了一个非纯的责任链。
下面是java示例代码:
Request 封装客户端请求
package com.jelly.mypattern.chainOfResponsibility;
/**
* 请求对象 封装客户端请求
* @author jelly
*
*/
public class Request {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Request() {
super();
}
public Request(String name) {
super();
this.name = name;
}
}
Handler 链中的处理器
package com.jelly.mypattern.chainOfResponsibility;
/**
* 处理器, 处理链中的处理者
* @author jelly
*
*/
public class Handler {
//下一个处理者
private Handler nextHandler;
private String handlerName;
public Handler() {
}
public Handler(String hName) {
this.handlerName=hName;
}
public Handler getNextHandler() {
return nextHandler;
}
public void setNextHandler(Handler nextHandler) {
this.nextHandler = nextHandler;
}
public void handleRequest(Request request){
String name=request.getName();
System.out.println(handlerName+"处理请求:"+name);
if(nextHandler!=null){
System.out.println(handlerName+"将请求 "+name+" 传给下家...");
nextHandler.handleRequest(request);
}
}
}
客户端测试代码
package com.jelly.mypattern.chainOfResponsibility;
public class HandlerTest {
public static void main(String[] args) {
Request request=new Request("查询账户信息");
Handler handler1=new Handler("handler1");
Handler handler2=new Handler("handler2");
Handler handler3=new Handler("handler3");
Handler handler4=new Handler("handler4");
handler1.setNextHandler(handler2);
handler2.setNextHandler(handler3);
handler3.setNextHandler(handler4);
handler1.handleRequest(request);//处理请求
}
}
控制台输出
handler1处理请求:查询账户信息
handler1将请求 查询账户信息 传给下家...
handler2处理请求:查询账户信息
handler2将请求 查询账户信息 传给下家...
handler3处理请求:查询账户信息
handler3将请求 查询账户信息 传给下家...
handler4处理请求:查询账户信息
4 更典型的一个例子
JAVA Web开发中,开发 javax.servlet.Filter(接口) 过滤器 。
package com.jelly.mypattern.chainOfResponsibility;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyFilter implements Filter {
@Override
public void destroy() {
}
/**
* 拦截对servlet的请求
* 在执行servlet方法前, 打印访问者ip 和访问的页面。
*
*/
@Override
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request=(HttpServletRequest) req;
HttpServletResponse response=(HttpServletResponse)res;
String ip= request.getRemoteAddr();
String contextPath= request.getContextPath();
String uri= request.getRequestURI();
String requestPage=uri.replaceFirst(contextPath, "");//请求的页面
System.out.println(ip+" 请求页面:"+requestPage);
chain.doFilter(request, response);//将请求向后传递,进入链中下一个对象
}
@Override
public void init(FilterConfig arg0) throws ServletException {
}
}
。。。。。。