Como posso explicar a utilidade da herança? [fechadas]

16

Ao tentar explicar o conceito de herança na OOP, o exemplo comum é frequentemente o exemplo dos mamíferos. IMHO, este é realmente um mau exemplo, porque levará os novatos a usar esse conceito da maneira errada. Além disso, não é um design comum que eles enfrentarão no trabalho de design do dia-a-dia.

Então, qual será um problema agradável, simples e concreto que será resolvido usando a Herança?

Pierre Watelet
fonte
1
"o exemplo comum é frequentemente os mamíferos"? O que você quer dizer? Você pode fornecer um link, referência ou cotação para isso?
S.Lott
3
Qual seria o melhor exemplo real para explicar a utilidade do fundo fiduciário de Herança = Bill Gates?
Martin Beckett
1
@ Chris: como esta questão não é construtiva? Você está declarando que um solicitante e 14 respondedores estão desperdiçando o tempo de todos?
Dan Dascalescu
@ DanDascalescu - foi fechado há dois anos em resposta a uma bandeira que indica "por favor, considere o fechamento como não construtivo: a julgar pelas respostas que a acumulam, isso parece uma pergunta típica de lista / pesquisa". Se você acha que isso está errado, edite-o para deixar claro que não está e deixe a comunidade decidir pela fila de revisão de reabertura.
ChrisF

Respostas:

15

Não há nada errado com um exemplo puramente acadêmico como mamíferos. Também gosto do exemplo de retângulo / quadrado, porque aponta por que as taxonomias do mundo real nem sempre se traduzem diretamente no relacionamento de herança que você esperaria.

Na minha opinião, o exemplo mais canônico de todos os dias é um kit de ferramentas da GUI. É algo que todo mundo já usou, mas os iniciantes podem não ter raciocinado sobre como trabalham sob o capô. Você pode falar sobre quais comportamentos são comuns a todos os contêineres, todos os widgets, eventos etc. sem exigir conhecimento detalhado de qualquer implementação.

Karl Bielefeldt
fonte
5
+1 para kits de ferramentas da GUI ... e também gosto do exemplo de formas, com uma Forma como base com um mínimo de draw () e as formas descendentes com draw () personalizado.
yati sagade
14

Meu exemplo do mundo real é o modelo de domínio de um aplicativo de RH simples. Digo que podemos criar uma classe base chamada Employee , porque é claro que os gerentes também são empregados.

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.
    }
}

Então eu explico que desenvolvedores são funcionários , testadores são funcionários , gerentes de projeto são funcionários . Assim, todos eles podem herdar da classe de funcionários.

Saeed Neamati
fonte
2
Para mostrar vantagens de herança, pode ser interessante mostrar também como os desenvolvedores diferem dos funcionários. Se não houver diferença, não será necessário criar uma classe Developer.
David
3
Parece que também Employeepoderia ser uma abstractaula.
StuperUser
+1 é o exemplo que a maioria dos meus professores usou e eu realmente gostei. fazia todo sentido e dava um exemplo do mundo real de como usar a herança.
David Peterman
18
Exceto que isso nunca funciona na prática, porque sempre há pelo menos uma pessoa que precisa ser a Developere a Tester. Outra situação semelhante é um banco de dados de contatos em que você possui Customere Supplier, mas como qualquer pessoa que criou um sistema assim, sempre haverá um caso em que a Companyé ambos. É por isso que a maioria desses exemplos leva você na direção errada.
22112 Scott Scott
11

Encapsule o que varia ... mostre a eles um padrão de método de modelo , ele demonstra a utilidade da herança colocando comportamento comum em uma classe base e encapsulando comportamentos variáveis ​​nas subclasses.

UI controlse Streamstambém são um bom exemplo para a utilidade da herança.

Falcão
fonte
Eu acho que essa fábrica seria um exemplo melhor.
Let_Me_Be 14/09
1
@Let_Me_Be: Eu acho que o relacionamento entre uma fábrica e uma herança é de natureza indireta demais. Certamente, ele produz tipos concretos e retorna tipos abstratos / base, mas também pode retornar apenas um tipo de interface! Imho que não é melhor do que o exemplo clássico de animal.
Falcon
@Let_Me_Be: Além disso, uma fábrica abstrata é um exemplo bastante complexo, envolvendo diferentes hierarquias de herança (uma para os itens e outra para as fábricas). Eu acho que é um bom uso da herança, mas não um exemplo bom e simples.
Falcon,
3

Lembrar

Toda instância de um objeto é um exemplo concreto da utilidade da herança!

Se você quer dizer especificamente herança de classe , agora está no mundo das taxonomias, e essas variarão drasticamente de acordo com os objetivos do sistema que as utiliza. O exemplo de animais / mamíferos usa uma taxonomia comum e esperançosamente familiar da biologia, mas é (como você mencionou) quase inútil para a grande maioria dos problemas de programação.

