演化架构与紧急设计: 语言、表达性与设计:第 2 部分
2009-11-05 00:00:00 来源:WEB开发网
清单 14. 测试接口切换def test_pegs_switching
hole = RoundHole.new( 4.0 )
4.upto(7) do |i|
peg = SquarePeg.new(i)
peg.with_interface(:holes) do
if (i < 6)
assert hole.peg_fits?(peg)
else
assert ! hole.peg_fits?(peg)
end
end
end
end
在 with_interface 代码块的作用域中,可以调用使用该接口名定义的 radius 方法。清单 15 中的代码实现了此功能,其结构不但紧凑而且相当简洁。其作用是提供上下文参考;其大多数内容都是比较高级的 Ruby 元编程,因此本文并不会详细讨论它。
清单 15. 接口切换魔力class Class
def def_interface(interface, *syms)
@__interface__ = {}
a = (@__interface__[interface] = [])
syms.each do |s|
a << s unless a.include? s
alias_method "__#{s}_#{interface}__".intern, s
remove_method s
end
end
end
module InterfaceSwitching
def set_interface(interface)
unless self.class.instance_eval{ @__interface__[interface] }
raise "Interface for #{self.inspect} not understood."
end
i_hash = self.class.instance_eval "@__interface__[interface]"
i_hash.each do |meth|
class << self; self end.class_eval <<-EOF
def #{meth}(*args, &block)
send(:__#{meth}_#{interface}__, *args, &block)
end
EOF
end
@__interface__ = interface
end
def with_interface(interface)
oldinterface = @__interface__
set_interface(interface)
begin
yield self
ensure
set_interface(oldinterface)
end
end
end
清单 15 中比较有趣的地方是开放类 Class 定义的结束部分:为指定方法赋予了另一个名称(基于接口),然后通过代码将它从代码中删除。更加有趣的代码出现在 InterfaceSwitching 中:set_interface 方法为在 with_interface 方法中创建的代码块的作用域重新定义了原始(重命名的)方法。最后的 ensure 代码块是 Ruby 版本的 finally 代码块。
此练习的目的并不是深入探讨 Ruby 中的充满魔力的元编程,而是演示表达性极佳的语言能实现哪些功能。解释语言始终比编译语言具有更大的优势,因为它们可以执行编译语言无法执行的代码。事实上,Groovy 引入了一种编译时元编程机制,即 AST Transformations。
结束语
本文的所有这些论述证明了什么呢?在各种语言中,表达性就相当于其能力。本文中介绍的许多技巧都是 Java 语言所不支持的,甚至使用 Javassist 等工具提供的字节码生成功能也无法从技术上实现它们。但是,使用这些机制来解决问题可以说是极其麻烦的。此态度也影响了惯用模式。即便您可以看到特定于应用程序的模式,但如果获得收益的方法过于困难,则会让您的项目背上数不清的技术债务。表达性对计算机语言的重要性是不言而喻的!
更多精彩
赞助商链接