Freitag, 25. November 2011

Duck-Typing


Durch das sogenannte Duck-Typing ist es in Python möglich beliebige Methoden auf Objekten aufzurufen. Ob eine Methode tatsächlich existiert und richtig aufgerufen wird, wird erst zur Laufzeit geprüft. Dadurch ist das folgende Beispiel-Programm aber auch sehr kompakt.

class Bird:
    def __init__(self, name):
        self.name = name

    def __str__(self):
        return self.__class__.__name__+' '+self.name

class Duck(Bird):
    def quak(self):
        print str(self)+': quak'

class Frog:
    def quak(self):
        print str(self)+': quak' 

ducks = [Bird('Gustav'), Duck('Donald'), object(), Frog()]

for duck in ducks:
    if hasattr(duck, 'quak'):
        duck.quak()
    else:
        print 'Keine Ente:', duck

In Java muss man das Interface CanQuak zu Hilfe nehmen. Dadurch kann der Compiler aber schon zur Compile-Zeit prüfen, dass die Methode quak() existiert und richtig aufgerufen wird.

public class Ducks
{
  public static void main(String[] args)
  {
    Object[] ducks = new Object[] {new Bird("Gustav"), new Duck("Donald"), new Object(), new Frog()};
    for (Object duck : ducks)
    {
      if (duck instanceof CanQuak)
      {
        ((CanQuak) duck).quak();
      }
      else
      {
        System.out.println("Keine Ente: " + duck);
      }
    }
  }
}

interface CanQuak
{
  public void quak();
}

class Bird
{
  private final String name;

  public Bird(String name)
  {
    this.name = name;
  }

  public String toString()
  {
    return getClass().getName() + " " + name;
  }
}

class Duck extends Bird implements CanQuak
{
  public Duck(String name)
  {
    super(name);
  }

  public void quak()
  {
    System.out.println(this + ": quak");
  }
}

class Frog implements CanQuak
{
  public void quak()
  {
    System.out.println(this + ": quak");
  }
}

Die Sprache Go verwendet ein Konzept bei dem ein Interface automatisch von Klassen implementiert wird, wenn diese Klasse die Methoden implementiert, die von dem Interface gefordert werden. Auch damit kann man das Duck-Typing-Beispiel nachbauen. Genau wie in Java kann in Go auch bereits zur Compile-Zeit sichergestellt werden, dass die Mehtode Quak() existiert und richtig aufgerufen wird.

package main

import (
  "fmt"
)

type Object struct {
}

type Bird struct {
  name string
}

type Duck struct {
  Bird
}

type Frog struct {
}

type Quaker interface {
  Quak()
}

func (b Bird) String() string { return b.name }
func (d Duck) Quak() { fmt.Printf("%s: quak\n", d) }
func (f Frog) Quak() { fmt.Printf("%s: quak\n", f) }

func main() {
  ducks := []interface{}{&Bird{"Gustav"}, &Duck{Bird{"Donald"}}, &Object{}, &Frog{}}
  for _, duck := range ducks {
    if quaker, ok := duck.(Quaker); ok {
      quaker.Quak()
    } else {
      fmt.Printf("Keine Ente: %s\n", duck)
    }
  }
}