Observer模式的宗旨是在多个对象之间定义一对多的关系,以便当一个对象状态改变的时候,其他所有依赖于这个对象的对象都能够得到通知,并被自动更新。
1.经典范例:
借助于Observer模式,当某个对象发生变化时,关注该对象的其他对象可以被通知。这种模式的一个最常见的例子是图形用户界面。每当图形界面的用户单击按钮或者调整滑动条的时候,该应用程序中许多对象都会对此做出反应。Java开发者假定应用程序都会关心图形界面用户何时改变GUI组件。很显然,Observer模式在Java Swing库中得到了广泛应用。在Java Swing架构中,关注Swing组件变化的类被称为“监听器”。我们可以为自己关心的每个事件注册一个监听器,以便组件事件发生的时候,我们能够得到通知。
ShowBallistics类和BallisticsPanel类位于app.observer.ballistics包中。BallisticsFunction接口位于com.oozinoz.ballistics包中,它定义了燃烧速率和推进力曲线。该Java包中还有一个Ballisticstics工具类,它实现了BallisticsFunction接口。
这个弹道学应用程序在初始化滑动条的时候,将自己注册为滑动条事件的监听器。当滑动条滑动的时候,该应用程序就会更新用于显示曲线的面板和用于显示tpeak值的标签。
习题1:请完成ShowBallistics类的slider()方法和stateChanged()方法,该绘图面板和tpeak标签能够根据滑动条值的改变而显示不同的曲线和数据。
public JSlider slider()
{ if(slider == null)
{ slider = new JSlider();
sliderMax = slider.getMaximum();
sliderMin = slider.getMinimum();
slider.addChangeListener(this);
slider.setValue(slider.getMinimum());
}
return slider;
}
public void stateChanged(ChangeEvent e)
{ double val = slider.getValue();
double tp = (val - sliderMin)/(sliderMax - sliderMin);
burnPanel().setTPeak(tp);
thrustPanel().setTPeak(tp);
valueLabel().setText(Format.formatToNPlace(N,2);
}
ShowBallistics类根据滑动条的值更新燃烧速率面板、推进力面板以及显示tpeak值的标签。这种设计方式很常见,虽然它不是很糟糕,但值得注意的是,它与Observer模式的意图完全相悖!Java Swing架构采用Observer模式,以便滑动条无需了解那些用户关注自己。但是ShowBallistics使我们重新面临这种本希望避免的状况:将单个依赖滑动条组件的对象---即它自身--注册为滑动条事件的监听器,在得到滑动条事件通知之后,该对象又将事件分发给各个相关的对象。在这种设计方式中,该对象必须清楚哪些对象依赖于滑动条组件,而这些依赖滑动条组件的对象并没有注册自己以监听滑动条事件。
为了与Observer模式的意图保持一致,我们可以对上面的代码进行一下改动,从而让关注滑动条变化的组件直接注册自己以监听滑动条事件。这种新的设计的类图如下:
在新的设计中,我们可以将addChangeListener()方法的调用从slider()方法中移出来,移到各个依赖滑动条的组件的构造器中:
public BallisticsPanel2(BallisticsFunction func,JSlider slider)
{
this.func = func;
this.slider = slider;
slider.addChangeListener(this);
}
每当滑动条滑动BallisticsPanel2类对象便会得到通知,然后标签会重新计算并显示tpeak值: