ALL is Well!

敏捷是一条很长的路,摸索着前进着

  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  30 随笔 :: 23 文章 :: 71 评论 :: 0 Trackbacks

做Swing桌面程序,该怎样将组件与业务逻辑分离?

这是一个问题。

因为没有深入学习过这方面的知识,所以自己也没有想过如何能实现这种分离。

今天有个朋友用Swing做了一个小的桌面程序,是一个简单的管理系统。

代码很复杂,主要是写的很复杂,没有逻辑和层次感,

到处是组件,到处是判断,每追加一个功能,代码就要翻来翻去找很久。

于是今天晚上自己闲来无事,简单的写了一些代码,

看是否可以将组件与业务逻辑分离开呢?

如果大侠看到了不要见笑啊~

首先,我要的是一个登录界面,

界面很简单,上面留一个JLabel的位置,输出错误提示信息用。

然后下面就是 用户名和密码 的输入框。

见下图:


待用户输入用户名和密码后,点击登录按钮,根据用户名和密码进行验证。

如果用户名和密码为空,则报错。

如果填写了用户名和密码,则认为登录成功,由于只是演示,就没有真正的去实现如何验证。

现在代码的结构是这样的。

|--com

    |--bzwm

        |--testp

            |--common

            |--event

            |--frame

            |--listener

如上图,主要由4个包。

common包,放一些通用的类,可以有一些工具类等等。

这次我将EventMgr.java放在common包下,用来管理事件。

event包,放入每个动作的event,

我先定义了一个抽象类BaseEvent.java

然后是本次要实现的功能的事件:LogonEvent.java

frame包,是一些画面的实现类。

本次登录用的LoadFrame.java放在这个包下。

listener包,是放具体的业务逻辑实现类的。

首先定义接口 IListener.java

画面实现 IListener 接口,方便回调回主画面,方便根据业务逻辑处理结果来重画组件。

然后定义接口 IService.java

业务逻辑类要实现 IService 接口,

我在这个接口中定义了两个方法,

第一个方法,检查用户的输入数据是否正确,或者说操作是否正确,

第二个方法,根据用户的输入和事件,来完成业务逻辑处理。

本次登录用的逻辑类 LogonService.java 实现了IService 接口。

好,结构介绍完了,

来看代码。

1.IListener.java

 1package com.bzwm.testp.listener;   
 2  
 3import com.bzwm.testp.event.BaseEvent;   
 4  
 5/**  
 6 * @author bzwm  
 7 *   
 8 */
  
 9public interface IListener {   
10  
11    /**  
12     * 处理事件结束  
13     * 这里将回调到画面上,方便对画面进行重画  
14     * @param event  
15     */
  
16    public void eventCompleted(BaseEvent event);   
17}
  


2.IService.java
 1package com.bzwm.testp.listener;   
 2  
 3import com.bzwm.testp.event.BaseEvent;   
 4  
 5/**  
 6 * @author bzwm  
 7 *  
 8 */
  
 9public interface IService {   
10    /**  
11     * 开始处理事件  
12     * 对用户的输入,操作做初步的检查,如果检查失败,则没有必要继续向下进行  
13     * @param event  
14     */
  
15    public void eventStarted(BaseEvent event);   
16  
17    /**  
18     * 处理事件  
19     * 进行一些逻辑处理  
20     * 得到单纯的数据,不用考虑画面上的组件  
21     * @param event  
22     */
  
23    public void service(BaseEvent event);   
24}
  

3.LoadFrame.java

