在第一部分中,我们一起回顾了常见的GoF设计模式然后用Java 8的语法重写了一个更加干净,简洁的实现。第二部分我们继续还是这样的研究方式,一起看一下两个广泛使用的GoF设计模式:Template和Observer,然后我们使用Java 8的Consumer接口重新实现它们。
Template(模板)模式
通过一个例子来看Template模式,我们定义一个Resource类
public class Resource { public Resource() { System.out.println("Resource created"); } public void useResource() { riskyOperation(); System.out.println("Resource used"); } public void employResource() { riskyOperation(); System.out.println("Resource employed"); } public void dispose() { System.out.println("Resource disposed"); } private void riskyOperation() { if ( new Random().nextInt( 10 ) == 0) { throw new RuntimeException(); } } }
我们的Resource类有一个构造函数,一些使用资源的方法,这些方法可能会调用失败,比如是一个数据库或网络连接,失败之后会抛出RuntimeException。这里我们通过随机方式来模拟出错。无论成功与否,最后都是调用dispose来释放相关资源,比如内存、数据库连接、打开的文件。如果用如下方式使用这个类:
Resource resource = new Resource(); resource.useResource(); resource.employResource(); resource.dispose();
是错的,因为useResource、employResource可能会失败。当抛出异常的时候资源没有办法释放。所以正确的姿势应该是:
Resource resource = new Resource(); try { resource.useResource(); resource.employResource(); } finally { resource.dispose(); }
如果要避免资源泄露就必须强制API调用者遵循这种代码结构。换句话说,我们要保证所有资源操作都遵循这样的代码结构:
openResource(); try { doSomethingWithResource(); } finally { closeResource(); }
问题是,我们不能保证所有人调用的时候都遵循这种模式。所以我们需要一个抽象模板类,来封装这样的“代码结构”:
public abstract class AbstractResourceManipulatorTemplate { protected Resource resource; private void openResource() { resource = new Resource(); } protected abstract void doSomethingWithResource(); private void closeResource() { resource.dispose(); resource = null; } public void execute() { openResource(); try { doSomethingWithResource(); } finally { closeResource(); } } }
这个抽象模板封装我们使用资源的方式,具体的对资源的操作我们在子类中实现(doSomethingWithResource)
public class ResourceUser extends AbstractResourceManipulatorTemplate { @Override protected void doSomethingWithResource() { resource.useResource(); } } public class ResourceEmployer extends AbstractResourceManipulatorTemplate { @Override protected void doSomethingWithResource() { resource.employResource(); } }
API使用者只需要调用execute,根据不同的子类实现doSomethingWithResource使用资源。execute中的代码结构保证了资源最终肯定会被释放。
new ResourceUser().execute(); new ResourceEmployer().execute();
Template模式可以帮我们实现对资源的管理,并且我们看到了GoF所采用的实现有多么的长。。长。。长。如果是函数式思路来做,我们只需要定义一个接受Consumer接口作为参数的方法:
public static void withResource( Consumer<Resource> consumer) { Resource resource = new Resource(); try { consumer.accept( resource ); } finally { resource.dispose(); } }
注意这个方法相当于上面的execute方法,唯一的区别是模板的doSomethingWithResource的方法被Consumer参数代替了。子类中使用资源的业务逻辑被lambda表达式代替了:
withResource( resource -> resource.useResource() ); withResource( resource -> resource.employResource() );
Observer(观察者)模式
观察者模式是一个很常见、广泛使用的设计模式。它解决的问题是,当触发某个事件的时候可以通知一个或多个对象响应该事件。一般我们会定义一个Listener接口:
interface Listener { void onEvent(Object event); }
当一个对象需要响应或者监听事件,只需要实现这个接口,然后在onEvent中编写处理事件的代码。与之对应的我们还需要定义一个Observable,它用来注册监听的事情,并且在事件到来之后把事件转发给的Listener。
public class Observable { private final Map<Object, Listener> listeners = new ConcurrentHashMap<>(); public void register(Object key, Listener listener) { listeners.put(key, listener); } public void unregister(Object key) { listeners.remove(key); } public void sendEvent(Object event) { for (Listener listener : listeners.values()) { listener.onEvent( event ); } } }
我们可以用这种内部类的方式来实现Listener,注册自己感兴趣的事件:(注意Observer1这个时候不需要实现Listener接口)
public class Observer1 { Observer1(Observable observable) { observable.register( this, new Listener() { @Override public void onEvent( Object event ) { System.out.println(event); } } ); } }
可以直接通过实现Listener接口
public class Observer2 implements Listener { Observer2(Observable observable) { observable.register( this, this ); } @Override public void onEvent( Object event ) { System.out.println(event); } }
两个观察者的使用方式是一样的,当发送一个事件后会广播给所有的观察者:
Observable observable = new Observable(); new Observer1( observable ); new Observer2( observable ); observable.sendEvent( "Hello World!" );
和所有GoF模式犯的错误一样:把“函数”封装成一个对象(Java中的事件监听本质上就是一个函数),要么通过一个匿名类,要么单独实现一个类(这种类往往只有一个方法)。如果使用Java 8的特性,我们可以通过Consumer来替代Listener,在调用的时候直接传递“事件处理函数”,不用定义一大堆类。
Observable observable = new Observable(); observable.register( "key1", e -> System.out.println(e) ); observable.register( "key2", System.out::println ); observable.sendEvent( "Hello World!" );
在接下来的第三部分,我们继续尝试用函数式的方式来实现GoF模式中的——Decorator和Chain Of Responsibility模式。




