java设计模式之监听者(二)

监听者模式是一种非常经典的设计模式,与访问者模式类型,都是在处理一组复杂数据结构时,将业务逻辑与数据结构解耦。

 

 

 举一个简单 例子,我们要处理一个Excel对象,Excel包含很多行,每一行有很多列,这就是一个典型的树状结构。

如果我们需要在进入Excel前后、处理每一行前后、甚至处理每一列前后,做一些自定义的动作。

这个时候就应该使用监听者模式,首先定义一个Listener接口。

public interface Listener {
    void enterExcel(Excel excel);

    void enterRow(Row row);

    void exitExcel(Excel excel);

    void exitRow(Row row);

}

Listener接口给外界暴露了可以执行动作的切点,实现Listener接口的对象,可以在给定的这些点做一些需要的动作。

比如下面这个实现类,就在进入和退出Excel时大约Excel的名字,进入每一行之前打印每一行的名称。

public class BaseListener implements Listener {
    @Override
    public void enterExcel(Excel excel) {
        System.out.println("enterExcel="+excel.getName());
    }

    @Override
    public void exitExcel(Excel excel) {
        System.out.println("exitExcel="+excel.getName());
    }

    @Override
    public void enterRow(Row row) {
        System.out.println("enterRow="+row.getName());
    }

    @Override
    public void exitRow(Row row) {

    }
}

接下来看我们Excel本身的数据结构。

首先我们定义一个抽象的接口TreeNode,用来规范所有节点的动作。Excel、Row都实现了TreeNode类。

public interface TreeNode {

    int getChildCount();

    TreeNode getChild(int i);

    void enterNode(Listener listener);

    void exitNode(Listener listener);
}


@Data
@NoArgsConstructor
@AllArgsConstructor
public class Row implements TreeNode {
    private String name;

    @Override
    public int getChildCount() {
        return 0;
    }

    @Override
    public TreeNode getChild(int i) {
        return null;
    }

    @Override
    public void enterNode(Listener listener) {
        listener.enterRow(this);
    }

    @Override
    public void exitNode(Listener listener) {
        listener.exitRow(this);
    }
}


@Data
public class Excel implements TreeNode {
    private String name;
    private List<Row> rowList;

    public Excel(List<Row> rowList) {
        this.rowList = rowList;
        this.name = "CSV";
    }

    @Override
    public int getChildCount() {
        return rowList.size();
    }

    @Override
    public TreeNode getChild(int i) {
        return rowList.get(i);
    }

    @Override
    public void enterNode(Listener listener) {
        listener.enterExcel(this);
    }

    @Override
    public void exitNode(Listener listener) {
        listener.exitExcel(this);
    }
}

监听者模式不同于访问者模式最大的区别在于,监听者模式不负责显示调用子节点的访问方法。

而访问者模式必须显式触发子节点访问以便对树的访问能够正常进行。

访问者模式因为显示调用子节点访问,所有它能够控制访问顺序。

监听者模式就必须自己实现一个遍历逻辑。就像下面的ExcelWalker

public class ExcelWalker {
    void walk(Listener listener, TreeNode node) {
        node.enterNode(listener);
        int childCount = node.getChildCount();
        for (int i = 0; i < childCount; i++) {
            this.walk(listener, node.getChild(i));
        }
        node.exitNode(listener);
    }

   
}

下面就可以进行实战了。

构造一个Excel对象,它包含5行数据,构造一个我们自己的BaseListener,在创建一个ExcelWalker类

通过ExcelWalker类的walk方法对Excel数据结构进行变量,并传入我们的BaseListener。

public class App {
    public static void main(String[] args) {
        List<Row> rows = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            rows.add(new Row("row-" + i));
        }
        Excel excel = new Excel(rows);
        BaseListener listener = new BaseListener();
        ExcelWalker walker = new ExcelWalker();
        walker.walk(listener, excel);
    }
}  

输出:

enterExcel=CSV
enterRow=row-0
enterRow=row-1
enterRow=row-2
enterRow=row-3
enterRow=row-4
exitExcel=CSV

可以看到,在进入退出Excel都打印了Excel的名称,进入每一行之前都打印了每一行的名称。

posted @ 2022-08-25 19:29  Mars.wang  阅读(888)  评论(0编辑  收藏  举报