`
kakaluyi
  • 浏览: 438269 次
  • 性别: Icon_minigender_1
  • 来自: 苏州
社区版块
存档分类
最新评论

主题: Design Pattern 个人读书笔记(转)

阅读更多

    Design  Pattern  个人读书笔记     (2006-12)
要谈Design Pattern就得从复用说起。我们每个人不管是使用经验,公式,工具大都从使用别人已经设计,发现了的东西开始的,而这便是复用的在我们生活中的具体表现。在软件开发中Design Pattern是为便于将来复用(直接或稍作改变使用)前人的成功设计而被提炼出的再经验(从经验中发现共有的部分后进行分类定义)。
Design Pattern这个名词最早是从Christopher Alexander的建筑学名著《A Pattern Language》中引入的。正如Christopher Alexander所说:“每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的解决方案的核心。这样,你就能一次又一次地使用该方案而不必做重复劳动”。正是基于这样的目的GOF在其书中便是以面向对象设计为背景总结了23个Design Pattern。其各模式组织模板为:模式名和分类,意图,别名,动机,适用性,结构,参与者,协作,效果,实现,代码示例,已知应用,相关模式。
我是从GOF的书开始看的。因为其是以面向对象设计为背景的。故很有必要对面向对象中几个基本概念作一说明。
类:定义了一类活动对象的行为,属性的模板。
对象:封装了状态和行为的静态实体。
接口:仅定义需具体实现的规则集合,以便外部直接使用而屏蔽其具体实现部分从而最终实现了使用与实现的分离。
抽象类:为其子类定义公共接口,而将它的部分或全部的实现延迟到子类。不可实例化。
动态绑定:对象发送的请求所引起的具体操作既与请求本身有关又与接受对象有关,从而支持相同请求的不同对象可能对请求激发的操作有不同的实现。发送给对象的请求和它的相应操作直到运行时才连接起来。
多态:允许在运行时刻彼此替换有相同接口的对象,这种可替换性称为多态性。
继承(范化):类之间为共享部分操作而实现的一种“is a kind of”关系。
组合:类之间为共享部分操作而实现的一种“use a/ has a”关系。(可具体分:关联/聚集“has a”,依赖“use a”)。
在正式进入主题前,还有必要提几条使用面向对象设计的重要原则:
① 针对接口编程,而不是针对实现编程。(尽量使用接口而非实际对象)
② 优先使用对象组合,而非类继承。(依赖于对象组合的设计有更好的复用性)
③ 接口优先于抽象类。(接口使我们可以构造出非层次结构的类型框架)
④ 尽量做到高内聚,低耦合。
⑤ 设计应支持变化,而我常常会遇到类似如下的问题而不得不重新设计。
1. 通过显式地指定一个类来创建对象。
2. 对特殊操作的依赖。
3. 对硬件和软件平台依赖。
4. 对对象表示或实现依赖。
5. 算法依赖。
6. 紧耦合。
7. 通过生成子类来扩充功能。
8. 不能方便的对类进行修改。
现在已经出现了许多应用设计模式的工具(Eclipse,等 ),语言(Smalltalk, Java, C++STL, Python, Ruby等),框架(Structs, Spring, MFC等 )。而即使是C这样的结构化语言,其中的函数指针,变参函数,结构体,宏定义等,某种程度上也有设计模式的影子。

  下面我具体针对几个非常常见的设计模式使用实例进行探讨。
模式的基本概念:即增加一个抽象层。无论什么时候,当你想把某些东西抽象出来的时候,实际上你是在分离特定的细节,这么做的一个有说服力的动机就是把变化的东西从那些不变的东西里分离出来。 
    比喻:就是具体事情做得越多,越容易范错误.这每个做过具体工作的人都深有体会,相反,官做得越高,说出的话越抽象越笼统,犯错误可能性就越少。

抽象工厂 (Abstract Factory):

图略

在GOF书中是由支持多种视感(Look-and-feel)标准的用户界面工具包引入的。


以上设计可使得通过往Player上增加游戏角色,往Obstacle上增加角色行为。只需再实现Player,Obstacle的新成员而不需要改变原来其他不相关代码。也可在GameElementFactory下任意组合新的游戏角色,角色行为组合成新的游戏类型。另外,即使GameEnviroment出问题换一个新的只要实现了相应接口仍可在原系统中使用。
抽象工厂最大的优势便在于可用于系列产品开发。

工厂方法 (Factory Method):

图略


另外,有必要介绍一下Java中的一种动态创建对象方法-Reflect机制:
软件包 java.lang.reflect 的描述(获取关于类和对象的反射信息。在安全限制内,反射允许编程访问关于加载类的字段、方法和构造方法的信息,并允许使用反射字段、方法和构造方法对对象上的基本对等项进行操作。)
Class c = Class.forName(className);  //由类名创建Class对象
Method[] m=c.getMethods();  //由Class对象获得类中的方法
//可使用Method中的invoke来调用具体方法。
// public Object invoke(Object obj,Object... args)Throws IllegalAccessException,
IllegalArgumentException,InvocationTargetException
// obj - 从中调用基础方法的对象 , args - 用于方法调用的参数 
Constructor[] ctor=c.getConstructors();  //由Class对象获得类中的构造函数
factory = (ForumFactory)c.newInstance();  //由Class对象实际创建特型对象
实例如下:(以Jive 的ForumFactory 为例)
public abstract class ForumFactory {
private static Object initLock = new Object();
private static String className ="com.jivesoftware.forum.database.DbForumFactory";
private static ForumFactory factory = null;
public static ForumFactory getInstance(Authorization authorization) {
//If no valid authorization passed in, return null.
if (authorization == null) {   return null;  }
//以下使用了Singleton 单态模式
if (factory == null) {
synchronized(initLock) {
if (factory == null) {
......
try {//动态转载类  
Class c = Class.forName(className);  factory = (ForumFactory)c.newInstance();
}catch (Exception e) {   return null;   }  }  }   }
//Now, 返回 proxy.用来限制授权对forum 的访问
return new ForumFactoryProxy(authorization, factory,factory.getPermissions(authorization));
}
//真正创建forum 的方法由继承forumfactory 的子类去完成.
public abstract Forum createForum(String name, String description)
                   throws UnauthorizedException, ForumAlreadyExistsException;
....
}

单件(Singleton) 模式
Singleton 模式通常有几种形式:
public class Singleton {
private static Singleton _instance = new Singleton();
private Singleton() {  ...  }
public static Singleton getInstance() {   return _instance;  }
}
调用方法:Singleton.getInstance();
第二种形式:
public class Singleton {
private static Singleton _instance = null;
private Singleton() {  ...  }
public static Singleton getInstance() {
if (_instance==null)   _instance=new Singleton()
return _instance;
}
}
调用方法:Singleton.getInstance();
单件模式一般在框架的系统级常常有使用:
使用一个单件注册表(registry of singleton)。可能的Singleton类的集合不是由Instance定义的,Singleton类可以根据名字在一个众所周知的注册表中注册它们的单件实例。
这个注册表在字符串名字和单件之间建立映射。当Instance需要一个单件时,它参考注册表,根据名字请求单件。注册表查询相应的单件(如果存在的话)并返回它。
这个方法使得Instance不再需要知道所有可能的Singleton类或实例。它所需要的只是所有Singleton类的一个公共的接口,该接口包括了对注册表的操作:

代理(Proxy)模式 
是比较有用途的一种模式,而且变种较多,应用场合覆盖从小结构到整个系统的大结构。
Proxy 应用范围很广,现在流行的分布计算方式RMI 和Corba 等都是Proxy 模式的应用.
为什么要使用Proxy?
1. 授权机制 不同级别的用户对同一对象拥有不同的访问权利。
2. 某个客户端不能直接操作到某个对象,但又必须和那个对象有所互动。
两种方式:

图略

在Java1.3之后甚至引入了动态代理机制:
import java.lang.reflect.*; //反射包必须包括以支持RTTI
public class DynamicProxyDemo { 
public static void main(String[] clargs) { 
Foo prox = (Foo)Proxy.newProxyInstance( Foo.class.getClassLoader(), 
new Class[]{ Foo.class }, new InvocationHandler() { 
public Object invoke( Object proxy, Method method, Object[] args) {  
System.out.println( "InvocationHandler called:" + "\n\tMethod = " + method); 
if (args != null) {  System.out.println("\targs = "); 
for (int i = 0; i < args.length; i++) 
System.out.println("\t\t" + args[i]); 

return null; 
} }); 
prox.f("hello");   prox.g(47);   prox.h(47, "hello"); 
} }
 Public class Foo{   Void f(String h){  System.out.println(“”+h);  } Void g(int m){System.out.println(“”+m);}
Void h(int k, Stringo){ System.out.println(“”+k+o);}}
以上示例中DynamicProxyDemo便作了类Foo的代理,且动态的实现了代理其功能。


Adapter(适配器)模式

图略


1. 可实现提供的接口,但是如果我们没有源代码,或者,我们不愿意为了一个应用而修改各自的接口,可使用适配器模式。
最典型的莫过于Java.awt中Event包,例如当我们要实现一个Window退出功能,就必须要实现WindowListener接口中的所有方法,否则不可实例化,无效。于是包中便又提供了另外适配器类WindowAdapter,而只要实现其中的windowClosing方法便可以了。实际上在适配器类WindowAdapter中也是实现WindowListener接口,只不过其余的方法全部被实现成空方法,仅留下windowClosing方法供子类来实现。
2. 适配器(Adaper)接受一种类型,并为其它类型产生一个接口。 当你手头有某个类,而你需要的却是另外一个类,你可以通过Adapter来解决问题。唯一需要做的就是产生出你需要的那个类,有多种方法可以完成这种配接。
示例如下:
class WhatIHave{  public void g() {}  public void h() {}  }
interface WhatIWant{  void f();  }
class SurrogateAdapter implements WhatIWant{
   WhatIHave whatIHave;
   public SurrogateAdapter(WhatIHave wih){  whatIHave = wih; }
   public void f(){   whatIHave.g();   whatIHave.h();  }
}
class WhatIUse{// Approach 1:通过传递的接口进行:
    public void op(WhatIWant wiw){  wiw.f();  }
}
class WhatIUse2 extends WhatIUse{// Approach 2: build adapter use into op():
   public void op(WhatIHave wih){ new SurrogateAdapter(wih).f(); }
}
class WhatIHave2 extends WhatIHave implements WhatIWant{// Approach 3   
public void f(){  g();   h(); }
}
class WhatIHave3 extends WhatIHave{ // Approach 4: use an inner class:
 private class InnerAdapter implements WhatIWant 
{  public void f(){ g();  h();  }  }
public WhatIWant whatIWant(){  return new InnerAdapter(); }
}
public class use_adapter_pattern{
    public static void main(String args[]){
     WhatIUse whatIUse = new WhatIUse();
     WhatIHave whatIHave = new WhatIHave();
     WhatIWant adapt= new SurrogateAdapter(whatIHave);

 WhatIUse2 whatIUse2 = new WhatIUse2();
     WhatIHave2 whatIHave2 = new WhatIHave2();
     WhatIHave3 whatIHave3 = new WhatIHave3();

whatIUse.op(adapt);
     whatIUse2.op(whatIHave);
     whatIUse.op(whatIHave2);
     whatIUse.op(whatIHave3.whatIWant());
   }}

图略

原型(protype)模式

图略

举例如下:


框架系统级往往会使用一个原型管理器当一个系统中原型数目不固定时(也就是说,它们可以动态创建和销毁),要保持一个可用原型的注册表。客户不会自己来管理原型,但会在注册表中存储和检索原型。客户在克隆一个原型前会向注册表请求该原型。我们称这个注册表为原型管理器(prototype manager)。原型管理器是一个关联存储器( associative store),它返回一个与给定关键字相匹配的原型。它有一些操作可以用来通过关键字注册原型和解除注册。客户可以在运行时更改甚或浏览这个注册表。这使得客户无需编写代码就可以扩展并得到系统清单。
例如Structs中在RequestProcess.java中processActionCreate(request, response, mapping)方法中便有:
/* Acquire the Action instance we will be using (if there is one)*/
 String className = mapping.getType()
 Action instance = null
 synchronized (actions) {
    /*Return any existing Action instance of this class*/
    /*先在 actions中找 existing  Action instance,并返回*/
    instance = (Action) actions.get(className)
   /*若 actions中不存在  Create and return a new Action instance*/
     instance = (Action) RequestUtils.applicationInstance(className)
            ->applicationClass(className).newInstance() //典型的动态的工厂方法的应用
 instance.setServlet(this.servlet)
     actions.put(className, instance)   //典型的原型管理器的使用
  }
状态变化的时候使用数据成员,行为变化的时候使用多态。

装饰器Decorator(油漆工)模式

图略

为何说是油漆工?
动态给一个对象添加一些额外的职责,就象在墙上刷油漆.,可以任意增加颜料(功能)。
使用Decorator 模式相比用生成子类方式达到功能的扩充显得更为灵活。
而使用继承实现功能拓展,我们必须可预见这些拓展功能,这些功能是编译时就确定了,是静态的。而Decorator 提供了"即插即用"的方法,在运行期间决定何时增加何种功能。
当使用派生类方法产生过多(不灵活的)类的时候考虑应用Decorator模式。
所有外覆在原始对象上的装饰类都必须有同样的基本接口。Decorator通过包裹(wrapping)一个组件来给它增加功能, 但Decorator兼容它所包裹的那个类的接口, 这样,对component的包裹就是透明的。 Decorator自身又可以被(别的Decorator)包裹而不丧失其透明性。

实际上Java 的I/O API 就是使用Decorator 实现的,I/O 变种很多,如果都采取继承方法,将会产生很多子类,显然相当繁琐:
FileReader fr = new FileReader(filename);
BufferedReader br = new BufferedReader(fr);
另外Java中经常使用的内部匿名类等也是Decorator的具体实现:
Frame f = new Frame("FTP Client"); 
f.addWindowListener(new WindowAdapter()
{ public void windowClosing(WindowEvent e){ System.exit(0); } 
});
另外典型示例如下:
interface DrinkComponent{   String getDescription();  float getTotalCost();  }
abstract class Decorator implements DrinkComponent{
   protected DrinkComponent component;
   Decorator(DrinkComponent component){  this.component = component;  }
   public float getTotalCost(){  return component.getTotalCost(); }
public abstract String getDescription();
}
class Mug implements DrinkComponent{
   public String getDescription(){  return "mug"; }
   public float getTotalCost(){  return 0;  }
}
class Decaf extends Decorator{
   private String description = " decaf";
   public Decaf(DrinkComponent component){  super(component);  }
   public String getDescription(){  return component.getDescription() +description; }
}
class SteamedMilk extends Decorator{
   private float cost = 0.25f;   private String description = " steamed milk";
   public SteamedMilk(DrinkComponent component){  super(component); }
   public float getTotalCost(){  return component.getTotalCost() + cost; }
   public String getDescription() {  return component.getDescription() +description; }
}
class Whipped extends Decorator{
   private float cost = 0.25f;   private String description = " whipped cream";
   public Whipped(DrinkComponent component) {  super(component);  }
   public float getTotalCost() {  return component.getTotalCost() + cost; }
   public String getDescription(){  return component.getDescription() +description; }
}
class Chocolate extends Decorator{
    private float cost = 0.25f;   private String description = " chocolate";
    public Chocolate(DrinkComponentcomponent){   super(component);  }
    public float getTotalCost(){  return component.getTotalCost() + cost; }
    public String getDescription(){  return component.getDescription() +description; }
}
public class decorator_pattern{
   public static void main(String[] args){    
        // Create a decaf cafe mocha with whipped cream
        DrinkComponent cafeMocha = new Espresso(
                            new SteamedMilk(new Chocolate(new Whipped(
                                    new Decaf(new Mug())))));
        System.out.println(cafeMocha.getDescription().trim() + ": $"
                                 + cafeMocha.getTotalCost());
   }
}
注:这种方法当然提供了最灵活的体积 。你只需从很少几个组件(components)里挑出你需要的,但是拼接这些“关于咖啡的描述”就变的十分费劲。折衷是通过创建合适大小的供常规选择的菜单来实现的,这个菜单基本上是不怎么变化的,但是如果你想给这些基本饮品加点伴侣(比如whipped cream, decaf等),那你可以用decorators来改变基本饮品。这其实也是大多数咖啡馆提供的那种菜单。

Observer(观察者)模式

图略


是比较常用的一个模式,尤其在界面设计中应用广泛。
Observer 能自动观察到这种变化,并能进行及时的update 或notify 动作。
Java 的API 还为为我们提供现成的Observer 接口Java.util.Observer.我们只要直接使用它就可以。我们必须继承 Java.util.Observer 才能真正使用它。
什么时候用Observer?
当你需要用完全动态的方式分离呼叫源和被呼叫代码的时候 Observer模式都是你的首选。Observer模式允许你通过挂钩程序(hook point)改变代码。从本质上说,Observer模式是完全动态的。它经常被用于需要根据其它对象的状态变化来改变自身(状态)的场合,
而且它还经常是事件管理系统(event management)的基本组成部分。
典型应用:
Java中的事件处理是典型的Observer模式。包括被监视者添加监视器(addXXListener)而监视者需要实现(implements XXListener)事件发生时的处理函数。
示例如下:
public class product extends Observable{
private String name;  private float price;
public String getName(){ return name;}
public void setName(){
    this.name=name;
    setChanged();  //设置变化点
    notifyObservers(name);
}
public float getPrice(){ return price;}
public void setPrice(){
      this.price=price;
      setChanged();  //设置变化点
      notifyObservers(new Float(price));
}
.  public void saveToDb(){…}  //以下可以是数据库更新 插入命令
}
//观察者NameObserver 主要用来对产品名称(name)进行观察的
public class NameObserver implements Observer{
private String name=null;
public void update(Observable obj,Object arg){
        if (arg instanceof String){
             name=(String)arg;
            System.out.println("NameObserver :name changet to "+name);
       }
}}
//观察者PriceObserver 主要用来对产品价格(price)进行观察的
public class PriceObserver implements Observer{
private float price=0;
public void update(Observable obj,Object arg){
     if (arg instanceof Float){
        price=((Float)arg).floatValue();
        System.out.println("PriceObserver :price changet to "+price);
     }
}}
public class ObserverTest {
public static void main(String args[]){
    Product product=new Product();
    NameObserver nameobs=new NameObserver();
    PriceObserver priceobs=new PriceObserver();
//加入观察者
    product.addObserver(nameobs);
    product.addObserver(priceobs);
    product.setName("橘子红了");
    product.setPrice(9.22f);
}}
你会发现下面信息:   NameObserver :name changet to 橘子红了
PriceObserver :price changet to 9.22
这说明观察者在行动了!!
其实,Java中观察者模式原理在于:由Observable内定义了Observer接口的对象列表,当由Observable继承的对象(被观察者)发生改变时,会触发Observable的notify方法去遍历调用所有与被观察者有关系的Observer接口的实现对象具体实现的接口中的update方法,从而实现了“据被观察者变化实时改变观察者行为”的目的。

Flyweight 模式

图略

Flyweight 模式作用:
为避免大量拥有相同内容的小类的开销(如耗费内存),使大家共享一个类(元类)。
是一个提高程序效率和性能的模式,会大大加快程序的运行速度。
应用场合很多:比如你要从一个数据库中读取一系列字符串,这些字符串中有许多是重复的,那么我们可以将这些字符串储存在Flyweight 池(pool)中。
实例如下:
public interface Flyweight{  public void operation( ExtrinsicState state );  }
public class ConcreteFlyweight implements Flyweight {
   private IntrinsicState state;
   public void operation( ExtrinsicState state ) {
       //具体操作
   }
}
public class FlyweightFactory {
   private Hashtable flyweights = new Hashtable();  //Flyweight pool
      public Flyweight getFlyweight( Object key ) {
           Flyweight flyweight = (Flyweight) flyweights.get(key);
           if( flyweight == null ) {
              flyweight = new ConcreteFlyweight();
              flyweights.put( key, flyweight );
           }
        return flyweight;
      }
}
从调用上看,好象是个纯粹的Factory 使用,但其奥妙就在于Factory 的内部设计上.
调用:  FlyweightFactory factory = new FlyweightFactory();
Flyweight fly1 = factory.getFlyweight( "Fred" );
Flyweight fly2 = factory.getFlyweight( "Wilma" );

命令(Command)模式

图略


为何使用Command模式?
从本质上说,Command就是一个函数对象:一个被封装成对象的方法。
通过把方法封装到一个对象,你可以把它当作参数传给其它方法或者对象,
让它们在实现你的某个请求(request)的时候完成一些特殊的操作。
你可能会说Command其实就是一个把数据成员换成行为的messenger(因为它的目的和使用方法都很直接)。Commands其实就是回调函数(callbacks)的面向对象替代品。
Command模式最重要的一点就是:你可以通过它把想要完成的动作(action)交给一个方法或者对象。
使用Command 模式的一个好理由还因为它能实现Undo 功能。
每个具体命令都可以记住它刚刚执行的动作,并且在需要时恢复。
Command 模式在界面设计中应用广泛。
Command模式的应用:
其实在C++STL中的函数对象(仿函数,functor)便是一种Command模式。 
#include<iostream.h>
#include<vector>
#include<algorithm>
//Simple function object that prints the passed argument
Class PrintInt{
Public : Void operator() (int elem)const{   Cout<<elem<<’ ’;   }
};
Int main(){
Vector<int> coll;
for(int i=1; i<=9;++i){   coll.push_back(i);   }
for_each( coll.begin(),  coll.end(),  PrintInt());
/*其实for_each()算法定义如下:
  Namespace std{
      Template<class Iterator,  class operation>
       Operation for_each(Iterator act,  Iterator end,  operation op) {
           While(act!=end){
               Op(*act);
               ++act;
           }
         Return op;
      }
}
For_each()使用暂时对象op(仿对象),针对每个元素调用op(*act)。若第三参数是个一般函数,以*act为参数调用之。若第三参数是个仿函数则以*act为参数,调用仿函数op的operation()。For_each()调用 PrintInt::operation()(*act)。
*/
cout<<endl;
}
而Java 的Swing 中菜单命令都是使用Command 模式。
Java中的一些个标志接口如Serializable是Command模式的一种特化。
示例如下:
public interface Command {  public abstract void execute ( );  }
public class Engineer implements Command {
  public void execute( ) {  //do Engineer's command  }
}
public class Programmer implements Command {
public void execute( ) {  //do programmer's command  }
}
public class Politician implements Command{
   public void execute( ) {  //do Politician's command    }
}
public class producer{
  public static List produceRequests() {
    List queue = new ArrayList();
    queue.add( new DomesticEngineer() );
    queue.add( new Politician() );
    queue.add( new Programmer() );
    return queue;
  }}
public class TestCommand {
public static void main(String[] args) {
     List queue = Producer.produceRequests();
     for (Iterator it = queue.iterator(); it.hasNext(); )
                 //取出List 中东东,其他特征都不能确定,只能保证一个特征是100%正确,
                 // 他们至少是接口Command 的"儿子".所以强制转换类型为接口Command((Command)it.next()).execute();
}}
策略(Strategy)模式
定义一系列的算法,把这些算法一个个封装成单独的类。从而达到了在运行期间,可以自由切换算法的目的。

图略


Strategy模式的应用:
Strategy 适合被用于下列场合:
1.以不同的格式保存文件。
2.以不同的算法压缩文件。
3.以不同的算法截获图象。
4.以不同的格式输出同样数据的图形,比如曲线 或框图bar 等。
另外策略模式不仅在面向对象中大量使用,在面向过程的设计中也有使用:如C的函数指针,空指针均为这种设计模式提供了天然的土壤。C库函数中的qsort(快速排序)便是最典型的例证了:
void qsort(void * _base, size_t _nmemb, size_t _size, int(*_compar)(const void *, const void *));
其中函数指针_compar便是用来替换各种不同比较策略的。
Strategy与Command的使用区别:
Command模式在编码阶段提供灵活性,而Strategy模式的灵活性在运行时体现出来。
Java里Strategy模式的例子:comparator objects(Comparator 对象方法)。
示例如下:
public abstract class RepTempRule{
  protected String oldString="";
  public void setOldString(String oldString){ this.oldString=oldString; }
  protected String newString="";
  public String getNewString(){  return newString;  }
  public abstract void replace() throws Exception;
}
public class RepTempRuleOne extends RepTempRule{
   public void replace() throws Exception{
     newString=oldString.replaceFirst("aaa", "bbbb")  //replaceFirst 是jdk1.4 新特性
     System.out.println("this is replace one");
   }
}
public class RepTempRuleTwo extends RepTempRule{
   public void replace() throws Exception {
     newString=oldString.replaceFirst("aaa", "ccc")
     System.out.println("this is replace Two");
   }
}
public class RepTempRuleSolve {
  private RepTempRule strategy;
  public RepTempRuleSolve(RepTempRule rule){  this.strategy=rule;  }
  public String getNewContext(Site site,String oldString) {
     return strategy.replace(site,oldString);
  }
  public void changeAlgorithm(RepTempRule newAlgorithm) {  strategy = newAlgorithm; }
}
调用如下:
public class test{
public void testReplace(){
  //使用第一套替代方案
  RepTempRuleSolve solver=new RepTempRuleSolve(new RepTempRuleOne());
  solver.getNewContext(site,context);
  //使用第二套
  solver=new RepTempRuleSolve(new RepTempRuleTwo());
  solver.getNewContext(site,context);
}}

TemplateMethod模式

图略


Template Method模式的特征:
它是在基类里定义的, 而且不能够被(派生类)更改。有时候它是私有方法(private method),但实际上它经常被声明为final。它通过调用其它的基类方法(覆写过的)来工作,但它经常是作为初始化过程的一部分被调用的,这样就没必要让客户端程序员能够直接调用它了。基类的构造函数负责完成必要的初始化和启动应用程序“引擎”(template method)(在一个图形用户界面(GUI)程序里,这个“引擎”通常是“主事件循环(main event loop)”)。
客户端程序员只需简单的提供customize1( )和 customize2( )方法的定义,整个程序就可以跑起来了。
需注意Template Method模式与C++或Java中的Template不完全一样,Template Method模式中包含C++或Java中的Template的方式。
Template Method模式的应用:

图略

现今的绝大部分软件开发框架均是Template Method模式的最明显应用。
比如说MFC:
CObject
|_CCmdTarget
  |__CWinThread
  |   |__CWinApp
  |      |__CMyWinApp  //必须由用户实现
  |__CWnd
  |   |__CView
  |   |   |__CMyView    //必须由用户实现
  |   |__CFrameWnd
  |      |__CMyFrameWnd  //由用户实现(可选)
  |__CDocument
     |__CMyDoc   //必须由用户实现

再如Structs:

图略

另外,我们给出一个最简单的示例如下:
abstract class ApplicationFramework{
   public ApplicationFramework(){  templateMethod();  }
   abstract void customize1(); //系统提供给用户实现的客户化函数部分
   abstract void customize2(); //系统提供给用户实现的客户化函数部分
   final void templateMethod(){
       //在一个图形用户界面(GUI)程序里,这个“引擎”
       // 通常是“主事件循环(main event loop)”
       for(int i = 0; i < 5; i++) {
         customize1();  customize2();
       }
   }
}
// Create a new "application":
// 用户具体实现客户化函数部分
class MyApp extends ApplicationFramework {
    void customize1(){  System.out.print("Hello ");  }
    void customize2(){  System.out.println("World!");  }
}
public class templateMethod_pattern{
   public static void main(String args[]){
       MyApp app = new MyApp();
   }
}
MVC模式:
MVC最早是由Alan Kay在设计的Smalltalk中使用,而随着现今网络的席卷,BS模式的架构被大量的使用,故MVC这种优良的模式也被广泛的使用。J2EE中提供了Servlet, EJB, Jsp便很好的支持了MVC。
MVC交互

图略

另外Structs中交互流程:

图略

外观模式(Facade):
在应用中,经常需要对数据库操作,每次都写上述一段代码肯定比较麻烦,需要将其中不变的
部分提炼出来,做成一个接口,这就引入了facade 外观对象.如果以后我们更换
Class.forName 中的<driver>也非常方便,比如从Mysql 数据库换到Oracle 数据库,只要更
换facade 接口中的driver 就可以.
我们做成了一个Facade 接口,使用该接口,上例中的程序就可以更改如下:
public class DBCompare {
String sql = "SELECT * FROM <table> WHERE <column name> = ?";
try {
Mysql msql=new mysql(sql);
prep.setString( 1, "<column value>" );
rset = prep.executeQuery();
if( rset.next() ) {
System.out.println( rset.getString( "<column name" ) );
}
} catch( SException e ) {
e.printStackTrace();
} finally {
mysql.close();
mysql=null;
}
}
facade 实际上是个理顺系统间关系,降低系统间耦合度的一个常用的办法,也许你已经不知不觉在使用,尽管不知道它就是façade。

组合模式(Composite):
将对象以树形结构组织起来,以达成“部分-整体” 的层次结构,使得客户端对单个对象和组合对象的使用具有一致性。
示例:
public abstract class Equipment
{
private String name;
public abstract double netPrice();//网络价格
public abstract double discountPrice();//折扣价格
public boolean add(Equipment equipment) { return false; }//增加部件方法
public boolean remove(Equipment equipment) { return false; }//删除部件方法
//注意这里,这里就提供一种用于访问组合体类的部件方法。
public Iterator iter() { return null; }
public Equipment(final String name) { this.name=name; }
}
public class Disk extends Equipment
{
public Disk(String name) { super(name); }
public double netPrice() { return 1.; }//定义Disk 网络价格为1
public double discountPrice() { return .5; }//定义了disk 折扣价格是0.5 对折。
}

abstract class CompositeEquipment extends Equipment{
  private int i=0;
  private Lsit equipment=new ArrayList();//定义一个Vector 用来存放'儿子'
  public CompositeEquipment(String name) { super(name); }
  public boolean add(Equipment equipment) {
    this.equipment.add(equipment);
    return true;
  }
public double netPrice(){
double netPrice=0.;
Iterator iter=equipment.iterator();
for(iter.hasNext())   netPrice+=((Equipment)iter.next()).netPrice();
return netPrice;
}
public double discountPrice(){
double discountPrice=0.;
Iterator iter=equipment.iterator();
for(iter.hasNext())   discountPrice+=((Equipment)iter.next()).discountPrice();
return discountPrice;
}
//注意这里,这里就提供用于访问自己组合体内的部件方法。
//上面dIsk 之所以没有,是因为Disk 是个单独(Primitive)的元素.
public Iterator iter(){  return equipment.iterator() ;  }//重载Iterator 方法
public boolean hasNext() { return i<equipment.size(); }//重载Iterator 方法
public Object next(){
if(hasNext())  return equipment.elementAt(i++);
else   throw new NoSuchElementException();
}
}
public class Chassis extends CompositeEquipment{
  public Chassis(String name) { super(name); }
  public double netPrice() { return 1.+super.netPrice(); }
  public double discountPrice()  { return 0.5+super.discountPrice(); }
}
public class Cabinet extends CompositeEquipment{
  public Cabinet(String name) { super(name); }
  public double netPrice() { return 1.+super.netPrice(); }
  public double discountPrice() { return 0.3+super.discountPrice(); }
}
客户端调用Composite 代码:
Cabinet cabinet=new Cabinet("Tower");
Chassis chassis=new Chassis("PC Chassis");
cabinet.add(chassis);  //将PC Chassis 装到Tower 中 (将盘盒装到箱子里)
chassis.add(new Disk("10 GB"));  //将一个10GB 的硬盘装到 PC Chassis (将硬盘装到盘盒里)
System.out.println("netPrice="+cabinet.netPrice());  //调用 netPrice()方法;
System.out.println("discountPrice="+cabinet.discountPrice());
实际上Composite 使用Iterator 遍历了整个树形结构,寻找同样包含这个方法的对象并实现调用执行。
Composite 是个很巧妙体现智慧的模式,在实际应用中,如果碰到树形结构,我们就可以尝试是否可以使用这个模式。

迭代器(Iterator)模式:
迭代器将算法从它所使用的特定类型的容器中分离出来。
这就意味着在描述算法的时候,可以不必考虑它所操作的特定序列。
更为一般情况,用迭代器写的任何代码都与它所操作的数据结构相分离,
这样一来这些代码就更为通用并且易于重用。
在C++ STL中便存在大量的迭代器与容器,算法一起联合使用。
在STL中迭代器是一种“能够遍历某个序列内所有元素”的对象。它可通过与一般指针一致的接口来完成自己的工作。迭代器奉行一个纯抽象概念:任何东西,只要行为类似迭代器就是一种迭代器。不同的迭代器具有不同的能力(指行进或存取能力)。而不同算法需要不同的迭代器。故迭代器被分成不同的类型(如:InputIterator, OutputIterator, ForwardIterator, BidirectionalIterator, RandomAccessIterator等)。
先举个最简单使用STL迭代器来操作的例子:
#include<vector>
# include<iostream>
using namespace std; //在这儿是必须的
int main()
{
   vector<int> coll;
   for(int i=0; i<3;i++)  coll.push_back(i);
   vector<int>::iterator pos; //由此可看出迭代器被包含在容器中
   for(pos=coll.begin(); pos<coll.end(); ++pos)  //使用STL容器Vector提供的迭代器
cout<<*pos<<‘ ’;
   cout<<endl;
}
我们再来实现一个自己的迭代器:
# include <iterator>
template <class Container>
class my_insert_iterator:public std::iterator<std::output_iterator_tag, void, void, void, void>
{
Protected: Container&container;
Public: explicit my_insert_iterator(Container& c):container(c){}
my_insert_iterator<Container>&operator=(const typename Container::value_type& value){
   container.insert(value);
return *this;
}
my_insert_iterator<Container>&operator*(){
   return *this;
}
my_insert_iterator<Container>&operator++(){
   return *this;
}
my_insert_iterator<Container>&operator++(int){
   return *this;
}
};
template <class Container>
inline my_insert_iterator<Container> my_insert(Container& c){
    return  my_insert_iterator<Container>(c);
}
使用自己编写的迭代器(my_insert_iterator):
# include<iostream>
# include<set>
# include<algorithm>
using namespace std; //在这儿是必须的
//包含迭代器的头文件
int main()
{
   set<int> coll;
   my_insert_iterator<set<int>> iter(coll);
   *iter=1;
   iter++;
   *iter=2;
   iter++;
   *iter=3;
//打印输出迭代器内容
   my_insert(coll)=44;
   my_insert(coll)=55;
//打印输出 插入的元素
   int vals[]={22,33,44};
   Copy(vals, vals+(sizeof(vals)/sizeof(vals[0])), my_insert(coll)); //使用算法插入元素
    //打印出内容
}
再来看看在java中的迭代器使用
自己来编写一个 迭代器的外覆类:
我们不可能重写现有的Java类库,它已经包含了枚举器和迭代器。但是,我们可以用Decorator模式简单的创建一个枚举器或者迭代器的外覆类,产生一个具有我们想要的迭代行为(本例中,指在类型不正确的时候抛出RuntimeException异常)的新对象,而这个新对象跟原来的枚举器或者迭代器有相同的接口,这样一来,它就可以用在相同的场合(或许你会争论说这实际上是Proxy模式,但是从它的目的(intent)来说它更像Decorator模式)。
保证此迭代器内部所有元素均相同,这个新对象覆盖了原来迭代器的接口 以保证操作的兼容。
import java.util.*;
public class TypedIterator implements Iterator{
     private Iterator imp;
     private Class type;
     public TypedIterator(Iterator it, Class type){
        imp = it;
        this.type = type;
     }
     public boolean hasNext(){  return imp.hasNext();  }
     public void remove() { imp.remove(); }
     public Object next(){
         Object obj = imp.next();
         if(!type.isInstance(obj))
             throw new ClassCastException(
                            "TypedIterator for type " + type +
                            " encountered type: " + obj.getClass());
         return obj;
     }
}


其他还有些模式,由于我还没来得及学习,故这便不再叙述。
另外,其实各个模式间的联系也非常紧密,比如说:Factory Method几乎其他模式都会用到。单件与原型也经常一起使用。另外有种说法:MVC模式是Observer设计模式的扩展形式也有些道理,等等。如今,找寻其他种模式也是很好的方向。

Design Pattern的应用及延伸:
1 Analysis Pattern(分析模式)
2 Refactor (主要是代码重构)
3 Framework (尤其是freeware框架) 
4 与case工具的集成
如Rational Rose:

图略

注:由于粘图比较麻烦,省去了一些图,表。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics