2008-11-14 7 views
44

Sigo viendo la frase "pato escribiendo", e incluso encontré un ejemplo de código o dos. Soy demasiado perezoso ocupado para hacer mi propia investigación, alguien puede decirme, brevemente:¿En qué se diferencia la tipificación de pato del tipo y las interfaces "variante" anterior?

  • la diferencia entre un 'tipo de pato' y un viejo-skool 'tipo de variante', y
  • proporcionan un ejemplo de donde podría preferir escribir pato sobre mecanografía variante, y
  • proporcionar un ejemplo de algo que yo haría tiene para usar mecanografía de pato para lograr?

duck typing illustration courtesy of The Register

no me refiero a parecer aves por dudar del poder de esta 'nueva' construir, y no estoy esquivando el tema al negarse a hacer la investigación, pero estoy hasta Quacking en todo el bombo publicitario que he estado viendo últimamente. Parece que no mecanografía (también conocido como mecanografía dinámica), por lo que no estoy viendo las ventajas de inmediato.

ADENDA: Gracias por los ejemplos hasta ahora. Me parece que usar algo como 'O-> can (Blah)' es equivalente a hacer una búsqueda de reflexión (que probablemente no es barato), y/o es casi lo mismo que decir (O ​​es IBlah) que el compilador podría poder verificar por usted, pero este último tiene la ventaja de distinguir mi interfaz de IBlah de su interfaz de IBlah, mientras que las otras dos no lo hacen. De acuerdo, tener una gran cantidad de pequeñas interfaces flotando alrededor de cada método sería desordenado, pero también lo es comprobar muchos métodos individuales ...

... así que de nuevo no lo entiendo. ¿Es un ahorro de tiempo fantástico, o lo mismo en un nuevo saco? ¿Dónde está el ejemplo que requiere pato escribiendo?

+0

Creo que no quiere decir "variante", sino más bien vinculante a través de la interfaz de IDispatch. – wasker

+0

muy bien hecho! Muy bien hecho. Gracias por hacerme sonreír hoy. – Tim

+0

Hace falta algo de trabajo para enseñar a los patos a escribir, pero los rendimientos de la productividad son bastante valiosos ;-) – Rudiger

Respuesta

18

La respuesta simple es la variante débilmente tipada, mientras que la tipificación de pato está fuertemente tipada.

La tipificación del pato se puede resumir muy bien ya que "si camina como un pato, parece un pato, actúa como un pato, entonces es un pato". Los términos de informática consideran que Pato es la siguiente interfaz.

interface IDuck { 
    void Quack(); 
} 

Ahora vamos a examinar Lucas

class Daffy { 
    void Quack() { 
    Console.WriteLine("Thatsssss dispicable!!!!"); 
    } 
} 

Lucas no es en realidad un iDuck en este caso. Sin embargo, actúa como un pato. Por qué hacer que Daffy implemente IDuck cuando es bastante obvio que Daffy es en realidad un pato.

Aquí es donde entra el tipado Duck. Permite una conversión de tipo seguro entre cualquier tipo que tenga todos los comportamientos de un IDuck y una referencia IDuck.

IDuck d = new Daffy(); 
d.Quack(); 

Ahora se puede llamar al método Quack en "d" con seguridad de tipo completo. No hay posibilidad de un error de tipo de tiempo de ejecución en esta asignación o llamada de método.

+7

Lo que no entiendo sobre Ducktyping es esto: ¿cómo puedes estar seguro de que el método Quack de Daffy es igual a IDuck.Quack? El hecho de que tenga el mismo nombre y el mismo tipo de parámetro coincide, no significa necesariamente que el método haga lo que quiere/necesita. ... – Otherside

+1

... Si Daffy implementa explícitamente la interfaz IDuck, está más seguro de que está llamando al método correcto. Por ejemplo, un método Refresh podría significar: actualizar/volver a pintar la GUI, o volver a cargar datos de la base de datos y actualizar este objeto. – Otherside

