动态行为或动态类型





0/5 (0投票)
动态类型带来的动态行为或动态装饰
大家都知道动态类型提供了动态行为(运行时行为)。鲜为人知的是,静态类型也能提供动态行为。随着 .NET 的对象可扩展性框架 Component-Based Object Extender (CBO Extender) 的出现,可以在运行时向对象添加行为。
这对于 .NET 开发者,或者所有使用静态编程语言的开发者来说是个好消息。然而,有了这个选项,选择静态编程语言还是动态编程语言进行开发就变得更加复杂。在这里,我将尝试从动态行为的角度讨论静态类型和动态类型的区别,希望能让决策过程稍微轻松一些。
动态类型下的动态行为
动态类型有两种:基于原型的编程 和 基于类的编程。在基于原型的编程中,没有类,行为重用是通过克隆现有对象(充当原型)的过程来实现的。JavaScript 是一个使用基于原型编程的例子。在基于类的编程中,在创建并使用对象之前会定义一个类。Ruby 是一个使用基于类编程的例子。
无论只使用对象还是同时使用类和对象,动态行为都是通过更改对象类型来实现的,即在没有验证步骤(编译)的情况下添加、删除或修改对象或类的函数。动态行为在运行时解析为对象的一个方法。
静态类型下的动态行为
静态类型是带有验证步骤(编译)的基于类的编程。在 .NET 4 之前,C# 是一种静态类型语言。现在,它混合了动态类型和静态类型。它仍然需要编译步骤,但有意地省略了某些类型的验证。
与动态类型不同,在静态类型中,动态行为是通过装饰对象的方法来实现的,而不会改变对象类型。动态行为被解析为一个装饰器,装饰器本身是一个独立于被装饰对象的函数。
动态类型与静态类型动态行为的比较
首先,让我们用一个例子来看看动态类型和静态类型是如何实现动态行为的。在接下来的部分中,将使用三种编程语言:JavaScript、Ruby 和 C#,用动态行为编写一个“Hello World!”应用程序。
JavaScript 中的动态行为
下面的代码是用 JavaScript 编写的
function doAnything() {
}
doIt = new doAnything();
doIt.hello = function () {
alert("Hello!");
}
doIt.bye = function () {
alert("Bye!");
}
//doIt.bye = function () {
// alert("Not bye yet!");
//}
doIt.hello();
//delete doIt['bye'];
doIt.bye();
doAnything
函数是空的,用于创建 doIt
对象。然后,向 doIt
添加了两个属性:hello
和 bye
。最后,调用 doIt.hello();
和 doIt.bye();
来输出“Hello!
”和“Bye!
”。
请注意,doIt
对象的行为可以随时通过添加、删除或修改其属性来更改。
通过取消注释代码...
//doIt.bye = function () {
// alert("Not bye yet!");
//}
...输出将是“Hello!
”和“Not bye yet!
”。
通过取消注释代码 //delete doIt['bye'];
,您将获得一个运行时错误。
想象一下,在一个大型项目中,这些代码行深深地埋藏在数十个文件和数百万行代码中。你完蛋了!它太灵活了,以至于不安全且不可预测。
Ruby 中的动态行为
相同的示例重写为 Ruby 如下
class DoAnything
end
class DoAnything
def Hello
puts "Hello!"
end
def Bye
puts "Bye!"
end
end
#class DoAnything
# def Bye
# puts "Not Bye Yet!"
# end
#end
doIt = DoAnything.new
doIt.Hello
#def doIt.Bye
# puts "Bye from instance"
#end
#class << doIt
# remove_method(:Bye)
#end
doIt.Bye
上面的代码输出“Hello!
”和“Bye!
”。
通过取消注释代码...
#class DoAnything
# def Bye
# puts "Not Bye Yet!"
# end
#end
...输出将是“Hello!
”和“Not bye yet!
”。
通过取消注释代码...
#def doIt.Bye
# puts "Bye from instance"
#end
...输出将是“Hello!
”和“Bye from instance
”。
通过取消注释代码...
#class << doIt
# remove_method(:Bye)
#end
...输出将是“Hello!
”和“Not bye yet!
”。
它与 JavaScript 存在同样的问题:太灵活,不安全且不可预测!
C# 中的动态行为
示例使用 C# 重写如下
using CBOExtender;
namespace HelloWorld
{
public interface IDoAnything {
void DoThing();
}
public class DoAnything : IDoAnything {
public void DoThing() { }
}
class Program
{
static void Main(string[] args)
{
IDoAnything doAThing = new DoAnything();
doAThing = ObjectProxyFactory.CreateProxy2<IDoAnything>(
doAThing,
new string[] { "DoThing" },
new Decoration2(SayHello, null),
new Decoration2(SayBye, null)
);
doAThing.DoThing();
}
public static void SayHello(AspectContext2 ctx, dynamic parameters)
{
System.Console.WriteLine("Hello!");
}
public static void SayBye(AspectContext2 ctx, dynamic parameters)
{
System.Console.WriteLine("Bye!");
}
}
}
代码...
doAThing = ObjectProxyFactory.CreateProxy2<IDoAnything>(
doAThing,
new string[] { "DoThing" },
new Decoration2(SayHello, null),
new Decoration2(SayBye, null)
);
...装饰了原始对象并返回其代理。上面的代码输出“Hello!
”和“Bye!
”。
动态装饰在接口层面保留了对象类型而不是实现,并且不改变原始对象的行为。它只是向对象添加了额外的行为。因此,它是类型安全且可预测的。它以松散耦合的方式灵活,而不是以动态类型的方式灵活。
总而言之,
- 动态编程语言和静态编程语言都可以拥有动态行为。
- 动态编程语言通过动态类型实现动态行为,而静态编程语言通过动态装饰实现动态行为。
- 动态装饰保留了对象及其功能的类型,并且安全可预测,而动态类型则改变了对象及其功能的类型,并且不安全不可预测。
结论
可以肯定地说,除了最简单的应用程序,其他应用程序都受益于动态行为,但并非所有应用程序都需要动态类型。
如果您需要动态行为,并且类型安全和可预测性很重要,则应选择支持动态装饰的 .NET。如果您需要动态行为,并且类型安全和可预测性不重要,则可以选择动态类型。
目前,动态装饰仅支持 .NET。
