用Scheme解释器项目来了解Java(四)

本章简介

本章主要介绍编译器中最主要的设计模式,观察者模式,这里我无法讲述观察者模式的理论或者是用UML画类图等等,只想就这个项目,说说该如何运用观察者模式。

观察者模式

观察者模式举例

以本项目为基础,结合个人理解,观察者模式具有下面的定义

观察的对象只有一个,而观察者有多个,每个观察者在对观察对象进行观察以后,可以执行不同的操作。

这里就可以简单的举两个观察者的例子,最基本的例子就是如何对程序进行树状打印,如下例子

(+ 1 (* 2 3) 4)

树状打印之后的输出有如下的形式

op:+
   1
   op:*
       2
       3
   4

而对表达式计算的观察者,有如下的顺序:
- 看见(,进行第一层的观察
- 看见+, 记录
- 看见1, 记录
- 看见(, 进行第二层的观察
- 看见2,记录
- 看见3,记录
- 看见),本层分析完毕,进行计算,结果为6
- 看见4,记录
- 看见),本层分析完毕,进行计算,结果为11

观察者模式详细分析

本项目中,设计了接口Execuable,代码如下:

public interface Executable {
    SchemeToken accept(Executor executor);
}

同时设计了观察者类Executor

public class Executor {

    ...

    /**
     * the execution part
     */

    public SchemeToken execute(SchemeToken Token) {
        ...
    }

    public SchemeToken execute(SchemeList Token) {
        ...
    }

    public SchemeNumber execute(SchemeArithmetic expr) {
        ...
    }

    public SchemeToken execute(SchemeDefine expr) {
        ...
    }

    public SchemeBoolean execute(SchemeStringCompare expr) {
        ...
    }

    public SchemeBoolean execute(SchemeNumberCompare expr) {
        ...
    }

    public SchemeToken execute(SchemeCall expr) {
        ...
    }

    public SchemeToken execute(SchemeCond expr) {
        ...
    }

    public SchemeToken execute(SchemeIf expr) {
        ...
    }

    public SchemeToken execute(SchemeApply expr) {
        ...
    }

    public SchemeToken execute(SchemeAtom expr) {
        return TopScope().GetValue(expr.getContent());
    }

    public SchemeString execute(SchemeString expr) {
        return expr;
    }

    public SchemeBoolean execute(SchemeBoolean expr) {
        return expr;
    }

    public SchemeNumber execute(SchemeNumber expr) {
        return expr;
    }

    public SchemeVoid execute(SchemeVoid expr) { return expr; }

    public SchemeToken execute(SchemeQuoted expr) {
        return expr.getContent();
    }

}

此时我们可以让SchemeToken类来实现Execuable接口,实现了这个接口,用白话文解释就是,SchemeToken变成可执行的对象了,而Executor(执行者)只能执行Execuable(可执行的)对象。这句话可能听起来比较拗口,但是我觉得这是一种比较好理解的方式。我还是复述一次

Executor只能执行Execuable的对象

这里可以想象程序执行的时候有一段对话。
如果执行的程序是

(define f (lambda (x y) (+ x y)))
(apply f '(1 2 3 4)) => 观察对象

起始观察对象为SchemeApply
Executor正在执行(观察)整个表达式,可是本次的执行(观察)并不能得出结果,因为此时apply的函数和后面的apply的对象在Executor看来是两个Token,所以,Executor按照顺序对这两个Token说:“我这里有全部对象的执行(观察)步骤,可是我不知道你是谁,那我先把我自己当作参数传递给你,然后,你使用我进行你自己的的计算吧,就和我计算SchemeApply一样!”
第二个参数说:“好,此时我是一个Atom,开始计算,我的计算步骤很简单,就是获取运行时所绑定的对象,,,,计算完毕,此时我也不知道我绑定的是什么对象,我获取到的也是一个Token,我先传递给你(Executor)吧!”
Executor说:“好的,我已经获取到了第二个参数的计算结果,此时我进行合法性的判断,,,,合法,这个对象在运行时是一个SchemeProcedure,并且具有两个参数”(SchemeApply的第三个参数和第二个参数的过程类似)
Executor说:“好,此时后面两个参数都已经计算出结果了,这个时候开始计算,进行reduce,结果出来了,为10,执行(观察)完毕。”

这里我再设计一段代码,并且附上流程图,流程图分析的是最后一句代码,其中每一个节点是一个CallSite

(define a (lambda (x y) (+ x y)))
(define f '(1 2 3 4))
(apply a f)
Created with Raphaël 2.1.0 exec(SchemeToken) exec(SchemeToken) Apply_accept(Executor) Apply_accept(Executor) exec(SchemeApply) exec(SchemeApply) Atom_accept(Executor) Atom_accept(Executor) exec(SchemeAtom) exec(SchemeAtom) 我发现这个token类的运行时类是你, 你接受我的观察吧!!! 你看看我~ 运行时发现你的第二个参数是一个Atom, 我需要看看它究竟指向啥? 你也看看我~~ 我也不知道你指向啥, 但是我把你指向的东西给你了 我也不知道我是啥, 你自己做做合法性判断, 我把我交给你了~~,嘻嘻 做了合法性检查, 确实是一个过程,下面看看第三个参数, 居然还是一个Atom,继续!!! 不然,你再看看我~~ 我tm要吐了!行了, 这是你指向的结果,给你了! 我把我交给你了,你自己看看对不对~~ 好了,两个参数都是合法的,reduce it!!, 计算好了,给你本次观察的结果! 这就是你要我观察的结果,给你吧~~

结语

本篇博客我自认为写的一般般,同时我也尽了自己最大的努力使其变得书面化,但是我自认为设计模式这个东西,理论实在是太枯燥,所以如果大家想更好的理解观察者模式在编译器构造中的使用,可以看我的第一篇博客中的码云地址,里面有详细的代码,或者是可以买一本书《自制编译器》,作者是青木峰郎,里面有详细的解释,非常的全面,也不难懂。同时这里希望我的每一篇博客能够让想学编译的同学更快地入门,发现其中的乐趣。
下一章内容,有毒的函数式编程。

猜你喜欢

转载自blog.csdn.net/u014713475/article/details/78408573