现在很多JAVA书籍,在介绍SWING的事件模型时,为了使JAVA初学者容易理解和上手,通常是在主类(自己的JFrame子类或JPanel)中做所有事情:要么由主类作为监听器,在接口的方法中通过判定事件源来决定具体做何事;要么在主类中使用内隐类来实现监听器,(因为内隐类可以方便的访问主类中的成员变量)。而很多JAVA初学者在往后的编程中也采用这种模式(至少我以前是这样的)。其实,这种做法虽然简单,却缺乏可读性、可扩展性和可维护性。之前我看到了一篇关于这个问题的文章(来自于《JavaPro》),现整理出来与大家分享。
下面我将用一个简单的例子来说明如何使用模式来解决这个问题。
现在,假设我们要编写一个使用几个单选按钮从一个长列表中选择不同分组的程序,就象下图中看到的。单击Female单选按钮时,只有女孩的名字被显示;当你点击Male按钮时,程序显示男孩的名字。
这个程序最简单的版本是在主类中做每件事情,主类包含一些持有孩子姓名的数组和向量(Vector),一个另一个最简单的方法是为每个孩子创建一个对象,每个对象有一个象isFemal()这样的方法来帮助我们确定要显示的是男的还是女的。在程序中我们把每个孩子的类称为Swimmer:
public class Swimmer{ private String name; private int age; private String club; private boolean female;
public Swimmer(String name,int age,String club,boolean female){ this.name=name; this.age=age; this.club=club; this.female=female; }
public boolean isFemale(){ return female; }
public String getName(){ return name; }
public int getAge(){ return age; }
public String getClub(){ return club; } } 下一步我们要做的是决定整个列表中的哪些孩子会被显示。我们可以在主用户接口类中做到这点,但是最好的是在一个叫做Swimmers的集合类中来做,这个类有一个getList()方法,它带有布尔型的参数来决定是男孩还是女孩:
//get a vector of swimmers who are (female) public Vector getList(boolean female){ Vector v=new Vector(); for(int i=0;i<kids.size();i++){ Swimmer swm=(Swimmer)kids.elementAt(i); if(swm.isFemal()==female) v.add(swm); } return v; } 我们又提供了一个不带参数的多态getList()方法来返回整个列表。
现在让我们进入程序中的第一个重要的部分,为了显示这些孩子的姓名,我们需要为三个单选按钮都添加一个事件监听器,使得每个都能显示出正确的孩子们:
public void actionPerformed(ActionEvent e){ //listen for button clicks and do the right thing Object obj=e.getSource(); if(obj==female) loadFemales(); if(obj==male) loadMales(); if(obj==both) loadBoth(); }
private void loadFemales(){ //display female swimmers Vector v=swimmers.getList(true); loadList(v); }
private void loadMales(){ //display male swimmers Vector v=swimmers.getList(false); loadList(v); } 尽管这个方法在简单、小型的情况下工作得很好,但是它没有良好的可扩展性。如果你有10个按钮,扩展actionPerformed()方法来测试每个按钮和调用某些对应的操作,会使得可读性非常差。相反,我们最好在包含用户接口的类中去掉所有的判定。
做到这点的一个途径是使用Command模式。当我们使用这个模式的时候,需要我们创立一个Command接口:
//the Command interface public interface Command{ public void execute(); } 我们将三个单选按钮扩展成特定的带有Command接口的类,我们就可以把对命令的执行从JFrame类转移到每个按钮的类。同时,我们创建一个叫做SexButton的基类,并将ActionListener代码移到基类中,这样就不需要分别在每个按钮中添加事件监听器:
//abstract radio button class public abstract class SexButton extends JRadioButton implements Command{ protected Swimmers simmers; //JawList is a subclass of JScrollPane contained a JList; protected JawList kidList;
public SexButton(String title,Swimmers sw, JawList klist,ActionListener al){ super(title); swimmers=sw; kidList=klist; addActionListener(al); }
//abstract execute method public abstract void execute(); } 注意这个类必须被扩展以使得它有用,因为我们没有具体化execute()方法。这个基本的抽象按钮类只是我们从中导出的具体类的一个模板,这实际也是模板设计模式的一个简单例子。
//radio button to select female swimmers public class FemaleButton extends SexButton{ public execute(){ Vector v=swimmers.getList(true); loadList(v); }
private void loadList(Vector v){ kidList.clear(); for(int i=0;i<v.size();i++){ Swimmer swm=(Swimmer)v.elementAt(i); kidList.add(swm.getName()); } } } 现在,来看一下我们完成的工作。所有的按钮都成为了Command按钮了,actionPerformed()方法也简化成下面这样简单的方法:
public void actionPerformed(ActionEvent e){ Command cmd=(Command)e.getSource(); cmd.execute(); } 正如你看到的,这更加简单而且完全是可扩展的。
|