INTERPRETER 是反模式吗?

Posted

技术标签:

【中文标题】INTERPRETER 是反模式吗?【英文标题】:Is INTERPRETER an anti-pattern? 【发布时间】:2010-11-04 11:19:40 【问题描述】:

对我来说,Interpreter 模式听起来很像被称为 Greenspun 第十规则的反模式:

任何足够复杂的 C 或 Fortran 程序都包含一个临时的、非正式指定的、充满错误的、缓慢的 Common Lisp 一半实现。

也就是说,如果您需要使用解释器,您可能会创建一些缓慢、临时且指定不明确的东西。正确的解决方案是从一开始就使用正确的语言。

或者,或者,将一种众所周知且明确指定的语言嵌入到您的应用程序中,例如 Guile(GNU 可嵌入方案)。或者使用 Haskell 作为嵌入式领域特定语言。

但我还没有在实践中看到这一点——您在构建自己的嵌入式语言方面有什么经验?这是个好主意吗?是否比嵌入现有语言更好?

(我不是特别喜欢 lisp。它很好,但 C、Haskell 和 python 以及许多其他语言也是如此。)

【问题讨论】:

有一种学派认为,四人组中的 Smalltalkers 将解释器模式放在书中作为一个笑话,只是为了嘲笑 C++ 人群。不幸的是,C++ 人群没有得到这个笑话,现在我们被它困住了。 (当然,笑话是,在 Smalltalk 中,程序员可以获得对编译器和解释器的完全编程运行时访问权限,因此不需要像解释器模式这样的东西。) 【参考方案1】:

解释器模式中没有任何内容表明它必须是您正在解释的另一种编程语言的语法。如果你需要解析一个简单的数学表达式,那么解释器就是这个。

知道何时使用模式可以防止它成为反模式。

【讨论】:

解释器模式已经过时了(简单的数学表达式往往会变得越来越复杂,直到您拥有自己的 Mathematica)。在比较短期收益与长期收益时,您确实必须考虑这种风险。【参考方案2】:

也许 Lisp 编译器的实现包括解释器模式的示例。

我认为你不应该说“***”是一种反模式,即使你通常应该购买现成的***而不是重新发明它们。

【讨论】:

【参考方案3】:

解释器是 JavaCC 解析器生成器背后的想法。我认为它工作正常。

Interpreter 是一种比 Singleton 更受人尊敬的 GoF 模式。那是需要在岛上投票的人。也许纪念品也是如此。

【讨论】:

【参考方案4】:

发明 XML 的原因之一是让我们免于编写 EDI 解释器;但至少范围是明确定义的,并且我们都大致平等地(至少足够)有效地做到了。至少我仍然有足够的错觉,认为这是正确的做法。

听起来你正在尝试开始一个新的都市传奇?您是否天生就反对特定领域的语言?

【讨论】:

“你天生就反对领域特定语言吗?” - 一点也不。事实上,我正在为我的 wiimote-to-XTest 适配器应用程序使用嵌入式方案,在用户配置文件 API 中使用一组有限的原语。我只是不确定如何理解 INTERPRETER 设计模式。 // 也许我以错误的方式呈现给我设计模式:作为一套严格的规则(我称之为“设计模式的紧身衣模型”),而实际上它们只是一套松散的指导方针和灵感(我称之为“设计模式的缪斯模型”)。【参考方案5】:

如果被滥用,任何设计模式都是反模式。

Interpreter Pattern 的良好用法:

软件编译器 SQL 评估引擎 图形计算器输入解析器 XML 解析器

这些都是解决评估语言中单词问题的程序,whatever that language may be。

【讨论】:

"SQL 评估引擎" -- Ermm...不是吗? AFAICT,解释器模式说要自下而上地评估树。一个适度复杂的查询计划器将对足够复杂的查询进行转换,从而加快它们的速度,有时甚至是显着的。使用自下而上的评估执行 SQL 不是正确的方法。 (对于编译器也是如此)。但是...... INTERPRETER 是在谈论任何一种 AST 遍历吗?另外,它不是与访客相反/对偶/正交吗?【参考方案6】:

通常编译器/解释器的阶段如下所示:

    语法 解析树 AST 编译或解释

在 Lisp 中,1 和 2 合并为 3。

因此,很明显,复杂的程序中可能嵌入了一种自定义语言,它是“一种临时的、非正式指定的、充满错误的、对一半 Common Lisp 的缓慢实现”。

但是我不得不说,手写 AST 树是令人不快的,而且不是所有语言的终结,无论有多少 Lispers 声称它是。

【讨论】:

【参考方案7】:

有时,一个项目可能需要一个配置系统。一开始通常只需要一些简单的东西,比如键值对,这样系统就可以连接到本地数据库,或者类似的东西。如果这就是所需要的,通常只需为 INI 或 CSV 的方言编写一个简单的解析器并继续前进。但是随着系统的发展和成熟,配置更加重要。这是正常且健康的,将功能和责任重构到正确的抽象层。从这里开始,开发人员甚至(喘不过气的)用户很快就会想要一种更具表现力的配置语言。甚至在任何人都没有注意到之前,该系统已经内置并使用了图灵完备的语言。

