¿Cómo puedo explicar la utilidad de la herencia? [cerrado]

16

Cuando se trata de explicar el concepto de herencia en OOP, el ejemplo común es a menudo el ejemplo de los mamíferos. En mi humilde opinión, este es realmente un mal ejemplo, porque llevará a los novatos a utilizar este concepto de la manera incorrecta. Y, además, no es un diseño común al que se enfrentarán en su trabajo de diseño diario.

Entonces, ¿cuál será un problema agradable, simple y concreto que se resuelva con Herencia?

Pierre Watelet
fuente
1
¿"el ejemplo común es a menudo los mamíferos"? ¿Qué quieres decir? ¿Puede proporcionar un enlace, referencia o presupuesto para esto?
S.Lott
3
¿Cuál sería el mejor ejemplo real para explicar la utilidad del fondo fiduciario para niños de Bill Gates?
Martin Beckett
1
@ Chris: ¿cómo es que esta pregunta no es constructiva? ¿Estás declarando que un autor de la pregunta y 14 personas están perdiendo el tiempo de todos?
Dan Dascalescu
@DanDascalescu: se cerró hace dos años en respuesta a una bandera que decía "considere cerrar como no constructivo: a juzgar por las respuestas que se acumulan, esto parece una pregunta típica de lista / encuesta". Si cree que esto está mal, edítelo para dejar en claro que no lo es y deje que la comunidad decida a través de la cola de revisión de reapertura.
ChrisF

Respuestas:

15

No hay nada malo con un ejemplo puramente académico como los mamíferos. También me gusta el ejemplo de rectángulo / cuadrado porque señala por qué las taxonomías del mundo real no siempre se traducen directamente a la relación de herencia que esperarías.

En mi opinión, el ejemplo más canónico de todos los días es un kit de herramientas GUI. Es algo que todos han usado, pero que los principiantes pueden no haber razonado sobre cómo trabajan bajo el capó. Puede hablar sobre qué comportamientos son comunes a todos los contenedores, todos los widgets, eventos, etc. sin requerir un conocimiento detallado de ninguna implementación dada.

Karl Bielefeldt
fuente
55
+1 para kits de herramientas GUI ... y también me gusta el ejemplo de formas, con una Forma como base con un dibujo mínimo () y las formas descendientes con dibujos personalizados ().
yati sagade
14

Mi ejemplo del mundo real es el modelo de dominio de una aplicación de recursos humanos simple. Le digo que podemos crear una clase base llamada Empleado , porque, por supuesto, los gerentes también son empleados.

public class Employee
{
    public string FirstName { get; set; }

    public string LastName { get; set; }

    public int Code { get; set; }

    public string GetInsuranceHistory()
    {
        // Retrieving insurance history based on employee code.
    }
}

Luego explico que los desarrolladores son empleados , los probadores son empleados , los gerentes de proyecto son empleados . Por lo tanto, todos pueden heredar de la clase de empleado.

Saeed Neamati
fuente
2
Para mostrar las ventajas de herencia, puede ser interesante mostrar también cómo los desarrolladores difieren de los empleados. Si no hay diferencia, entonces no hay necesidad de crear una clase Developer.
David
3
Parece que también Employeepodría ser una abstractclase.
StuperUser
+1 este es el ejemplo que usaron la mayoría de mis maestros y realmente me gustó. tenía mucho sentido y dio un ejemplo del mundo real sobre cómo usar la herencia.
David Peterman
18
Excepto que esto nunca funciona en la práctica porque siempre hay al menos una persona que necesita ser tanto a Developercomo a Tester. Otra situación similar es una base de datos de contactos donde tiene Customery Supplier, pero como cualquiera que haya creado dicho sistema le dirá, siempre hay un caso en el que a Companyes ambos. Es por eso que la mayoría de estos ejemplos lo llevan en la dirección equivocada.
Scott Whitlock
11

Encapsula lo que varía ... muéstrales un patrón de método de plantilla , demuestra la utilidad de la herencia colocando el comportamiento común en una clase base y encapsulando el comportamiento variable en las subclases.

UI controlsy Streamstambién son un muy buen ejemplo de la utilidad de la herencia.

Halcón
fuente
Creo que esa fábrica sería un mejor ejemplo.
Let_Me_Be
1
@ Let_Me_Be: Creo que la relación entre una fábrica y la herencia es demasiado indirecta en su naturaleza. Claro, produce tipos concretos y devuelve tipos abstractos / base, ¡pero también podría devolver solo un tipo de interfaz! En mi opinión, eso no es mejor que el clásico ejemplo animal.
Falcon
@Let_Me_Be: Además, una fábrica abstracta es un ejemplo bastante complejo que involucra diferentes jerarquías de herencia (una para los artículos, otra para las fábricas). Creo que es un buen uso de la herencia, pero no es un buen y simple ejemplo.
Falcon
3

