十二 系统解耦(System decoupling)

系统解耦System decoupling

观察者(Observer)

与其它的回调形式类似,观察者包含了一个钩子,在那你能够改变代码。不同的地方在于,观察者是完全动态的。它经常应用于特定的变化,而这个变化是基于其他对象状态的改变,它也是事件管理的基本。任何时候你想要解耦调用源和被调用代码可以一种动态的方式。

观察者模式决绝了一个相当普通的问题:当某些对象改变状态时一组对象需要更新他们自身。这能被在MVC的“model-view”方面看到,或者与之差不多的“Document-view-Architecture.假设你有一些数据(“文档”)和多个视图,如一小块区域和文本视图。当你改变数据的时候,两个视图必须知道更新他们自己,这就是观察者模式的便利性。它是个足够普通的问题,所以标准java类库中有这样一些解决方案。Java中,有两种类型的对象被用来实现观察者模式。Observable类跟踪任何想要被通知的类当变化发生时,无论“状态”是否改变。当某人说“OK,每个人都应该检查和潜在的更新他们自己”,Observable类执行这项任务通过调用notifyObservers( )方法为列表上每个类。它是基类Observable的一部分。

确实有两个“变化的事物”在观察者模式中:观察对象的数量和更新他们的方式。这意味着,观察者模式允许你修改这两者不会影响到其它代码。

Observer是一个接口类,只有一个成员函数,update( )。这个函数通过被观察的对象调用,当这个对象决定去更新他所有的观察者时。参数是可选的;你可以拥有一个不带参数的update( )方法并且这仍然适合观察者模式;然而这是更一般的它允许被观察的对象传递引起更新的对象(因此一个观察者也许被注册与多于一个的被观察对象)并且任何额外的扩展信息,如果它是有帮助的,而非强迫观察者对象去捕捉谁在更新和获取任何所需要的信息。

“被观察的对象”决定什么时候和怎样去更新将被称作Observable

Observable有一个标识指示出是否它被改变了。在一个简单的设计中,没有标识的;如果有事情发生,任何人将被通知到。标识允许你等待,并且仅当你决定通知观察者时。注意,标识的状态控制为protected因此只有继承者才能决定什么构成一个变化,并非被派生的Observer最终结果的用户。

大多数的任务被执行在notifyObservers( )中。如果变化的标识没有被设置,这不会做任何事。否则,第一个清除变化标识如此的重复调用notifyObservers( )不会浪费时间。这在通知观察者之前被调用一旦调用update( )来执行引起变化回调到Observable的对象。然后遍历这个集合并且回调每一个观察者update( )成员函数。

首先你能够使用普通的Observable对象来管理更新。但是这不会工作,若获得效果的话,你必须从Observable继承,并且在你的派生类代码中调用setChanged( )方法。这是设置“变化”标识的成员函数,它意味着当你调用notifyObservers( ),所有的观察者将,事实上,获得通知。在那你调用setChanged( )依赖于程序逻辑。


Observing flowers

下面是一个观察者模式的例子:

//: observer:ObservedFlower.java

// Demonstration of "observer" pattern.

package observer;

import java.util.*;

import junit.framework.*;

class Flower {

private boolean isOpen;

private OpenNotifier oNotify =

new OpenNotifier();

private CloseNotifier cNotify =

new CloseNotifier();

public Flower() { isOpen = false; }

public void open() { // Opens its petals

isOpen = true;

oNotify.notifyObservers();

cNotify.open();

}

public void close() { // Closes its petals

isOpen = false;

cNotify.notifyObservers();

oNotify.close();

}

public Observable opening() { return oNotify; }

public Observable closing() { return cNotify; }

private class OpenNotifier extends Observable {

private boolean alreadyOpen = false;

public void notifyObservers() {

if(isOpen && !alreadyOpen) {

setChanged();

super.notifyObservers();

alreadyOpen = true;

}

}

public void close() { alreadyOpen = false; }

}

private class CloseNotifier extends Observable{

private boolean alreadyClosed = false;

public void notifyObservers() {

if(!isOpen && !alreadyClosed) {

setChanged();

super.notifyObservers();

alreadyClosed = true;

}

}

public void open() { alreadyClosed = false; }

}

}