主画面,程序的入口,实现了IListener接口

  1package com.bzwm.testp.frame;   
  2  
  3import com.bzwm.testp.common.EventMgr;   
  4import com.bzwm.testp.event.BaseEvent;   
  5import com.bzwm.testp.event.LogonEvent;   
  6import com.bzwm.testp.listener.IListener;   
  7import com.bzwm.testp.listener.LogonService;   
  8import java.awt.BorderLayout;   
  9import java.awt.Color;   
 10import java.awt.FlowLayout;   
 11import java.awt.event.ActionEvent;   
 12import java.awt.event.ActionListener;   
 13import javax.swing.JButton;   
 14import javax.swing.JFrame;   
 15import javax.swing.JLabel;   
 16import javax.swing.JPanel;   
 17import javax.swing.JPasswordField;   
 18import javax.swing.JTextField;   
 19  
 20/**  
 21 * @author bzwm  
 22 *   
 23 */
  
 24public class LoadFrame extends JFrame implements IListener {   
 25    /** 标题:用户ID */  
 26    private JLabel userIdLbl = null;   
 27  
 28    /** 输入框:用户ID */  
 29    private JTextField userIdTxt = null;   
 30  
 31    /** 标题:密码 */  
 32    private JLabel passwordLbl = null;   
 33  
 34    /** 输入框:密码 */  
 35    private JPasswordField passwordTxt = null;   
 36  
 37    /** 登录按钮 */  
 38    private JButton logonBtn = null;   
 39  
 40    /** 重置按钮 */  
 41    private JButton resetBtn = null;   
 42  
 43    /** 新用户注册按钮 */  
 44    private JButton registerBtn = null;   
 45  
 46    /** 错误提示信息 */  
 47    private JLabel errorLbl = null;   
 48  
 49    EventMgr mrg = null;   
 50  
 51    /**  
 52     * 构造方法  
 53     *   
 54     * @param title  
 55     *            标题  
 56     */
  
 57    public LoadFrame(String title) {   
 58        super(title);   
 59        initComponents();   
 60        layoutComponents();   
 61        mrg = new EventMgr();   
 62        // 追加监听器   
 63        mrg.addListener(this);   
 64        mrg.addService(new LogonService());   
 65    }
   
 66  
 67    /**  
 68     * 对组件进行初始化  
 69     *   
 70     */
  
 71    private void initComponents() {   
 72        userIdLbl = new JLabel("用户名");   
 73        userIdTxt = new JTextField(10);   
 74  
 75        passwordLbl = new JLabel("密码");   
 76        passwordTxt = new JPasswordField(10);   
 77  
 78        logonBtn = new JButton("登录");   
 79        resetBtn = new JButton("重置");   
 80        registerBtn = new JButton("注册");   
 81  
 82        errorLbl = new JLabel();   
 83        errorLbl.setForeground(Color.RED);   
 84    }
   
 85  
 86    /**  
 87     * 进行布局,给组件添加事件监听  
 88     *   
 89     */
  
 90    private void layoutComponents() {   
 91        JPanel topPnl = new JPanel();   
 92        topPnl.add(errorLbl);   
 93  
 94        JPanel mainPnl = new JPanel(new BorderLayout());   
 95        JPanel inputPnl = new JPanel(new java.awt.GridLayout(2222));   
 96        inputPnl.add(userIdLbl);   
 97        inputPnl.add(userIdTxt);   
 98        inputPnl.add(passwordLbl);   
 99        inputPnl.add(passwordTxt);   
100        inputPnl.setSize(280100);   
101        mainPnl.add(inputPnl, BorderLayout.NORTH);   
102  
103        JPanel footPnl = new JPanel(new FlowLayout(FlowLayout.RIGHT));   
104        logonBtn.addActionListener(new ActionListener() {   
105            public void actionPerformed(ActionEvent e) {   
106                logonBtnClick();   
107            }
   
108        }
);   
109        resetBtn.addActionListener(new ActionListener() {   
110            public void actionPerformed(ActionEvent e) {   
111                resetBtnClick();   
112            }
   
113        }
);   
114           
115        registerBtn.addActionListener(new ActionListener() {   
116            public void actionPerformed(ActionEvent e) {   
117                regBtnClick();   
118            }
   
119        }
);   
120           
121        footPnl.add(logonBtn);   
122        footPnl.add(resetBtn);   
123        footPnl.add(registerBtn);   
124        mainPnl.add(footPnl, BorderLayout.SOUTH);   
125        this.add(topPnl, BorderLayout.NORTH);   
126        this.add(mainPnl, BorderLayout.CENTER);   
127        this.setLocation(200200);   
128        this.setSize(300300);   
129        this.setVisible(true);   
130        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);   
131    }
   
