2009-11-13 11 views

Respuesta

29

En un comentario, se preguntaba si la idea de incrustación era suficiente para "reemplazar la herencia por completo". Yo diría que la respuesta a esa pregunta es "sí". Hace unos años jugué muy brevemente con un sistema Tcl OO llamado Snit, que usaba composición y delegación con exclusión de la herencia.Snit sigue siendo muy diferente del enfoque de Go, pero en ese aspecto tienen un terreno filosófico común. Es un mecanismo para unir piezas de funcionalidad y responsabilidad, no una jerarquía para las clases.

Como han dicho otros, se trata realmente del tipo de prácticas de programación que los diseñadores de idiomas desean apoyar. Todas esas elecciones vienen con sus propios pros y contras; No creo que las "mejores prácticas" sean una frase que se aplique necesariamente aquí. Probablemente veremos a alguien desarrollar una capa de herencia para Go eventualmente.

(Para cualquier lector que esté familiarizado con Tcl, sentí que Snit era un emparejamiento ligeramente más cercano a la "sensación" del idioma que [incr Tcl]. Tcl tiene que ver con la delegación, al menos a mi modo de pensar.)

36

principio crucial El Gang of 4 's es "prefieren composición a la herencia"; Vaya hace lo sigue ;-).

+4

La herencia es un uso excesivo, y aprecio cómo Go simplifica la composición, pero la pregunta que realmente me gustaría saber es si la incorporación puede reemplazar completamente la herencia. Supongo que esta es una pregunta difícil de responder sin realmente ir y escribir algún código. – Casebash

+0

Bueno, no obtiene (directamente) algunos patrones de diseño con bisagras de herencia clave, como el Método de plantilla, pero eso no parece ser un asesino. - en el peor de los casos, parece implicar la pérdida de cierta comodidad (que requiere una codificación ligeramente más explícita). –

+0

@Casebash: La gente ha podido comenzar con el prototipo JS, que podemos decir que es solo una composición. –

12

Los únicos usos reales de la herencia son:

  • polimorfismo

    • sistema de "pato tipos estáticos" de la interfaz de Go resuelve este problema
  • aplicación Endeudamiento de otra clase

    • Esto es lo que la incrustación es para

enfoque de Go no exactamente en el mapa 1-a-1, considere este ejemplo clásico de la herencia y polimorfismo en Java (based on this):

//roughly in Java (omitting lots of irrelevant details) 
//WARNING: don't use at all, not even as a test 

abstract class BankAccount 
{ 
    int balance; //in cents 
    void Deposit(int money) 
    { 
     balance += money; 
    } 

    void withdraw(int money) 
    { 
     if(money > maxAllowedWithdrawl()) 
      throw new NotEnoughMoneyException(); 
     balance -= money; 
    } 

    abstract int maxAllowedWithdrawl(); 
} 

class Account extends BankAccount 
{ 
    int maxAllowedWithdrawl() 
    { 
     return balance; 
    } 
} 

class OverdraftAccount extends BankAccount 
{ 
    int overdraft; //amount of negative money allowed 

    int maxAllowedWithdrawl() 
    { 
     return balance + overdraft; 
    } 
} 

Aquí, la herencia y el polimorfismo se combinan, y no se puede traducir esto a Go sin cambiar la estructura subyacente.

no he profundizado en Go, pero supongo que sería algo como esto:

//roughly Go? .... no? 
//for illustrative purposes only; not likely to compile 
// 
//WARNING: This is totally wrong; it's programming Java in Go 

type Account interface { 
    AddToBalance(int) 
    MaxWithdraw() int 
} 

func Deposit(account Account, amount int) { 
    account.AddToBalance(amount) 
} 

func Withdraw(account Account, amount int) error { 
    if account.MaxWithdraw() < amount { 
     return errors.New("Overdraft!") 
    } 
    account.AddToBalance(-amount) 
    return nil 
} 

type BankAccount { 
    balance int 
} 

func (account *BankAccount) AddToBalance(amount int) { 
    account.balance += amount; 
} 

type RegularAccount { 
    *BankAccount 
} 

func (account *RegularAccount) MaxWithdraw() int { 
    return account.balance //assuming it's allowed 
} 

type OverdraftAccount { 
    *BankAccount 
    overdraft int 
} 

func (account *OverdraftAccount) MaxWithdraw() int { 
    return account.balance + account.overdraft 
} 

Según la nota, esto es totalmente una manera incorrecta de codificar ya que uno está haciendo Java en Ir . Si uno escribiera tal cosa en Go, probablemente se organizaría de manera muy diferente.

+0

Usted mencionó que esto no compilaría sino algunos puntos para ayudar a otros que lean esto: Los tipos necesitan un tipo literal en Go. Use 'escriba RegularAccount struct {}' en lugar de 'escriba RegularAccount {}' No puede poner prototipos de func en la definición de tipo. Use la sintaxis del receptor fuera del tipo: 'func (this * receiverType) funcName (parms) returnType' Debe proporcionar tipos de devolución para los funcs que devuelven un valor, p. 'func (account * RegularAccount) maxWithdraw() int {}' Finalmente, se requiere en Go que finalice la línea "func" con la llave de apertura, en lugar de colocarla en su propia línea. – burfl