+5

Ese es el peligro en la tipa de patos: no hay comprobación semántica, solo comprobación sintáctica. Pero entonces, solo porque esté "más seguro" cuando se trata de un tipo que implementa una interfaz, no significa que usted sea "cierto". –

5

Duck typing es simplemente otro término para el tipado dinámico o el enlace tardío. Un objeto variante que analiza/compila con cualquier acceso de miembro (por ejemplo, obj.Anything) que pueda definirse o no en realidad durante el tiempo de ejecución es el tipado de pato.

+0

+1 gracias: he añadido una solicitud de ejemplos si también quieres tomar una foto –

1

Creo que el punto central de la tipificación del pato es cómo se usa. Uno usa la detección de métodos y la introspección de la entidad para saber qué hacer con ella, en lugar de declarar de antemano qué será ser (cuando sepa qué hacer con ella).

Probablemente sea más práctico en los lenguajes OO, donde las primitivas no son primitivas, y en su lugar son objetos.

Creo que la mejor manera de resumir, en el tipo de variante, una entidad es/puede ser cualquier cosa, y lo que es es incierto, en contraposición a una entidad única ve como cualquier cosa, pero se puede trabajar qué es al preguntarlo.

Esto es algo que no creo que sea plausible sin el ducktyping.

sub dance { 
    my $creature = shift; 
    if($creature->can("walk")){ 
     $creature->walk("left",1); 
     $creature->walk("right",1); 
     $creature->walk("forward",1); 
     $creature->walk("back",1); 
    } 
    if($creature->can("fly")){ 
      $creature->fly("up"); 
      $creature->fly("right",1); 
      $creature->fly("forward",1); 
      $creature->fly("left", 1); 
      $creature->fly("back", 1); 
      $creature->fly("down"); 
    } else if ($creature->can("walk")) { 
     $creature->walk("left",1); 
     $creature->walk("right",1); 
     $creature->walk("forward",1); 
     $creature->walk("back",1); 
    } else if ($creature->can("splash")) { 
     $creature->splash("up") for (0 .. 4); 
    } 
    if($creature->can("quack")) { 
     $creature->quack(); 
    } 
} 

my @x =(); 
push @x, new Rhinoceros ; 
push @x, new Flamingo; 
push @x, new Hyena; 
push @x, new Dolphin; 
push @x, new Duck; 

for my $creature (@x){ 

    new Thread(sub{ 
     dance($creature); 
    }); 
} 

Cualquier otra manera requeriría que poner restricciones para funciones de tipo, lo que reduciría a cabo diferentes especies, necesitando que le permite crear diferentes funciones para diferentes especies, por lo que el código realmente infernal de mantener.

Y eso realmente apesta en términos de tratar de realizar una buena coreografía.

+0

gracias por el ejemplo ! ¿No podría lograr lo mismo si tuviera interfaces IWalk, IFly, ISplash e IQuack? De acuerdo, sería más doloroso ... alternativamente, podría usar el reflejo para ver si los métodos estaban disponibles (¿cuál es probablemente lo que el operador de "lata" hace bajo el capó) ...? –

+1

Bueno, supongo que, en esencia, no hay/directo/diferencia, puedes hacer tipado dinámico en lenguajes estáticos si ** pruebas ** lo suficiente, C++ tiene una implementación lamda, por ejemplo. Solo el lenguaje se centra en ese estilo, haciéndolo más fácil. –

1

Una variante (al menos como las he usado en VB6) contiene una variable de tipo único, bien definido, generalmente estático. Por ejemplo, puede contener un int, o un float, o un string, pero los variats se usan como enteros, los flotantes variantes se usan como flotantes, y las variantes se usan como cadenas.

Duck typing en su lugar utiliza la escritura dinámica. En el tipado de pato, una variable podría ser utilizable como int, o float, o como cadena, si resulta que admite los métodos particulares que admite un int o float o cadena en un contexto particular.