132  
133    /**  
134     *   
135     */
  
136    private void logonBtnClick() {   
137        new Thread() {   
138            public void run() {   
139                mrg.dispatch(new LogonEvent(userIdTxt.getText(), passwordTxt.getText()));   
140            }
   
141        }
.start();   
142    }
   
143       
144    /**  
145     *   
146     */
  
147    private void resetBtnClick() {   
148        userIdTxt.setText("");   
149        passwordTxt.setText("");   
150    }
   
151       
152    /**  
153     *   
154     */
  
155    private void regBtnClick() {   
156    }
   
157  
158    /*  
159     * (non-Javadoc)  
160     *   
161     * @see com.bzwm.testp.listener.IListener#eventCompleted(com.bzwm.testp.event.BaseEvent)  
162     */
  
163    public void eventCompleted(BaseEvent event) {   
164        String error = event.getError();   
165        if (error.equals("")) {   
166            errorLbl.setText("登录成功");   
167        }
 else {   
168            errorLbl.setText(error);   
169        }
   
170    }
   
171       
172    public static void main(String args[]) {   
173        new LoadFrame("LogonFrame");   
174    }
   
175}
 

4.LogonService.java

登录事件的逻辑处理类

实现了IService接口

 1package com.bzwm.testp.listener;   
 2  
 3import com.bzwm.testp.event.BaseEvent;   
 4import com.bzwm.testp.event.LogonEvent;   
 5  
 6/**  
 7 * @author bzwm  
 8 *   
 9 */
  
10public class LogonService implements IService {   
11  
12    /*  
13     * (non-Javadoc)  
14     *   
15     * @see com.bzwm.testp.listener.IService#eventStarted(com.bzwm.testp.event.BaseEvent)  
16     */
  
17    public void eventStarted(BaseEvent event) {   
18        LogonEvent logonEvent = (LogonEvent) event;   
19        String id = logonEvent.getId();   
20        String password = logonEvent.getPassword();   
21        // 对用户的输入进行初步check   
22        if (id == null || "".equals(id)) {   
23            event.setError("用户名为空");   
24        }
   
25        if (password == null || "".equals(password)) {   
26            event.setError("密码为空");   
27        }
   
28    }
   
29  
30    /*  
31     * (non-Javadoc)  
32     *   
33     * @see com.bzwm.testp.listener.IService#service(com.bzwm.testp.event.BaseEvent)  
34     */
  
35    public void service(BaseEvent event) {   
36        if (!event.isError()) {   
37            LogonEvent logonEvent = (LogonEvent) event;   
38            // 用户id和密码已经经过的初步check,现在去登录,检查是否已经注册   
39            logon(logonEvent);   
40            // 这里停顿秒,算是去服务器验证了   
41            try {   
42                Thread.sleep(5000);   
43            }
 catch (InterruptedException e) {   
44                e.printStackTrace();   
45            }
   
46        }
   
47    }
   
48  
49    private void logon(LogonEvent e) {   
50        // 验证DB,用户ID是否存在,密码是否正确   
51        // 如果不存在,则setError(),密码不正确,setError()   
52        //    
53    }
   
54}
  

5.EventMgr.java

管理事件

 1package com.bzwm.testp.common;   
 2  
 3import com.bzwm.testp.event.BaseEvent;   
 4import com.bzwm.testp.listener.IListener;   
 5import com.bzwm.testp.listener.IService;   
 6import java.util.ArrayList;   
 7import java.util.List;   
 8  
 9/**  
10 * @author bzwm  
11 *   
12 */
  