class Bee {

private String name;

private OpenObserver openObsrv =

new OpenObserver();

private CloseObserver closeObsrv =

new CloseObserver();

public Bee(String nm) { name = nm; }

// An inner class for observing openings:

private class OpenObserver implements Observer{

public void update(Observable ob, Object a) {

System.out.println("Bee " + name

+ "'s breakfast time!");

}

}

// Another inner class for closings:

private class CloseObserver implements Observer{

public void update(Observable ob, Object a) {

System.out.println("Bee " + name

+ "'s bed time!");

}

}

public Observer openObserver() {

return openObsrv;

}

public Observer closeObserver() {

return closeObsrv;

}

}

class Hummingbird {

private String name;

private OpenObserver openObsrv =

new OpenObserver();

private CloseObserver closeObsrv =

new CloseObserver();

public Hummingbird(String nm) { name = nm; }

private class OpenObserver implements Observer{

public void update(Observable ob, Object a) {

System.out.println("Hummingbird " + name

+ "'s breakfast time!");

}

}

private class CloseObserver implements Observer{

public void update(Observable ob, Object a) {

System.out.println("Hummingbird " + name

+ "'s bed time!");

}

}

public Observer openObserver() {

return openObsrv;

}

public Observer closeObserver() {

return closeObsrv;

}

}

public class ObservedFlower extends TestCase {

Flower f = new Flower();

Bee

ba = new Bee("A"),

bb = new Bee("B");

Hummingbird

ha = new Hummingbird("A"),

hb = new Hummingbird("B");

public void test() {

f.opening().addObserver(ha.openObserver());

f.opening().addObserver(hb.openObserver());

f.opening().addObserver(ba.openObserver());

f.opening().addObserver(bb.openObserver());

f.closing().addObserver(ha.closeObserver());

f.closing().addObserver(hb.closeObserver());

f.closing().addObserver(ba.closeObserver());

f.closing().addObserver(bb.closeObserver());

// Hummingbird B decides to sleep in:

f.opening().deleteObserver(

hb.openObserver());

// A change that interests observers:

f.open();

f.open(); // It's already open, no change.

// Bee A doesn't want to go to bed:

f.closing().deleteObserver(

ba.closeObserver());

f.close();

f.close(); // It's already closed; no change

f.opening().deleteObservers();

f.open();

f.close();

}

public static void main(String args[]) {

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

}

} ///:~

有趣的事是一朵花能够开放或者关闭。因为内部类惯用法,开放和关闭能够分离可观察的现象。OpenNotifier CloseNotifier类都是从Observable类继承而来,因此他们访问setChanged( )方法,并且能够能够被调用对于任何需要一个Observable类。

内部类惯用法也能够便利的定义多于一种观察者,在Bee Hummingbird,因为这两个类也许想要独立的观察花开花谢。注意内部类惯用法怎样提供大多数继承的好处而无关乎同样的限制(例如,外部类访问私有数据的能力)。

在主函数main( )中,你能够看到观察者模式的一个主要的好处:运行时改变行为的能力,通过动态的注册和注销Observables的观察者。

如果你研究上面的代码,你将看到OpenNotifierCloseNotifier使用基本的Observable 接口。这意味着你能够继承其他完全不同的观察者类;唯一的用Flowers关联到ObserversObserver 接口。

一个可视化的观察者的例子

下面的例与《Think in java》第十四章中的ColorBoxes子相似。盒子被放在屏幕中的网格上,并且每一个被初始化为一个随机颜色。此外,每个盒子实现了Observer接口。并且被注册以一个Observable 对象。当你单击一个盒子的时候,所有的另外的盒子被通知变化发生了,因为Observable 对象自动调用每一个Observer对象的update( ) 方法。在这个方法中,盒子检查是否它与被点击的盒子相邻,如果相邻的话,它的颜色会变成与被点击的盒子一致。

//: observer:BoxObserver.java

// Demonstration of Observer pattern using

// Java's built-in observer classes.

package observer;

import javax.swing.*;

import java.awt.*;

import java.awt.event.*;

import java.util.*;

// You must inherit a new type of Observable:

class BoxObservable extends Observable {

public void notifyObservers(Object b) {

// Otherwise it won't propagate changes:

setChanged();

super.notifyObservers(b);

}

}