+0

Intenté escribir esto como un ejercicio: en los primeros días para mí en Ir ... Casi lo conseguí, y realmente lo agradecería si alguien con más experiencia pudiera responder y corregirlo/completarlo. https://gist.github.com/mindplay-dk/807179beda57e676b8fb –

3

Acabo de enterarme de Go, pero ya que está pidiendo una opinión, ofreceré una basada en lo que sé hasta ahora. La incrustación parece ser típica de muchas otras cosas en Go, que es un soporte de lenguaje explícito para las mejores prácticas que ya se están realizando en los idiomas existentes. Por ejemplo, como señaló Alex Martelli, la pandilla de 4 dice "preferir la composición a la herencia". Go no solo elimina la herencia, sino que hace que la composición sea más fácil y más poderosa que en C++/Java/C#.

Me han dejado perplejos los comentarios como "Go no proporciona nada nuevo que yo no pueda hacer en el lenguaje X" y "¿por qué necesitamos otro idioma?" Me parece que, en cierto sentido, Go no proporciona nada nuevo que no se haya podido hacer antes con algún trabajo, pero en otro sentido, lo nuevo es que Go facilitará y fomentará el uso de las mejores técnicas que son ya en la práctica usando otros idiomas.

+3

De alguna manera, lo nuevo en Go es lo que se ha quitado, esa es una razón clave para un nuevo idioma. Si solo estaban agregando características, podría haber sido C+++;) pero para quitar características (herencia, aritmética de puntero, asignación manual de memoria) requiere un nuevo idioma. –

3

La gente ha solicitado enlaces a información sobre la inserción en Go.

Aquí hay un documento "Effective Go" en el que se analiza la inserción y se proporcionan ejemplos concretos.

http://golang.org/doc/effective_go.html#embedding

El ejemplo tiene más sentido cuando ya tiene una buena comprensión de las interfaces y tipos Go, pero se puede fingir por el pensamiento de una interfaz como un nombre para un conjunto de métodos y si te ocurre una estructura como similar a una estructura C

Para obtener más información sobre estructuras, se puede ver la especificación de lenguaje Go, que menciona explícitamente a los miembros anónimos de estructuras como tipos implícitos:

http://golang.org/ref/spec#Struct_types

Hasta ahora sólo hemos usado como una manera conveniente para poner una estructura en otra sin tener que usar un nombre de campo para la estructura interna, cuando un nombre de campo no agrega ningún valor al código fuente. En el siguiente ejercicio de programación, estoy agrupando un tipo de propuesta dentro de un tipo que tiene una propuesta y un canal de respuesta.

https://github.com/ecashin/go-getting/blob/master/bpaxos.go#L30

7

incrustación proporciona delegación automática. Esto en sí mismo no es suficiente para reemplazar la herencia, ya que la integración no proporciona ninguna forma de polimorfismo. Las interfaces de Go proporcionan polimorfismo, son un poco diferentes de las interfaces que puede usar (algunas personas las comparan con la tipificación de pato o estructural).

En otros idiomas, las jerarquías de herencia deben diseñarse cuidadosamente porque los cambios son amplios y, por lo tanto, difíciles de hacer. Go evita estas trampas al tiempo que proporciona una alternativa poderosa.

He aquí un artículo que profundiza en programación orientada a objetos con ir un poco más: http://nathany.com/good

3

me gusta.

El lenguaje que usa afecta sus patrones de pensamiento. (Simplemente solicite a un programador de C que implemente el "recuento de palabras". Probablemente usarán una lista vinculada, luego cambiarán a un árbol binario para el rendimiento. Pero cada programador de Java/Ruby/Python usará un diccionario/Hash. cerebros tanto que no pueden pensar en usar ninguna otra estructura de datos.)

Con la herencia, tiene que construir hacia abajo - comience con lo abstracto, luego subclasselo a los detalles. Su código útil real será enterrado en una clase N niveles profundos. Esto dificulta el uso de una "parte" de un objeto, porque no puede volver a usar el código sin arrastrar en las clases principales.

En Go, puede 'modelar' sus clases de esta manera (con interfaces). Pero usted no (no puede) codificar de esta manera.

En su lugar, puede usar incrustación. Su código se puede dividir en módulos pequeños y aislados, cada uno con sus propios datos. Esto hace que la reutilización sea trivial. Esta modularidad tiene poco que ver con sus objetos "grandes". (es decir, In Go, puede escribir un método "quack()" que ni siquiera conoce su clase Duck. Pero en un lenguaje OOP típico, no puede declarar "mi implementación Duck.quack() no tiene dependencias en cualquier otro método de Duck. ")

En Go, esto obliga constantemente al programador a pensar en la modularidad. Esto conduce a programas que tienen bajo acoplamiento. Bajo acoplamiento hace que el mantenimiento sea mucho más fácil. ("oh, mira, Duck.quack() es realmente largo y complejo, pero al menos sé que no depende del resto de Duck.")

Cuestiones relacionadas