观察者模式体验就是一个:
问题:在做UI设计时,遇到一个问题,就是当我点击一个自定义对话框的复选框时,自定义对话框的父界面必须做出相应的改变,还有,自定义对话框是做成的组件,应用于很多场合(有很多的界面用到此对话框);
//这是自定义的对话框,当事件是checkbox时,让父界面做//出改变
Public class DialogBox implements ClickListener {
Public void onClick(event e){
}
}
//这是父界面,他弹出对话框
Public class ParentFrame extends ParentClass{
Public void popDialogBox(){
DialogBox dialogbox = new DialogBox(this) ;
}
}
解决方案一:
我把父界面的父类改变成自定义的父类,让父界面继承自定义的父类:
Public class DialogBox implements ClickListener{
Private Ojbect object = null ;
Private CheckBox checkbox = new CheckBox() ;
Public DialogBox(Ojbect object){
this.object = object ;
}
Public void onClick(event e){
If(e == checkbox){
//当事件是checkbox时,强制转换成Custom调用//update方法
((Custom)object).update();
}
}
}
//这是自定义类,它继承了ParentClass,并抽象了个update方//法
Public abstact CustomClass extends ParentClass{
Public abstract void update() ;
}
Public class ParentFrame extends CustomClass {
Public void popDialogBox(){
DialogBox dialogbox = new DialogBox(this) ;
}
Public void update(){
//父界面的更新操作
}
}
这个解决方案确实解决的问题,当自定义对话框点击checkbox时,确实会使他的父界面发生改变,但他的缺点太大:必须继承CustomClass才能具备这样的功能,而且主要是因为他改变了继承关系,在ParentFrame和ParentClass之间加了一层,这样使耦合度加大了;而且每次都把自身的引用通过构造传过去(new DialogBox(this)),在dialogbox那边还要强行转换回来,确实不是什么好办法J
解决方案二:
//定义CustomListener接口
Public interface CustomListener (){
Public void update() ;
}
Public class DialogBox implements ClickListener{
Private Ojbect object = null ;
Private CheckBox checkbox = new CheckBox() ;
Public DialogBox(Ojbect object){
this.object = object ;
}
Public void onClick(event e){
If(e == checkbox){
//当事件是checkbox时,强制转换成Custom调用//update方法
((CustomListener)object).update();
}
}
}
//实现了CustomListener接口
Public class ParentFrame extends ParentClass implements CustomListener {
Public void popDialogBox(){
DialogBox dialogbox = new DialogBox(this) ;
}
Public void update(){
//父界面的更新操作
}
}
这个解决方案也是解决了问题,尤其对第一解决方案而言,这个已经不会打乱继承关系,但是还没有脱离第二个缺陷;
解决方案三:
//定义Listener接口
Public interface CustomListener(){
Public void update() ;
}
//定义event接口
Public interface CustomEvent(){
Public void addCustomListener(CustomListener listener) ;
Public void removeCustomListener(CustomListener listner) ;
Public void notify() ;
}
Public class DialogBox implements ClickListener, CustomEvent {
Private List listenerList = new ArrayList() ;
Private CheckBox checkbox = new CheckBox() ;
Public DialogBox(Ojbect object){
this.object = object ;
}
Public void onClick(event e){
If(e == checkbox){
notify() ;
}
}
//注册一个观察者
Public void addCustomListener(CustomListener listener) {
listenerList.add(listener) ;
}
//删除一个观察者
Public void removeCustomListener(CustomListener listner){
listenerList.remove(listener) ;
}
//调用所以注册的观察者
Public void notify() {
If(listenerList != null && !( listenerList.isEmpty)){
for(int I = 0 ; I < listenerList.size() ; I ++){
((CustomListener)listenerList.get(i)).update();
}
}
}
}
//实现了CustomListener接口
Public class ParentFrame extends ParentClass implements CustomListener {
Public void popDialogBox(){
DialogBox dialogbox = new DialogBox() ;
dialogbox. addCustomListener((CustomListener)this) ;
}
Public void update(){
//父界面的更新操作
}
}
第三中解决方案才是观察者模式是应用,这才是较为合理的方案;
观察者模式应用前提是:有一个对象出发事件时,其他对象也发生改变;是个一对多的关系
在GWT中,在widget的方法remove是非常常用的,但请您好好注意一个小问题:
当你用循环remove时,请注意最好使用倒循环,因为如果你要是用正循环的话,remove一个widget后,他的下个widget就会到他的“位置”,例如:
int count = widgetPanel.getWidgetCount() ;
for(int i = 0 ; i < count ; i ++ )
{
widgetPanel.remove(i) ;
}
这样肯定会出异常的;
因为当你remove掉第一个widget后,第二个widget就变成第一个widget了,所以,当你认为remove掉第二个时,其实是把原来的第三个widget给remove掉了;
解决方案:
1.倒循环:
int count = widgetPanel.getWidgetCount() ;
for(int i = count - 1 ; i >= 0 ; i -- )
{
widgetPanel.remove(i) ;
}
这样就不会出现以上的情况:)
2.remove第一个;
int count = widgetPanel.getWidgetCount() ;
for(int i = 0 ; i < count ; i ++ )
{
widgetPanel.remove(0) ;
}
这种方法,也正说明了GWT转换成javascript后的特性,它是根据remove的元素,在他父元素里的“位置”进行remove,这点是和Swing最大的区别,一定要注意哟:)