这是 Greenspunning 的基本模式。一切都经过精心设计,直到最后一点,一种即席语言被推入了真正的计算工作领域。

任何大小合适的系统都可能应该包含至少一半的 clisp。

提前知道是一大步。编写大型系统的一种非常方便的方法是选择一种很好的、​​易于破解的解释性语言并用它来编写你的系统。这恰好正是 TCL 旨在做的事情,但如今很难让任何人支持该语言的大型项目。另一方面,现在有许多其他语言可以提供所有这些好处。

这样做的好处是Active File Pattern。简单配置的编写方式与系统可用的语言解析器兼容。由于文件是由语言解析的,因此可以轻松嵌入更复杂的逻辑。

django 的 settings.py 就是一个比较常见的例子。出于某种原因,django 在猜测 django 项目的安装位置方面并不是很聪明。在配置文件中使用少量standard python,这可以在一般情况下以可移植的方式处理,这将适合几乎所有可能的用户。尽管如此,大部分的 settings.py 文件看起来像 .ini 样式配置文件中典型的普通旧键 = 值对。

与解释器模式的关系是,这些成熟的语言可能使模式正确用于某些病态用途。一旦你知道你需要解析一些东西,就想出一个很好的理由不使用现有的语言。

【讨论】:

【参考方案8】:

请记住,“解释器模式”是 OOP 中的一种特定设计模式。

它与“解释器”或其一般用途无关。

【讨论】:

【参考方案9】:

解释器模式并不意味着编写一个完整的通用脚本语言。如果您需要,显然使用人们已经写过好书的知名语言会更有意义(或者最重要的是,允许用户选择语言)。

解释器模式更多地是关于持久化对象图的想法,但选择一种人类可读和人类可编辑的持久化格式(例如2 + 3代表一个Addition对象,并带有指向几个@987654323的指针@ 对象)。即使这从未作为一种“语言”向客户公开,它仍然有助于调试、支持、现场黑客攻击等。其他常见的例子是查询语言,例如 [N]Hibernate 中的 HQL - 没有任何现有语言能像描述抽象级别的查询一样好。

正如其他人所建议的那样,XML 通常用作此的基本语法(尤其是在将持久对象图设想为“配置文件”时,另请参见 Microsoft 的 XAML),但它实际上不是最佳选择。 UTF-8(或类似)中的 JSON 会更干净、更容易解析、更具可读性和可编辑性,因此对于大多数应用程序来说可能更好。但是有一些很棒的轻量级解析器库,它们采用类似 BNF 的语法描述,然后可以在运行时将文本解析成类似 DOM 的可步行结构,因此编写高度具体的简洁语法是没有问题的。

【讨论】:

【参考方案10】:

你显然不是 Lisp 的“***者”,因为符合该描述的男孩和女孩通常可以知道 Lisp 是一种编译语言。 Lisp 出现于 1958 年。1961 年的 Lisp 1 手册已经描述了编译。

口译很有用;它为我们提供了语义,而无需先编写编译器。这种语义为编译语义提供了一个参考模型:理想情况下,解释程序和编译程序做同样的事情。当我们发现他们不这样做时,我们要么解决它,要么以某种方式描述和证明这种情况。

解释可以用于引导编译的 Lisp 系统,而不需要现有的 Lisp 实现,而是使用另一种语言,例如 C。解释避免了使用引导语言编写 Lisp 编译器的需要。

ANSI Common Lisp 的 CLISP 实现就是一个很好的例子。要构建 CLISP,您只需要一个 C 编译器。 CLISP 的 Lisp 编译器是用 Lisp 编写的。因此,当然,您没有任何东西可以运行该编译器。解决方案是使用用 C 编写的解释器来解释编译器。

在解释运行时,编译器会编译大部分 Lisp 库。其结果就是 CLISP 所称的“半编译内存映像”:包含已编译例程但一些解释例程的映像。 (我认为,编译器本身仍然是解释代码)。

然后使用这个半编译的镜像来编译剩余的代码,从而得到完全编译的镜像。

没有解释器,CLISP 的编译器只能通过坚持首先安装另一个 Lisp 实现来引导。 (您需要 C 编译器来引导 GCC 的方式)。否则,CLISP 的编译器必须用 C 语言编写,这样编译器就使用现有的 C 编译器进行编译,然后应用于 Lisp 代码进行编译,然后才能运行。

没有人想用 C 语言编写 Lisp 编译器,并且在 Lisp 并不普遍的世界中,需要 Lisp 实现来构建 Lisp 实现是一个很大的缺点。在 Lisp-compiler-in-C 助推模型下会发生什么,用 C 语言编写的 Lisp 编译器将是一个完全最小且可能不完整的编译器,它会发出质量差的代码,仅足以启动助推,并且是“真正的”编译器仍然是用 Lisp 编写的。

【讨论】:

以上是关于INTERPRETER 是反模式吗?的主要内容,如果未能解决你的问题,请参考以下文章

深度嵌套字典是反模式吗?

像isInUnitTest()这样的检查是反模式吗?

设计模式---领域规则模式之解析器模式(Interpreter)

精读《设计模式 - Interpreter 解释器模式》

为啥 Singleton 被认为是反模式? [复制]

馄饨代码 - 为啥是反模式? [关闭]