Olá, tudo bem? Estou hoje aqui virtualmente, no Departamento de Ciência da Computação, do Instituto de Matemática Estatística da USP, para falar para vocês sobre Herança, que é conceito fundamental de orientação a objetos. O que é essa ideia de Herança? Herança já surgiu Simula-67, que foi a primeira linguagem orientada a objetos da história e a linguagem Simula foi criada para fazer simulações científicas, por exemplo, simulações de gases, onde você cria classes para os diferentes tipos de gases e depois você cria instâncias para as moléculas daqueles gases e daí você simula aqueles gases num determinado ambiente. Ao fazer isso, programando milhares e milhares de linhas de código na linguagem Simula, eles perceberam que eles tinham essas várias classes, mas boa parte do código das classes ficava duplicado ou triplicado, porque parte do comportamento dessas moléculas era o mesmo, independente se fosse uma molécula de oxigênio, gás carbônico, hidrogênio, parte do comportamento era igual, então o código era igual, daí eles falaram "e se a gente pegasse esse comportamento que é igual, esse código que é duplicado, triplicado nas classes e a gente colocasse numa nova classe, que seria essa classe mãe e daí criasse subclasses para ter só o que é específico de cada dos tipos de gases específicos" e daí surgiu o conceito de subclasses, eles não falavam ainda Herança, depois a comunidade de orientação a objetos começou a falar Herança, que é essa ideia de você ter superclasses e subclasses. E a ideia é que é mecanismo poderoso para evitar essa duplicação de código, porque se você tem vários pedaços de código duplicado, várias classes, tem essa possibilidade de você pegar o código duplicado e colocar numa superclasse e usar a Herança para reutilizar esse código. Também é uma forma de promover o reuso de códigos, se você já tem algum código escrito de uma determinada classe, mas você precisa adaptar ele pouquinho, vez de reescrever tudo isso do zero, você simplesmente pode criar uma subclasse da classe que já está pronta e colocar a sua customização, a sua personalização que você precisa, criando uma subclasse. É preciso ter uma relação "é-um" entre a classe mãe e a classe filha, então, por exemplo, se a classe filha é pato e a classe mãe é ave, pato é uma ave, então essa relação é-um está correta, por outro lado se você tem uma classe tesoura e uma classe veículo, essa relação é-um não está correta, porque uma tesoura não é veículo. Vamos ver mais exemplos, porque daí a coisa vai ficar mais clara, para isso eu vou pegar aqui o meu tablet, para a coisa ficar mais clara para vocês. [ÁUDIO_EM_BRANCO] [ÁUDIO_EM_BRANCO] [ÁUDIO_EM_BRANCO] [ÁUDIO_EM_BRANCO] [ÁUDIO_EM_BRANCO] Então pronto, vocês já estão vendo a minha tela e vamos ver exemplo, vamos supor que a gente está desenvolvendo sistema para gerenciar as pessoas de uma universidade, por exemplo, da Universidade de São Paulo, então a gente poderia ter esquema de classes, que começaria com uma classe mãe aqui, chamada de pessoa [ÁUDIO_EM_BRANCO] e porque eu vou gerenciar as pessoas da universidade, daí nessa classe mãe eu posso colocar tudo que uma pessoa normal tem, que toda pessoa vai ter. Toda pessoa vai ter nome, sobrenome, no Brasil CPF, RG, data de nascimento, nome da mãe, nome do pai, então qualquer pessoa tem essas informações, então isso pode ir na classe pessoa, mas uma universidade tem tipos específicos de pessoas, então a gente pode criar subclasses para esses tipos específicos. O jeito que a gente representa isso é fazendo triangulozinho aqui e daí saindo essas linhas, então eu posso dizer que eu tenho uma subclasse chamada aluno, aluno é tipo específico de pessoa. Aluno é uma pessoa, então essa relação aqui tá OK. Outro tipo de classe de pessoa que eu posso ter aqui, uma subclasse de pessoa, é professor. Desculpa minha letra, minha letra já é ruim, ainda com esse tablet e essa caneta aqui fica péssima, mas dá para entender. Eu posso ter funcionário, funcionário também é tipo de pessoa que a universidade tem que gerenciar, então esse diagrama, esse triangulozinho e essas linhas saindo para essas três subclasses aqui está indicando que eu tenho uma classe mãe chamada pessoa e três subclasses. Eu posso continuar, por exemplo, tem vários tipos de aluno da universidade, então eu tenho, por exemplo, aluno de graduação, que eu poderia ter uma classe "Aluno Grad", eu tenho aluno de pós graduação, vou botar aqui como "Aluno Pós" e, por exemplo, aluno que faz uma disciplina separada, individual, seria aluno especial, então eu podia ter uma classe "Aluno Especial", e daí qual que é a ideia aqui? Essas diferentes classes estão representando diferentes abstrações e também eu tenho reuso de código, então todos o atributos que estiverem na minha classe pessoa, todo o código para gerenciar os atributos que estiverem na classe pessoa, para gerenciar nome, CPF, RG, vão estar todas essas outras subclasses aqui, porque essas subclasses herdam da classe pessoa esses atributos. Já na classe aluno, por exemplo, eu devo ter informações que são específicas de aluno, mas que não estão outros tipos de pessoas que não são alunos, por exemplo, aluno vai ter o curso no qual ele está matriculado ou a lista de disciplinas que ele está cursando, então se eu tenho aqui atributo chamado lista de disciplinas, provavelmente eu vou ter código, métodos para gerenciar essa lista de disciplinas. Fazer a matrícula numa disciplina, cancelar a matrícula, listar todas as disciplinas que o aluno concluiu. Isso faz sentido para o aluno, mas não faz sentido nem para o professor nem para o funcionário, então esse código vai estar na classe aluno, se o código está na classe aluno, o código automaticamente vai ser herdado pela classe aluno de graduação, aluno pós graduação e o aluno especial, então a gente tem essa ideia que a gente tá gerando uma hierarquia de classes, então a gente pode escolher aonde que cada atributo, que cada método vai se encaixar nessa hierarquia de classes de forma a maximizar o reuso e o código estar bem organizado. Então isso aqui é exemplinho, vamos ver outro exemplo, vamos supor que você está desenvolvendo sistema para gerenciar arquivos, sistema de arquivos e você quer visualizar esses arquivos de diferentes formas, então você poderia ter uma classe inicial chamada "Arquivo" e depois você poderia ter diferentes subclasses, então coloca o triângulo e essa linha, para representar diferentes subclasses, a gente poderia pensar que a gente tem arquivo texto, teria uma subclasse de arquivo, podia ter arquivo de áudio, que é outro tipo de arquivo e também arquivo de vídeo, seriam, por exemplo, esses três tipos. A gente podia continuar, se falando arquivo de áudio, a gente podia pensar diferentes tipos de arquivos de áudio, a gente podia pensar arquivo MIDI, arquivo MIDI é arquivo que tem uma representação simbólica, normalmente de uma música, das notas musicais, daí você precisa de software específico para tocar aquela música, transformar aquela representação simbólica no áudio ou você pode ter arquivo já de áudio, por exemplo, arquivo WAV, que tem a onda sonora digitalizada ou, por exemplo, arquivo onde essa onda sonora está comprimida, arquivo MP3, então, se você quiser fazer sistema para visualizar esses diferentes tipos de arquivo, você poderia, por exemplo, aqui nessa classe, ter o código que lê arquivo MIDI e que transforma aquilo num sinal de áudio que possa ser tocado pelo computador. Já essa classe WAV pode, diretamente, já ser jogado para a placa de som que o computador já toca. Já essa classe MP3 teria que descomprimir o arquivo primeiro, usando os algortimos de MP3 para daí sim jogar o áudio para a placa de som. Arquivo texto, você provavelmente chamaria editor de textos para abrir aquele arquivo texto, arquivo vídeo, tocador de vídeo. Já nessa classe arquivo aqui cima, você poderia colocar as informações genéricas de todo arquivo, todo arquivo tem nome, tem a data de criação, tem diretório onde ele está armazenado, então essas informações genéricas de todos os arquivos ficariam aqui cima, então isso é jeito de Pensar termos de orientação a objetos de como eu organizaria sistema de arquivos para construir sistema para visualizar esses arquivos de diferentes formas. Arquivo de áudio, a gente visualizar arquivo de áudio na verdade é sonoralizar, ao oralizar o arquivo de áudio. Arquivo de vídeo, aí você mostra aquivo de texto você mostra também. Há outro exemplo que eu gosto de mostrar. Eu escrevi uma apostilha de Java numa certa ocasião. E eu coloquei nessa apostilha uma hierarquia de classes para representar os seres vivos. a gente poderia ter uma superclasse aqui chamada ser vivo e depois, por herança, a gente criaria subclasses para os diferentes tipos de seres vivos. Quando eu aprendi na escola, eu aprendi que os Reinos eram Animal, Vegetal, Monera, Protista e Fungo. Hoje dia acho que a Biologia já evolui, tem mais Reinos. Animal poderia também ser animal vertebrado ou animal invertebrado. Entre os animais vertebrados tinha outros subtipos. Você podia continuar muito aqui representando toda a taxonomia de seres vivos como se fosse conjunto de classes e essa relação de herança. Se a gente fosse desenvolver, por exemplo, sistema para gerenciar banco de dados para guardar informações sobre biodiversidade de uma determinada região, por exemplo, para fazer uma pesquisa sobre biodiversidade, esse seria modo natural de você organizar o seu banco de dados. Porque cada uma dessas classes teriam informações específicas para aquele tipo de ser vivo. Particular, você poderia, se você quisesse ter algum código para fazer análises, esse código poderia estar também aguma dessas classes específicas para aquele tipo de ser vivo que você está lidando. E note que aquela relação é satisfeita. Animal vertebrado é animal, e animal é ser vivo, então a relação 'é ' também está sendo satisfeita. Vamos ver mais pouco de terminologia. Lembra, naquele exemplo da gestão de pessoa de uma Universidade, você tinha a classe mãe ou a super classe, que era a classe pessoa e depois várias subclasses ou classes filhas, que eram: aluno, professor e funcionário. Quando a gente vai, a gente está pensando na superclasse e a gente decide que quer especializar o código, ter subtipos, subclasses, a gente está fazendo processo que a gente chama de especialização, a gente está especializando a superclasse para subclasses mais específicas. Ou pode ser o oposto, se você está trabalhando com várias classes e você identifica que elas têm coisas comum, você pode generalizar essas várias classes para uma superclasse. Esse é o processo de generalização. Você pega o código comum e coloca numa superclasse e o código, só o código que é diferente é que fica na subclasse, é o processo de generalização. A gente vai falar superclasse, subclasse, classe mãe, classe filha, especialização, generalização. Tudo isso é a terminologia de herança. Quando é que a gente deve usar a herança? Primeiro para organizar as abstrações. As classes de determinado sistema, elas representam as abstrações do nosso mundo real ou virtual. E para organizar essas abstrações numa hierarquia, a gente pode utilizar a herança. Além disso, tem algo mais instrumental que a gente pode fazer, usar a herança para acrescentar comportamento novo. Então, quando a gente tem uma classe A e cria uma subclasse B, essa classe B pode acrescentar novos métodos à classe A e novos atributos. Então, a classe B, a classe filha, ela vai ter todos os métodos e atributos da classe mãe mas, além disso, ela acrescenta alguns novos métodos e novos atributos. Eu posso também alterar o comportamento. Se eu tenho uma classe mãe que tem tudo o que eu preciso, só dos métodos que têm comportamento que não é exatamente o que eu quero, eu quero mudar aquele comportamento, tudo bem, eu crio essa subclasse e reimplemento aquele método que eu quero alterar. A gente diz que está sobrepondo, fazendo uma sobreposição ali do método. A gente pode alterar o comportamento também ao usar a herança. Vamos ver exemplos, agora, Python. Depois, mais para a frente no curso, a gente vai ver exemplos também Java, mas agora eu vou mostrar exemplos Python. Vocês estão vendo, eu abri o editor Spyder que eu gosto bastante de usar para executar código Python e criei esse exemplo. Acabei de criar esse exemplo, que é para representar figuras geométricas. Para você que não está habituado com a sintaxe de Python, esse é o jeito de eu definir uma classe. Aqui a classe se chama polígono. E para definir o construtor, que é o método que é chamado quando eu crio novas instâncias de polígono Python, eu coloco esse sublinhado. sublinhado, init, sublinhado, sublinhado. Esse é o nome do construtor. Toda a classe vai ter o método init, que define como é que eu crio objetos daquele tipo. Python, todos os métodos recebem como primeiro parâmetro, uma referência para o próprio objeto, que é esse self aqui e depois opcionalmente pode ter outros parâmetros. Nesse caso, no init eu coloquei parâmetro chamado número de lados, que vai dizer o número de dados do polígono. E eu guardo esse número de lados no atributo n e esse 'lados' é uma lista que eu crio aqui é uma lista que vai guardar o tamanho dos lados. Esse método le_lados não recebe nenhum parâmetro, ele vai ler do teclado o tamanho de cada dos lados do meu polígono e o mostra_lados vai imprimir o tamanho de dos lados. Vamos ver só exemplo aqui do polígono. Poderia ter polígono, vamos ver, pentágono que eu vou chamar de pent recebe polígono de cinco. Então, pentágono tem cinco lados. Pronto, essa variável pent agora está apontando para objeto da classe polígono que tem cinco lados. Eu posso particular chamar o método pent.le_lados. E ele vai falar, digite o tamanho do lado. Eu vou ter que digitar o tamanho dos lados do polígono aqui. Lado três, lado quatro lado cinco. Pronto, então eu já tenho agora polígono de cinco lados, se eu quiser eu posso fazer pent.mostra_lados e ele vai mostrar ali o comprimento ali dos cinco lados. Essa é uma classe bem simples. Para exemplificar a herança, eu criei esta subclasse chamada triângulo e a sintaxe de Python, para definir subclasses é isso, eu coloco classe, triângulo e aqui, entre parênteses, o nome da classe mães, de quem é que eu estou herdando. O triângulo está herdando de polígono. Isso significa que todos os métodos poligono também vão estar presentes na classe triângulo. Mas o método init eu estou sobrescrevendo, eu estou criando novo método init, que na verdade, a única coisa que ele faz, ele é método sem parâmetros, eu não preciso dizer quantos lados triângulo tem, porque triângulo sempre tem três lados. Então, é método sem parâmetros e que a única coisa que ele faz é chamar o init da superclasse do polígono, passando o três como parâmetro. E o init da superclasse vai guardar ali o número de lados e criar essa lista para receber o tamanho dos lados. Mas esses métodos le_dados e mostra_lados continuam existindo, porque eles foram herdados da classe mãe. E eu acrescentei método novo que é essa área. Polígono, eu não sei como calcular a área genérica de polígono genérico, mas para triângulo eu sei. Tem uma formula matemática, então, eu posso calcular a área de determinado triângulo. Então se eu criar aqui tri recebe triângulo, eu posso fazer tri.le_lados le_lados e eu digo os lados: três, vou botar 30 40, 50. Note que esse le_lados é o le_lados da superclasse. Como é que funciona isso? Quando eu faço tri.le_lados ele primeiro verifica se a classe triângulo possui esse método le_lados. Se possuir ele executa. Mas nesse caso ele não possui, não tem le_lados aqui na classe triângulo. Então o que é que ele faz? Ele vai para a superclasse que é polígono e vê se a superclasse implementa esse método le_lados. Nesse caso sim, implementa. E daí ele executa o da superclasse. E ele vai subindo nessa hierarquia, superclasse, superclasse, até que ele ache a primeira classe que realmente implementa aquele método que você chamou. É como se a gente enviasse para o triângulo a mensagem le_lados, e ele vai procurando na hierarquia de classe, qual é a primeira classe que implementa o método com o nome dessa mensagem le_lados. A mesma coisa aqui para o mostra_lados. Se eu faço o tri.mostra_lados ele vai executar o mostra_lados da superclasse e mostra o tamanho dos lados. Só que eu posso fazer tri.area, área, e ele vai mostrar: a área do triângulo é 600. Por outro lado, se eu pedir para o pentágono: a área do pentágono. Vai dar uma mensagem de erro. O objeto polígono não tem nada chamado área, então, ele não consegue executar esse pent.área. E a gente pode continuar nesse tipo de coisa. Então, uma classe retângulo, por exemplo, também herda de polígono. Retângulo tem quatro lados. Só que eu alterei o le_lados aqui. Porque? Porque no retângulo os lados têm que ser o mesmo comprimento dois a dois. Então, eu não posso ter retângulo com quatro lados de quatro tamanhos diferentes. Então, eu leio só dois tamanhos e armazeno esses dois tamanhos nos lados. O primeiro tamanho no lado e três, no outro lado dois e quatro. Eu reimplemento a área porque o jeito de calcular a área de retângulo é diferente, e eu implemento também o método diagonal porque eu sei calcular a diagonal de determinado retângulo. E eu implementei aqui uma subclasse de subclasse. Eu fiz triângulo retângulo que é uma subclasse de triângulo, que é uma subclasse de polígono. Esse triângulo retângulo então, ele vai herdar tudo de triângulo e tudo de polígono e ele ainda implementa novo método, que aqui nesse caso é booleano de se realmente é triangulo retângulo ou se não é. Depois eu deixo esse código disponível para vocês, para quem quiser fazer uma brincadeira. Vamos ver outro exemplo Python para terminar? Vamos supor que você está implementando sistema de folha de pagamento de uma empresa, para gerenciar os pagamentos dos funcionários de uma empresa. Você poderia fazer uma modelagem orientada o objetos disso, criando uma classe empregado onde você teria as informações do empregado, nome, CPF, RG e ter método 'pagamento' que diz quanto que você tem que pagar para aquele empregado todo o mês, por exemplo. Só que dependendo do tipo de empregado quanto você tem que pagar é diferente. Então, eu criei subclasses aqui para empregado. Tem empregadorista, empregado CLT e empregado prestador de serviços. E para cada deles no construtor eu posso passar informações diferentes. Então, por exemplo, para o empregado horista eu passo quantas horas ele trabalhou e o pagamento por hora e para cada subclasse eu tenho uma implementação de pagamento diferente que é cálculo diferente de acordo com o tipo de funcionário. Depois a gente vai ter uma aula de polimorfismo e a gente vai ver mais detalhes esse exemplo aqui e de como isso é uma coisa poderosa orientação a objetos. Vamos então voltar para os nossos slides e relembrar os conceitos fundamentais de orientação a objetos que a gente viu até agora. Então, objetos são coisas que encapsulam código e atributos. Classe funcionam como fábricas de objetos e também elas especificam o que uma família de objetos vai conter. Elas servem para abstrair coisas do nosso mundo real ou virtual lembrando que as variáveis, os atributos nos objetos podem ser de instância e cada instancia da classe vai ter valor diferente para essa variável, ou pode ser uma variável da classe, onde todas as instâncias compartilham. A gente viu que os objetos se comunicam entre si por trocas de mensagens que geram a execução de chamadas de determinados métodos, de uma execução de métodos. E os sistemas de orientação de objetos identifica qual o método executar percorrendo a hierarquia de classes. E a gente viu esse conceito de herança que é uma forma poderosa de organizar nossas classes e de reutilização de código. Mais para a frente, nas próximas aulas, a gente vai ver outros conceitos importantes como a ideia de que os nomes das nossas classes variáveis, métodos precisam revelar a intenção do que elas fazem. O nome de uma variável tem que representar muito claramente o que aquela variável está guardando. O nome de método tem que ser muito claro sobre o que aquele método faz. O nome de uma classe também representa claramente o que aquela classe faz. Porque se você fizer isso ao olhar para o código, qualquer pessoa vai entender rapidamente o que aquele código está fazendo. Se você usa nomes abreviados, nomes que não são muito claros, que você não raciocinou muito na escolha dos nomes, vai ser muito dificil de outra pessoa entender o seu código o que vai tornar o seu código código de muito má qualidade. Então, bons programadores pensam muito bem excelentes nomes para as variáveis, para os métodos, para as classes. Então, eu diria que essa é a característica mais importante de bom programador orientado a objetos, é a escolha de bons nomes. A gente vai ver também essa ideia do princípio da responsabilidade única que é a ideia que cada classe deve fazer uma única coisa bem feita. E a gente vai ver o importante o conceito de polimorfismo. Então, não percam as próximas aulas. Tchauzinho.