Recuerda

¡Cada instancia de un objeto es un ejemplo concreto de la utilidad de la herencia!

Si te refieres específicamente a la herencia de clase , ahora estás en el mundo de las taxonomías, y éstas variarán drásticamente según los objetivos del sistema que las usa. El ejemplo de animales / mamíferos utiliza una taxonomía común y con suerte familiar de la biología, pero es (como usted mencionó) casi inútil para la gran mayoría de los problemas de programación.

Intente algo universal: la noción de un programa. Cada programa comienza, se ejecuta y termina. Cada programa tiene un nombre y parámetros opcionales de línea de comandos. Por lo tanto, una clase de programa base sería muy útil para iniciar la ejecución, tomar y procesar los argumentos de la línea de comandos, ejecutar la lógica principal y cerrar con gracia.

Es por eso que tantos lenguajes de programación orientados a objetos proporcionan una clase de Programa, o algo que se comporta exactamente como una clase de Programa.

Steven A. Lowe
fuente
Entonces, ¿programa un programa que tiene muchos programas? :) En mi experiencia, los Objetos de programa son casi siempre singletons que no tienen herencia, por lo que, en mi opinión, no son el mejor ejemplo.
keppla
@keppla: ¿alguna vez has usado Java o .NET? .NET tiene una clase de programa explícita, la de Java es implícita. No son solteros
Steven A. Lowe
Utilicé Java, alrededor de la versión 1.4.2. En aquel entonces, solo había un vacío principal estático, así que supongo que cambió un poco. ¿Cuál sería una razón típica para tener más de una instancia de la clase Programm?
keppla
@keppla: el vacío estático principal de java implícitamente hace que la clase de entrada represente el programa. Cada usuario que ejecuta su programa crea una nueva instancia del mismo. En este momento, tengo tres instancias de Google Chrome ejecutándose, cuatro documentos de Word, tres Blocs de notas y dos Exploradores de Windows. Si fueran todos solteros, nunca podría hacer eso.
Steven A. Lowe
1
Creo que estás estirando un poco la definición. class Programm { public static void main(String[] args) { system.out.println('hello world'); }}Es un programa mínimo de Java. Cuando lo llamo, no hay instancia de Programa. El programa no se hereda de nada. Cuando inicio 3 procesos (como lo hace con crhome), puede haber 3 programas, pero en sus áreas individuales de memoria, todavía hay un solo programa. En mi opinión, singleton implica 'Solo una instancia por proceso', no por máquina. Si es así, sería imposible hacer singletons, nada le impide ejecutar ningún código dos veces.
keppla
3

Estoy trabajando con cámaras en el trabajo. Tenemos dispositivos que se conectan a diferentes modelos, por lo que tenemos una "clase de cámara" abstracta y cada modelo hereda de esta clase para admitir la funcionalidad específica de esa cámara. Es un ejemplo del mundo real y no es difícil de entender.

Lucas
fuente
2
Esto podría romperse si tiene, por ejemplo, un modelo que sea tanto a Cameracomo a Phone(como todos lo hacemos en nuestros bolsillos ahora). ¿De qué clase base debería heredar? ¿O no debería simplemente implementar las interfaces ICameray IPhone? (ja, ja)
Scott Whitlock
2
@Scott: No puede implementar la interfaz de iPhone, o Apple lo demandará.
Mason Wheeler
3

Ejemplo de elementos químicos

Este es otro ejemplo que salió de mi cerebro:

clase Element_
{
    doble peso atómico; // peso atómico del elemento
    doble número atómico; // número atómico del elemento
    Propiedades de cadena; // Propiedades del elemento
    // Otros, si los hay
}


clase Isótopo extiende Elemento_ // Pueden existir isótopos de elemento
{
    doble vida media;
   // Otros si alguno

}
marca
fuente
2
Aunque atomicNumber puede (¿debería?) Probablemente ser entero ...
Andrew
No usaría la herencia para eso. isotopeNo es un caso especial de Elemenet. Prefiero tener una Elementpropiedad Isotope.
CodesInChaos
2

Los ejemplos del mundo real casi siempre se equivocan porque dan ejemplos donde siempre existe la posibilidad de que algo sea ambos TypeAy TypeBla jerarquía de herencia única de muchos idiomas no lo permita.