Ejemplo de variantes en comparación con pato a escribir:

para una aplicación web, supongamos que yo quiero que mi información de usuario que venir de LDAP en lugar de partir de una base de datos, pero todavía quiero mi información de usuario sea utilizable por el resto de el marco web, que se basa en una base de datos y un ORM.

Uso de variantes: sin suerte. Puedo crear una variante que pueda contener un objeto UserFromDbRecord o un objeto UserFromLdap, pero los objetos UserFromLdap no serán utilizables por las rutinas que esperan objetos de la jerarquía FromDbRecord.

Usando el tipado de pato: Puedo tomar mi clase UserFromLdap y agregar un par de métodos que lo hacen actuar como una clase UserFromDbRecord. No necesito replicar toda la interfaz FromDbRecord, solo lo suficiente para las rutinas que necesito usar. Si hago esto bien, es una técnica extremadamente poderosa y flexible. Si lo hago mal, produce un código muy confuso y quebradizo (sujeto a rotura si la biblioteca de DB o la biblioteca de LDAP cambian).

+0

¡gracias por el ejemplo! entonces puedo simplemente agregar los métodos que voy a necesitar en lugar de agregar todos los métodos requeridos por una interfaz (o cambiar la clase base) - eso es genial. ¿No te confunde eso si lo haces mucho? –

3

Intenta leer el primer párrafo del artículo de Wikipedia sobre tipado de patos.
Duck typing on Wikipedia

Puedo tener una interfaz (IRunnable) que define el método Run().
Si tengo otra clase con un método como este:
pública RunSomeRunnable vacío (rn IRunnable) {...}

En un lenguaje amigable tipo de pato que podía pasar en cualquier clase que tenía un método run() en el método RunSomeRunnable().
En un lenguaje estáticamente tipado, la clase que se pasa a RunSomeRunnable necesita implementar explícitamente la interfaz IRunnable.

"Si Run() como un pato"

variante es más como objeto en .NET por lo menos.

+0

+1 gracias - agregué una solicitud de ejemplos si también desea tomar una foto –

+1

No puedo creer que se haya perdido el uso del método Quack() en ese ejemplo;) – korona

2

@Kent Fredric

Su ejemplo más sin duda se puede hacer sin necesidad de escribir pato mediante el uso de interfaces explícitas ... más feo que sí, pero no es imposible.

Y personalmente, me parece que tener contratos bien definidos en las interfaces es mucho mejor para aplicar el código de calidad que confiar en la mecanografía de pato ... pero esa es solo mi opinión y la tomo con migajas.

public interface ICreature { } 
public interface IFly { fly();} 
public interface IWalk { walk(); } 
public interface IQuack { quack(); } 
// ETC 

// Animal Class 
public class Duck : ICreature, IWalk, IFly, IQuack 
{ 
    fly() {}; 
    walk() {}; 
    quack() {}; 
} 

public class Rhino: ICreature, IWalk 
{ 
    walk(); 
} 

// In the method 
List<ICreature> creatures = new List<ICreature>(); 
creatures.Add(new Duck()); 
creatures.Add(new Rhino()); 

foreach (ICreature creature in creatures) 
{ 
    if (creature is IFly)   
     (creature as IFly).fly();   
    if (creature is IWalk) 
     (creature as IWalk).walk();   
} 
// Etc 
+0

¿no es así? esperar a que Jon Skeet responda todas las preguntas primero? ;-) –

+0

Creo que está durmiendo (¿Qué hora es en el Reino Unido?) – FlySwat

+0

Actualmente son las 4:47 AM en el Reino Unido –

32

En algunas de las respuestas aquí, he visto un uso incorrecto de la terminología, que ha llevado a las personas a dar respuestas incorrectas.

