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

做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

package com.bzwm.testp.listener; import com.bzwm.testp.event.BaseEvent; /** * @author bzwm * */ public interface IListener { /** * 处理事件结束 * 这里将回调到画面上,方便对画面进行重画 * @param event */ public void eventCompleted(BaseEvent event); }

2.IService.java

package com.bzwm.testp.listener; import com.bzwm.testp.event.BaseEvent; /** * @author bzwm * */ public interface IService { /** * 开始处理事件 * 对用户的输入,操作做初步的检查,如果检查失败,则没有必要继续向下进行 * @param event */ public void eventStarted(BaseEvent event); /** * 处理事件 * 进行一些逻辑处理 * 得到单纯的数据,不用考虑画面上的组件 * @param event */ public void service(BaseEvent event); }

3.LoadFrame.java

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

package com.bzwm.testp.frame; import com.bzwm.testp.common.EventMgr; import com.bzwm.testp.event.BaseEvent; import com.bzwm.testp.event.LogonEvent; import com.bzwm.testp.listener.IListener; import com.bzwm.testp.listener.LogonService; import java.awt.BorderLayout; import java.awt.Color; import java.awt.FlowLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JPasswordField; import javax.swing.JTextField; /** * @author bzwm * */ public class LoadFrame extends JFrame implements IListener { /** 标题:用户ID */ private JLabel userIdLbl = null; /** 输入框:用户ID */ private JTextField userIdTxt = null; /** 标题:密码 */ private JLabel passwordLbl = null; /** 输入框:密码 */ private JPasswordField passwordTxt = null; /** 登录按钮 */ private JButton logonBtn = null; /** 重置按钮 */ private JButton resetBtn = null; /** 新用户注册按钮 */ private JButton registerBtn = null; /** 错误提示信息 */ private JLabel errorLbl = null; EventMgr mrg = null; /** * 构造方法 * * @param title * 标题 */ public LoadFrame(String title) { super(title); initComponents(); layoutComponents(); mrg = new EventMgr(); // 追加监听器 mrg.addListener(this); mrg.addService(new LogonService()); } /** * 对组件进行初始化 * */ private void initComponents() { userIdLbl = new JLabel("用户名"); userIdTxt = new JTextField(10); passwordLbl = new JLabel("密码"); passwordTxt = new JPasswordField(10); logonBtn = new JButton("登录"); resetBtn = new JButton("重置"); registerBtn = new JButton("注册"); errorLbl = new JLabel(); errorLbl.setForeground(Color.RED); } /** * 进行布局,给组件添加事件监听 * */ private void layoutComponents() { JPanel topPnl = new JPanel(); topPnl.add(errorLbl); JPanel mainPnl = new JPanel(new BorderLayout()); JPanel inputPnl = new JPanel(new java.awt.GridLayout(2, 2, 2, 2)); inputPnl.add(userIdLbl); inputPnl.add(userIdTxt); inputPnl.add(passwordLbl); inputPnl.add(passwordTxt); inputPnl.setSize(280, 100); mainPnl.add(inputPnl, BorderLayout.NORTH); JPanel footPnl = new JPanel(new FlowLayout(FlowLayout.RIGHT)); logonBtn.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { logonBtnClick(); } }); resetBtn.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { resetBtnClick(); } }); registerBtn.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { regBtnClick(); } }); footPnl.add(logonBtn); footPnl.add(resetBtn); footPnl.add(registerBtn); mainPnl.add(footPnl, BorderLayout.SOUTH); this.add(topPnl, BorderLayout.NORTH); this.add(mainPnl, BorderLayout.CENTER); this.setLocation(200, 200); this.setSize(300, 300); this.setVisible(true); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } /** * */ private void logonBtnClick() { new Thread() { public void run() { mrg.dispatch(new LogonEvent(userIdTxt.getText(), passwordTxt.getText())); } }.start(); } /** * */ private void resetBtnClick() { userIdTxt.setText(""); passwordTxt.setText(""); } /** * */ private void regBtnClick() { } /* * (non-Javadoc) * * @see com.bzwm.testp.listener.IListener#eventCompleted(com.bzwm.testp.event.BaseEvent) */ public void eventCompleted(BaseEvent event) { String error = event.getError(); if (error.equals("")) { errorLbl.setText("登录成功"); } else { errorLbl.setText(error); } } public static void main(String args[]) { new LoadFrame("LogonFrame"); } }

4.LogonService.java

登录事件的逻辑处理类

实现了IService接口

package com.bzwm.testp.listener; import com.bzwm.testp.event.BaseEvent; import com.bzwm.testp.event.LogonEvent; /** * @author bzwm * */ public class LogonService implements IService { /* * (non-Javadoc) * * @see com.bzwm.testp.listener.IService#eventStarted(com.bzwm.testp.event.BaseEvent) */ public void eventStarted(BaseEvent event) { LogonEvent logonEvent = (LogonEvent) event; String id = logonEvent.getId(); String password = logonEvent.getPassword(); // 对用户的输入进行初步check if (id == null || "".equals(id)) { event.setError("用户名为空"); } if (password == null || "".equals(password)) { event.setError("密码为空"); } } /* * (non-Javadoc) * * @see com.bzwm.testp.listener.IService#service(com.bzwm.testp.event.BaseEvent) */ public void service(BaseEvent event) { if (!event.isError()) { LogonEvent logonEvent = (LogonEvent) event; // 用户id和密码已经经过的初步check,现在去登录,检查是否已经注册 logon(logonEvent); // 这里停顿秒,算是去服务器验证了 try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } } } private void logon(LogonEvent e) { // 验证DB,用户ID是否存在,密码是否正确 // 如果不存在,则setError(),密码不正确,setError() // ... } }

5.EventMgr.java

管理事件

package com.bzwm.testp.common; import com.bzwm.testp.event.BaseEvent; import com.bzwm.testp.listener.IListener; import com.bzwm.testp.listener.IService; import java.util.ArrayList; import java.util.List; /** * @author bzwm * */ public class EventMgr { /** 回调回主画面的监听集合 */ private List<IListener> listeners = new ArrayList<IListener>(); /** 业务逻辑的监听集合 */ private List<IService> services = new ArrayList<IService>(); /** * * @param event */ public void dispatch(BaseEvent event) { fireEventStarted(event); fireEventService(event); fireEventCompleted(event); } /** * * @param listener */ public void addListener(IListener listener) { listeners.add(listener); } /** * * @param listener */ public void removeListener(IListener listener) { listeners.remove(listener); } /** * * @param service */ public void addService(IService service) { services.add(service); } /** * * @param service */ public void removeService(IService service) { services.remove(service); } /** * * @param event */ private void fireEventStarted(BaseEvent event) { for (IService service : services) { service.eventStarted(event); } } /** * * @param event */ private void fireEventService(BaseEvent event) { for (IService service : services) { service.service(event); } } /** * * @param event */ private void fireEventCompleted(BaseEvent event) { for (IListener listener : listeners) { listener.eventCompleted(event); } } }

6.BaseEvent.java

抽象类,抽象的事件

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

package com.bzwm.testp.event; /** * @author bzwm * */ public abstract class BaseEvent { /** * 处理过程中的错误信息 这里可以选择自定义一个Error类来完成 由于只是示范,就不写了 另外,这种保存error信息的方法确实很笨 */ private String error = ""; /** * 保存处理返回的结果 这里可以自定义一个Data来管理 由于是示范,也就不写了 用一个Object返回处理结果,有很多局限性 */ private Object result = null; /** * @return the result */ public Object getResult() { return result; } /** * @param result * the result to set */ public void setResult(Object result) { this.result = result; } /** * @return the error */ public String getError() { return error; } /** * @param error * the error to set */ public void setError(String error) { this.error = this.error + " " + error; } public boolean isError() { return !error.equals(""); } }

7.LogonEvent.java

用户登录事件

package com.bzwm.testp.event; /** * @author bzwm * */ public class LogonEvent extends BaseEvent { /** 用户ID */ private String id = null; /** 密码 */ private String password = null; public LogonEvent(String i, String p) { setId(i); setPassword(p); } /** * @return the id */ public String getId() { return id; } /** * @param id * the id to set */ public void setId(String id) { this.id = id; } /** * @return the password */ public String getPassword() { return password; } /** * @param password * the password to set */ public void setPassword(String password) { this.password = password; } }

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

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

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

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

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

那么比较简单,

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

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

下面是输出log用的service

8.LogService.java

package com.bzwm.testp.listener; import com.bzwm.testp.event.BaseEvent; import com.bzwm.testp.event.LogonEvent; /** * @author bzwm * 关于输出log类的设计,还可以改进,比如LogService可以是个抽象类,下面是具体的LogonLogService, * 在父类中做一些共同的操作,比如IO操作等。 * 因为很多操作都要有log,可以试着给每个画面定义一个输出log的类, * 专门来管理log信息。 * 这里为简单起见,也就不做那么多了,并且只将信息打印在控制台。 */ public class LogService implements IService { /* * (non-Javadoc) * * @see com.bzwm.testp.listener.IService#eventStarted(com.bzwm.testp.event.BaseEvent) */ public void eventStarted(BaseEvent event) { // TODO Auto-generated method stub if (event instanceof LogonEvent) { LogonEvent e = (LogonEvent) event; System.out.println(e.getId() + " 准备登录了"); } } /* * (non-Javadoc) * * @see com.bzwm.testp.listener.IService#service(com.bzwm.testp.event.BaseEvent) */ public void service(BaseEvent event) { // TODO Auto-generated method stub if (event instanceof LogonEvent) { LogonEvent e = (LogonEvent) event; String error = e.getError(); if (error.equals("")) { System.out.println(e.getId() + " 登录了"); } else { System.out.println(e.getId() + " 登录失败了 " + error); } } } }

接着,改 LoadFrame.java类。

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

public LoadFrame(String title) { super(title); initComponents(); layoutComponents(); mrg = new EventMgr(); // 追加监听器 mrg.addListener(this); mrg.addService(new LogonService()); mrg.addService(new LogService()); }

这样,把代码拷贝下来,

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

这只是登录的功能,

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

则还是一样的步骤,

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

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

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

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

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

好了。欢迎交流,谢谢!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值