支持Open Class特性的编程语言中的开闭原则(Open-Closed Principle)

社区广播:运维派(Yunweipai.com)是国内最早成立的IT运维社区,欢迎大家投稿,让运维人不再孤寂的成长!

我们知道Ruby的语法特性支持Open Class,可以让用户重新定义系统中已经存在的类,给其添加方法或属性。例如:

# foo.rb
class Foo
def method1 *args
...
end
end

你定义了上面的类Foo之后,可以重新Open它,往其中添加方法:

# foo2.rb
class Foo
def method2 *args
...
end
end

使用Foo的时候,就可以用这两个方法,与其初始就定义两个方法没有区别:

foo = Foo.new
foo.method1 :arg1, :arg2
foo.method2 :arg1, :arg2

那么这么做是违背了Open-Close原则吗?Bertrand Meyer第一个阐述了Open-Close,他的定义是:

软件实体(如类Class,模块Module,函数Function等),对扩展要开放,对修改要封闭。

他在这里有详细的阐述:

这就是开闭原则,我认为这是面向对象的核心变革:你可以完整地使用一个组件,并且以后还可以通过继承扩展其功能。和传统的结构体的实现方式不一样,面向对象既可以Close又可以Open:Close是因为我们可以在上层代码(clients)中直接使用;Open是因为我们可以扩展其属性和方法而不影响上层代码(clients)。
关于更多的开闭原则,参考这篇文章:《言简意赅,隐喻的力量》

因此,若client只依赖foo.rb且只用method1,那client就根本不关注method2。但是,如果client同时依赖foo.rb和foo2.rb,这时候就会有问题,除非client不关注foo2.rb中的改动。好像可以使用标准的继承来解决这种问题?

实际不是的,我们并没有违背开闭原则,我们只是使用Open Class规则来扩展一个类,和标准继承是一样的。

如果我们使用标准的继承:

# foo.rb
class Foo
def method1 *args
...
end
end
...
class DerivedFoo < Foo def method2 *args ... end end ... foo = SubFoo.new # Instantiate different class... foo.method1 :arg1, :arg2 foo.method2 :arg1, :arg2

继承与Open Class的显著区别就是:继承多创建了一个类。如果你经常使用继承的话你可以会首选继承来实现,要用继承有一个条件就是你必须能够完全控制你要创建的类(Open Class就不一样,可以扩展现有的类库而不需要完全维护这个类)并且能够很容易的更换你使用的类。当然,继承是最好的方法,如果你想同时改变类的所有的行为。

有时候你想改变某个类所有实例的行为,且对client是透明的,不会影响已经创建了的实例。
我们举一个纪录日志方法调用的例子,来说说明如何实现上面所说的。我们使用Ruby中著名的alias_method方法,该方法是将类中已经定义好的方法再取一个别名(Ruby中的AOP也通常是使用alias_method来实现的)。

# foo.rb
class Foo
def method1 *args
...
end
end
# logging_foo.rb
class Foo
alias_method :old_method1, :method1
def method1 *args
p "Inside method1(#{args.inspect})"
old_method1 *args
end
end
...
foo = Foo.new
foo.method1 :arg1, :arg2

Foo.method1的行为与上面DerivedFoo继承实现是一致的,且遵循了LSP原则(关于Ruby中的LSP原则参考浅谈ruby core library 与 Liskov Substitution Principle原则)。

因此我认为开闭原则可以稍加修正并重申为:

软件实体(如类Class,模块Module,函数Function等),对扩展要开放,对修改源代码要封闭。

我们不能修改其源代码,但是通过一个新的文件,增加新的功能是可以的。

软件实体(如类Class,模块Module,函数Function等),对扩展要开放,对修改源代码和约定要封闭。

关于约定是LSP原则的冗余或加强定义,我不认为这个加强的定义不好,这个约定是接口提供者和使用者之间约定好的接口的行为方式。我们在使用Open-Class时不能违背这个接口约定规则,就像我们在使用继承的时候不能违背LSP原则一样。

开闭原则(OCP)和LSP是面向对象语言设计中两条很重要的原则,继承是符合这两个原则的一种扩展方法,我们今天讨论的Open Class也是符合这两个原则的另外一种。面向方面的编程(AOP)是第三种。

原文来自: The Open-Closed Principle for Languages with Open Classes

网友评论comments

发表评论

电子邮件地址不会被公开。 必填项已用*标注

暂无评论

Copyright © 2012-2017 YUNWEIPAI.COM - 运维派 - 粤ICP备14090526号-3
扫二维码
扫二维码
返回顶部