Então tente algo universal: a noção de um programa. Todo programa inicia, executa e termina. Todo programa tem um nome e parâmetros opcionais de linha de comando. Portanto, uma classe de programa base seria muito útil, para iniciar a execução, pegar e processar os argumentos da linha de comando, executar a lógica principal e desligar normalmente.

É por isso que tantas linguagens de programação orientadas a objetos fornecem uma classe Program ou algo que se comporta exatamente como uma classe Program.

Steven A. Lowe
fonte
Então, você programa um programa que possui muitos programas? :) Na minha experiência, os Objetos de Programa quase sempre são singletons que não têm herança; portanto, eles não são o melhor exemplo.
keppla
@keppla: você já usou Java ou .NET? O .NET tem uma classe de programa explícita, a do Java está implícita. Eles não são singletons
Steven A. Lowe
Eu usei Java, em torno da versão 1.4.2. Naquela época, havia apenas static void main, então acho que mudou um pouco. Qual seria um motivo típico para ter mais de uma instância da classe Programm?
Keppla
@keppla: o static void main do java faz implicitamente a classe de entrada representar o programa. Todo usuário que executa seu programa cria uma nova instância dele. Neste momento, tenho três instâncias do Google Chrome em execução, quatro documentos do Word, três blocos de notas e dois Windows Explorer. Se fossem todos singletons, eu nunca seria capaz de fazer isso.
Steven A. Lowe
1
Eu acho que você está ampliando um pouco a definição. class Programm { public static void main(String[] args) { system.out.println('hello world'); }}é um programa Java mínimo. Quando eu chamo, não há instância do programa. Programa não herda de nada. Quando inicio 3 processos (como você faz com o crhome), pode haver 3 programas, mas em suas áreas individuais de memória, ainda há apenas um programa. Imho, singleton implica 'Apenas uma instância por processo', não por máquina. Nesse caso, seria impossível criar singletons, nada impede você de executar qualquer código duas vezes.
keppla
3

Eu estou trabalhando com câmeras no trabalho. Como temos dispositivos que se conectam a modelos diferentes, temos uma "classe de câmera" abstrata e cada modelo é herdado dessa classe para suportar funcionalidades específicas dessa câmera. É um exemplo do mundo real e não é difícil de entender.

Lucas
fonte
2
Isso pode quebrar se você tiver, por exemplo, um modelo que seja a Camerae a Phone(como todos fazemos agora em nossos bolsos). De qual classe base ela deve herdar? Ou não deveria apenas implementar as interfaces ICamerae IPhone? (ha ha)
Scott Whitlock
2
@ Scott: Você não pode implementar a interface do iPhone ou será processado pela Apple.
Mason Wheeler
3

Exemplo de elementos de química

Este é outro exemplo que saiu do meu cérebro:

classe Element_
{
    double atomicWeight; // Peso atômico do elemento
    double atomicNumber; // Número atômico do elemento
    Propriedades da string; // Propriedades do elemento
    // Outros, se houver
}


classe Isótopo estende Element_ // Pode existir isótopos de elemento
{
    halflife dupla;
   // Outros, se houver

}
Marca
fonte
2
Embora atomicNumber pode (? Deve) ser provavelmente inteiro ...
Andrew
Eu não usaria herança para isso. isotopenão é um caso especial de Elemenet. Prefiro ter uma Elementpropriedade Isotope.
CodesInChaos
2

Exemplos do mundo real quase sempre errar, porque eles dão exemplos em que há sempre a possibilidade de algo ser tanto TypeAe TypeBmas a hierarquia de herança única de muitas línguas não permitem isso.

Quanto mais eu programo, mais me afasto da herança.

Até a palavra "herdar" é usada incorretamente aqui. Por exemplo, você herda cerca de 50% dos traços de seu pai e 50% dos traços de sua mãe. Realmente seu DNA é uma composição de metade do DNA de seu pai e metade do DNA de sua mãe. Isso porque a biologia realmente favorece a composição sobre a herança , e você também deveria.

Simplesmente implementar interfaces, ou melhor ainda, "digitação de pato", além de injeção de dependência, é uma coisa muito melhor para ensinar as pessoas que são novas na programação orientada a objetos.

Scott Whitlock
fonte
1

Eu apenas mostraria a eles um exemplo da vida real. Por exemplo, na maioria das estruturas de interface do usuário, você deriva de algum tipo de classe "Caixa de diálogo" ou "Janela" ou "Controle" para criar sua própria.

Nemanja Trifunovic
fonte
1

Um bom exemplo é a função de comparação na classificação:

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);

O único problema é que os novatos costumam pensar que o desempenho é mais importante do que um bom código ...

tp1
fonte
0