13public class EventMgr {   
14    /** 回调回主画面的监听集合 */  
15    private List<IListener> listeners = new ArrayList<IListener>();   
16  
17    /** 业务逻辑的监听集合 */  
18    private List<IService> services = new ArrayList<IService>();   
19  
20    /**  
21     *   
22     * @param event  
23     */
  
24    public void dispatch(BaseEvent event) {   
25  
26        fireEventStarted(event);   
27  
28        fireEventService(event);   
29  
30        fireEventCompleted(event);   
31    }
   
32  
33    /**  
34     *   
35     * @param listener  
36     */
  
37    public void addListener(IListener listener) {   
38        listeners.add(listener);   
39    }
   
40  
41    /**  
42     *   
43     * @param listener  
44     */
  
45    public void removeListener(IListener listener) {   
46        listeners.remove(listener);   
47    }
   
48  
49    /**  
50     *   
51     * @param service  
52     */
  
53    public void addService(IService service) {   
54        services.add(service);   
55    }
   
56  
57    /**  
58     *   
59     * @param service  
60     */
  
61    public void removeService(IService service) {   
62        services.remove(service);   
63    }
   
64  
65    /**  
66     *   
67     * @param event  
68     */
  
69    private void fireEventStarted(BaseEvent event) {   
70        for (IService service : services) {   
71            service.eventStarted(event);   
72        }
   
73    }
   
74  
75    /**  
76     *   
77     * @param event  
78     */
  
79    private void fireEventService(BaseEvent event) {   
80        for (IService service : services) {   
81            service.service(event);   
82        }
   
83    }
   
84  
85    /**  
86     *   
87     * @param event  
88     */
  
89    private void fireEventCompleted(BaseEvent event) {   
90        for (IListener listener : listeners) {   
91            listener.eventCompleted(event);   
92        }
   
93    }
   
94}

6.BaseEvent.java

抽象类,抽象的事件

这其中的设计还很有待商榷

 1package com.bzwm.testp.event;   
 2  
 3/**  
 4 * @author bzwm  
 5 *   
 6 */
  
 7public abstract class BaseEvent {   
 8  
 9    /**  
10     * 处理过程中的错误信息 这里可以选择自定义一个Error类来完成 由于只是示范,就不写了 另外,这种保存error信息的方法确实很笨  
11     */
  
12    private String error = "";   
13  
14    /**  
15     * 保存处理返回的结果 这里可以自定义一个Data来管理 由于是示范,也就不写了 用一个Object返回处理结果,有很多局限性  
16     */
  
17    private Object result = null;   
18  
19    /**  
20     * @return the result  
21     */
  
22    public Object getResult() {   
23        return result;   
24    }
   
25  
26    /**  
27     * @param result  
28     *            the result to set  
29     */
  
30    public void setResult(Object result) {   
31        this.result = result;   
32    }
   
33  
34    /**  
35     * @return the error  
36     */
  
37    public String getError() {   
38        return error;   
39    }
   
40  
41    /**  
42     * @param error  
43     *            the error to set  
44     */
  
45    public void setError(String error) {   
46        this.error = this.error + "  " + error;   
47    }
   
48  
49    public boolean isError() {   
50        return !error.equals("");   
51    }
   
52}

7.LogonEvent.java

用户登录事件

 1package com.bzwm.testp.event;   
 2  
 3/**  
 4 * @author bzwm  
 5 *   
 6 */
  
 7public class LogonEvent extends BaseEvent {   
 8  
 9    /** 用户ID */  
10    private String id = null;   
11  
12    /** 密码 */  
13    private String password = null;   
14  
15    public LogonEvent(String i, String p) {   
16        setId(i);   
17        setPassword(p);   
18    }
   
19  
20    /**  
21     * @return the id  
22     */
  
23    public String getId() {   
24        return id;   
25    }
   
26  
27    /**  
28     * @param id  
29     *            the id to set  
30     */
  
31    public void setId(String id) {   
32        this.id = id;   
33    }
   
34  
35    /**  
36     * @return the password  
37     */
  
38    public String getPassword() {   
39        return password;   
40    }
   
41  
42    /**  
43     * @param password  
44     *            the password to set  
45     */
  
46    public void setPassword(String password) {   
47        this.password = password;   
48    }
   
49}

