java设计模式之监听者

一、前言

监听者并不在23种设计模式之中,有点类似于观察者模式,但又不完全相同。

实际项目中,特别在与用户交互的前端设计或UI设计中使用的非常广泛。

最近的项目中需要加载分析excel中的数据并存入DB,在easyexcel的代码中也运用了监听者模式。

写了一个简单的demo,来理解它的工作原理。

之前很多excel分析工具Apache poi,jxl等,是将整个文件读到内存进行解析的,所以非常耗内存。easyexcel不同,他是一行一行进行解析的,所以即便是几百万行的excel文件,也完全能够搞定。

这个时候就会遇到一个问题,我们一行一行解析,数据放在哪?还是内存里吗?那就失去了一行一行解析的意义。

但easyexcel也不知道解析完之后,用户到底要干什么,那之后把这个问题抛给使用者。

二、Listener

easyexcel不知道解析完数据,使用者要干什么,因此他设计并让分析对象持有了一个Listener接口

接口很简单,只有两个方法:

1.invoke()方式是这一行解析完了,我给你封装成了T对象,你要不要干点啥?

2.doAfterAllAnalysed()方法是整个文件都解析完了,你爱干啥就干点啥吧。

public interface Listener<T> {
    void invoke(T t);
    void doAfterAllAnalysed();
}

三、事件

有监听者就有被监听的事件,在excel解析这个过程中,事件显然就是文件的每一行被解析得到的对象T t。

产生事件的自然就是excel分析器,下面是分析器的实现

分析器的输入是一个文件,因此一定要持有一个File对象,当然InputStream也可以

每一行需要被解析成一个java对象,构造对象需要相应的Class,所以也需要传入一个Class<T>对象

每解析完以后都需要通知监听者,因此还需要持有监听者对象

核心的实现是doAnalysis()方法,每解析成功一行,就调用监听者的invoke方法,所有行解析完了调用doAfterAllAnalysed()方法。

public class ExcelAnalyzer<T> {

    private File file;
    private Class<T> clazz;
    private Listener<T> listener;

    public ExcelAnalyzer<T> setListener(Listener<T> listener) {
        if (listener != null) {
            this.listener = listener;
        }
        return this;
    }

    void doAnalysis() throws FileNotFoundException {
        BufferedReader reader = new BufferedReader(new FileReader(file));
        reader.lines().forEach(line -> {
            T t = process(line);
            if (this.listener != null) {
                this.listener.invoke(t);
            }
        });
        if (this.listener != null) {
            this.listener.doAfterAllAnalysed();
        }
    }

    /**
     * 实际要处理更多类型
     *
     * @param line
     * @return
     */
    public T process(String line) {
        T t = null;
        try {
            t = clazz.newInstance();
            String[] split = line.split(",");
            Field[] declaredFields = clazz.getDeclaredFields();
            int length = declaredFields.length;
            for (int i = 0; i < length; i++) {
                Field field = declaredFields[i];
                field.setAccessible(true);
                if (field.getType().getName().equals(Integer.class.getName())) {
                    field.set(t, Integer.valueOf(split[i]));
                } else {
                    field.set(t, split[i]);
                }
            }
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
        return t;
    }

    public ExcelAnalyzer<T> read(File file, Class<T> clazz) {
        this.file = file;
        this.clazz = clazz;
        return this;
    }
}

四、主程序

在主程序执行之前,还需要定义一个java对象类User,它是每一行对应的java对象,这里我们简单定义一下,当然实际的对象可能会更复杂。

@Data
public class User {
    private String name;
    private Integer age;

}

主程序实现:

主程序模仿了easyExcel的实现,看起来还是很像的。

public class Test {
    public static void main(String[] args) throws FileNotFoundException {
        File file = new File("C:\Users\G007112\Desktop\excel.txt");
        ExcelAnalyzer<User> userExcelAnalyzer = new ExcelAnalyzer<User>().read(file,User.class).setListener(new Listener<User>() {
            @Override
            public void invoke(User user) {
                System.out.format("I get line=%s", user);
                System.out.println();
            }
            @Override
            public void doAfterAllAnalysed() {
                System.out.println("Excel file analysis success!!!");
            }
        });
        userExcelAnalyzer.doAnalysis();
    }
}

当然只是想执行一遍程序,啥也不干也是可以的,分析器不传入Listener也完全可以很健壮的运行。只是没有啥意义而已。

以上代码是模仿了easyExcel,源代码中的实现更复杂。

输出:

I get line=User(name=line, age=1)
I get line=User(name=line, age=2)
I get line=User(name=line, age=3)
I get line=User(name=end, age=4)
Excel file analysis success!!!
原文地址:https://www.cnblogs.com/wangbin2188/p/15157423.html