Meu exemplo do mundo real é um veí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 exemplo pode ser tão detalhado quanto você quiser e existem todos os tipos de propriedades associadas aos veículos para explicar o uso de qualquer modificador que você queira ensinar.

Joel Etherton
fonte
2
Eu sempre odiei usar veículos como exemplo, pois ele não aproxima um novo programador de entender como a herança pode ser usada para melhorar o código. Os veículos são máquinas imensamente complicadas que lembram muitas idéias não abstratas da mente dos não programadores. Tentar descrever isso no código faz com que o novato médio acredite que muitos detalhes são deixados de fora do exemplo e dá a sensação de que eles não estão mais perto de conseguir que algo funcione. Digo isso por experiência própria, pois foi exatamente assim que me senti quando alguém tentou usar veículos para me explicar.
riwalk
@ Stargazer712: Eu uso veículos principalmente porque eles podem ser tão complicados ou simples quanto você quiser. Deixo ao julgamento do instrutor determinar o nível de seu aluno. Expliquei OOP básico para minha esposa (que não tem experiência em programação) usando as propriedades simples de um veículo que descreve os conceitos básicos comuns. Todos os veículos têm portas, todos os veículos têm rodas, etc. O exemplo de objeto não pode ser responsabilizado por um plano de aula ruim.
Joel Etherton
sua esposa não estava tentando escrever código. Posso dizer com muita segurança que os exemplos de veículos não ajudaram a entender a herança. Tudo o que você usa para descrever a herança, ela deve ser completa e prática . O objetivo não é descrevê-lo de tal maneira que um não programador possa entendê-lo. O objetivo é descrevê-lo de forma que um programador iniciante possa usá- lo, e a única maneira de fazer isso é mostrar os exemplos iniciantes de como um programador profissional o usaria.
riwalk
@ Stargazer712: Eu colocaria sua incapacidade de entender inicialmente a herança em um plano de aula ruim. Também usei veículos para explicar a herança aos juniores com quem trabalho e nunca tive problemas com o conceito. Na minha opinião, se um plano de aula é completo e adequadamente construído, um objeto veículo é completo e prático. Um cara aleatório na internet não vai mudar isso diante dos 30 ou mais estagiários e desenvolvedores juniores a quem eu ensinei OOP. Se você não gostar do veículo, faça o voto negativo e siga em frente.
Joel Etherton
Como você deseja ...
riwalk 14/09
0

Este exemplo não mamífero, não pássaro e não peixe pode ajudar:

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 */
}

Então

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: Apenas não conte o segredo: a pessoa estende o mamífero.

Tulains Córdova
fonte
1
Trabalha até que um de seus funcionários também seja um paciente.
22112 Scott Scott
Nesse caso, acho que faz mais sentido declarar Patient e Employee como interfaces em vez de classes abstratas. Isso oferece a flexibilidade de fazer com que o Person implemente várias interfaces.
Jin Kim
@ JinKim - eu concordo totalmente, essa é a melhor abordagem.
22312 Scott Scottlock
@JinKim Eles não são exclusivos. Você trata uma pessoa como funcionário ou como paciente a qualquer momento, mas não ao mesmo tempo. Duas interfaces estão OK, mas quando você chama uma classe concreta implementando ambas, EmployeePatient? Quantas combinações você terá?
Tulains Córdova
Você pode chamar a classe concreta como quiser. Se o código apenas espera lidar com Funcionários, você declara a referência como Funcionário. (ou seja, funcionário empregado = nova pessoa ();) Se o código espera apenas lidar com um paciente, você declara a referência como paciente. Raramente você deseja declarar uma referência diretamente como a classe concreta.
Jin Kim
0

Que tal uma hierarquia de expressões algébricas. É bom porque inclui herança e composição:

+--------------------+------------------------+
| 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

Com exceção da expressão raiz Constant, todas as outras expressões são Expressões e contêm uma ou mais expressões.

edalorzo
fonte
-1

Vou usar pássaros como exemplo

como frango, pato, águia

Explicarei que ambos têm garras, beijinhos e asas, mas seus atributos são diferentes.

Galinhas não podem voar, não sabem nadar, podem comer vermes, podem comer grãos

Os patos não podem voar, podem nadar, podem comer grãos, não podem comer vermes

Águia pode voar, não sabe nadar, pode comer vermes, não pode comer grãos

Raju yourPepe
fonte
4
Eu li uma vez que a composição e as interfaces são provavelmente a melhor maneira de transmitir esse tipo de conceito ou seja, voar, nadar etc
dreza
Um pato não pode voar ?!
Adam Cameron
-3

Seu típico rails-clone fornece muitos exemplos práticos : você tem a classe de modelo base (abstrata), que encapsula toda a manipulação de dados e a classe de controlador base, que encapsula toda a comunicação HTTP.

keppla
fonte
Gostaria de esclarecer por que essa resposta é ruim?
22813 keppla