Cuanto más programo, más me alejo de la herencia.

Incluso la palabra "heredar" se usa incorrectamente aquí. Por ejemplo, usted hereda aproximadamente el 50% de los rasgos de su padre y el 50% de los rasgos de su madre. Realmente tu ADN es una composición de la mitad del ADN de tu padre y la mitad del ADN de tu madre. Esto se debe a que la biología en realidad favorece la composición sobre la herencia , y usted también debería hacerlo.

Simplemente implementar interfaces, o incluso mejor, "escribir pato", además de la inyección de dependencia, es mucho mejor para enseñar a las personas que son nuevas en la programación orientada a objetos.

Scott Whitlock
fuente
1

Solo les mostraría un ejemplo de la vida real. Por ejemplo, en la mayoría de los marcos de UI, usted deriva de algún tipo de clase "Diálogo" o "Ventana" o "Control" para crear la suya propia.

Nemanja Trifunovic
fuente
1

Un buen ejemplo es la función de comparación en la clasificación:

template<class T>
class CompareInterface {
public:
   virtual bool Compare(T t1, T t2) const=0;
};
class FloatCompare : public CompareInterface<float> { };
class CompareImplementation : public FloatCompare {
public:
   bool Compare(float t1, float t2) const { return t1<t2; }
};
template<class T>
void Sort(T*array, int size, CompareInterface<T> &compare);

El único problema es que los novatos a menudo piensan que el rendimiento es más importante que un buen código ...

tp1
fuente
0

Mi ejemplo del mundo real es un vehículo:

public class Vehicle
{
    public Vehicle(int doors, int wheels)
    {
        // I describe things that should be
        // established and "unchangeable" 
        // when the class is first "made"
        NumberOfDoors = doors;
        NumberOfWheels = wheels;
    }

    public void RollWindowsUp()
    {
        WindowsUp = true;
    }

    // I cover modifiers on properties to show
    // how to protect certain things from being
    // overridden
    public int NumberOfDoors { get; private set; }
    public int NumberOfWheels { get; private set; }

    public string Color { get; set; }
    public bool WindowsUp { get; set; }
    public int Speed { get; set; }
}

public class Car : Vehicle
{
    public Car : base(4, 4)
    {

    }
}

public class SemiTruck : Vehicle
{
    public SemiTruck : base(2, 18)
    {

    }
}

Este ejemplo puede ser tan detallado como desee, y hay todo tipo de propiedades asociadas a los vehículos para explicar el uso de cualquier modificador que desee enseñar.

Joel Etherton
fuente
2
Siempre he odiado el uso de vehículos como ejemplo, ya que no hace que un nuevo programador esté más cerca de comprender cómo se puede usar la herencia para mejorar el código. Los vehículos son máquinas inmensamente complicadas que recuerdan muchas ideas no abstractas en la mente del no programador. Intentar describir esto en código hace que el principiante promedio crea que hay muchos detalles que quedan fuera del ejemplo y da la sensación de que no están más cerca de hacer que algo funcione. Lo digo por experiencia, ya que así es exactamente como me sentí cuando alguien intentó usar vehículos para explicármelo.
Riwalk
@ Stargazer712: Utilizo vehículos principalmente porque pueden ser tan complicados o tan simples como quieras. Lo dejo a juicio del instructor para determinar el nivel de su alumno. Le expliqué OOP básico a mi esposa (que tiene cero experiencia en programación) usando las propiedades simples de un vehículo que describe los conceptos básicos comunes. Todos los vehículos tienen puertas, todos los vehículos tienen ruedas, etc. El ejemplo del objeto no se puede culpar por un mal plan de lección.
Joel Etherton
Tu esposa no estaba tratando de escribir el código. Puedo decir con mucha seguridad que los ejemplos de vehículos no hicieron nada para ayudarme a entender la herencia. Cualquier cosa que use para describir la herencia, debe ser completa y práctica . El objetivo no es describirlo de tal manera que un no programador pueda entenderlo. El objetivo es describirlo de tal manera que un programador novato pueda usarlo , y la única forma de hacerlo es mostrar los ejemplos novatos de cómo lo usaría un programador profesional.
Riwalk
@ Stargazer712: Pondría su incapacidad para comprender inicialmente la herencia en un plan de lección deficiente. También he usado vehículos para explicar la herencia a los jóvenes con los que trabajo, y nunca he tenido un problema con el concepto. En mi opinión, si el plan de una lección es completo y está construido adecuadamente, el objeto del vehículo es completo y práctico. Un tipo aleatorio en Internet no va a cambiar eso frente a los aproximadamente 30 pasantes y desarrolladores junior a quienes les he enseñado OOP. Si no te gusta el vehículo, vota abajo y sigue adelante.
Joel Etherton
Como lo desee ...
riwalk
0

