(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( )依赖于程序逻辑。
下面是一个观察者模式的例子:
//: 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的观察者。
如果你研究上面的代码,你将看到OpenNotifier和CloseNotifier使用基本的Observable 接口。这意味着你能够继承其他完全不同的观察者类;唯一的用Flowers关联到Observers是Observer 接口。
一个可视化的观察者的例子
下面的例与《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类包含单一个被称作notifier的Observable对象,每次OCBox对象被创建时,它被关联到notifier。在OCBox对象中,无论什么时候你单击鼠标notifyObservers( )方法都会被调用,作为一个参数传递这个被单击的对象,以至于所有的盒子都会接收到这个消息(在它们的update( ) 方法中),知道谁被点击了并且能够决定是否去改变它们自身。使用一个代码的组合在notifyObservers( ) 和 update( )中,你能够做出相当复杂的架构。
也许观察者被通知的方式必须在编译时被冻结在notifyObservers( )方法中。然而,如果你仔细的查看上面的代码,你将看到在BoxObserver或OCBox中唯一的地方,在那你知道你正以BoxObservable工作,是在创建Observable对象的这点上—从那时起每个对象使用基本的Observable接口。这意味着你能够从其他的Observable类继承并且运行时交换它们,如果你想要改变通知的行为。
Mediator)
清理内部耦合,它与MVC有什么不同呢?
MVC有严格区分的model层和view层;调停者能够是任何东西。MVC更像是一个调停者。
Exercises
1. 创建一个最小的Observer-Observable设计在两个类中。仅仅在两个类中最小化创建,然后描述你的设计,通过创建一个Observable和许多Observers对象,并且用Observable对象更新Observers。
2. 创建一个最小化的Observer 系统,使用java.util.Timer 在Observable类中,产生事件报告给 Observers对象.使用内部匿名类创建几个不同的 Observers, 用Observable注册这些内部类, 当Timer 事件发生时显示
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/25966/viewspace-53325/,如需转载,请注明出处,否则将追究法律责任。