根据上述代码,基本功能完成了。

也许会感觉为这一点功能写这么多类不值得,

但代码会清晰一点,虽然设计上,比如类的构造上还不够合理,

我也没思考太多,想到这些就写出来了。

不过这样还有一个好处,比如说,你想给登录事件加输出log的功能,

那么比较简单,

只要写一个类,实现了IService即可。(因为log写在本地文件里,所以不用回调回主画面)

然后在LoadFrame.java中的构造方法改一下就OK了。

下面是输出log用的service

8.LogService.java
 1package com.bzwm.testp.listener;   
 2  
 3import com.bzwm.testp.event.BaseEvent;   
 4import com.bzwm.testp.event.LogonEvent;   
 5  
 6/**  
 7 * @author bzwm  
 8 * 关于输出log类的设计,还可以改进,比如LogService可以是个抽象类,下面是具体的LogonLogService,  
 9 * 在父类中做一些共同的操作,比如IO操作等。  
10 * 因为很多操作都要有log,可以试着给每个画面定义一个输出log的类,  
11 * 专门来管理log信息。  
12 * 这里为简单起见,也就不做那么多了,并且只将信息打印在控制台。  
13 */
  
14public class LogService implements IService {   
15  
16    /*  
17     * (non-Javadoc)  
18     *   
19     * @see com.bzwm.testp.listener.IService#eventStarted(com.bzwm.testp.event.BaseEvent)  
20     */
  
21    public void eventStarted(BaseEvent event) {   
22        // TODO Auto-generated method stub   
23        if (event instanceof LogonEvent) {   
24            LogonEvent e = (LogonEvent) event;   
25            System.out.println(e.getId() + " 准备登录了");   
26        }
   
27    }
   
28  
29    /*  
30     * (non-Javadoc)  
31     *   
32     * @see com.bzwm.testp.listener.IService#service(com.bzwm.testp.event.BaseEvent)  
33     */
  
34    public void service(BaseEvent event) {   
35        // TODO Auto-generated method stub   
36        if (event instanceof LogonEvent) {   
37            LogonEvent e = (LogonEvent) event;   
38            String error = e.getError();   
39            if (error.equals("")) {   
40                System.out.println(e.getId() + " 登录了");   
41            }
 else {   
42                System.out.println(e.getId() + " 登录失败了  " + error);   
43            }
   
44        }
   
45    }
   
46}
  
47

接着,改 LoadFrame.java类。

只写出改的部分,它的构造方法:

 1public LoadFrame(String title) {   
 2    super(title);   
 3    initComponents();   
 4    layoutComponents();   
 5    mrg = new EventMgr();   
 6    // 追加监听器   
 7    mrg.addListener(this);   
 8    mrg.addService(new LogonService());   
 9    mrg.addService(new LogService());   
10}
  

这样,把代码拷贝下来,

重新运行下,就出现log了。

这只是登录的功能,

如果还要完成注册的功能,

则还是一样的步骤,

写一个Dialog类,继承JDialog,实现IListener接口,

定义一个RegEvent.java继承BaseEvent.java,

写一个RegService类,实现IService接口,

总之把相应包下面,加入自己的实现,

然后将监听还是注册到EventMgr里就可以实现了。


----2009年01月22日
posted on 2010-09-01 11:25 李 明 阅读(681) 评论(0)  编辑  收藏 所属分类: J2SE

只有注册用户登录后才能发表评论。


网站导航: