Duck Typing in .net
时间:2011-05-08 来源:Bright Zhang
动态类型和静态类型
所谓动态类型(dynamic typing),就是在运行时(run-time)做类型检查,反之静态类型就是在编译时(compile-time)做类型检查,类型检查的目的自然是确定代码是否可以正确执行。在动态类型中,值具有类型,而变量没有类型,在运行时再确定变量的类型。而对于未确定类型的变量,如一个空心的“管道”,在编译时这个“管道”不会被碰触,直到运行时,才会向管道中注入所需要的材料(值),这也是“依赖注入”中“注入”一词的本来含义。
Duck Typing
Duck Typing,是面向对象编程语言中的一种动态类型风格。这种动态类型风格,不以基类或接口的方法和属性作为检查的依据,而以一个类实际拥有的方法及属性作为检查依据。这似乎与静态语言的类型检查很相似,只是前者的检查是在运行时,而后者则是在编译时。在运行时如此放开,唯一的约束就是在使用的地方约定被注入的对象具有被访问的方法或属性。或者说,就是用松散的约定代替接口,一旦在有的地方违背了约定,那么在运行时将会报错。这就是Duck Typing,而之所以叫Duck Typing,则来自于这一句,“When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck”。因此Duck Typing只是一个代号,其背后的含义是用约定代替接口或基类的动态类型风格。
dynamic in .net 4.0
在.net 4.0中引入了一个新的关键字:dynamic,而引入dynamic的则是IronPython之父,Jim Hugunin,这位大牛已经已于去年10月份离开微软加入谷歌了,在.net 4.0中引入DLR,据他自己讲也是他颇为得意的一件事,当然DLR不止于dynamic,但dynamic绝对是DLR的核心所在。此外,从IronPython之父的名头可以看出,.net 4.0中的dynamic与Python的渊源,你们可以发现关于Duck Typing的最早的介绍及例子大都是基于Python的。
Duck Typing的使用场景
什么样的场景能够发挥Duck Typing的效用呢?换言之,也就是哪一种场景需要甚至放弃接口约束,而采用完全放开只采用约定的方式呢?接口已经可以解决所有的问题了,而且定义约束在开发过程中也是必要的一个环节,为何还要放开约束呢,似乎这种的场景真的不多,按照大多数Duck Typing的介绍文章,最常见的例子主要有基于已有系统的开发时的可扩展性,以及测试时便于对象模拟。
一个简单工厂的例子
使用接口时,我们一般可以这样实现一个简单工厂:
例码1:
public interface ICar
{
string Brand { get; private set; }
string Color { get; set; }
void Go();
}
public class CarFactory
{
public ICar Create(string name)
{
if (name == "qirui") return new Qirui { Color="White" };
else if (name == "jili") return new Jili { Color ="Black" };
return null;
}
}
public class Qirui : ICar
{
public Qirui() { Brand = "Qirui"; }
public string Brand { get; private set; }
public string Color { get; set; }
public void Go()
{
Console.WriteLine("A Qirui is going...");
}
}
public class Jili : ICar
{
public Jili() { Brand = "Jili"; }
public string Brand { get; private set; }
public string Color { get; set; }
public void Go()
{
Console.WriteLine("A Jili is going...");
}
}
使用时这样:
static void Drive(ICar machine)
{
jili.Go();
}
var factory = new CarFactory ();
ICar jili = factory.Create("jili");// parameter can be from configuration.
Drive(jili);
如果用dynamic来替换接口,将如何实现呢?
例码2:
public class CarFactory
{
public dynamic Create(string name)
{
if (name == "qirui") return new Qirui { Color="White" };
else if (name == "jili") return new Jili { Color ="Black" };
return null;
}
}
public class Qirui
{
public Qirui() { Brand = "Qirui"; }
public string Brand { get; private set; }
public string Color { get; set; }
public void Go()
{
Console.WriteLine("A Qirui is going...");
}
}
public class Jili
{
public Jili() { Brand = "Jili"; }
public string Brand { get; private set; }
public string Color { get; set; }
public void Go()
{
Console.WriteLine("A Jili is going...");
}
}
使用的方式如下:
使用时这样:
static void Drive(dynamic machine)
{
jili.Go();
}
var factory = new CarFactory ();
dynamic jili = factory.Create("jili");// parameter can be from configuration.
Drive(jili);
从这个例子我们可以看出,使用dynamic成功代替了接口,并约定car都有三个共同的成员,Brand,Color,以及都可以前进(Go)。
如何基于已有的系统进行扩展
在我们的例子中是驾驶汽车,Drive(ICar car),如果需要驾驶战斗机呢?显然我们的Drive(ICar car)就难以实现了,而对于例码2中的Drive(dynamic car)就很容易实现了,因为战斗机也像Car,可以在跑道上开着前进,那么在驾驶的方法中,所有行为都像Duck(Car)的,都可以认为它是Duck(Car)。
我们只需要在例码2的基础上实现一个J10Fighter类即可:
例码3:
public class J10Fighter
{
public J10Fighter() { Brand = "J10"; }
public string Brand { get; private set; }
public void Go()
{
Console.WriteLine("A J10 fighter is going...");
}
public void Fly()
{
Console.WriteLine("A J10 fighter is flying...");
}
}
然后在CarFactory中增加一行:
else if(name == "j10") return new J10Fighter ();
那么调用时这样:
var factory = new CarFactory ();
dynamic jili = factory.Create("j10"); // parameter can be from configuration.
Drive(jili);
哦,飞机可以跑动了,也许你有一个疑问,那就是这里只有飞机跑动,而没有飞机起飞,甚至发射导弹的动作,似乎流于模拟的嫌疑,或许我应该拿我女儿的玩具车作为例子或许更为贴切。
关于模拟测试的场景,或许你可以参考一下这篇文章:How Duck Typing Benefits C# Developers。
至于Duck Typing的应用场景,或许将会有更多的发现也说不定。在这篇文章里,我只做一些基本的介绍。
Duck Typing的短处
其实除了Duck Typing的应用场景我比较关心之外,另外一个最为关心的就是Duck Typing的短处。应用Duck Typing,有两种情况,一种是整个系统从始至终都在使用这Duck Typing的风格,另一种是,已上线的系统,或者依赖的第三方系统没有使用这种动态类型的风格。
对于第一种的情况,应该比较少,尤其在.net开发环境下,.net 4.0也刚出来不是很长时间,还不足以出现这种情况。第二种情况可能更多,而第二种情况相当于在将你的系统从一种传统的流行的紧凑的风格改向松散的风格,这本身具有一定的风险,而且也会引起风格上的混乱。再说了,从法制到自由,不能一蹴而就。过度的自由,极有可能造成混乱,看看台湾大选乱象就知道了,呵呵。所以,我个人感觉Duck Typing作为一种动态类型的风格,就目前而言,作用还是有限的。