Bom dia, boa tarde, boa noite ou boa madrugada, dependendo do horário que você estiver assistindo esse vídeo. Então, a gente vai falar agora de uma coisa que talvez seja a mais imporante de todas desse curso que é como ser bom programador programação orientada a objetos. Na verdade, o que eu vou falar aqui é meio genérico. Isso serve qualquer paradigma de programação mas a gente vai falar alguns como isso se aplica quando você está programando numa linguagem orientada a objetos. E basicamente o que a gente vai falar hoje é a diferença entre o programador meia boca e real desenvolvedor de software que produz software de alta qualidade. A gente vai ver detalhes sobre o que a gente vai apresentar hoje ao longo do curso, mas hoje é a visão geral. Então, esse é vídeo que é bom você assistir até mais do que uma vez. Talvez você pode assistir no final do curso também depois de assistir ele agora. Então, vamos lá. Então, nós vamos falar sobre boas práticas de programação orientada a objetos. Eu diria que a principal boa prática, a principal característica de bom programador orientação a objetos é o programador que escolhe bons nomes para os elementos do seu programa. Bons nomes para para as variáveis. Então, o nome da variável tem que descrever claramente o valor que ela contém, e não alguma outra coisa. Então, o nome você não deve ser excessivamente econômico usando só uma ou duas letras para o nome da variável, ou abreviar tanto o nome da variável que não dá para bater o olho e entender o que ela significa. Idealmente qualquer pessoa que vai ler o seu código tem que bater o olho no seu código e olhando os nomes das variáveis e já entender os dados que os dados que estão circulando pelo seu programa sem nenhum esforço, ou muito menos tendo que consultar alguma documentação externa para entender o que a variável X22 significa. Os atributos e as propriedades, os atributos de uma classe têm que descrever também claramente alguma informação que instâncias daquela classe contém. A mesma coisa para os métodos e seus argumentos. Então, lendo o método além da chamada de método ao longo do código você tem que imediatamente entender que que aquele método está fazendo. Sem precisar ele ler a documentação do método. Se você seguir essas boas práticas e também ter os nomes das classes representando muito bem qual é a abstração que aquela classe representa aí sim você vai ter código que é fácil de entender, e por conseguinte você já fez 50% do que precisa para ter código de ótima qualidade. Tem termo inglês muito comum para se referir a isso que é Intention-revealing names. Os nomes que você escolhe no seu programa têm que revelar a intenção daquele elemento que você está programando. Normalmente as classes, o nome das classes são substantivo no singular. Normalmente, não é uma regra de ouro que sempre deve ser seguida mas geral o nome da classe é substantivo e geral o nome do método é verbo. Porque o método faz alguma coisa e a classe representa alguma coisa. Isso de novo não é 100% dos casos pode ter casos que faz sentido ter algo diferente disso, mas geral é isso. Então, não se esqueçam dessa que é a principal característica de programador. Não só 'iii' a objetos mas qualquer paradigma de programação. Depois o uso extensivo de testes automatizados, porque ao programar a gente comete erros, e a melhor forma de evitar que esses erros se propagem e eles continuem existindo no nosso código é ter uma boa bateria de testes automatizados. Então, geral para cada componente mais minimamente complexo que a gente desenvolve no nosso software, a gente deveria ter software associado que é o teste automatizado que é código que testa aquele outro código para garantir pelo menos certo nível de que os nossos software está funcionando. E a gente quer buscar também uma boa cobertura de código. Existem algumas ferramenta que dado uma bateria de testes eles apontam quais linhas do seu programa que são cobertas por algum código, e quais linhas que não estão sendo testadas por nenhum teste. A cobertura de códigos diz quanto do seu código que é coberto por testes, da sua bateria de testes. Você tanto pode ver exatamente quais são as linhas cobertas quanto você pode ter percentual, por exemplo. Então tipicamente software que tenha 90% das suas linhas de código cobertas por testes automatizados está caminhando para ser código alí bem coberto por testes. Não é necessário ter 100%. Na verdade, geral nem faz sentido chegar 100% mas muitos casos acima de 90% é desejável sim. E você não vai construir esses testes do zero. Existem arcabouços frame works específicos para ajudar a escrever esses testes automatizados, e para automatizar a execução desses testes. O primeiro que apareceu foi o SUnit, no mundo do Small Talk, mas logo depois ele foi implementado também na linguagem Java que é o Junit, que ficou muito famoso. Depois que o Junit foi lançado apareceram uma série de outros arcabouços para várias linguagens de programação. Praticamente hoje dia qualquer linguagem principal aí que você imagina tem arcabouço equivalente a testes automatizados. Por exemplo C++ tem o C++Unit CppUnit. No caso de Pyton que muitos de vocês aqui programam tem o PyTest que é uma ferramente muito bom que a gente até tem vídeo aqui sobre isso, que serve para a gente testar as unidades fazer os testes de unidades do nosso software Python. As unidades são tipicamente os métodos individuais alí do nosso programa. Tem outro tipo de teste que a gente chama de teste de aceitação. Que Python tem uma ferramenta chamada Robot que é bem legal, que permite você simular que você é usuário usando aquele software. E daí o código que você escreve no Robot é código muito fácil de entender. Até pessoas que não são programadores podem entender esse código que você escreve no Robot. E ele simula a ação de usuário no sistema. A gente chama isso de teste de aceitação. No mundo 'iii' tem série de ferramentas bem interessantes PSpec, Cucumber, Capybara que ajudam a fazer testes de unidades, testes de aceitação e no caso no Capybara, testes de web. Supondo que o sistema tem uma interface web. Particular Selenium é uma ferramentas que pode ser utilizada diferentes contextos de diferentes linguagens de programação, e o que ele permite é exatamente fazer testes de sistemas web. Porque ele simula que ele é usuário visitando uma página web, e clicando coisas, digitando coisas numa página web. Então, você consegue escrever como se fosse script de usuário utilizando sistema web, e ele vai dizer se o sistema se comportou da forma correta ou não. Então, escrever testes automatizados dessa forma é algo alí fundamental que todo excelente programador faz. Eu acho difícil ser excelente programador e não escrever testes. Particular tem alguns excelentes programadores que gostam de radicalizar, e adotam o que a gente chama de TDD, Test Driven Development, que é você guiar o desenvolvimento do seu software pela escrita de testes. Geral quando você está fazendo o TDD você escreve o teste, depois você escreve o código que foi testado para aquele teste que você escreveu primeiro, e daí uma vez que aquele código passa você refatora. Melhora-se pouco o código e daí vai para a próxima funcionalidade. Na próxima funcionalidade você escreve o teste, escreve o código e refatora. Escreve o teste, escreve o código e refatora. E você fica ciclo assim dezenas de vezes por dia. E você vai desenvolvendo o seu software a medida que você vai desenvolvendo conjunto a sua bateria de testes. E daí quando você faz usa TDD, o seu código naturalmente sempre tem uma cobertura de testes excelente. Então, é uma possibilidade aí de metodologia de desenvolvimento que muitos excelentes programadores gostam de utilizar. Outra coisa que é muito importante é a revisão por pares. Por que o testes até agora é você revendo o seu próprio código, revisando o seu próprio código. Mas muitas vezes a gente olha para o código, e a gente escreve o testes, e a gente não consegue encontrar defeito bug, ou problema de design, mas você mostrando para uma outra pessoa que pensa pouco diferente de você essa outra pessoa vai encontrar ou problemas no seu software, ou formas de melhorar o software. Então, tenha essa prática na sua equipe de desenvolvimento de mostrar o seu código para outros. Geral bom código é código que é visto por diferentes programadores, e diferentes programadores contribuem para a melhoria da qualidade daquele código. Particular no mundo de software livre se fala muito isso aqui "Given enough eyeballs all bugs are shallow." Significa se tem muita gente olhando para determinado código os bugs acabam sumindo, porque cada pessoa vai ter uma forma diferente de analisar aquele código, e vai encontrar bugs que eventualmente não estavam lá anteriormente. Aprenda a aceitar as críticas. Quando alguém olha o seu código e critica o código não pense que ele está criticando você. " você é uma má pessoa." "Você é mal programador." Não. Se a pessoa criticou é porque de alguma forma aquele código incomodou ela. Então, você tem que levar isso consideração, aprender. Então, bom desenvolvedor vai estar aberto a aceitar críticas de diferentes pessoas. Então aprenda, aprenda a aprender com críticas. E também aprenda a criticar de forma construtiva o código de outras pessoas. Então, se você está numa equipe, e você vê código alí mal feito, e você Não fala nada, guarda aquilo para você, você não está sendo bom participante daquela equipe, você não está contribuindo para a equipe. Toda a vez que você encontrar formas de melhoria, você deve comunicar isso aos outros, você deve praticar como é que você vai comunicar isso para os outros sem ofender outras pessoas. Você não precisa só aprender a receber críticas mas aprender a fazer críticas e ensinar as outras pessoas que receber críticas é uma coisa boa. Equipe de excelente qualidade vai ser uma equipe onde todos criticam e todos sabem receber críticas de uma forma positiva. Tem uma forma específica de programação pares, que é a programação duplas, que é: duas pessoas sentam na frente do computador, uma digitando e a outra olhando e analisando código tempo real. O que a gente chama de programação pares, que são duas pessoas sentadas no mesmo computador é uma forma de você fazer essa revisão por pares tempo real. Note que aqui a revisão por pares quer dizer peer review inglês, se fala code review, revisão do código por pares, peers. Programação pares é programação duplas mesmo, duas pessoas sentadas na frente do computador, peer programming inglês. Coisa que a gente deve pensar e isso tem bastante a ver com a orientação a objetos é a questão de usar herança ou usar delegação. Herança a viu que é mecanismo poderoso de a gente representar nossas abstrações e de herdar, de extrair de uma classe mãe funcionalidades que podem ser usadas classes filhas. E a gente pode criar essas grandes hierarquias de classe e subclasse, subclasse, subclasse. Quando a orientação a objetos se tornou popular na década de 80 e 90, as pessoas adoravam fazer aquelas hierarquias de classes enormes, super profundas e super largas. Mas acabou se percebendo que o código fica muito amarrado porque herança é uma relação entre uma superclasse e uma subclasse muito forte, amarrando. É você agarrado à sua mãe, é uma relação muito forte. Então isso torna o código, ás vezes, pouco flexível. Então, não é recomendado você ter hierarquias muito profundas com classes, subclasses, subclasses.., cinco, seis vezes. O ideal é você ter hierarquias de classes mais leves e você usar outros mecanismos para compor comportamentos. Muitas vezes o uso de delegação leva a sistemas mais simples e flexíveis. Vez de você criar uma subclasse que é super amarrada você cria uma outra classe independente e você delega a execução de código para essa subclasse independente. Então, por exemplo, eu estou implementado sistema de login, que vai fazer o login das atividades de determinado sistema, eu quero ter login específico. Eu poderia criar uma subclasse específica. Mas isso o tornaria muito amarado, talvez o que eu posso fazer é simplesmente delegar a impressão dos strings para uma classe separada e eu posso imprimir os strings de formas diferentes. Outra coisa que a gente deve pensar é a questão do encapsulamento e a modularização do nosso sistema, principalmente quando esse sistema cresce, quando ele acaba tendo dezenas de classes, centenas ou milhares de classes. O que é que é o encapsulamento? A gente vê aquela ideia de agrupar o código junto com os dados que aquele código manipula, numa classe ou num módulo. Só que a gente deve pensar também muito bem o que é que a classe deve expor para os seus clientes, para as outras classes que vão utilizar. Tem muitas coisas das classes, atributos que são coisas internas ou métodos que são internos da classe e não devem ser expostas para o mundo exterior, devem ser encapsuladas. Quanto menos você expor mais, modularizado seu sistema vai estar. Agora deve pensar muito bem, o que é que a classe deve expor e o que é que a classe deve esconder. A vantagem de você esconder coisas é que se você muda aquelas coisas que estão escondidas, ninguém mais precisa ficar sabendo que você mudou. Já se a coisa é exposta e tem outras pessoas que usam aquilo que é exposto, se você muda aquilo que é exposto, você vai ter que mexer nas classes também. Particular uma classe não deve mexer nos atributos da outra classe. A gente deve utilizar só tipicamente métodos puros. E daí a gente chega naquilo que a gente chama de modularização de sistema mais complexo. Sistema idealmente deve ser bem organizado módulos de forma a lidar com essa complexidade. E como é que a gente organiza bem sistema módulos? Tem dois princípios base. Primeiro, é que esses módulos devem ser coesos. Se a gente está falando de classes, as classes devem ser coesas. O que é uma classe coesa? Num sistema orientado a objetos as classes devem possuir uma única responsabilidade, elas são responsáveis por fazer uma única coisa, a gente chama isso de Princípio da Responsabilidade Única. E uma classe coesa é uma classe que faz uma única coisa e faz bem feita aquela única coisa. Uma classe não deve tratar de assuntos diferentes. Por exemplo, você não deve ter uma classe que trata de criptografia e de persistência de dados, ao mesmo tempo, na mesma classe. São duas coisas diferentes, têm que estar classes diferentes. Se você vê no seu sistema uma classe que trata de dois ou três assuntos diferentes pare pouco e veja se não dá para quebrar ela duas ou três classes menores onde cada uma dessas classes vai tratar de único assunto. Aí o seu sistema vai ficar mais coeso. Outra coisa fundamental para ter sistema modularizado, bem modular, é o acoplamento entre as classes, que é algo que a gente deve minimizar, quanto menos acoplamento entre as classes, melhor. E o acoplamento é basicamente o quanto uma classe conhece do seu entorno, onde conhece, a gente quer dizer com tem referências ou usa nomes de outras classes. Se tem uma classe que faz referência a dez outras classes, ele tem no código fonte o nome de dez outras classes, ele é muito acoplado ao seu entorno. E quanto mais acoplado ao seu entorno, menos flexível ela vai ser, por que você não vai conseguir usá-la direito outro contexto, outro ambiente, outro pedaço do seu sistema. Porque ela está muito acoplada àquelas várias classes que ela cita explicitamente. Se você tem muitas referências a outras classes, seu código está muito acoplado. E se você tem referências a tipos muito específicos, pensando naquele nosso exemplo do logger, que faz o login, na hora que ele vai imprimir uma mensagem no login, se você faz uma referência a tipo muito específico de uma classe, por exemplo, no seu login, você quer fazer o login na tela e na hora que você vai imprimir você faz uma referência a uma classe que diz respeito a imprimir na tela. Esse é tipo muito específico. Ele imprime de jeito muito específico, na tela do computador. Se você está fazendo uma referência a tipo muito específico, o seu código está muito amarrado aquilo. Se você quiser, vez de imprimir na tela, imprimir na impressora, escrever num arquivo disco ou mandar pela rede, você vai ter de reescrever essa sua classe de logger. Se você tem que ter uma referência, era mais interessante se tivesse uma referência a uma classe mais abstrata, mais genérica, ou até uma interface, por exemplo. Uma interface que representa algo que imprime. E essa interface poderia ser implementada por diferentes classes. Uma classe que imprime na tela, uma classe que imprime num arquivo disco, que imprime na impressora, uma classe que envia via rede. Mas o seu código do logger simplesmente faz uma referência a uma interface de alguém que imprime e não interessa o tipo específico. Se você pluga novo jeito de imprimir, você não precisa mexer nessa classe logger, basta plugar novo componente então, sua nova classe logger ficou desacoplada dessas outras classes específicas, porque você fez uma referência para item mais genérico. É jeito de diminuir o acoplamento, que é algo interessante para ter sistema mais modular e mais flexível. Classes muito acopladas são pouco flexíveis e portanto pouco reutilizáveis diferentes contextos. Quanto melhor a gente conseguir escrever código reutilizável mais fácil vai ser o desenvolcimento do nosso sistema. Livro muito interessante sobre esse assunto, que é o livro 'iii' que a gente vai usar no resto desse curso de design patterns, tem uma frase, slogan muito legal. Programe para as interfaces e não para as implementações. Quando você está escrevendo seu código você tem de pensar nas interfaces, nas Apis das outras classes, não no como que ela faz internamente. Porque se você pensar nas interfaces o seu código vai ser mais flexível, mais orientado a objetos realmente. Outra coisa importante. Melhoria contínua e refatoração. Boas equipes de desenvolvimento de software estão sempre procurando oportunidades de tornar o sistema melhor, mais simples e mais eficaz. Porque sistema sempre pode ficar pouco melhor do que já está. Todo o dia de trabalho você deve pensar pouco sobre como melhorar a arquitetura do seu sistema. Todo o dia você vai implementar novos métodos, novas classes. Você vai tornar o sistema pouco mais complexo naturalmente, à medida que vai você vai desenvolvendo. Mas você deve colocar nessa rotina também, pensar "como eu posso melhorar essa arquitetura, como eu posso tornar mais simples, como eu posso domar esta complexidade que está crescendo. E uma forma de fazer isso é o que a gente chama de refatoração. Uma refatoração é uma pequena reestruturação no seu código que não altera o seu comportamento, ele continua fazendo exatamente a mesma coisa, mas melhora pouco o seu design, torna ele pouco mais claro, pouco mais simples, pouco mais flexível. Refatoração deve ser parte da rotina de uma boa equipe de desenvolvimento de software. Tem equipes que escolhem, por exemplo, dia por semana vai ser de refatoração ou uma semana por mês vai ser de refatoração. Tem outras equipes que não, refatoração simplesmente faz parte da rotina. Todo o dia eles desenvolvem código, eles testam o código e eles refatoram para melhorar o seu design. Mas você tem sempre que pensar nessa ideia de melhoria contínua. Se você não tem essa preocupação, à medida que o código vai crescendo ele tende a ficar mais complexo, mais amarrado, menos robusto, menos flexível. Recapitulando então, para terminar. A principal característica de bom programador é a escolha de bons nomes. Depois é muito importante você ter uma boa bateria de testes automatizados, você usar revisão por pares, outras pessoas olhando para o seu código, sugerindo melhorias no seu código, lembrar que tem essa dicotomia entre herança e delegação. Que delegação tende a transformar o seu sistema num mais flexível. Você deve estar preocupado encapsular bem os elementos do seu programa, as suas abstrações de forma a criar códigos bem modularizados, maximizando a coesão dos elementos e minimizando o acoplamente entre eles. E você deve ter espírito de melhoria contínua, sempre utilizando refatoração para sempre ir melhorando a qualidade do seu código. Está bom? Então, pensem nisso. Esse aqui é vídeo que você deve assistir mais de uma vez, porque eu falei de muita coisa. E espero que vocês tenham gostado. Abração. Tchau, tchau.