您的位置:首页 > 编程语言 > Java开发

校验业务与主逻辑解耦设计探讨与实践(观察者模式篇)

2015-12-04 15:21 579 查看
又好久没写博客了,偷懒成本太低了,想记录的东西蛮多但是太细碎就懒癌发作,Gson的博客也就这么吹了。其实这类技术基础的博客网上很多,自己记录还没有人家写的好,这种情况用收藏夹就能搞定了。我觉得我记录的不应该是网上存在的第10001个拷贝,应该是自己实践过思考过的,和实际应用更加贴近的东西。

这次写的这个主题是一个在现在工作中困扰了我很久的问题。我现在的项目是基于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 设计模式