public class BoxObserver extends JFrame {

Observable notifier = new BoxObservable();

public BoxObserver(int grid) {

setTitle("Demonstrates Observer pattern");

Container cp = getContentPane();

cp.setLayout(new GridLayout(grid, grid));

for(int x = 0; x < grid; x++)

for(int y = 0; y < grid; y++)

cp.add(new OCBox(x, y, notifier));

}

public static void main(String[] args) {

int grid = 8;

if(args.length > 0)

grid = Integer.parseInt(args[0]);

JFrame f = new BoxObserver(grid);

f.setSize(500, 400);

f.setVisible(true);

f.setDefaultCloseOperation(EXIT_ON_CLOSE);

}

}

class OCBox extends JPanel implements Observer {

Observable notifier;

int x, y; // Locations in grid

Color cColor = newColor();

static final Color[] colors = {

Color.BLACK, Color.BLUE, Color.CYAN,

Color.DARK_GRAY, Color.GRAY, Color.GREEN,

Color.LIGHT_GRAY, Color.MAGENTA,

Color.ORANGE, Color.PINK, Color.RED,

Color.WHITE, Color.YELLOW

};

static Random rand = new Random();

static final Color newColor() {

return colors[rand.nextInt(colors.length)];

}

OCBox(int x, int y, Observable notifier) {

this.x = x;

this.y = y;

notifier.addObserver(this);

this.notifier = notifier;

addMouseListener(new ML());

}

public void paintComponent(Graphics g) {

super.paintComponent(g);

g.setColor(cColor);

Dimension s = getSize();

g.fillRect(0, 0, s.width, s.height);

}

class ML extends MouseAdapter {

public void mousePressed(MouseEvent e) {

notifier.notifyObservers(OCBox.this);

}

}

public void update(Observable o, Object arg) {

OCBox clicked = (OCBox)arg;

if(nextTo(clicked)) {

cColor = clicked.cColor;

repaint();

}

}

private final boolean nextTo(OCBox b) {

return Math.abs(x - b.x) <= 1 &&

Math.abs(y - b.y) <= 1;

}

} ///:~

当你第一次看到有关Observable类的在线文档时,它有点令人疑惑,因为看起来你可以使用一个普通的Observable对象管理更新。但是这样不会工作的;在BoxObserver类中试一下,创建一个Observable对象而非一个BoxObservable 对象并且看看什么会发生:什么也不会。去获得一个效果,你必须从Observable继承并且在你的派生类的某个地方调用setChanged( )方法。这个方法设置“变化”标识,这意味着当你调用notifyObservers( )方法时,所有的观察者事实上会被通知到。上面的例子中setChanged( )被简单的调用在notifyObservers( )方法中,但是你能够使用任何标准决定什么时候调用setChanged( )方法。

BoxObserver类包含单一个被称作notifierObservable对象,每次OCBox对象被创建时,它被关联到notifier。在OCBox对象中,无论什么时候你单击鼠标notifyObservers( )方法都会被调用,作为一个参数传递这个被单击的对象,以至于所有的盒子都会接收到这个消息(在它们的update( ) 方法中),知道谁被点击了并且能够决定是否去改变它们自身。使用一个代码的组合在notifyObservers( ) update( )中,你能够做出相当复杂的架构。

也许观察者被通知的方式必须在编译时被冻结在notifyObservers( )方法中。然而,如果你仔细的查看上面的代码,你将看到在BoxObserverOCBox中唯一的地方,在那你知道你正以BoxObservable工作,是在创建Observable对象的这点上从那时起每个对象使用基本的Observable接口。这意味着你能够从其他的Observable类继承并且运行时交换它们,如果你想要改变通知的行为。

调停者模式(Mediator

清理内部耦合,它与MVC有什么不同呢?

MVC有严格区分的model层和view层;调停者能够是任何东西。MVC更像是一个调停者。

练习Exercises

1. 创建一个最小的Observer-Observable设计在两个类中。仅仅在两个类中最小化创建,然后描述你的设计,通过创建一个Observable和许多Observers对象,并且用Observable对象更新Observers

2. 创建一个最小化的Observer 系统,使用java.util.TimerObservable类中,产生事件报告给 Observers对象.使用内部匿名类创建几个不同的 Observers, 用Observable注册这些内部类, Timer 事件发生时显示

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/25966/viewspace-53325/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/25966/viewspace-53325/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值