监听器设计模式总结

[size=x-large]监听器模式设计总结[/size]

[size=large][color=red]为何需要用到监听器设计模式?[/color]


假设现在有这样一个情景:我们要在一个文本区域中显示一些我们指定的信息。这个信息假设是在一个线程中每隔三秒发布一个信息并显示在该文本区域,我们一般的做法会在,窗体界面类中实例化这样一个文本区域对象,然后再实例化一个信息产生对象(即前面所说的线程),在实例化这个信息产生对象的时候将该文本区域对象作为一个参数传到信息产生的类内部,并在信息产生代码语句后添加该文本区域对信息的处理。(此处为将信息添加到文本区域中)。说的可能有点模糊。请看以下代码,相信很好理解。

先定义一个信心产生类(利用线程),和一个信息类,代码如下:[/size]

package 监听器设计模式;

import java.util.ArrayList;

import javax.swing.JTextArea;;

public class NetConnector extends Thread{
private JTextArea textarea;

public NetConnector(JTextArea textArea){
this.textarea=textArea;
}
public void run(){
//线程运行时模拟产生信息
int t=0;
while(true){
try {
sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
t++;
Msg msg=new Msg();
msg.setId(t);
msg.setContent("第"+t+"条信息\n");
textarea.append(msg.getContent());
}


}

}

class Msg{
private int id; //消息id
private String content;//消息内容
public String toString(){//重写的toString方法
return content;
}
public int getId(){
return id;
}
public void setId(int id){
this.id=id;
}
public String getContent(){
return content;
}
public void setContent(String content){
this.content=content;
}
}


[size=large]在主界面的代码:[/size]
package 监听器设计模式;

import java.awt.FlowLayout;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;

public class UI extends JFrame{
public static void main(String[] args){
UI ui=new UI();
ui.show_Ui();
}

public void show_Ui(){
this.setTitle("测试界面");
this.setDefaultCloseOperation(3);
this.setLayout(new FlowLayout());
this.setLocationRelativeTo(null);
this.setSize(400,300);
JTextArea textarea=new JTextArea(10,20);
JScrollPane jscollpane=new JScrollPane(textarea);
NetConnector connector=new NetConnector(textarea);
connector.start();

this.add(jscollpane);
this.setVisible(true);
}
}


[size=large]那么这样在主界面的文本区域内就能3秒产生一条信息。

[color=red]这样的设计有什么弊端呢?[/color]

假设我们要在另外一个组件中也对这些信息做相应的处理,按照这种做法就必须在在实例化信息产生对象的时候在传入相应组件的参数,然后再信息产生代码后再添加相应组件对信息的处理,要是后面有更多的组件要处理这些相同的信息,那么我们就每次都要去修改信息产生的构造方法。并要去添加信息产生后相应组件处理信息的代码,这些都必须要去修改源代码,显然这样会使代码代码质量下降。那么我们该怎么去处理这样一个问题呢?下面的监听器模式能帮你解决。

回想我们以前用过的监听器,比如鼠标监听器、键盘监听器等。拿鼠标监听器来讲。我们用一个自定义类去实现鼠标监听器,实现里面的方法,如鼠标按下的方法。这个过程它是怎么来处理的?当我们按下鼠标,这个就相当于一条信息的发送。那么这条信息要怎么处理呢?这个只要在鼠标Press的方法中实现即可。然后将这个实现了鼠标监听器的类对象添加给某个处理该信息的组件,如一个面板。要是你想在其他组件如一个窗体上,那么同样只要实现这个鼠标监听器的接口,在相应的处理方法中实现我们对信息的处理,在将实现了该鼠标监听器接口的对象添加给窗体。想想这个模式和我们上面涉及到的对同一些信息要多种组件来处理的是不是一样的?答案是是的,完全一样。这样的做法并不用像上面举到的例子一样要去修改来原来的代码,只要做相应的添加就行了。要是还不是很理解,请看如下监听器设计模式的步骤。(该处还是以上面多种组件来处理同一信息的情况)


既然是对同一信息的处理。我们先定义这个一个接口,该接口中有一个信息处理的方法。

接口如下:[/size]
package 监听器设计模式;

public interface IMsgReciverListener {
public void reciveMsg(Msg m);//监听器中必须实现的处理消息的方法
}



[size=large]某一个组件要去处理这类信息那么它就必须去实现这个接口。并实现相应的信息的处理的方法。如上面所说的文本区域。


那么实现了接口,就要实现了相应的方法。最后的工作只要去调用这个方法了。这个方法要在什么地方调用呢?最佳答案是在信息产生的类内部。这边就要考虑到这样一个问题,这个信息是要在多种组件下同时做处理的。那么该如何将这些能处理信息对像(即实现了接口的类的实例化对象)放置在一起呢?有这样一个想法,当我们实例化一个这样的对象时我们就动态地将这个对象添加到信息产生的类内部,并用一个队列保存,那么要去调用全部的这些对象的方法就迎刃而解了。只要遍历队列,取出所有的对象,并调用相应的信息处理方法即可。


以下代码是对消息产生类的修改(实现监听器设计模式):[/size]

package 监听器设计模式;

import java.util.ArrayList;

import javax.swing.JTextArea;

public class NetConnector extends Thread{
//内部保存消息监听器对象的队列
private ArrayList<IMsgReciverListener> listeners=new ArrayList();
public void run(){
//线程运行时模拟产生信息
int t=0;
while(true){
try {
sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
t++;
Msg msg=new Msg();
msg.setId(t);
msg.setContent("第"+t+"条信息\n");
//遍历队列调用相应信息处理方法
fireMsgRecive(msg);
}


}

//给NetConnector对象中增加一个监听器的方法调用
public void addListener(IMsgReciverListener listener){
listeners.add(listener);
}
//将接收到的消息通知到队列中的所有监听器对象去处理
private void fireMsgRecive(Msg m){
for(int i=0;i<listeners.size();i++){
listeners.get(i).reciveMsg(m);
}
}

}



[size=large]还要定义实现了接口的文本区域类:[/size]
package 监听器设计模式;

import javax.swing.JTextArea;

public class MyTextArea extends JTextArea implements IMsgReciverListener{

@Override
public void reciveMsg(Msg m) {
this.append(m.getContent());
}

}


[size=large]在主界面中代码修改如下:[/size]

package 监听器设计模式;

import java.awt.FlowLayout;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;

public class UI extends JFrame{
public static void main(String[] args){
UI ui=new UI();
ui.show_Ui();
}

public void show_Ui(){
this.setTitle("测试界面");
this.setDefaultCloseOperation(3);
this.setLayout(new FlowLayout());
this.setLocationRelativeTo(null);
this.setSize(400,300);
MyTextArea textarealistener=new MyTextArea(200,150);
JScrollPane jscollpane=new JScrollPane(textarealistener);
NetConnector connector=new NetConnector();
connector.addListener(textarealistener);
connector.start();

this.add(jscollpane);
this.setVisible(true);
}
}



[size=large]想看效果,可以运行下啊。[/size]

[size=large]这样,要是我们想在另外一个组件比如一个按钮上让他显示最新产生的消息,我们只要自定义一个继承JButton的类并实现上述接口,在信息处理方法中做我们想要的信息处理方法就可以。代码如下:[/size]
package 监听器设计模式;

import javax.swing.JButton;

public class MyButton extends JButton implements IMsgReciverListener{

@Override
public void reciveMsg(Msg m) {
this.setText(m.getContent());
}

}


[size=large]然后再主界面类中实例化我们实现了接口的按钮,这时这个按钮也是一个监听器对象(由于实现了接口),别忘了将该监听器添加给消息产生类中的队列。最后在把按钮添加到界面上即可。这个自己动手尝试下。[/size]


[size=large]以下是我的一些看法:
1.很多书上可能较多用监听术语来描述,类似事件源、事件处理着。上述对应于事件源的就是消息产生的类,要处理的信息就是这里产生的。那么我们实现了的事件处理方法就要在这里调用,(将消息传入)。事件处理者就是上述实现了事件处理接口的那些自定义类(当然继承了系统提供的组件类)。可以看出,用这样的方式,事件源和事件处理者是独立的。这样可以减少代码的耦合性。

2.将所有要处理事件的类都实现了事件处理接口,这本身就是理所当然,由于它要对事件进行处理,必须实现,而后在调用的时候,不同组件却可以被当成同一类型的对象存在队列中,并遍历调用。也正是由于实现了同一接口,(可以理解为他们都是这个接口类的一个对象),不同组件对象可以当成同一类型对象也就显得顺理成章。

3.大家可按自己的理解去理解监听器设计模式,而不用刻意按监听器术语去理解他,我昨天刚看hu总给的文档时,看前面写的那些也是一头晕,当然自己看了一遍后按自己的理解整理了一下思路,然后再去类比那些术语,这样感觉那些知识一下子浅显了了很多。[/size]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值