从一个小实例开始 - Boo开篇
前一阵小组内部培训时,提到下面这个例子:
“假设我们有个高档的热水器,我们给它通上电,当水温超过95度的时候:
1、警报器会开始发出语音,告诉你水的温度;
2、显示器也会改变水温的显示,提示水已经快烧开了。 ”
相信大家能很快明白这是GoF设计模式中典型的Observer模式,下面我想通过这个例子用Boo实现来作为Boo系列的开篇。
在继续之前,为了方便大家看到C#与Boo两者在语法上的不同,我先写出C#下Observer模式的实现(采用事件机制):
#region C# Observer Pattern public delegate void NotifyEventHandler(Heater heater); public class Heater { public event NotifyEventHandler Notify = delegate { }; public int Temperature { get; set; } public void Boil() { if (Temperature > 95) Notify(this); } } public abstract class Device { public virtual void Update(Heater heater) { Do(); Console.WriteLine("The water is boiling! Water temperature is {0}", heater.Temperature); } protected abstract void Do(); } public class Alarm : Device { protected override void Do() { Console.WriteLine("Alarm!"); } } public class Monitor : Device { protected override void Do() { Console.WriteLine("Monitor!"); } } #endregion
这里我暂且不介绍Boo,而是直接给出Boo版本下的Observer模式:
namespace Observer import System // Boo Observer Pattern //callable NotifyEventHandler(heater as Heater); class Heater: //event Notify as NotifyEventHandler event Notify as callable(object) [Property(Temperature)] _temperature as int def Boil(): // if Temperature > 95: // Notify(self) Notify(self) if Temperature > 95 // Equivalent of the above // abstract class Device: virtual def Update(heater as Heater): Do() print "The water is boiling! Water temperature is ${heater.Temperature}." abstract def Do(): pass class Alarm(Device): override def Do(): print "Alarm!" class Monitor(Device): override def Do(): print "Monitor!" // heater = Heater(Temperature:98) alarm = Alarm() monitor = Monitor() //heater.Notify += def(heater as Heater): // print heater.Temperature heater.Notify += alarm.Update heater.Notify += monitor.Update heater.Boil() Console.ReadKey()
Boo是一个类似Python语法、静态类型、运行在CLR之上的面向对象语言。它有一个强大的编译器扩展能力和更容易构建DSL的特性,目前Boo主要应用在DSL上。
Boo的编译器扩展性意味着你可以对编译器进行自定义的扩展,使你能够在编译器层次上对Boo进行加强,这也使得Boo非常适合于DSL的编写。Boo编译器允许你直接与Pipeline管道交互,Boo编译器管道由一系列的Compiler steps组成,每个Compiler Step都可以与解析后的代码交互从而改变其AST(Abstract Syntax Tree),最终AST会生成.NET程序集代码(IL)。
大家可以看看大师Ayende Rahien使用Boo写的例子,大家可以感受到Boo语言的灵活与强大:
namespace BSDLiB import System import System.Xml import System.Threading import AstModifications import AstModifications.MetaMethods import AstModifications.SLA import AstModifications.SLA.MyExtensions import Boo.Lang.Compiler.Ast unroll i, 5: print i unroll2 i, 5: print i print "Using XML OBject" print " - - - - - - - - - " xml = """ <People> <Person> <FirstName>John</FirstName> </Person> <Person> <FirstName>Jane</FirstName> </Person> </People> """ xmlDocument = XmlDocument() xmlDocument.LoadXml(xml) doc = XmlObject(xmlDocument.DocumentElement) for person as XmlObject in doc.Person: print person.FirstName print " - - - - - - - - - " print "Using meta methods" print " - - - - - - - - - " # Using Meta Methods try: verify 1 == 2 except e: print e # Using macros print " - - - - - - - - - " print "Using Macros" print " - - - - - - - - - " limitedTo 200.ms: Thread.Sleep(201) whenExceeded: print "Took more than 200 ms!" #using AST Attributes print " - - - - - - - - - " print "Using AST Attributes" print " - - - - - - - - - " customer = Customer("test") try: customer.SetName(null) except e: print e
【注】:
1, unroll/unroll2
是Boo中的Macro,用两种不同的方式实现了Macro。Boo中的Macro允许我们后期通过[macro name]来引用宏代码。
2, XmlObject
是实现了IQuackFu接口的类,IQuackFu接口可以让类自己负责方法、属性的调用,非常适合处理“Missing Method”的问题。
3, verify
Meta方法,标记了Meta属性的静态方法可以很容易对AST节点进行操作(Static methods tagged with the Meta attribute should be perceived as a shortcut to the compiler that accepts AST (Abstract Syntax Tree) Nodes and returns an AST node.),这个特性使之很容易创建新的keywords。
4, limitedTo
较复杂的Macro使用:两个Macro之间的交互、Extension的使用。
例子中的具体特性以后我会慢慢向大家讲解,这里只是展示一下Boo的强大扩展性。
借助于Boo所内建的语言特性以及简单的语法结构,加上其静态特性,我们可以用该语言更加高效地编写.NET应用程序。另外,在Boo中,我们还可以使用任何CLI平台上的现存类库,Boo代码同样能够容易地在其他CLI语言中被重用!稍后我会分几部分来详细介绍Boo语言以及其在C#中的使用(DSL方面)。
最后,顺便也试试把JavaScript版本的Observer写出来:
// JavaScript Observer Pattern function Heater(t) { this.Temperature = t; } Heater.prototype = { Notify: function() { if (this.events) { if (this.Temperature > 95) { for (var i = 0; i < this.events.length; i++) this.events[i].Update(); } } }, Attach: function(handler) { if (!this.events) this.events = []; this.events.push(handler); } } // Base class var Device = { Initialize: function(name) { this.Name = name; }, Update: function() { alert(this.Name); } } // subclasses function Alarm(name) { Device.Initialize.call(this, name); } Alarm.prototype = Device; function Monitor(name) { Device.Initialize.call(this, name); } Monitor.prototype = Device; // The example var alarm = new Alarm("Alarm"); var monitor = new Monitor("Monitor"); var heater = new Heater(98); heater.Attach(alarm); heater.Attach(monitor); heater.Notify();
大家有兴趣可以参考下面链接看看Boo的介绍,这会增加对Boo的认识很有帮助,稍后我会分几个部分循序渐进地向大家展现Boo的魅力,希望能有更多的人关注Boo,大家一起学习使用:
Boo Website
The Boo extensibility tutorials
Ayende Rahien - Boo
在CLR之上的构建领域特定语言
概览CLI之上的新语言 - Boo