Por lo tanto, antes de dar mi respuesta, voy a dar algunas definiciones:

  1. fuertemente tipado

    una lengua es fuertemente tipado si hace cumplir la seguridad de tipos de un programa. Eso significa que garantiza dos cosas: algo llamado progreso y otra cosa llamada preservación. El progreso básicamente significa que todos los programas "escritos válidamente" pueden ser ejecutados por la computadora, pueden bloquearse, lanzar una excepción o ejecutarse en un bucle infinito, pero en realidad se pueden ejecutar. Conservación significa que si un programa está "tipeado válidamente", siempre será "Tipeado válidamente", y que ninguna variable (o ubicación de la memoria) contendrá un valor que no se ajuste a su tipo asignado.

    La mayoría de los idiomas tienen la propiedad de "progreso". Sin embargo, hay muchos que no satisfacen la propiedad de "preservación". Un buen ejemplo es C++ (y C también). Por ejemplo, es posible en C++ forzar cualquier dirección de memoria para que se comporte como si fuera de cualquier tipo. Esto básicamente permite a los programadores violar el sistema de tipos en cualquier momento que deseen. Aquí está un ejemplo sencillo:

    struct foo 
    { 
        int x; 
        iny y; 
        int z; 
    } 
    
    char * x = new char[100]; 
    foo * pFoo = (foo *)x; 
    foo aRealFoo; 
    *pFoo = aRealFoo; 
    

    Este código permite que alguien tome una serie de personajes y escribir una instancia de "foo" a ella. Si C++ fue fuertemente tipado, esto no sería posible. Escriba lenguajes seguros, como C#, Java, VB, lisp, ruby, python y muchos otros, lanzarían una excepción si intentara lanzar una matriz de caracteres a una instancia "foo".

  2. tipos débiles

    Algo tipos débiles si no se escribe con fuerza.

  3. tipos estáticos

    una lengua es de tipo estático si su sistema de tipos se verifica en tiempo de compilación. Un lenguaje estáticamente tipado puede ser "tipeado débilmente" como C o fuertemente tipado como C#.

  4. tipos dinámicos

    Un lenguaje de tipos dinámicos es un lenguaje donde los tipos se verifican en tiempo de ejecución. Muchos idiomas tienen una mezcla, de algún tipo, entre tipeo estático y dinámico. C#, por ejemplo, verificará muchas conversiones dinámicamente en tiempo de ejecución porque no es posible verificarlas en tiempo de compilación.Otros ejemplos son lenguajes como Java, VB y Objective-C.

    También hay algunos idiomas que están "completamente" o "mayoría" de tipos dinámicos, como "ceceo", "Ruby" y "pequeña charla"

  5. pato escribir

    tipificación de pato es algo eso es completamente ortogonal a tipeo estático, dinámico, débil o fuerte. Es la práctica de escribir código que funcionará con un objeto independientemente de su identidad de tipo subyacente. Por ejemplo, el siguiente código VB.NET:

    function Foo(x as object) as object 
        return x.Quack() 
    end function 
    

    funcionará, independientemente de lo que el tipo de objeto es que traspasó "Foo", siempre que se define un método llamado "Quack". Es decir, si el objeto se ve como un pato, camina como un pato y habla como un pato, entonces es un pato. Duck typing viene en muchas formas. Es posible tener tipado de pato estático, tipado de pato dinámico, tipado de pato fuerte y tipado de pato semanal. Las funciones de plantilla de C++ son un buen ejemplo de "tipado de pato estático débil". El ejemplo que se muestra en la publicación "JaredPar" muestra un ejemplo de "fuerte tipificación de pato estática". La vinculación tardía en VB (o el código en Ruby o Python) habilita "tipado de pato dinámico fuerte".

  6. Variant

    Una variante es una estructura de datos de tipos dinámicos que puede contener una amplia gama de tipos de datos predefinidos, incluyendo cadenas, tipos de enteros, fechas y objetos COM. A continuación, define un conjunto de operaciones para asignar, convertir y manipular datos almacenados en variantes. Si una variante está fuertemente tipada depende del idioma en el que se usa. Por ejemplo, una variante en un programa VB 6 está fuertemente tipada. El tiempo de ejecución de VB asegura que las operaciones escritas en código VB se ajustarán a las reglas de tipado para las variantes. La vinculación para agregar una cadena a un IUnknown a través del tipo de variante en VB dará como resultado un error de tiempo de ejecución. En C++, sin embargo, las variantes están débilmente tipadas porque todos los tipos de C++ están débilmente tipados.

