duck typing java_鸭子类型: Duck Typing in Ruby | 天码营

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.

——James Whitcomb Riley

总所周知,面向对象编程语言有三大特性:封装、继承、多态。以Java为例,封装把数据结构与相关方法打包到一个类里面,继承通过extends单继承并接受父类的数据结构和方法,多态通过父类引用调用子类方法。最值得一提的是,Java中还可以通过interface实现多态。

Java中的interface极其重要,许多重要的设计原则地实现都离不开使用interface或与interface相关,包括单一职责原则、依赖倒置原则、接口隔离原则等等,而与之相关的具体的设计模式就更多了,不再列举。

"既然interface这么重要,那我一定要多多注意和使用",一位java工程师如是想,直到有一天他遇到了Ruby。在搜遍了所有关键字之后发现竟然没有interface,他抱头痛哭,因为他认为如果没有interface,那么敏捷开发、设计模式课程上学到的设计原则与模式岂不是不能用了。"这让我如何面向对象!?"。

可是事实是如此么?

不仅仅是Ruby,大部分的动态语言,包括Python、Javascript等等,都没有interface。但是依然可以实现多态、践行设计原则和设计模式,其中的一个关键概念就是Duck Typing。本文以Ruby为例,简单介绍Duck Typing.

Duck Typing

Duck Typing,即鸭子类型,是一种从行为层面看待“类”的方式。开篇题中这样描述:如果一只鸟,它走路像鸭子、游泳像鸭子、叫起来像鸭子,那么我就认为它就是只鸭子。哪怕这是一只装成鸭子的人。可以看到,Duck Typing认为一个物体的类别由它的行为决定,如“走路”“游泳”“叫”。类似,如果认为一个“能说人话”的东西是人,那么从Duck Typing的角度来看,电视机是人、复读机是人、MP3是人、会模仿人说话的鹦鹉也是人。

说了这么多,到底怎么用呢?下面就以一个例子来看如何使用Duck Typing实现多态。

首先是Java中使用interface的典型情景 (想听BGM,请戳What does the fox say?):public interface FoxSay() { public void say(); }

public class FoxRed implements FoxSay {

public void say() {

System.out.println("Hatee-hatee-hatee-ho!");

}

}

public class FoxBlue implements FoxSay {

public void say() {

System.out.println("Wa-pa-pa-pa-pa-pa-pow!");

}

}

public class FoxYellow implements FoxSay {

public void say() {

System.out.println("Chacha-chacha-chacha-chow!");

}

}

public static void main () {

fox1 = new FoxRed();

fox2 = new FoxBlue();

fox3 = new FoxYellow();

List fox = new ArrayList();

fox.add(fox1);

fox.add(fox2);

fox.add(fox3);

for (FoxSay foxsay : fox) {

System.out.println("What does the fox say?");

foxsay.say();

}

}

而在Ruby中,没有interface,直接定义方法,然后调用即可。class FoxRed

def say; puts "Hatee-hatee-hatee-ho!" end

end

class FoxBlue

def say; puts "Wa-pa-pa-pa-pa-pa-pow!" end

end

class FoxYellow

def say; puts "Chacha-chacha-chacha-chow!" end

end

fox1 = FoxRed.new

fox2 = FoxBlue.new

fox3 = FoxYellow.new

[fox1, fox2, fox3].each do |fox|

puts "What does the fox say?"

fox.say

end

对于一个平时不怎么写Java,偶尔写写Ruby的人来说,事实上完全没有从如上两个例子,尤其是后一个例子中,感受到什么。这是因为作为动态语言,Duck Typing已经无处不在,反而感受不到了。但是放到一起和Java静态类型语言相比,还是能够稍微凸显不同。如果不用接口,那么Java很难实现如上,因为Java会进行静态类型检查。

最后以装饰者模式结束本文。

装饰者模式

首先来看一下Java实现的装饰者模式。被装饰者是个篮子,最初是空的,通过不断地加入装饰,即把苹果、香蕉、橘子装进去。show方法被用于打印装进去的东西。public interface Basket {

public void show();

}

public class Original implements Basket{

public void show(){

System.out.println("The original Basket contains");

}

}

public class AppleDecorator implements Basket{

private Basket basket;

public AppleDecorator( Basket basket){

super();

this.basket = basket;

}

public void show(){

basket.show();

System.out.println("An Apple");

}

}

public class BananaDecorator implements Basket{

private Basket basket;

public BananaDecorator(Basket basket){

super();

this.basket = basket;

}

public void show(){

basket.show();

System.out.println("A Banana");

}

}

public class OrangeDecorator implements Basket{

private Basket basket;

public OrangeDecorator(Basket basket){

super();

this.basket = basket;

}

public void show(){

basket.show();

System.out.println("An Oranage");

}

}

public class DecoratorPattern {

public static void main(String[] args) {

Basket basket = new Original();

Basket myBasket =new AppleDecorator(new BananaDecorator(new OrangeDecorator(basket)));

myBasket.show();

// The original Basket contains

// An Oranage

// A Banana

// An Apple

}

}

这段装饰者模式在Ruby中的实现如下:class Basket

def show; puts "The origin Basket contains" end

end

module Apple

def show

super

puts "An Apple"

end

end

module Banana

def show

super

puts "A Banana"

end

end

module Orange

def show

super

puts "An Orange"

end

end

basket = Basket.new

basket.extend Apple, Banana, Orange

basket.show

# The original Basket contains

# An Oranage

# A Banana

# An Apple

与Java的实现相比,这里不需要定义接口,因为有了Duck Typing,直接写方法即可。其中extend方法为basket实例扩充了Apple,Banana,Orange模块的方法。方法查找参见如下祖先链:basket.singleton_class.ancestors

#=> [#>, Apple, Banana, Orange, Basket, Object, Kernel, BasicObject]

其中第一个类是basket实例的本体类(或单件类、虚类),顺着该祖先链查找show方法,首先找到的是Apple模块的show,但是由于有super所以会继续向上查找,依次类推,最终找到Basket的show方法。所以最先打印的是Basket.show,然后是Orange.show,Banana.show,Apple.show。

最后的最后,关于使用Ruby实现设计模式,有个很棒的链接,请戳用Ruby踩踩四人帮。

参考资料


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部