Duck typing

第一次了解到这个词汇是我在知乎提了一个问题,知乎传送地址

vczh在答案中提到了duck type,于是google了一番,大概了解了一下,附上wiki地址

在面向对象的编程语言中,所谓的duck type是指:对象的属性和方法决定了类的语义,而不是它的继承关系或者是它实现了某个接口。这个概念最早是由James Whitcomb Riley提出的,下面这句话可以帮助我们理解:

         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.

程序员只需要确保对象在特定的上下文中的行为符合他们的期望就可以了,而不用确保对象是某种特殊的类型。比如说,在一个none-duck-type的编程语言中,一个类的方法需要传递一个Duck类型的参数,以确保方法能正确调用参数的quack和walk方法(两个方法都是Duck类型的对象所拥有的方法)。而在一个duck-type的编程语言中,一个类的方法可以传递任意类型的参数,并且直接调用它们的quack和walk方法,如果参数没有这两个方法的话,会抛出一个运行时异常,duck-type编程语言不依赖于类型,而是依赖于文档约束、清晰的代码、以及测试来确保程序的正确性。

下面贴出一段支持duck type的伪代码:

function calculate(a, b, c) => return (a+b)*c

example1 = calculate (1, 2, 3)
example2 = calculate ([1, 2, 3], [4, 5, 6], 2)
example3 = calculate ('apples ', 'and oranges, ', 3)

print to_string example1
print to_string example2
print to_string example3

可以看到calculate使用的参数分别是number、list和string,三者之间毫无继承关系,只要对象支持+和*操作,calculate就能正常使用。如果将这段伪代码转换成Python、Ruby的话,会有如下的输出:

9
[1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6]
apples and oranges, apples and oranges, apples and oranges,

从多态的角度来看,duck type跟其它none-duck-type OO语言没啥区别,都能正常工作。但是从继承的角度来看,两者是不一样的,对于none-duck-type编程语言,如果你caculate定义的参数是string类型,在调用时却传递了一个nubmer参数,编译器会报错。

下面贴一段Java代码,用于模拟duck-type:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
 
public class DuckTyping {
 
    interface Walkable  { void walk(); }
    interface Swimmable { void swim(); }
    interface Quackable { void quack(); }
 
    public static void main(String[] args) {
        Duck d = new Duck();
        Person p = new Person();
 
        as(Walkable.class, d).walk();   //OK, duck has walk() method
        as(Swimmable.class, d).swim();  //OK, duck has swim() method
        as(Quackable.class, d).quack(); //OK, duck has quack() method
 
        as(Walkable.class, p).walk();   //OK, person has walk() method
        as(Swimmable.class, p).swim();  //OK, person has swim() method
        as(Quackable.class, p).quack(); //Runtime Error, person does not have quack() method
    }
 
    @SuppressWarnings("unchecked")
    static <T> T as(Class<T> t, final Object obj) {
        return (T) Proxy.newProxyInstance(t.getClassLoader(), new Class[] {t},
            new InvocationHandler() {
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    try {
                        return obj.getClass()
                            .getMethod(method.getName(), method.getParameterTypes())
                            .invoke(obj, args);
                    } catch (NoSuchMethodException nsme) {
                        throw new NoSuchMethodError(nsme.getMessage());
                    } catch (InvocationTargetException ite) {
                        throw ite.getTargetException();
                    }
                }
            });
    }
}
 
class Duck {
    public void walk()  {System.out.println("I'm Duck, I can walk...");}
    public void swim()  {System.out.println("I'm Duck, I can swim...");}
    public void quack() {System.out.println("I'm Duck, I can quack...");}
}
 
class Person {
    public void walk()  {System.out.println("I'm Person, I can walk...");}
    public void swim()  {System.out.println("I'm Person, I can swim...");}
    public void talk()  {System.out.println("I'm Person, I can talk...");}
}

duck-type可以这么理解:我不关系type,我只关心你有没有对应的方法,以上面的calculate方法为例,我不关心你是string、number还是list,你只要实现了+和*方法,我就能正常工作。

posted @ 2014-06-29 21:35  Peter_Wu  阅读(241)  评论(0编辑  收藏  举报