OK .... ahora que he conseguido las definiciones fuera del camino, ahora puedo responder a su pregunta:

Una variante, en VB 6, permite una forma de hacer la tipificación de pato. Hay mejores formas de hacer tipa de pato (el ejemplo de Jared Par es uno de los mejores), que las variantes, pero puedes hacer pato escribiendo con variantes. Es decir, puede escribir una pieza de código que operará en un objeto independientemente de su identidad de tipo subyacente.

Sin embargo, hacerlo con variantes realmente no da mucha validación. Un mecanismo de tipo de pato estáticamente tipado, como el que describe JaredPar, ofrece los beneficios de la tipificación de pato, más alguna validación adicional del compilador. Eso puede ser realmente útil.

+3

Gracias por la información extendida, pero tengo curiosidad acerca de una afirmación: "Algo se tipea semanalmente si no está fuertemente tipado". - ¿No se escribiría algo semanalmente si solo se escribía una vez por semana? ;-) –

+2

Sí ... No soy el mejor en la revisión ortográfica de mis publicaciones. –

2

En lo que respecta a su solicitud de un ejemplo de algo que necesita para usar la mecanografía de pato para lograr, no creo que exista tal cosa. Pienso en ello como si pensara en usar recursión o si usar iteración. A veces uno solo funciona mejor que el otro.

En mi experiencia, el tipado de pato hace que el código sea más legible y fácil de entender (tanto para el programador como para el lector). Pero creo que la tipificación estática más tradicional elimina muchos errores de tipeo innecesarios. Simplemente no hay forma de decir objetivamente que uno es mejor que otro o incluso decir qué situaciones uno es más efectivo que el otro.

Digo que si te sientes cómodo usando el tipado estático, entonces úsalo. Pero al menos deberías intentar escribir pato (y usarlo en un proyecto no trivial si es posible).

Para contestar de manera más directa:

... así que de nuevo no estoy entendiendo. ¿Es un ahorro de tiempo fantástico, o lo mismo en un nuevo saco?

Ambos. Todavía estás atacando los mismos problemas. Lo estás haciendo de otra manera. A veces eso es todo lo que necesita hacer para ahorrar tiempo (incluso si no es por ninguna otra razón para obligarse a pensar en hacer algo de otra manera).

¿Es una panacea que salvará a toda la humanidad de la extinción? No. Y cualquiera que te diga lo contrario es un fanático.

4

Probablemente nada requiere pato-tipado, pero puede ser conveniente en ciertas situaciones. Digamos que tiene un método que toma y usa un objeto de la clase sellada Duck de una biblioteca de terceros. Y quiere que el método sea comprobable. Y Duck tiene una API terriblemente grande (algo así como ServletRequest) de la cual solo necesitas preocuparte por un pequeño subconjunto. ¿Cómo lo pruebas?

Una forma es hacer que el método tome algo que grazna. Entonces puedes simplemente crear un objeto de simulación de graznido.

0

Todo lo que puede hacer con pato-tipado también lo puede hacer con interfaces. El tipado de patos es rápido y cómodo, pero algunos argumentan que puede conducir a errores (si se nombran dos métodos/propiedades distintos). Las interfaces son seguras y explícitas, pero las personas pueden decir "¿por qué indicar lo obvio?". El descanso es una llama. Todo el mundo elige lo que le conviene y nadie tiene "razón".

Cuestiones relacionadas