刘晓伟ID:lxwde
165854次访问,排名445好友0人,关注者2
lxwde的文章
原创 27 篇
翻译 34 篇
转载 0 篇
评论 244 篇
最近评论
mldstk:wow power leveling
lshvs2006:你好,你設計的東東非常不錯。
但是,現在有個疑問,不知道,怎樣保存設計的文件,
存成XML 文件格式可能會好一點,但是,不知道如何去存? 需要調用什么接口 或是 需要寫什么 方法嗎?

期待您的指教。謝謝

lsh2011@163.com
zeeler:补上这一部分吧(本人中文表述能力没有lxwde强哦 :) ):
The Singleton
也许最简单的设计模式是Singleton模式了,它可以给某种类型提供唯一的对象,下面是个例子:
(译者按:例子省略)
创建一个唯一对象的关键是防止客户程序员(client programmer)用其他任意方法创建对象,只能用你提供的方法。你必须把所有构造器写成p……
zeeler:翻译的不错呀,全力支持!
不过在Design principles和Classifying patterns之间还有个The Singleton部分好像lxwde漏掉了?还是Bruce Eckel修改版面了?
总之,支持呀,本来我也想翻译一下的,不过没有lxwde这么有毅力,翻译一节就停了,实在很累的,所以非常敬佩lxwde能坚持做这么多!
roger_77:可惜,
这个库被boost放弃了,加入另一个ASIO的网络类库
文章分类
收藏
    相册
    链接
    My articles on codeproject
    SharpFormEditor下载
    Thinking in Patterns中文版
    友情链接
    alai04
    C++的罗浮宫
    fatalerror99
    存档
    软件项目交易
    订阅我的博客
    XML聚合  FeedSky
    订阅到鲜果
    订阅到Google
    订阅到抓虾
    订阅到BlogLines
    订阅到Yahoo
    订阅到GouGou
    订阅到飞鸽
    订阅到Rojo
    订阅到newsgator
    订阅到netvibes

    翻译 翻译TIPatterns--对象去耦(Object decoupling)收藏

    新一篇: 翻译TIPatterns--分解共同性(Factoring Commonality) | 旧一篇: 翻译TIPatterns--对象数量(Object quantity)

    对象去耦(Object decoupling)

     

        代理(Proxy)模式和状态(State)模式分别提供了供你使用的代理类(surrogate class);正真干活的那个类被代理类隐藏了。当你调用代理类的一个方法的时候,代理类只是简单的调用实现类(implementing class)所对应的方法。这两种模式非常相似,实际上,代理(Proxy)模式只是状态(State)模式的一个特例。

        有人试图将这两种模式合在一起统称为Surrogate模式,但是“代理(proxy)”这个术语已经用了很长时间了,而且它有自己特殊的含义,它的这些含义基本上体现了这两种模式的差别所在。

        这两种模式的基本概念非常简单:代理类 (surrogate) 和 实现类都由同一个基类派生出来:

     

     


        当创建一个代理对象 (surrogate object) 时,同时会创建一个实现(对象),代理对象会把所有的方法调用传递给实现对象。

        从结构上看,代理(Proxy)模式和状态(State)模式之间的差别非常简单:一个代理(Proxy)只对应一个实现(implementation),而一个状态(State)却可以对应多个实现。《设计模式》一书认为,这两种两种模式的应用场合是截然不同的:代理(Proxy)模式用于控制对实现(类)的访问,而状态(State)模式可以动态地改变实现(类)。但是,如果把“控制对实现类的访问”这个概念扩展开来的话,这两种模式就可以优雅的结合在一起了。

     

    代理:替另外一个对象打点一切(Proxy: fronting for another object)

        我们按照上面的图示实现代理(Proxy)模式,下面是实现代码:

     

    //: proxy:ProxyDemo.java

    // Simple demonstration of the Proxy pattern.

    package proxy;

    import junit.framework.*;

     

    interface ProxyBase {

     void f();

     void g();

     void h();

    }

     

    class Proxy implements ProxyBase {

     private ProxyBase implementation;

     public Proxy() {

      implementation = new Implementation();

     }

     // Pass method calls to the implementation:

     public void f() { implementation.f(); }

     public void g() { implementation.g(); }

     public void h() { implementation.h(); }

    }

     

    class Implementation implements ProxyBase {

     public void f() {

      System.out.println("Implementation.f()");

     }

     public void g() {

      System.out.println("Implementation.g()");

     }

     public void h() {

      System.out.println("Implementation.h()");

     }

    }

     

    public class ProxyDemo extends TestCase  {

     Proxy p = new Proxy();

     public void test() {

      // This just makes sure it will complete

      // without throwing an exception.

      p.f();

      p.g();

      p.h();

     }

     public static void main(String args[]) {

      junit.textui.TestRunner.run(ProxyDemo.class);

     }

    } ///:~

        当然,并不是说实现类和代理类必须实现完全相同的接口;既然代理类只是在一定程度上代表那个需要它提交(referring)方法的类,这就已经满足了proxy模式的基本要求(注意这里的陈述和GoF一书所给出的定义是有差别的)。尽管如此,定义一个公共的接口还是很方便的,这样就可以强制实现类(Implementation)实现(fulfill)代理类(Proxy)需要调用的所有方法。

     

    用Proxy模式实现PoolManager

    //: proxy:PoolManager.java

    package proxy;

    import java.util.*;

     

    public class PoolManager {

     private static class PoolItem {

      boolean inUse = false;

      Object item;

      PoolItem(Object item) { this.item = item; }

     }

     public class ReleasableReference {  // Used to build the proxy

      private PoolItem reference;

      private boolean released = false;

      public ReleasableReference(PoolItem reference) {

       this.reference = reference;

      }

      public Object getReference() {

       if(released)

        throw new RuntimeException(

        "Tried to use reference after it was released");

       return reference.item;

      }

      public void release() {

       released = true;

       reference.inUse = false;

      }

     }

     private ArrayList items = new ArrayList();

     public void add(Object item) {

      items.add(new PoolItem(item));

     }

     // Different (better?) approach to running out of items:

     public static class EmptyPoolItem {}

     public ReleasableReference get() {

      for(int i = 0; i < items.size(); i++) {

       PoolItem pitem = (PoolItem)items.get(i);

       if(pitem.inUse == false) {

        pitem.inUse = true;

        return new ReleasableReference(pitem);

       }

      }

      // Fail as soon as you try to cast it:

      // return new EmptyPoolItem();

      return null; // temporary

     }

    } ///:~

     

     

    //: proxy:ConnectionPoolProxyDemo.java

    package proxy;

    import junit.framework.*;

     

    interface Connection {

     Object get();

     void set(Object x);

     void release();

    }

     

    class ConnectionImplementation implements Connection {

     public Object get() { return null; }

     public void set(Object s) {}

     public void release() {} // Never called directly

    }

     

    class ConnectionPool { // A singleton

     private static PoolManager pool = new PoolManager();

     private ConnectionPool() {} // Prevent synthesized constructor

     public static void addConnections(int number) {

      for(int i = 0; i < number; i++)

       pool.add(new ConnectionImplementation());

     }

     public static Connection getConnection() {

      PoolManager.ReleasableReference rr =

       (PoolManager.ReleasableReference)pool.get();

      if(rr == null) return null;

      return new ConnectionProxy(rr);

     }

     // The proxy as a nested class:

     private static

     class ConnectionProxy implements Connection {

      private PoolManager.ReleasableReference implementation;

      public

       ConnectionProxy(PoolManager.ReleasableReference rr) {

        implementation = rr;

       }

       public Object get() {

        return

         ((Connection)implementation.getReference()).get();

       }

       public void set(Object x) {

        ((Connection)implementation.getReference()).set(x);

       }

       public void release() { implementation.release(); }

     }

    }

     

    public class ConnectionPoolProxyDemo extends TestCase {

     static {

      ConnectionPool.addConnections(5);

     }

     public void test() {

      Connection c = ConnectionPool.getConnection();

      c.set(new Object());

      c.get();

      c.release();

     }

     public void testDisable() {

      Connection c = ConnectionPool.getConnection();

      String s = null;

      c.set(new Object());

      c.get();

      c.release();

      try {

       c.get();

      } catch(Exception e) {

       s = e.getMessage();

       System.out.println(s);

      }

      assertEquals(s,

       "Tried to use reference after it was released");

     }

     public static void main(String args[]) {

      junit.textui.TestRunner.run(

       ConnectionPoolProxyDemo.class);

     }

    } ///:~

     

     

    动态代理(Dynamic Proxies)

        JDK1.3引入了动态代理 (Dynamic Proxy). 尽管一开始有些复杂,但它确实是一个吸引人的工具。下面这个有趣的小例子证明了这一点, 当invocation handler被调用的时候,代理机制(proxying)开始工作。这是非常Cool的一个例子,它就在我的脑海里,但是我必须得想出一些合理的东西给invocation handler,这样才能举出一个有用的例子…(作者还没有写完)

    // proxy:DynamicProxyDemo.java

    // Broken in JDK 1.4.1_01

    package proxy;

    import java.lang.reflect.*;

     

    interface Foo {

     void f(String s);

     void g(int i);

     String h(int i, String s);

    }

     

    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");

     }

    } ///:~

     

    练习:用java的动态代理创建一个对象作为某个简单配置文件的前端。例如,在good_stuff.txt文件里有如下条目:

    a=1

    b=2

    c="Hello World"

    客户端程序员可以使用(你写的)NeatPropertyBundle类:

    NeatPropertyBundle p =

      new NeatPropertyBundle("good_stuff");

    System.out.println(p.a);

    System.out.println(p.b);

    System.out.println(p.c);

    配置文件可以包含任何内用,任意的变量名。动态代理要么返回对应属性的值要么告诉你它不存在(可能通过返回null)。如果你摇设置一个原本不存在的属性值,动态代理会创建一个新的条目。ToString()

    方法应该显示当前的所有条目。 

    练习:和上一道练习类似,用Java的动态代理连接一个DOS的Autoexec.bat文件。 

     

    练习:接受一个可以返回数据的SQL查询语句,然后读取数据库的元数据(metadata)。为每一条记录(record)提供一个对象,这个对象拥有一下属性:列名(column names)和对应的数据类型(data types). 

     

    练习:用XML-RPC写一个简单的服务器和客户端.每一个客户端返回的对象都必须使用动态代理的概念(dynamic proxy concept)来实现(exercise)远端的方法。(瞎翻的,不知道啥意思)

     

    读者Andrea写道:

        除了最后一个练习,我觉得你给出的上面几个练习都不咋的。我更愿意把Invocation handler看成是能和被代理对象正交的 (orthogonal) 东东。

        换句话说,invocation handler的实现应该是和动态创建的代理对象所提供的那些接口完全无关的。也就是说,一旦invocation handler写好之后,你就可以把它用于任何暴露接口的类,甚至是那些晚于invocation handler出现的类和接口。

        这就是我为什么要说invocation handler所提供的服务是和被代理对象正交的(orthognal)。Rickard 在他的SmartWorld例子里给出了几个handler,其中我最喜欢的是那个调用-重试(call-retry)handler。它首先调用那个(被代理的)实际对象,如果调用产生异常或者等待超时,就重试三次。如果这三次都失败了,那就返回一个异常。这个Handler可以被用于任何一个类。

        那个handler的实现相对于你这里讲的来说过于复杂了,我用这个例子仅仅是想说明我所指的正交(orthogonal)服务到底是什么意思。

        您所给出的那几个练习,在我看来,唯一适合用动态代理实现的就是最后那个用XML-RPC与对象通信的那个练习。因为你所使用的用以分发消息的机制(指XML-RPC)是和你想要建立通信的那个对象完全正交的。

     

    状态模式:改变对象的行为(State: changing object behavior)

        一个用来改变类的(状态的)对象。

        迹象:几乎所有方法里都出现(相同的)条件(表达式)代码。

        为了使同一个方法调用可以产生不同的行为,状态(State)模式在代理(surrogate)的生命周期内切换它所对应的实现(implementation)。当你发现,在决定如何实现任何一个方法之前都必须作很多测试的情况下,这是一种优化实现代码的方法。例如,童话故事青蛙王子就包含一个对象(一个生物),这个对象的行为取决于它自己所处的状态。你可以用一个布尔(boolean)值来表示它的状态,测试程序如下: 

    //: state:KissingPrincess.java

    package state;

    import junit.framework.*;

     

    class Creature {

     private boolean isFrog = true;

     public void greet() {

      if(isFrog)

       System.out.println("Ribbet!");

      else

       System.out.println("Darling!");

     }

     public void kiss() { isFrog = false; }

    }

     

    public class KissingPrincess extends TestCase  {

     Creature creature = new Creature();

     public void test() {

      creature.greet();

      creature.kiss();

      creature.greet();

     }

     public static void main(String args[]) {

      junit.textui.TestRunner.run(KissingPrincess.class);

     }

    } ///:~

        但是,greet() 方法(以及其它所有在完成操作之前必须测试isFrog值的那些方法)最终要产生一大堆难以处理的代码。如果把这些操作都委托给一个可以改变的状态对象(State object),那代码会简单很多。

    //: state:KissingPrincess2.java

    package state;

    import junit.framework.*;

     

    class Creature {

     private interface State {

      String response();

     }

     private class Frog implements State {

      public String response() { return "Ribbet!"; }

     }

     private class Prince implements State {

      public String response() { return "Darling!"; }

     }

     private State state = new Frog();

     public void greet() {

      System.out.println(state.response());

     }

     public void kiss() { state = new Prince(); }

    }

     

    public class KissingPrincess2 extends TestCase  {

     Creature creature = new Creature();

     public void test() {

      creature.greet();

      creature.kiss();

      creature.greet();

     }

     public static void main(String args[]) {

      junit.textui.TestRunner.run(KissingPrincess2.class);

     }

    } ///:~

        此外,状态(State)的改变会自动传递到所有用到它的地方,而不需要手工编辑类的方法以使改变生效。

     下面的代码演示了状态(State)模式的基本结构。

    //: state:StateDemo.java

    // Simple demonstration of the State pattern.

    package state;

    import junit.framework.*;

     

    interface State {

     void operation1();

     void operation2();

     void operation3();

    }

     

    class ServiceProvider {

     private State state;

     public ServiceProvider(State state) {

      this.state = state;

     }

     public void changeState(State newState) {

      state = newState;

     }

     // Pass method calls to the implementation:

     public void service1() {

      // ...

      state.operation1();

      // ...

      state.operation3();

     }

     public void service2() {

      // ...

      state.operation1();

      // ...

      state.operation2();

     }

     public void service3() {

      // ...

      state.operation3();

      // ...

      state.operation2();

     }

    }

     

    class Implementation1 implements State {

     public void operation1() {

      System.out.println("Implementation1.operation1()");

     }

     public void operation2() {

      System.out.println("Implementation1.operation2()");

     }

     public void operation3() {

      System.out.println("Implementation1.operation3()");

     }

    }

     

    class Implementation2 implements State {

     public void operation1() {

      System.out.println("Implementation2.operation1()");

     }

     public void operation2() {

      System.out.println("Implementation2.operation2()");

     }

     public void operation3() {

      System.out.println("Implementation2.operation3()");

     }

    }

     

    public class StateDemo extends TestCase  {

     static void run(ServiceProvider sp) {

      sp.service1();

      sp.service2();

      sp.service3();

     }

     ServiceProvider sp =

      new ServiceProvider(new Implementation1());

     public void test() {

      run(sp);

      sp.changeState(new Implementation2());

      run(sp);

     }

     public static void main(String args[]) {

      junit.textui.TestRunner.run(StateDemo.class);

     }

    } ///:~

        在main()函数里,先用到的是第一个实现,然后转入第二个实现。

        当你自己实现State模式的时候就会碰到很多细节的问题,你必须根据自己的需要选择合适的实现方法,比如用到的状态(State)是否要暴露给调用的客户,以及如何使状态发生变化。有些情况下(比如Swing的LayoutManager),,客户端可以直接传对象进来,但是在KissingPrincess2.java那个例子里,状态对于客户端来说是不可见的。此外,用于改变状态的机制可能很简单也可能很复杂-比如本书后面将要提到的状态机(State Machine),那里会讲到一系列的状态以及改变状态的不同机制。

         上面提到Swing的LayoutManager那个例子非常有趣,它同时体现了Strategy模式和State模式的行为。

        Proxy模式和State模式的区别在于它们所解决的问题不同。《设计模式》里是这么描述Proxy模式的一般应用的:

    1.  远程代理(Remote Proxy)为一个对象在不同的地址空间提供局部代理。A remote proxy is created for you automatically by the RMI compiler rmic as it creates stubs and

    2.  虚代理(Virtual proxy),根据需要,在创建复杂对象时使用“lazy initialization” .

    3.  保护代理(protection proxy) 用于你不希望客户端程序员完全控制被代理对象(proxied object)的情况下。

    4.  智能引用(smart reference). 当访问被代理对象时提供额外的动作。例如,它可以用来对特定对象的引用进行计数,从而实现copy-on-write,进而避免对象别名(object aliasing). 更简单的一个例子是用来记录一个特定方法被调用的次数。

        你可以把java里的引用(reference)看作是一种保护代理,它控制对分配在堆(heap)上的实际对象的访问(而且可以保证你不会用到一个空引用(null reference))。 

        【重写:在《设计模式》一书里,Proxy模式和State模式被认为是互不相干的,因为那本书给出的用以实现这两种模式的结构是完全不同的(我认为这种实现有点武断)。尤其是State模式,它用了一个分离的实现层次结构,但我觉着完全没有必要,除非你认定实现代码不是由你来控制的(当然这也是一种可能的情况,但是如果代码是由你来控制的,那还是用一个单独的基类更简洁实用)。此外,Proxy模式的实现不需要用一个公共的基类,因为代理对象只是控制对被代理对象的访问。尽管有细节上的差异,Proxy模式和State模式都是用一个代理(surrogate)把方法调用传递给实现对象。】 

        State模式到处可见,因为它是最基本的一个想法,比如,在Builder模式里,“Director”就是用一个后端(backend)的Buider object来产生不同的行为。

     

    迭代器:分离算法和容器(Iterators: decoupling algorithms from containers)

        Alexander Stepanov(和Dave Musser一起)写STL以前 ,已经用了好几年思考泛型编程(generic programming)的问题。最后他得出结论:所有的算法都是定义在代数结构(algebraic structures)之上的-我们把代数结构称作容器(container)。

        在这个过程中,他意识到i迭代器对于算法的应用是至关重要的,因为迭代器将算法从它所使用的特定类型的容器中分离出来。这就意味着在描述算法的时候,可以不必考虑它所操作的特定序列。更为一般情况,用迭代器写的任何代码都与它所操作的数据结构相分离,这样一来这些代码就更为通用并且易于重用。

        迭代器的另外一个应用领域就是函数式编程(functional programming),它的目标是描述程序的每一步是干什么的,而不是描述程序的每一步是怎么做的。也就是说,使用“sort”(来排序),而不是具体描述排序的算法实现。C++STL的目的就是为C++语言提供对这种泛型编程方法的支持(这种方法成功与否还需要时间来验证)。

        如果你用过Java的容器类(写代码不用到它们是很难的),那你肯定用过迭代器-Java1.0/1.1把它叫作枚举器(Enumeration),Java2.0叫作迭代器-你肯定已经熟悉它们的一般用法。如果你还不熟悉的话,可以参考Thinking in Java 第二版第九章 (可以从 www.BruceEckel.com免费下载).

        因为Java2的容器非常依赖于迭代器,所以它们就成了泛型编程/函数式编程的最佳候选技术。这一章节通过把STL移植到Java来讲解这些技术,(移植的迭代器)会和Java2的容器类一起使用。

     

    类型安全的迭代器(Type-safe iterators)

        在Thinking in Java 第二版里,我实现了一个类型安全的容器类,它只接受某一特定类型的对象。读者Linda Pazzaglia想要我实现另外一个类型安全的组件,一个可以和java.util里定义的容器类兼容的迭代器,但要限制它所遍历的对象必须都是同一类型的。

        如果Java有模板(template)机制,上面这种(类型安全的)迭代器很容易就可以返回某一特定类型的对象。但是没有模板机制,就必须得返回generic Objects,或者为每一种需要遍历的对象都手工添加代码。这里我会使用前一种方法。

        另外一个需要在设计时决定的问题(design decision)是什么时候判定对象的类型。一种方法是以迭代器遍历的第一个对象的类型(作为迭代器的类型),但是这种方法当容器类根据它自己的内部算法(比如hash表)重新为对象排序时就会有问题,这样同一迭代器的两次遍历就可能得到不同的结果。安全的做法是在构造迭代器的时候让用户指定迭代器的类型。

        最后的问题是如何构建迭代器。我们不可能重写现有的Java类库,它已经包含了枚举器和迭代器。但是,我们可以用Decorator模式简单的创建一个枚举器或者迭代器的外覆类,产生一个具有我们想要的迭代行为(本例中,指在类型不正确的时候抛出RuntimeException异常)的新对象,而这个新对象跟原来的枚举器或者迭代器有相同的接口,这样一来,它就可以用在相同的场合(或许你会争论说这实际上是Proxy模式,但是从它的目的(intent)来说它更像Decorator模式)。

     

    实现代码如下:

    //: com:bruceeckel:util:TypedIterator.java

    package com.bruceeckel.util;

    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;

     }

    } ///:~

     

    练习:

    1.写一个“virtual proxy”。

    2.写一个“Smartreference”代理,用这个代理记录某个特定对象的方法调用次数。

    3.仿照某个DBMS系统,写一个程序限制最大连接数。用类似于singleton的方法控制连接对象的数量。当用户释放某个连接时,必须通知系统将释放的连接收回以便重用。为了保证这一点,写一个proxy对象代替对连接的引用计数,进一步设计这个proxy使它能够将连接释放回系统。

    4.用State模式,写一个UnpredictablePerson类,它根据自己的情绪(Mood)改变对hello()方法的响应。再写一个额外的Mood类:Prozac。

    5.写一个简单的copy-on write实现。

    6.java.util.Map 没有提供直接从一个两维数组读入“key-value”对的方法。写一个adapter类实现这个功能。

    7.Create an Adapter Factory that dynamically finds and produces the adapter that you need to connect a given object to a desired interface.

    8.用java标准库的动态代理重做练习7。

    9.改写本节的Object Pool,使得对象再一段时间以后自动回收到对象池。

    10.改写练习9,用“租借(leasing)”的方法使得客户端可以刷新“租借对象”,从而阻止对象定时自动释放。

    11.考虑线程因素,重写Object Pool。

     

     

    目录

     


     

    发表于 @ 2004年08月06日 08:48:00|评论(loading...)|编辑

    新一篇: 翻译TIPatterns--分解共同性(Factoring Commonality) | 旧一篇: 翻译TIPatterns--对象数量(Object quantity)

    评论

    #yzx110 发表于2004-08-06 09:27:00  IP: 61.49.236.*
    加油!
    #lxwde 发表于2004-08-06 09:37:00  IP: 165.170.128.*
    等我把版面排一下,我在word里排好,到这里都乱了。
    #周星星 发表于2004-08-06 09:48:00  IP: 218.2.111.*
    请清楚您这篇文件的类属,不要将C#/Java代码放到C++类属中来。
    #猫呜 (maowu) 发表于2004-08-06 17:55:00  IP: 218.13.34.*
    加油
    发表评论  


    当前用户设置只有注册用户才能发表评论。如果你没有登录,请点击登录
    Csdn Blog version 3.1a
    Copyright © lxwde