装饰器模式
定义 :动态地给一个对象添加一些额外的职责。就增加功能来说,Decorator模式相比生成子类更为灵活。
解决问题 :一个对象需要经常动态增加属性或职责
适用性 :
- 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
- 处理那些可以撤消的职责。
- 当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。
为什么使用Decorator ?
我们通常可以使用继承来实现功能的拓展,如果这些需要拓展的功能的种类很繁多,那么势必生成很多子类,增加系统的复杂性,同时,使用继承实现功能拓展,我们必须可预见这些拓展功能,这些功能是编译时就确定了,是静态的.
使用Decorator的理由是:这些功能需要由用户动态决定加入的方式和时机.Decorator提供了"即插即用"的方法,在运行期间决定何时增加何种功能.
如何使用 ?
举Adapter中的打桩示例,在Adapter中有两种类:方形桩 圆形桩,Adapter模式展示如何综合使用这两个类,在Decorator模式中,我们是要在打桩时增加一些额外功能,比如,挖坑 在桩上钉木板等,不关心如何使用两个不相关的类.
我们先建立一个接口:
public interface Work } |
接口Work有一个具体实现:插入方形桩或圆形桩,这两个区别对Decorator是无所谓.我们以插入方形桩为例:
public class SquarePeg implements Work{ public void insert(){ System.out.println("方形桩插入"); } } |
现在有一个应用:需要在桩打入前,挖坑,在打入后,在桩上钉木板,这些额外的功能是动态,可能随意增加调整修改,比如,可能又需要在打桩之后钉架子(只是比喻).
那么我们使用Decorator模式,这里方形桩SquarePeg是decoratee(被刷油漆者),我们需要在decoratee上刷些"油漆",这些油漆就是那些额外的功能.
public class Decorator implements Work{ private Work work; //在构造器中使用组合new方式,引入Work对象; others.add("钉木板"); public void insert(){ newMethod();}
public void otherMethod() } |
在上例中,我们把挖坑和钉木板都排在了打桩insert前面,这里只是举例说明额外功能次序可以任意安排.
好了,Decorator模式出来了,我们看如何调用:
Work squarePeg = new SquarePeg();
Work decorator = new Decorator(squarePeg);
decorator.insert();
Decorator模式至此完成.
如果你细心,会发现,上面调用类似我们读取文件时的调用:
FileReader fr = new FileReader(filename);
BufferedReader br = new BufferedReader(fr);
实际上Java 的I/O API就是使用Decorator实现的 ,I/O变种很多,如果都采取继承方法,将会产生很多子类,显然相当繁琐.
观察者模式
定义: 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时, 所有依赖于它的对象都得到通知并被自动更新。
解决问题 :解决多个对象间相互依赖关系的相互通知。
常用地方 :一些数据有多个视图的表示,譬如Java中自带的图形事件应用。
意图 :
- 当一个抽象模型有两个方面, 其中一个方面依赖于另一方面。将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。
- 当对一个对象的改变需要同时改变其它对象, 而不知道具体有多少对象有待改变。
- 当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换言之, 你不希望这些对象是紧密耦合的。
虽然网上商店形式多样,每个站点有自己的特色,但也有其一般的共性,单就"商品的变化,以便及时通知订户"这一点,是很多网上商店共有的模式,这一模式类似Observer patern观察者模式.
具 体的说,如果网上商店中商品在名称 价格等方面有变化,如果系统能自动通知会员,将是网上商店区别传统商店的一大特色.这就需要在商品product中加入Observer这样角色,以便 product细节发生变化时,Observer能自动观察到这种变化,并能进行及时的update或notify动作.
Java的API还为为我们提供现成的Observer接口Java.util.Observer.我们只要直接使用它就可以.
我们必须extends Java.util.Observer才能真正使用它:
1.提供Add/Delete observer的方法;
2.提供通知(notisfy) 所有observer的方法;
//产品类 可供Jsp直接使用UseBean调用 该类主要执行产品数据库插入 更新 public class product extends Observable{ private String name; public String getName(){ return name;} } public float getPrice(){ return price;} } } |
我们注意到,在product类中 的setXXX方法中,我们设置了 notify(通知)方法, 当Jsp表单调用setXXX(如何调用见我的另外一篇文章 ),实际上就触发了notisfyObservers方法,这将通知相应观察者应该采取行动了.
下面看看这些观察者的代码,他们究竟采取了什么行动:
//观察者NameObserver主要用来对产品名称(name)进行观察的 private String name=null; public void update(Observable obj,Object arg){ name=(String)arg; } } } //观察者PriceObserver主要用来对产品价格(price)进行观察的 private float price=0; public void update(Observable obj,Object arg){ price=((Float)arg).floatValue(); } } } |
Jsp中我们可以来正式执行这段观察者程序:
<jsp:useBean id="product" scope="session" class="Product" /> <jsp:useBean id="nameobs" scope="session" class="NameObserver" /> <jsp:useBean id="priceobs" scope="session" class="PriceObserver" /> <% }else{ %> //request.getRequestURI()是产生本jsp的程序名,就是自己调用自己 <input type=hidden name="save" value="1"> </form> <% %>
|
执行改Jsp程序,会出现一个表单录入界面, 需要输入产品名称 产品价格, 点按Submit后,还是执行该jsp的
if (request.getParameter("save")!=null)之间的代码.
由于这里使用了数据javabeans的自动赋值概念,实际程序自动执行了setName setPrice语句.你会在服务器控制台中发现下面信息::
NameObserver :name changet to ?????(Jsp表单中输入的产品名称)
PriceObserver :price changet to ???(Jsp表单中输入的产品价格);
这说明观察者已经在行动了.!!
同时你会在执行jsp的浏览器端得到信息:
产品数据变动 保存! 并已经自动通知客户
上文由于使用jsp概念,隐含很多自动动作,现将调用观察者的Java代码写如下:
public class Test { public static void main(String args[]){ Product product=new Product(); //加入观察者 product.setName("橘子红了"); } }
|
你会在发现下面信息::
NameObserver :name changet to 橘子红了
PriceObserver :price changet to 9.22
这说明观察者在行动了.!!
模板模式:
Template模板模式
定义: 定义一个操作中算法的骨架,将一些步骤的执行延迟到其子类中.
解决问题 :重要是解决子类之间代码或者是流程的重复问题 。
常用地方 :DAO模式里面的模板类,Spring里面的常用模板,包括JdbcTemplate等等
使用Java的抽象类时,就经常会使用到Template模式,因此Template模式使用很普遍.而且很容易理解和使用。
public abstract class Benchmark { /** * 下面操作是我们希望在子类中完成 */ public abstract void benchmark(); /** for (int i = 0; i < count; i++) |
在上例中,我们希望重复执行benchmark()操作,但是对benchmark()的具体内容没有说明,而是延迟到其子类中描述:
public class MethodBenchmark extends Benchmark { /** * 真正定义benchmark内容 */ public void benchmark() { for (int i = 0; i < Integer.MAX_VALUE; i++){ System.out.printtln("i="+i); } } } |
至此,Template模式已经完成,是不是很简单?
我们称repeat方法为模板方法, 它其中的benchmark()实现被延迟到子类MethodBenchmark中实现了,
看看如何使用:
Benchmark operation = new MethodBenchmark();
long duration = operation.repeat(Integer.parseInt(args[0].trim()));
System.out.println("The operation took " + duration + " milliseconds");
也许你以前还疑惑抽象类有什么用,现在你应该彻底明白了吧? 至于这样做的好处,很显然啊,扩展性强,以后Benchmark内容变化,我只要再做一个继承子类就可以,不必修改其他应用代码.