Este ejemplo de no mamífero, no pájaro ni pez podría ayudar:

public abstract class Person {

    /* this contains thing all persons have, like name, gender, home addr, etc. */

    public Object getHomeAddr() { ... }

    public Person getName() { ... }

}

public class Employee extends Person{

    /* It adds things like date of contract, salary, position, etc */

    public Object getAccount() { ... }

}

public abstract class Patient extends Person {
    /* It adds things like medical history, etc */
}

Luego

public static void main(String[] args) {

    /* you can send Xmas cards to patients and employees home addresses */

    List<Person> employeesAndPatients = Factory.getListOfEmployeesAndPatients();

    for (Person p: employeesAndPatients){
        sendXmasCard(p.getName(),p.getHomeAddr());
    }

    /* or you can proccess payment to employees */

    List<Employee> employees = Factory.getListOfEmployees();

    for (Employee e: employees){
        proccessPayment(e.getName(),e.getAccount());
    }       

}

NOTA: Simplemente no digas el secreto: la persona extiende Mamífero.

Tulains Córdova
fuente
1
Funciona hasta que uno de sus empleados también sea paciente.
Scott Whitlock
En este caso, creo que tiene más sentido declarar al paciente y al empleado como interfaces en lugar de clases abstractas. Eso le brinda la flexibilidad de que Person implemente múltiples interfaces.
Jin Kim
@ JinKim: estoy totalmente de acuerdo, ese es el mejor enfoque.
Scott Whitlock
@ JinKim No son exclusivos. Usted trata a una persona como empleado o como paciente en un momento dado, pero no al mismo tiempo. Dos interfaces están bien, pero ¿cuándo se llama una clase concreta que implementa ambas, EmployeePatient? ¿Cuántas combinaciones tendrás?
Tulains Córdova
Puedes llamar a la clase concreta como quieras. Si el código solo espera tratar con Empleados, usted declara la referencia como Empleado. (es decir, empleado empleado = nueva persona ();) Si el código solo espera tratar con un paciente, usted declara la referencia como paciente. Raramente desea declarar una referencia directamente como la clase concreta.
Jin Kim el
0

¿Qué tal una jerarquía de expresiones algebraicas? Es bueno porque incluye herencia y composición:

+--------------------+------------------------+
| Expression         |<------------------+    |
+--------------------+----------+        |    |
| + evaluate(): int  |<---+     |        |    |
+--------------------+    |     |        |    |
          ^               |     |        |    |
          |               |     |        |    |
   +--------------+  +---------------+  +-------------+  ...
   | Constant     |  | Negation      |  | Addition    |
   +--------------+  +---------------+  +-------------+
   | -value: int  |  |               |  |             |
   +--------------+  +---------------+  +-------------+
   | +evaluate()  |  | +evaluate()   |  | +evaluate() |
   | +toString()  |  | +toString()   |  | +toString() |
   +--------------+  +---------------+  +-------------+

   Addition(Constant(5), Negation(Addition(Constant(3),Constant(2))))
   (5 + -(3 + 2)) = 0

Con la excepción de la expresión raíz Constant, todas las demás expresiones son una Expresión y contienen una o más expresiones.

edalorzo
fuente
-1

Usaré pájaros como ejemplo

como pollo, pato, águila

Explicaré que ambos tienen garras, picoteos y alas, pero sus atributos son diferentes.

Las gallinas no pueden volar, no pueden nadar, pueden comer gusanos, pueden comer granos

Los patos no pueden volar, pueden nadar, pueden comer granos, no pueden comer gusanos

El águila puede volar, no puede nadar, puede comer gusanos, no puede comer granos

Raju tuPepe
fuente
44
Una vez leí que la composición y las interfaces son probablemente la mejor manera de transmitir este tipo de concepto es decir, volar, nadar, etc
dreza
¿Un pato no puede volar?
Adam Cameron
-3

Su típico clon de rails proporciona muchos ejemplos prácticos : tiene la clase de modelo base (abstracta), que encapsula toda la manipulación de datos y tiene la clase de controlador base, que encapsula toda la comunicación HTTP.

keppla
fuente
¿Te importaría explicar por qué esta respuesta es mala?
keppla