princípios solid · 8 end masterclass contém ... 12 aberto/fechado definição paf ... i os...
TRANSCRIPT
Instituto de Matemática e EstatísticaUniversidade de São Paulo
Princípios SOLID
Caio Costa SalgadoLeonardo Pereira MacedoRodrigo Siqueira Jordão
22 de Junho de 2016
1
Sumário
Introdução
Responsabilidade ÚnicaLCOM
Aberto/FechadoPadrões de projeto úteisImplementando novos comportamentos
Substituição de Liskov
Injeção de Dependência
Lei de Demeter
Seminário POO | Princípios SOLID
2
IntroduçãoPadrões e Antipadrões
I Padrões de projetoI Solução reutilizável de um problema em design de softwareI Melhoram a qualidade e organização das classes
I AntipadrõesI Ineficientes, arriscados e contraprodutivosI Podem ser identificados pelo mau cheiro de projeto que criam
Seminário POO | Princípios SOLID
2
IntroduçãoPadrões e Antipadrões
I Padrões de projetoI Solução reutilizável de um problema em design de softwareI Melhoram a qualidade e organização das classes
I AntipadrõesI Ineficientes, arriscados e contraprodutivosI Podem ser identificados pelo mau cheiro de projeto que criam
Seminário POO | Princípios SOLID
3
Princípios SOLIDIntrodução
I Acrônimo para 5 princípios de POOI Criado por Robert C. Martin (Uncle Bob)
por volta do ano 2000
I Diminui o acoplamento entre classesI Separa as responsabilidades para melhorar
o código da aplicação desenvolvida
Seminário POO | Princípios SOLID
3
Princípios SOLIDIntrodução
I Acrônimo para 5 princípios de POOI Criado por Robert C. Martin (Uncle Bob)
por volta do ano 2000I Diminui o acoplamento entre classesI Separa as responsabilidades para melhorar
o código da aplicação desenvolvida
Seminário POO | Princípios SOLID
4
Acrônimo SOLIDIntrodução
S ingle Responsibility
Open/Closed
L iskov Substitution
I njection of Dependencies
D emeter Principle
Seminário POO | Princípios SOLID
6
Responsabilidade ÚnicaDefinição
PRU - Princípio de Responsabilidade ÚnicaUma classe deve ter uma, e apenas uma, responsabilidade (isto é,apenas uma razão para mudar)
I Uma responsabilidade pode ser descrita com 25 ou menospalavras
I Falta de coesão pode indicar uma violação do PRU
Seminário POO | Princípios SOLID
7
Responsabilidade ÚnicaExemplo
1 c lass MasterClass2 def p e r f o r m I n i t i a l i z a t i o n . . . ; end3 def readFromFile . . . ; end4 def w r i t e T o F i l e . . . ; end5 def displayToScreen . . . ; end6 def per fo rmCa lcu la t ion . . . ; end7 def v a l i d a t e I n p u t . . . ; end8 end
MasterClass contém responsabilidades diversas e poucorelacionadas
Seminário POO | Princípios SOLID
7
Responsabilidade ÚnicaExemplo
1 c lass MasterClass2 def p e r f o r m I n i t i a l i z a t i o n . . . ; end3 def readFromFile . . . ; end4 def w r i t e T o F i l e . . . ; end5 def displayToScreen . . . ; end6 def per fo rmCa lcu la t ion . . . ; end7 def v a l i d a t e I n p u t . . . ; end8 end
MasterClass contém responsabilidades diversas e poucorelacionadas
Seminário POO | Princípios SOLID
8
Responsabilidade ÚnicaExemplo
Solução: Separar em classes de acordo com a responsabilidade
1 c lass F i l e Inpu tOu tpu t2 def readFromFile . . . ; end3 def w r i t e T o F i l e . . . ; end4 end
1 c lass UserInputOutput2 def displayToScreen . . . ; end3 def v a l i d a t e I n p u t . . . ; end4 end
1 c lass Logic2 def p e r f o r m I n i t i a l i z a t i o n . . . ; end3 def per fo rmCa lcu la t ion . . . ; end4 end
Seminário POO | Princípios SOLID
9
Responsabilidade ÚnicaLCOM
I LCOM: Lack of Cohesion of MethodsI Analisa a coesão de uma classe, medindo se ela consiste em
múltiplos "aglomerados"I Duas variantes principais:
Variante Pontuação Interpretação
Henderson-Sellers 0 a 1Quanto mais próximo de 1, maisvariáveis de instância são aces-sadas por apenas um método
LCOM-4 1 a nSe n > 1, então n-1 responsabili-dades podem ser extraídas parasuas próprias classes
Seminário POO | Princípios SOLID
10
Responsabilidade ÚnicaLCOM-4
I Dois métodos estão relacionados se:I Acessam a mesma variável de instância/classeI Um chama o outro
I LCOM-4 conecta métodos relacionados em um grafoI Se o número n de grafos resultantes for maior que 1, pode-se
dividir em classe em partes menores
Seminário POO | Princípios SOLID
10
Responsabilidade ÚnicaLCOM-4
I Dois métodos estão relacionados se:I Acessam a mesma variável de instância/classeI Um chama o outro
I LCOM-4 conecta métodos relacionados em um grafoI Se o número n de grafos resultantes for maior que 1, pode-se
dividir em classe em partes menores
Seminário POO | Princípios SOLID
10
Responsabilidade ÚnicaLCOM-4
I Dois métodos estão relacionados se:I Acessam a mesma variável de instância/classeI Um chama o outro
I LCOM-4 conecta métodos relacionados em um grafoI Se o número n de grafos resultantes for maior que 1, pode-se
dividir em classe em partes menores
Exemplo 1: LCOM-4 = 2
Seminário POO | Princípios SOLID
10
Responsabilidade ÚnicaLCOM-4
I Dois métodos estão relacionados se:I Acessam a mesma variável de instância/classeI Um chama o outro
I LCOM-4 conecta métodos relacionados em um grafoI Se o número n de grafos resultantes for maior que 1, pode-se
dividir em classe em partes menores
Exemplo 2: LCOM-4 = 1
Seminário POO | Princípios SOLID
12
Aberto/FechadoDefinição
PAF - Princípio Aberto/FechadoUma classe deve ser aberta para extensão, mas fechada paramodificação
I Deseja-se estender o comportamento de classes sem modificarcódigo existente na qual dependem
Seminário POO | Princípios SOLID
13
Aberto/FechadoExemplo
1 c lass Report2 def output3 f o r m a t t e r =4 case @format5 when : html6 HtmlFormatter . new( s e l f )7 when : pdf8 PdfFormatter . new( s e l f )9 end
10 end11 end
I Cheiro de código Comando Case...I Cirurgia de Espingarda: Adicionar
um novo tipo de Formatter exigirámudanças em um ou mais métodos
Como resolver? Padrões de projeto!
Seminário POO | Princípios SOLID
13
Aberto/FechadoExemplo
1 c lass Report2 def output3 f o r m a t t e r =4 case @format5 when : html6 HtmlFormatter . new( s e l f )7 when : pdf8 PdfFormatter . new( s e l f )9 end
10 end11 end
I Cheiro de código Comando Case...
I Cirurgia de Espingarda: Adicionarum novo tipo de Formatter exigirámudanças em um ou mais métodos
Como resolver? Padrões de projeto!
Seminário POO | Princípios SOLID
13
Aberto/FechadoExemplo
1 c lass Report2 def output3 f o r m a t t e r =4 case @format5 when : html6 HtmlFormatter . new( s e l f )7 when : pdf8 PdfFormatter . new( s e l f )9 end
10 end11 end
I Cheiro de código Comando Case...I Cirurgia de Espingarda: Adicionar
um novo tipo de Formatter exigirámudanças em um ou mais métodos
Como resolver? Padrões de projeto!
Seminário POO | Princípios SOLID
13
Aberto/FechadoExemplo
1 c lass Report2 def output3 f o r m a t t e r =4 case @format5 when : html6 HtmlFormatter . new( s e l f )7 when : pdf8 PdfFormatter . new( s e l f )9 end
10 end11 end
I Cheiro de código Comando Case...I Cirurgia de Espingarda: Adicionar
um novo tipo de Formatter exigirámudanças em um ou mais métodos
Como resolver?
Padrões de projeto!
Seminário POO | Princípios SOLID
13
Aberto/FechadoExemplo
1 c lass Report2 def output3 f o r m a t t e r =4 case @format5 when : html6 HtmlFormatter . new( s e l f )7 when : pdf8 PdfFormatter . new( s e l f )9 end
10 end11 end
I Cheiro de código Comando Case...I Cirurgia de Espingarda: Adicionar
um novo tipo de Formatter exigirámudanças em um ou mais métodos
Como resolver? Padrões de projeto!
Seminário POO | Princípios SOLID
14
Aberto/FechadoSolução com Fábrica Abstrata
1 c lass Report2 def output3 f o rma t te r_c lass =4 begin5 @format . to_s . c l a s s i f y . cons tan t i ze6 rescue NameError7 # Handle " i n v a l i d f o r m a t t e r type "8 end9 f o r m a t t e r = fo rma t te r_c lass . send ( : new , s e l f )
10 end11 end
I @format se refere a um símbolo (:pdf, :html)
I Duck Typing: A partir do símbolo, adquirimos uma referênciapara a classe desejada e chamamos seu construtor
Seminário POO | Princípios SOLID
14
Aberto/FechadoSolução com Fábrica Abstrata
1 c lass Report2 def output3 f o rma t te r_c lass =4 begin5 @format . to_s . c l a s s i f y . cons tan t i ze6 rescue NameError7 # Handle " i n v a l i d f o r m a t t e r type "8 end9 f o r m a t t e r = fo rma t te r_c lass . send ( : new , s e l f )
10 end11 end
I @format se refere a um símbolo (:pdf, :html)I Duck Typing: A partir do símbolo, adquirimos uma referência
para a classe desejada e chamamos seu construtor
Seminário POO | Princípios SOLID
15
Aberto/FechadoSolução com Template
I Os passos (métodos) da tarefa (formatação) são os mesmospara todas as variantes de Formatter
I Implementação dos métodos de cada subclasse podem divergir
Seminário POO | Princípios SOLID
16
Aberto/FechadoSolução com Strategy
I Usando Strategy, os passos podem ser diferentes em cadasubclasse
Seminário POO | Princípios SOLID
17
Aberto/FechadoQueremos Novos Comportamentos!
I E se quisermos adicionar um novo comportamento em umaclasse existente? Por exemplo, arquivos PDF:
I Com/sem proteção de senhaI Com/sem uma marca d’água "rascunho"
I Uma possível ideia:
Não é DRY!
Seminário POO | Princípios SOLID
17
Aberto/FechadoQueremos Novos Comportamentos!
I E se quisermos adicionar um novo comportamento em umaclasse existente? Por exemplo, arquivos PDF:
I Com/sem proteção de senhaI Com/sem uma marca d’água "rascunho"
I Uma possível ideia:
Não é DRY!
Seminário POO | Princípios SOLID
17
Aberto/FechadoQueremos Novos Comportamentos!
I E se quisermos adicionar um novo comportamento em umaclasse existente? Por exemplo, arquivos PDF:
I Com/sem proteção de senhaI Com/sem uma marca d’água "rascunho"
I Uma possível ideia:
Não é DRY!
Seminário POO | Princípios SOLID
18
Aberto/FechadoPadrão Decorator
Padrão Decorator: "Decoramos" a classe ao envolvê-la em umaversão melhorada, com mesma interface
Código fonteSeminário POO | Princípios SOLID
20
Substituição de LiskovDefinição
Princípio de LiskovUm método projetado para trabalhar em umobjeto de tipo T deve também trabalhar em umobjeto de qualquer subtipo de T
I Herança é muito util para a reutilização de código, mas não é sópor isso que ela deve ser usada
I A herança é um compartilhamento de implementação. Se asubclasse não ganha vantagem com a implementação herdada,talvez ela não devesse ser uma subclasse
Seminário POO | Princípios SOLID
21
Substituição de LiskovExemplo
1 c lass Retangulo2 a t t r_accessor : la rgura , : a l t u ra , : canto_ in f_esq3 def new( la rgura , a l t u ra , canto_ in f_esq ) . . . ; end4 def area . . . ; end5 def dobrar_a l tu ra_sobre_a_ largura ( dim )6 s e l f . l a rgu ra = 2∗dim7 s e l f . a l t u r a = dim8 end9 end
10 c lass Quadrado < Retangulo11 a t t r _ r e a d e r : la rgura , : a l t u ra , : lado12 def l a rgu ra =(w) ; @largura = @altura = w ; end13 def a l t u r a =(w) ; @largura = @altura = w ; end14 def lado =(w) ; @largura = @altura = w ; end15 end
Seminário POO | Princípios SOLID
22
Substituição de LiskovCheiro
Mau cheiroI Modificação do funcionamento do método herdadoI Nesse caso, não faz sentido dobrar altura sobre a largura para
um quadradoI Método da superclasse jogado fora
Seminário POO | Princípios SOLID
23
Substituição de LiskovRefatoração
1 c lass Quadrado2 a t t r_accessor : r e t3 def i n i t i a l i z e ( : lado , : canto_ in f_esq )4 @ret = Retangulo . new( lado , lado , canto_ in f_esq )5 end67 def area8 r e t . area9 end
1011 def lado =( s )12 r e c t . width = r e c t . he igh t = s13 end14 end
Trocando herança por composição: implementação é delegada, enão herdada
Seminário POO | Princípios SOLID
23
Substituição de LiskovRefatoração
1 c lass Quadrado2 a t t r_accessor : r e t3 def i n i t i a l i z e ( : lado , : canto_ in f_esq )4 @ret = Retangulo . new( lado , lado , canto_ in f_esq )5 end67 def area8 r e t . area9 end
1011 def lado =( s )12 r e c t . width = r e c t . he igh t = s13 end14 end
Trocando herança por composição: implementação é delegada, enão herdada
Seminário POO | Princípios SOLID
24
Substituição de LiskovRefatoração
Delegação em Ruby: ForwardableMódulo em Ruby que implementa a delegação
1 c lass Quadrado2 extend Forwardable3 def_de legators : @ret , : area , : per imetro , : rotacao45 def i n i t i a l i z e ( : lado , : canto_ in f_esq )6 @ret = Retangulo . new( lado , lado , canto_ in f_esq )7 end89 def lado =( s )
10 @ret . l a rgu ra = @ret . a l t u r a = s11 end12 end
Seminário POO | Princípios SOLID
25
Substituição de Liskov
ResumoToda a implementação da super classe deve fazer sentido para assuas subclasses
Seminário POO | Princípios SOLID
27
Injeção de DependêciaDefinição
Injeção de dependênciaI Se duas classes dependem uma da outra, mas suas
implementações podem mudar, seria bom para ambasdependerem de uma interface abstrata separada que seja"injetada" entre elas
I O Princípio de Injeção de Dependência (PID) é a criaçãode uma interface com o objetivo de tratar a manipulaçãode objetos de forma correta em tempo de execução
Seminário POO | Princípios SOLID
27
Injeção de DependêciaDefinição
Injeção de dependênciaI Se duas classes dependem uma da outra, mas suas
implementações podem mudar, seria bom para ambasdependerem de uma interface abstrata separada que seja"injetada" entre elas
I O Princípio de Injeção de Dependência (PID) é a criaçãode uma interface com o objetivo de tratar a manipulaçãode objetos de forma correta em tempo de execução
Seminário POO | Princípios SOLID
28
Injeção de DependêciaExemplo
1 c lass Ema i l L i s t2 a t t r _ r e a d e r : ma i le r3 delegate : send_email , : to => : ma i le r4 def i n i t i a l i z e5 @mailer = MailerMonkey . new6 end7 end8 # i n RottenPotatoes E m a i l L i s t C o n t r o l l e r :9 def adver t i se_d iscount_ fo r_mov ie
10 moviegoers = Moviegoer . i n t e r e s t e d _ i n params [ :movie_id ]
11 Ema i l L i s t . new . send_email_to moviegoers12 end
Seminário POO | Princípios SOLID
29
Injeção de DependêciaExemplo
I Se quisermos adicionar novas maneiras para enviar mensagens,seria necessário modificar o funcionamento do controller,adicionando condicionais
I Nesse caso, entra em ação o Princípio de Injeção deDependência (PID)!
Seminário POO | Princípios SOLID
30
Injeção de DependêciaExemplo - Refatoração
Controller
1 def adver t i se_d iscount_ fo r_mov ie2 moviegoers = Moviegoer . i n t e r e s t e d _ i n ( params [ :
movie_id ] )3 Ema i l L i s t . new( Conf ig . emai le r ) . send_email_to (
moviegoers )4 end5 end
Seminário POO | Princípios SOLID
31
Injeção de DependêciaExemplo - Refatoração
Config Class
1 c lass Conf ig2 def s e l f . emai ler3 i f emai l_d isab led? then N u l l M a i l e r e lse4 i f has_amiko? then AmikoAdapter e lse
MailerMonkey end5 end6 end7 end
Seminário POO | Princípios SOLID
32
Injeção de DependêciaExemplo - Refatoração
AmikoAdapter Class
1 c lass AmikoAdapter2 def i n i t i a l i z e ; @amiko = Amiko . new ( . . . ) ; end3 def send_email4 @amiko . au then t i ca te ( . . . )5 @amiko . send_message ( . . . )6 end7 end
Seminário POO | Princípios SOLID
33
Injeção de DependêciaPadrões de Projeto
PID pode ser uma aplicação de um padrãoI FachadaI Fábrica abstrata: Cria diferentes tipos de objetos com o mesmo
objetivoI Adaptador: Interface que modifica a interação com uma classe
com o objetivo de padronizar o acesso (no nosso caso)I Proxy: Mesmo comportamento de outra classe, mas com
tratamento para casos "estranhos"
Seminário POO | Princípios SOLID
35
Lei de DemeterDefinição
Princípio de DemeterUm método pode chamar outros métodos em sua própria classe emétodos nas classes de suas próprias variáveis de instância; todo oresto é tabu
Conselhos...Converse com seus amigos - Não fique íntimode estranhos
Seminário POO | Princípios SOLID
35
Lei de DemeterDefinição
Princípio de DemeterUm método pode chamar outros métodos em sua própria classe emétodos nas classes de suas próprias variáveis de instância; todo oresto é tabu
Conselhos...Converse com seus amigos - Não fique íntimode estranhos
Seminário POO | Princípios SOLID
36
Lei de DemeterDemeter na Prática
Seus parâmetrosQuando o seu método recebe parâmetros, ele pode chamar algummétodo fornecido por este parâmetro diretamente
1 c lass JobPost2 def pos t_u r l ( i d )3 ’ h t t p : / / www. k u n i r i . org / posts / # { i d } ’4 end5 end
Seminário POO | Princípios SOLID
36
Lei de DemeterDemeter na Prática
Seus parâmetrosQuando o seu método recebe parâmetros, ele pode chamar algummétodo fornecido por este parâmetro diretamente
1 c lass JobPost2 def pos t_u r l ( i d )3 ’ h t t p : / / www. k u n i r i . org / posts / # { i d } ’4 end5 end
Seminário POO | Princípios SOLID
37
Lei de DemeterDemeter na Prática
Qualquer objeto criado ou instanciadoQuando o seu método cria objetos locais, ele pode chamar métodosnos objetos locais
1 c lass Ma i le r2 def prepare_emai ls ( l i s t )3 e m a i l _ s a n i t i z e r = Ema i lSan i t i ze r . new4 e m a i l _ s a n i t i z e r . s a n i t i z e ( l i s t )5 end6 end
Seminário POO | Princípios SOLID
37
Lei de DemeterDemeter na Prática
Qualquer objeto criado ou instanciadoQuando o seu método cria objetos locais, ele pode chamar métodosnos objetos locais
1 c lass Ma i le r2 def prepare_emai ls ( l i s t )3 e m a i l _ s a n i t i z e r = Ema i lSan i t i ze r . new4 e m a i l _ s a n i t i z e r . s a n i t i z e ( l i s t )5 end6 end
Seminário POO | Princípios SOLID
38
Lei de DemeterDemeter na Prática
Seu próprio componente de objetoSeu método pode chamar métodos nos seus próprios camposdiretamente (mas não em campos do campo)
1 c lass Ma i le r2 def i n i t i a l i z e ( ema i l _san i t i ze r , l i s t )3 @emai l_sani t izer = Ema i lSan i t i ze r . new4 @l is t = l i s t5 end67 def prepare_emai ls ( l i s t )8 @emai l_sani t izer . s a n i t i z e ( l i s t )9 end
10 end
Seminário POO | Princípios SOLID
38
Lei de DemeterDemeter na Prática
Seu próprio componente de objetoSeu método pode chamar métodos nos seus próprios camposdiretamente (mas não em campos do campo)
1 c lass Ma i le r2 def i n i t i a l i z e ( ema i l _san i t i ze r , l i s t )3 @emai l_sani t izer = Ema i lSan i t i ze r . new4 @l is t = l i s t5 end67 def prepare_emai ls ( l i s t )8 @emai l_sani t izer . s a n i t i z e ( l i s t )9 end
10 end
Seminário POO | Princípios SOLID
39
Lei de DemeterDemeter na Prática
A si mesmoSeu método pode chamar outros métodos ou atributos na suaprópria classe diretamente
1 c lass Address2 a t t r _ r e a d e r : c i t y , : s t a t e34 def f u l l _a d d ress5 " #{ c i t y } , #{ s t a t e } "6 end7 end
Seminário POO | Princípios SOLID
39
Lei de DemeterDemeter na Prática
A si mesmoSeu método pode chamar outros métodos ou atributos na suaprópria classe diretamente
1 c lass Address2 a t t r _ r e a d e r : c i t y , : s t a t e34 def f u l l _a dd ress5 " #{ c i t y } , #{ s t a t e } "6 end7 end
Seminário POO | Princípios SOLID
40
Lei de DemeterExemplo
12 def sold_brand_emai l ( order )3 @order = order4 mai l (5 to : @order . l i ne_ i t ems . l a s t . brand . designer . email ,6 sub jec t : ’You sold a brand ! ’ )7 end
Seminário POO | Princípios SOLID
40
Lei de DemeterExemplo
12 def sold_brand_emai l ( order )3 @order = order4 mai l (5 to : @order . l i ne_ i t ems . l a s t . brand . designer . email ,6 sub jec t : ’You sold a brand ! ’ )7 end
Seminário POO | Princípios SOLID
41
Lei de DemeterExemplo - Refatorando
1 c lass Ma i le r2 def send_mailer ( order )3 mai l (4 to : order . customer . email ,5 sub jec t : " Thanks f o r purchasing ! " )6 end7 end
Seminário POO | Princípios SOLID
42
Lei de DemeterExemplo - Aplicando a Lei de Demeter
1 c lass Ma i le r2 def send_mailer ( order )3 customer_email = @customer . emai l4 mai l (5 to : order . customer_email ,6 sub jec t : " Thanks f o r purchasing ! " )7 end8 end
Seminário POO | Princípios SOLID
43
Lei de DemeterExemplo - Aplicando a Lei de Demeter
1 c lass Ma i le r2 def send_mailer ( order )3 order . send_mai ler4 end5 end67 c lass Order8 def send_mailer9 mai l (
10 to : @customer . email ,11 sub jec t : " Thanks f o r purchasing ! " )12 end13 end
Seminário POO | Princípios SOLID
44
Bibliografia
I Fox, A.; Patterson, D. Engineering Software as a Service: AnAgile Approach using Cloud Computing. Versão 1.1.1.
I http://rails-bestpractices.com/posts/2010/07/24/the-law-of-demeter/
I https://scotch.io/bar-talk/s-o-l-i-d-the-first-five-principles-of-object-oriented-design
I http://gespinosa.org/2015/law-of-demeter/
Seminário POO | Princípios SOLID