Bom dia, boa noite ou boa tarde! Eu vou falar para vocês agora, sobre padrão que eu gosto muito. Qual é que é o padrão que eu gosto muito? Vamos abrir aqui, e ver o padrão Flyweight. Flyweight inglês significa peso mosca, que é termo que vem aí de luta, luta de boxe, esse tipo de luta, onde é uma coisa muito levinha. Porque é que tem algo muito levinho, peso mosca nesse padrão Flyweight? A ideia é a seguinte, é padrão estrutural, e o objetivo é permitir que uma aplicação mantenha uma quantidade enorme de objetos, mas de uma forma que economize memória, como ele faz isso? Compartilhando partes comuns de vários objetos ao invés de copiar todos os dados todas as cópias dos objetos. Como é que isso é feito? Vamos falar mais. Na verdade, é padrão pouco diferente dos outros design patterns dos nossos padrões de projeto. Porquê? Porque é na verdade uma técnica para otimizar o desempenho, é uma técnica muito simples para economizar memória. Então, a gente não está falando de organização arquitetural de componentes do sistema, não, a gente está falando de uma técnica para economizar memória, que foi escrita como se fosse padrão de projeto. A motivação inicial lá do livro do GoF, é editor de texto, pensa de editor de texto gráfico, tipo LibreOffice, ou Word que você vê na tela o texto da forma que ele seria impresso na impressora, com a mesma fonte. E a fonte então tem os desenhos dos caracteres, então por exemplo, aqui nessa minha tela, vocês estão vendo o caractere e, tem certo desenho especial do caractere e. A letra a aqui tem várias letras a nesta tela e ele tem esses desenho especial. Desenho é bitmap ali, uma sequência, uma matriz de bites que definem o formato de caractere. Mas num editor de texto, você pode ter dezenas ou centenas de letras a numa tela. Não faz sentido você carregar na memória para cada tela. Letra a, que é o mesmo desenho dado de uma fonte, você ter dezenas ou centenas de diferentes cópias do bitmap da letra a, porque é sempre o mesmo bitmap numa dada fonte. O que é que os editores de texto como o LibreOffice ou o Word fazem? Eles carregam uma única cópia dessa letra a, e todas as outras ocorrências da letra a, simplesmente têm ponteiro, ou uma referência para aquela única letra a. Agora, tem diferentes formas de implementar isso. Uma forma é você ter pequeno objeto para cada letra do seu texto, onde tem por exemplo, só o código SCI, ou o código UTF daquela letra e apontador para onde está o bitmap do desenho da letra, essa seria uma forma de implementar. Outra forma mais econômica ainda, é simplesmente ter vetor, onde, vetor de referências para as letras, onde essa referência pode ser o próprio caractere ASCII da letra. E depois você pode ter por exemplo, mapa, dicionário ou HashMap ligando cada letra a cada código ASCII ao respectivo bitmap. Mas então, a ideia é só ter uma única cópia desse bitmap, ao invés de para cada letra a, você repetir de novo o desenho ali da letra a, isso é a motivação inicial do GoF. Mas vamos ver outros contextos, que isso pode ser utilizado. Particular, quando é que a gente vai usar? Quando a aplicação tem que gerenciar uma grande quantidade de instâncias, então, só vai fazer sentido quando são centenas ou milhares ou milhões de objetos que esse sistema vai ter que gerenciar, e os objetos, muitos desses milhares de objetos são muito parecidos entre si. E daí a gente divide os objetos entre uma parte imutável e uma parte que muda. No exemplo ali, do editor de texto, o que é que é imutável? É a forma da letra a, sempre a letra a é a mesma letra a. A posição da letra a no texto, por exemplo, ou a posição da letra a na página, isso muda. Então tem a parte mutável e a parte imutável. A parte imutável, então, pode ser compartilhada entre muitas instâncias. Então, o bitmap da letra a vai ser compartilhado entre todas as ocorrências da letra a naquele texto. A identidade do objeto não é importante para a aplicação, não interessa qual, não interessa que essa letra a aqui é diferente dessa letra a, na verdade, todo o a para mim é a. Então, não tem essa identidade de objeto para o outro não é algo muito importante. Daí sim, o Flyweight faz sentido, a gente deve usar nesses casos. A ideia é que objetos do tipo Flyweight possuem, o que a gente chama de estado intrínseco, que é compartilhado entre todas as cópias e é imutável, então, a forma do a. E o estado extrínseco, que é alguma coisa que muda de uma instância para a outra. Eu até pensava que essa palavra extrínseco não existia na língua portuguesa, mas eu fui lá no dicionário e existe, pode olhar lá que é exatamente esse o significado. Portanto, para implementar o Flyweight você tem que montar esquema, onde todos os objetos compartilham o estado intrínseco. E o estado extrínseco pode ser armazenado outros objetos, é objeto que vai ser menor, vai ocupar muito menos memória, ou então, numa outra estrutura de dados, pode ser vetor, uma matriz, que vai guardar simplesmente referências para o Flyweight que tem a parte intrínseca, e o extrínseco fica implícito ali na posição do vetor, na posição da matriz, por exemplo. Outros exemplos, eu já falei aqui do exemplo do editor de texto, vamos com exemplos diferentes. Exemplo muito legal são jogos eletrônicos 2D. É muito comum nos jogos eletrônicos 2D, você ter desenho ali do ambiente, você pode ir andando para o desenho do ambiente, tem coisas que se repetem, uma coisa que repete pode ser, árvores, ou os inimigos, fantasminhas lá do Pac-Man, né? E uma árvore, quando você entra numa floresta, você vai ter ali milhares de árvores, você não precisa ficar repetindo todas elas carregando na memória todas elas, porque as árvores podem se parecer, pode ser a mesma árvore repetida ali, dezenas, centenas de vezes. Particular, lá na USP, a gente tem grupo de alunos que formaram o USPGameDev, que implementam os jogos de computadores muito legais, deixa eu abrir aqui jogo que eles fizeram. Que é esse Backdoor, é jogo open source, se você procurar USPGameDev Backdoor, eu vou até dar o url e vocês conseguem baixar esse jogo, jogar, ver o código fonte, alterar, compartilhar tá? Então, aqui está esse jogo, por exemplo, como é que a gente entra aqui? Assim, daí eu estou jogando aqui. Note que a a gente tem aqui no ambiente esses tiles, essas coisas aqui no chão, todos eles são idênticos. A única coisa que muda de lugar para o outro é o posição dele na tela, mas o desenho dele é idêntico, então é tipo de coisa que você gostaria de usar Flyweight, para representar, então teria objeto só que representaria o bitmap da imagem e a posição ficaria numa matriz, ou nuns outros objetos ali mais mais levinhos, que seriam ali os Flyweights. Então tá, mas deixa eu parar de jogar, eu comecei a jogar, mas eu já me distraí. Eu tô dando aula, não estou jogando, então vou sair desse jogo aqui, quit. Pronto, depois vocês podem jogar. Voltando à nossa aula. Outro tipo de exemplo, seria uma animação tri dimensional, vocês já devem ter assistido talvez esse filme aqui, esse filme do Star Wars. Então, por exemplo, você vê que tem esses vários tipos de droids aqui, e esse outro tipo aqui. Só que esse aqui, você tem dezenas de cópias e note que para representar numa animação 3D, você precisa de, provavelmente alguns poucos megabytes para representar todos os detalhes de droid desse tipo. Dezenas deles, então, você não vai carregar na memória, dezenas de cópias, na verdade, você pode ter centenas de cópias desse droid aqui, numa determinada cena. E todos eles têm a parte intrínseca, que é esse modelo 3D, que tem alguns poucos megabytes. E daí tem a parte extrínseca, que é a posição desse objeto no espaço tridimensional e a orientação, para onde é que ele está olhando, por exemplo. Nesse caso todos estão olhando para ele lá. Você pode ter uma cena, onde você vai ter milhares desses droids aqui e provavelmente, para implementar essa cena, para fazer essa animação, você vai criar algo do tipo Flyweight. Porque você vai querer só uma representação, uma única vez na memória, desses robots e daí a posição dos robots, ali, você vai repetindo ou num vetor, ou num objeto ali mais leve. Então, você vai ter milhares de droids Flyweights com estado extrínseco aqui, mas apenas ShareDroidFlyweight, com estado intrínseco, que é esse modelo 3D do seu objeto. Vamos ver exemplo de código de Python que eu fiz aqui, de uma forma bem Pythônica. Na verdade, esse meu exemplo, eu não terminei 100 por cento, é só para dar uma ideia, depois quem sabe algum de vocês pode terminar e mandar para mim e eu atualizo aqui o vídeo. Então essa ideia das árvores num jogo 2D. Então, eu começo aqui com uma espécie, que é Enum, dizendo que na minha espécie eu tenho quatro tipos de árvores, eu peguei árvores que tem aqui na cidade de São Paulo, carvalho, 'carvalho ipe tipuana ficus'. Temos uma classe árvore, que vai ser meu 'flyweight. E eu guardo nessa árvore uma floresta que é dicionário onde eu vou guardar as árvores ali dentro. E como eu faço isso nessa forma pythonica? Eu alterei o construtor new. Como eu alterei o construtor new para ele receber como parâmetro o tipo de árvore? Então vai ser uma dessas quatro árvores aqui. E ele vai receber sprite que é desenho, bitmap, de alguns kilobytes da representação desse meu sprite. E daí o que eu faço? O objeto, corrente, ele vai receber classe. Então isso aqui é a própria classe, vai ser a própria classe árvore. Ponto floresta. Então, ponto 'get', tipo de árvore. O que eu estou fazendo? Antes de criar o objeto, eu estou consultando para ver se nessa minha floresta aqui já tem uma árvore desse mesmo tipo. Se já tiver uma árvore desse mesmo tipo, então eu não crio novo. Eu a uso como se fosse 'flyweight', então, por exemplo, se eu pedir para criar carvalho. Alguém fez 'new' carvalho ali, 'new' arvore do tipo carvalho. E já existe carvalho, eu não crio novo carvalho, porque isso demandaria muito trabalho. Simplesmente, eu uso o carvalho que já tinha, já devolvo o carvalho que já tinha. Esse aqui, o 'get' vai fazer. Se eu não encontro, aí sim vai devolver 'none' e aí eu entro aqui no 'if'. Porque se esse objeto vale 'none', aí sim ele entra no 'if', daí sim eu chamo o 'new' da classe 'object'. E daí ele vai instanciar novo objeto, ele vai alocar espaço na memória com novo objeto. Daí eu guardo esse objeto na floresta, uma posição tipo de árvore. Como se fosse cache, que na próxima vez eu vou usar o que estiver nesse cache. Tipo de árvore vai ser o tipo de árvore que é passado como parâmetro. E esse sprite, a imagem, eu uso essa biblioteca aqui 'image' do PIL para abrir arquivo que tenha essa imagem. Vai ser arquivo. No meu caso, eu gravei o arquivo PNG, a imagem das árvores. Então é isso. Esse new, eu mudei para ele realmente só criar novo objeto se não existir algum objeto daquele tipo específico. Mais ou menos parecido com a idéia do Singleton. Ele só instancia o objeto mesmo se já não existir algum outro. E daí, aqui eu fiz só de brincadeira método 'desenhe' que, idealmente, ele deveria desenhar espaço 2D. Por enquanto, eu estou simplesmente imprimindo uma mensagem: Desenhe uma árvore do tipo e do tamanho tal na posição 'xy'! Futuramente, quem sabe de vocês realmente modifica isso para realmente fazer o desenho. Por enquanto, ele só está imprimindo só para a gente ver como funcionaria. E daí, aqui eu tenho método 'main'- é uma função main- onde eu crio monte de árvores. Então como é isso? Eu vou ter essa função 'random' aqui que eu vou usar para criar as árvores aleatoriamente. Eu vou ter esses vários tamanhos. Uma árvore pode ser pequena, média, grande ou gigante. As coordenadas da posição 'x.y' desse meu espaço 2D onde eu vou colocar as árvores vão de 0 a 1000. Eu criei contador de árvores, começa com zero, e daí eu vou criando alguns tipos de árvores. Então, primeiro eu crio 10 árvores da espécie carvalho. E o sprite é esse meu arquivo carvalho ponto 'PNG' aqui. E daí eu mando desenhar de tamanho aleatório e, também, das coordenadas 'x.y' aleatórias que eu gero aleatoriamente. E Conta_Árvores mais. Então, assim eu gero 10 carvalhos. E note que aqui eu estou instanciando dez objetos do tipo carvalho, mas como a gente viu, ele não vai instanciar 10 objetos. Ele vai devolver 10 referências, mas ele vai criar o carvalho só na primeira vez. Depois, ele vai reutilizar, então, como se o carvalho fosse singleton. Daí a mesma coisa aqui para o IPE. Eu crio 50 IPE´s. Aqui é o sprite do IPE. Daí eu crio 60 'ficus' e, finalmente, eu crio 200 'Tipuanas'. A cidade de São Paulo está cheia de Tipuanas, então, eu criei 200 Tipuanas. Daí, eu mando imprimir. Árvores desenhadas. Conta_árvores; Árvores efetivamente criadas. Eu coloco aqui o tamanho da floresta. E, depois, só de brincadeira, só para vocês verem. Eu mando. Eu faço umas coisinhas aqui. Eu crio três árvores novas: C4, C5 e C6. São dois IPE´s e uma Tipuana. Primeiro, aqui é para mostrar que o C4 e o C5, vocês vão ver, serão exatamente o mesmo objeto, porque as duas são IPE´s. Esse print vai mostrar que, na verdade, são objetos na mesma posição da memória. Ele não cria dois objetos diferentes mesmo ele tendo chamado o mesmo item duas vezes, ele devolve o mesmo objeto. Já o cinco e o seis serão objetos diferentes, porque o C5 é IPE e o C6 é Tipuana. Só de brincadeira, eu simplesmente mostro a imagem das árvores. Depois a gente tem que melhorar esse programa ou realmente fazer a coisa 2D e botar as árvores no espaço bidimensional. E, realmente, talvez até permitir navegação pela o espaço, mas, por enquanto, está uma versão simplificada. Então vamos dar uma olhada aqui? Eu vou mandar executar o programa e o que acontece? Deu uma saída aqui. Então, vamos ver aqui o texto que ele imprimiu. Então, ele começou imprimindo os carvalhos aqui. Então, desenhe uma árvore de espécie carvalho, de tamanho grande e na posição 730 a 351. E depois carvalho gigante 714 e 989. Foi gerando, automaticamente, aqueles carvalhos, depois gerou os IPE´s, depois os ficus e depois gerou as Tipuanas aleatoriamente. Então, aqui, a última que ele gerou é uma árvore da espécie Tipuana de tamanho grande nessa posição. Então, ele desenhou 320 árvores, mas a verdade só quatro árvores foram criadas, porque são quatro tipos de árvores. Então, é 'flyweight'. Quatro 'flyweights', mas, na verdade, tem 320 objetos apontando para aqueles quatro 'flywieghts'. E a gente vê aqui o 'C4' e 'C5', são dois IPE´s. Eles são o mesmo objeto. Particular, eu mandei imprimir o endereço da memória onde eles estão, é o mesmo endereço na memória. Já o C5 e o C6 são diferentes, endereços diferentes. Então falso. Por quê? Porque é IPE e o outro Tipuana. Então, esse aqui é exemplo só para ilustrar essa idéia do 'flyweight'. Então, já vimos o nosso exemplo de código para terminar aqui. Para vocês aprenderem mais tem livro muito legal que é esse 'Game Programming Patterns', que ele fala como usar padrões de projeto jogos. É livro gratuito que você achar na web. Então, vai esse endereço: 'gameprogrammingpatterns.com, particular, barra 'flyweight' ponto html. Você vai ver como você usar o flyweight. E jogos também simularão o que eu mostrei aqui. Tem esse livro, para quem gosta muito de Python, é o 'Mastering Python Design Patterns', que mostra formas pythônicas de implementar vários design patterns. É livro muito legal. Eu recomendo. Finalmente, para se divertir, o jogo 'Backdoor Route' do USPGameDev desenvolvido pelos alunos do IMI USP. Quem quiser jogar o jogo, aqui está o endereço. E vocês podem até baixar o código e ver como funciona. Então é isso. Eu espero que vocês tenham gostado do 'flyweight'. Resumindo, o 'flyweight' é usado quando você tem uma grande quantidade de objetos que são muito similares e você quer economizar memória separando a parte que muda da parte que não muda. Então, é padrão de otimização de desempenho, otimização do uso da memória. Esperam que vocês tenham gostado. Grande abraço para vocês.