校验业务与主逻辑解耦设计探讨与实践(观察者模式篇)
2015-12-04 15:21
579 查看
又好久没写博客了,偷懒成本太低了,想记录的东西蛮多但是太细碎就懒癌发作,Gson的博客也就这么吹了。其实这类技术基础的博客网上很多,自己记录还没有人家写的好,这种情况用收藏夹就能搞定了。我觉得我记录的不应该是网上存在的第10001个拷贝,应该是自己实践过思考过的,和实际应用更加贴近的东西。
这次写的这个主题是一个在现在工作中困扰了我很久的问题。我现在的项目是基于Java GUI的,系统逻辑的耦合度很高。
大家都有过这样的经验,比如说提交表单时,会在onCommit方法里面加上判断格式,空值等等各种验证内容,差一点的代码就是
稍微重构一下可以是这样:
但是这时候校验的逻辑其实还是在
这时候有同事就说了,
而且即使没有上面的废话,设计模式第一原则就是单一职责,校验和注册本身是两件事情,当然应该给不同的对象来做了。
为了实现这一步的解耦,我参考了数据校验器架构模式组,实现了可组装校验器结构。
大致代码如下:
这样之后干扰代码减少很多,可以专注在业务逻辑上,而且也方便将来的扩展。但是,但是,但是,我真是一个很懒的人,每到写这样的代码:
对于这个终极目标,脑袋中首先跳出来的面向切面AOP的解决方案,奈何没研究过AOP,而且不能为了优化一个可运行的功能而拖延其他工作的进度,所以这个想法就被搁置了,直到一个契机,看到了RxJava,它的异步执行方案似乎可以解决现在系统中的速度瓶颈(可能是我理解错了?我正准备进行一轮突击学习),关键是看到了观察者模式(设计模式什么的光看书不去用,一个星期后就忘的屁都不剩了,所以我现在的都是要使用的时候再看具体的实现步骤),把所有的业务页面视为被观察者,校验逻辑放在观察者中,每当
利用观察者模式的实现如下:
被观察者
观察者
测试类,把几个类写在一个文件里了
运行测试代码,如果没有
加入`frame.setData(new User(“XiWenRen”,”12345678”));后,运行结果如下:
基本达到了设计的要求,但是有个问题在我能力之外,没有办法在创建窗口的时候为观察者进行注册,这部分代码是在系统UI框架中的,没有源码的权限。
不过没关系,这篇博客本就是边学边写的,或者说写这篇文章的初衷就是让自己去学习AOP的,所以在实现观察者模式之前我就已经知道了这个方案不可行,下一步就是逼自己上梁山了。
这次写的这个主题是一个在现在工作中困扰了我很久的问题。我现在的项目是基于Java GUI的,系统逻辑的耦合度很高。
大家都有过这样的经验,比如说提交表单时,会在onCommit方法里面加上判断格式,空值等等各种验证内容,差一点的代码就是
onClick(){ if(name == null){ System.out.println("Please input your name"); ///... }else if(password == null){ ///... }//...以下省略1000个if else
稍微重构一下可以是这样:
onClick(){ doValidate(); } doValidate(){ if(name == null){ System.out.println("Please input your name"); ///... }else if(password == null){ ///... }//...以下省略1000个if else }
但是这时候校验的逻辑其实还是在
Register业务中,并没有达到解耦的目的。
这时候有同事就说了,
Register的校验只有在这里才会用到啊,本身就是一体的,为什么要拆散呢。那如果有这样一个校验规则,有一批产品,产品包括型号,等级。。。等等信息,现在要把产品装箱操作,肯定要校验这些关键属性是否相同才能包装吧,这就是一个
Compare的规则,这个规则在很多地方都要用到,比如系统的进出模块,要保证相同的产品才能同时进出,这时候校验规则就必须抽象出来,与上面的业务逻辑解耦。
而且即使没有上面的废话,设计模式第一原则就是单一职责,校验和注册本身是两件事情,当然应该给不同的对象来做了。
为了实现这一步的解耦,我参考了数据校验器架构模式组,实现了可组装校验器结构。
大致代码如下:
onClick(){ List<Status> list = DefaultValidator.doValidate(User,Register.class); statusHandle(list); } statusHandle(List<Status> list){ for(Status status : list){ System.out.println(status.getMessage()); } }
这样之后干扰代码减少很多,可以专注在业务逻辑上,而且也方便将来的扩展。但是,但是,但是,我真是一个很懒的人,每到写这样的代码:
if(a == null){}else{}的时候,总觉得又浪费时间又破坏代码美感和阅读的节奏。所以上面的方案还是不满意,还是没有实现理想中的解耦。因为几乎所有的
Button.onClick(),不论什么业务,都会有对应的
Validate,因此我觉得在这个场景中,
DefaultValidator.doValidate(User,Register.class)应该是
onClick()自动执行,不需要显示调用的,我大约是有强迫症的TAT。
对于这个终极目标,脑袋中首先跳出来的面向切面AOP的解决方案,奈何没研究过AOP,而且不能为了优化一个可运行的功能而拖延其他工作的进度,所以这个想法就被搁置了,直到一个契机,看到了RxJava,它的异步执行方案似乎可以解决现在系统中的速度瓶颈(可能是我理解错了?我正准备进行一轮突击学习),关键是看到了观察者模式(设计模式什么的光看书不去用,一个星期后就忘的屁都不剩了,所以我现在的都是要使用的时候再看具体的实现步骤),把所有的业务页面视为被观察者,校验逻辑放在观察者中,每当
onClick()时,通知观察者,观察者执行校验,而且被观察者是把本身作为参数给到观察者,所以可以显示
Dialog提示框。观察者模式的参考博客
利用观察者模式的实现如下:
被观察者
import javax.swing.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Vector; /** * 因为不能多继承,因此不能继承util包里的Observable类,所以把Observable的功能稍微简化一下直接写在基类中,这部分代码直接copy于Java源码,不知道会不会被Oracle告哦。 */ public class MyObservableFrame extends JFrame { private boolean changed = false; private Vector<MyObserver> obs; protected Object obj; /** * Construct an Observable with zero Observers. */ public MyObservableFrame(String title) { super(title); obs = new Vector<MyObserver>(); } /** * 将一个观察者添加到观察者聚集上面 */ public synchronized void addObserver(MyObserver o) { if (o == null) throw new NullPointerException(); if (!obs.contains(o)) { obs.addElement(o); } } public void notifyObservers() throws Exception { notifyObservers(null); } /** * 如果本对象有变化(那时hasChanged 方法会返回true) * 调用本方法通知所有登记的观察者,即调用它们的update()方法 * 传入this和arg作为参数 */ public void notifyObservers(Object arg) throws Exception { MyObserver[] arrLocal; synchronized (this) { if (!changed) return; arrLocal = obs.toArray(new MyObserver[obs.size()]); clearChanged(); } for (int i = arrLocal.length - 1; i >= 0; i--) arrLocal[i].update(this, arg); } /** * 将“已变化”设置为true */ protected synchronized void setChanged() { changed = true; } /** * 将“已变化”重置为false */ protected synchronized void clearChanged() { changed = false; } /** * 设置要校验的信息 * @param obj */ public void setData(Object obj) { this.obj = obj; } /** * 抽象的Listener,默认通知观察者,具体实现放在doAction抽象方法中 * 如果校验不通过,通过异常阻止doAction */ abstract class MyObservableListener implements ActionListener { @Override public void actionPerformed(ActionEvent e) { setChanged(); try { notifyObservers(obj); doAction(); } catch (Exception e1) { e1.printStackTrace(); System.out.println("Validate error, cannot register"); } } public abstract void doAction(); } }
观察者
public interface MyObserver { void update(MyObservableFrame o, Object arg) throws Exception; }
测试类,把几个类写在一个文件里了
import javax.swing.*; public class ObserverTest { public static void main(String[] args) { MyObservableFrame frame = new RegisterFrame(); // frame.setData(new User("XiWenRen","12345678")); frame.addObserver(new ValidateObserver()); frame.setVisible(true); } } class ValidateObserver implements MyObserver { @Override public void update(MyObservableFrame o, Object arg) throws Exception { if (arg == null) { throw new NullPointerException("User cannot be null"); } System.out.println(arg); //如果这里传入一个User类,就可以进行数据验证了 } } class RegisterFrame extends MyObservableFrame { JButton registerBtn; public RegisterFrame() { super("Register"); this.setSize(200, 200); initComps(); } private void initComps() { registerBtn = new JButton("Register"); this.add(registerBtn); registerBtn.addActionListener(new MyObservableListener() { @Override public void doAction() { System.out.println("Register...."); } }); } @Override public void setData(Object obj) { this.obj = obj; } } class User{ public User(String userName, String passWord){ this.userName = userName; this.passWord = passWord; } public String userName; public String passWord; }
运行测试代码,如果没有
setData,运行结果如下
加入`frame.setData(new User(“XiWenRen”,”12345678”));后,运行结果如下:
基本达到了设计的要求,但是有个问题在我能力之外,没有办法在创建窗口的时候为观察者进行注册,这部分代码是在系统UI框架中的,没有源码的权限。
不过没关系,这篇博客本就是边学边写的,或者说写这篇文章的初衷就是让自己去学习AOP的,所以在实现观察者模式之前我就已经知道了这个方案不可行,下一步就是逼自己上梁山了。
相关文章推荐
- java对世界各个时区(TimeZone)的通用转换处理方法(转载)
- java-注解annotation
- java-模拟tomcat服务器
- java-用HttpURLConnection发送Http请求.
- java-WEB中的监听器Lisener
- Android IPC进程间通讯机制
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- 介绍一款信息管理系统的开源框架---jeecg
- 聚类算法之kmeans算法java版本
- java实现 PageRank算法
- PropertyChangeListener简单理解
- 插入排序
- 冒泡排序
- 堆排序
- 快速排序
- 二叉查找树