Ap o
da Pro a em 21
o
Tony Sintes
Tradução
João Eduardo Nóbrcga Torlcllo Revisl10 Técn ic(l
Ana Fernanda Gomes Ascencio Bacharel em Ciência da Computação pela
pue-sp
Especialista cm Si stema de Infonnação pela UFSCAR c mestre cm computação pela UFROS
I• r
PEARSON
Makron Books São Paulo
Brasil Argentina Colômbia Costa Rica Chile Espanha Guatemala México Peru Porto Rico Venezuela
@ 2002 by Pearson Education do Brasil
Título Original: Object Oriented Programming in 21 Dias © 2002 by Sams Publishing Todos os direitos reservados Editora: Gisélia do Carmo Cosia Gerente de Produção: Silas Camargo Produtora Editorial: Sandra Cristina Pedri Revisão: Jorge Abdalla Neto Capa: Marcelo da Silva Françozo, sobre o projeto original
Editoração Efetr6nica: ERJ Informática LIda.
Dados de Catalogação na Publicação Sintes, Tony Aprenda Programação Orientada a Objetos em 21 Dias Tradução: João Eduardo Nóbrega TorteUo Revisão Técnica: Ana Fernanda Gomes Ascencio São Paulo: Pearson Education do Brasil, 2002 Titulo Original: Object Oriented Programming in 21 Dias
ISBN: 85.346.1461-X ,
Indice para Catálogo Sistemático 1. Programação Orientada a Objelos
2002 Direitos exclusivos para a língua portuguesa cedidos à Pearson Education do Brasil, uma empresa do grupo Pearson Educalion Av. Ermano Marchetti, 1435 CEP 05038-00 1 - Lapa - São Paulo - SP Tel: (11) 3613- 1222 Fax: ( 11) 3611-0444 e-mail :
[email protected]
Sobre o autor TONY SINTES traba lha com tecno logias orientadas a objetos há sele anos. Nesse tempo, Tony fez parte de muitos trabal hos de desenvo lvimento orientados a objetos de larga escala. Alualme nte, Tony trabalha na Firsl C lass Consult ing, uma empresa que fundou para ajudar as grandes empresas a integrar se us diversos s istemas em uma estrutura unificada. Antes de in iciar a First C lass Consulting, Tony trabalhou na BroadVision como consultor sênior, onde ajudou a construir alguns dos maiores sites Web do mundo . Atualmente, as principais responsabilidades de
Tony são como a rquiteto, líder técnico e mentor da equipe, ajudando a construir as habilidades em desenvo lvedores menos experientes.
Tony é um autor técnico ampl amente reconhecido, cujos trabalhos têm apa rec ido naJavaWorld, Dr. Dobb 's JOllrnol, LinuxWorld, JavaOne Today e Silicon Prairie, onde é co-autor de uma coluna mensal altamente respeitada sobre programação orientada a objetos. Atuahne nte, Tony escreve a coluna mensa l de perguntas e respostas daJavaWorld. Voeê pode entrar em contato com ele no endereço
[email protected].
Agradecimentos Escrever um livro é um processo como nenhum outro. A quantidade de pessoas que colaboram
em um livro, e que são necessárias para produzir a cópia final que você está le ndo agora, é s implesmente espantoso. Gostaria de estender minha gratidão à equ ipe inteira de ed itoriais da Sams. Sem seu trabalho árduo, este livro s implesmente não existiria. Pel o nome, gostaria de agradecer a Michael Stephens, Carol Ackerman, Tiffany Taylore George Nedeff. A liderança e toques sutis de Caro l foram o que realmente fi zeram este li vro prosseguir até sua conclusão. A capac idade de Tiffany de estruturar e encadear o material técnico, clara e
conci samente, é simplesmente espan tosa. Não apenas as edições de Tiffany tornaram este livro mai s leg íve l, mas acho que seu trabalho me ensi nou algumas lições valiosas sobre redação técnica. Também gostaria de agradecer a William Brown. Wi ll iam entrou em contato comigo a respeito do projeto STY oor, no início de agosto de 2000. Confiar tal projeto a um a utor relativamente desconhecido era arriscado e agradeço a William por me dar a chance de esc rever este li vro. Agradeço especia lmente aos edi tores técnicos, Mark Cashman e Ric hard Ba ld win, que garantiram que o material apresentado fosse tecnicamente bom. Agradeço a vocês pela awação técnica. Aos me us colegas, mu ito obrigado. Gostaria de estender os agradec imentos espec iai s a David Kim e Michael Hall . Eu comecei este livro enquanto estava na BroadVis ion e gostaria de agradecer a David Kim por me permit ir aquel as férias de pânico, quando os prazos de entrega começaram a se aprox imar. Ta mbém gostaria de agradecer a Michael Han, por SlIas idé ias téc nicas e por escrever o apêndice sobre Java deste livro. Por (lltimo, mas não menos importante, tenho o privilég io de agradecer à minha maravilhosa esposa, Amy, por se u apoio firme , revi sl'lo e paciência. Agradeço ainda à minha ramília e amigos, que ofereceram seu apoio e que o uviram minhas reclamações.
Diga-nos o que você acha! Como leitor deste livro, você é nosso c rít ico e co laborador mais importante. Valorizamos sua opini ão e queremos saber o que estamos fazendo correta mente. o que poderíamos fa zer melhor, sobre quais áreas você gostaria de nos ver publi cando e qua lquer outra sugestão importante que
deseje passar para nós. Receberemos com satis fação seus comentários. Você pode e nviar um fax, e-ma il ou esc rever difeiamen te, para que possamos saber o q ue gostou ou não neste livro - assim como o que podemos fazer para tornar nossos livros melhores. Por favor, entenda que não podemos ajudá-lo em prob lemas técnicos relacionados ao assunto
deste livro e que, dev ido ao grande volume de correspondência que recebemos, não possamos responder a todas as mensagens. Q uando você escrever, cert ifique-se de incluir o tílul o e o a utor deste livro, assi m como seu nome e n(nnero de tele fone ou fax. Examinarem os seus come ntários c uidadosamente e os compartilharemos com os aulores e editores que trabalharam no livro.
Fax: (1 1) 36 11 -9686 Fone: ( 11 ) 3613- 12 13 Endereço elelrônico: clientes@ma kron.com.br Endereço postal: Pearson Education do Brasi l Lida Rua Emílio Goe ld i, 747 - Lapa São Paulo - SP - CEPo05065-110
Sumário
Introdução XXIII Sobre os exemplos' .. ............ ......... .. •••• . . •••• ... . .. . XX IV O que você prec isa saber para usar este livro' ... ... .... .. . ••.. . ..... XX IV SEMANA 1 Dia 1
Definindo 00
,
Introdução à programação orientada a objetos
3 Programaçi'lo orientada a objelos em um contexto histórico' . .. .•.... . ....... 4 Precursores da POO .................................. ... .... ... 4 Programação orientada a objetos' . .... ................•.. .. . ..... . . 6 Uma estratégia de POO para software usando objetos ...... . ..... . ...... 6 O que é uma classe?' ............................. ... . .. .• .... .. 8
Reunindo tudo: classes e objetos' .....................•.....••..... 9 Fa7..endo os objetos trabalhar ' ............................. . ..... . I I Re lacionamentos de objeto ................................• . .... 13 Como a programação orientada a objetos fundamenta o passado ' ....•..... 14 Vantagens e objetivos da 00 .................... ................ . . 14 Natural' ........................... . ........................ 15 Confi ável ' .................. • .....• . .. . . .. .....•..... .. ..... 15 Reuti lizável ' .... • . ...... . ...•............•.................. 15 Manuten ível ' ....•..... .. . ...• . ................ . ... .. . . ...... 16 Extensível ........ . . . .. •• .... •• . . .. .•.. . . . .. . .. . . .. .. .•. .... 16 Oportuno' ........ . . . ... . . . .. .. ..... ............... ...... . .. 16 Armadilhas· ................................................... 16 Armad ilha I : pensar na POO simplesmen te como uma li ng uagem ... .. . . .. 17 Armadilha 2: medo da reutili zação' . . . .. .. ........................ 17 Armadilha 3: pen sar na 00 como uma so lução para tudo ' . . . . ........... 17 Armadilha 4: programação egoísta' . . . . .................• . . . . . . .... 18 A próx ima semana' ... . . ... . . . ... . ...................•• .. . • ..... 18 Resumo ' . .. ....................... . . .••........ .. . ..... . ..... 18 Pergu ntas e respostas' ...... . . . ......... . •.... .•.....•.....•..... 19 Workshop ............... •.....• .....•.....• . ..... • .....•..... 19 Teste' ................ ' ..... •.................. .. ... .. ..... 19 Exercícios ................. .... .. . .. . ................. . ..... 20 Dia 2
Encapsulamento: aprenda a manter os detalhes consigo mesmo 21 Encapsulamento: o prim eiro pilar ' ............................•.... ·22 Um exemplo de interface e implementação' ................. . . .. . . .. 24
x
Aprenda Progra m ação Orientada a Obj eto s em 21 Dia s
Público, privado e protegido' ............................•....... 25 Por que voce deve encapsul ar? ....... ................... .•. ..... . 25 Abstração: aprendendo a pensar e programar de forma abstraia ' .... . ........ 26 O que é abstração? ............................................ 26 Dois exemp los de abstração .................. ................... 27 Abslração eficaz' ............................................. 28 Guardando seus segredos através da ocuhação da imp lemen tação' ........ . .. 29 Protegendo seu objeto através do TAO (Abslract Data Type - Tipo Abstraio de Dados)- ...................................... . .... 30 O que é um tipo? . .. . . .. .. . . . .. . . . ......................... . .. 30 Um exemplo de TAO················ . ........................ 33 Protegendo outros de se us segredos, através da ocultação da impl emen tação' . 34 Um exemplo real de ocultação da implementação ................ . . . . . 36 Divisão da respon sabil idade : preocupando-se com seu próprio negócio' ....... 37 Dicas e armadilhas do encapsulamento' ........................ . . .. . . 41 Dicas e armadilhas da abstração .. . . . . .. .. .................. . . . .. . 41 Dicas e armadi lhas do TAD ···················· .... ............. 43 Dicas da ocultação da implementação' . ........................... . 43 Como o encapsulamento atende os objet ivos da programação orien tada a objetos ................................................... 44 Advertênc ias ............ . ..................................... 45 R eSUI110 . . . . . . . • • • . • • • • • •• ••••• • ••••• ••••• • • • • . • • • . . . • • • ••••.• 45 Perguntas e respostas ..... . . .....•.....•.....•......•.....•...... 45 Workshop .............. . .....•.....•............•.....•...... 46 Teste· .. .............•..................•..... . ............ 46 Exercícios .............. . .... .. .............. .. •.• . . . . •..... 47 Dia 3
Encapsu lamento: hora de escrever algum código
49
Laboratório I : config urando o ambiente Java' . . ...........•..... . ...... 49 Exposição do problema' ....................... ... . ..... .. •..... 50 Laboratório 2; c lasses básicas' .... .. . . . ...... .. . • .• .. . . .. . ... .... .. 50 Expos ição do problema' ...... . ...•. .. . . . .. . , .. " . .. . .... .. •..... 53 So luções e discussão' ...........•.. .. . ............ .. .. . ..•..... 54 Laboratório 3: o encapsu lamento ' .. .... . .. . ......•.....•...... .... . . 56 Exposição do problema ' ........................................ 57 Sol uções e discussão' ................................... .. ..... 57 Laboratório 4: estudo de caso - os pacotes de pri mitivas Java (opc ional ) ..... 62 Ex posição do problema ' ........................................ 66 Sol uções e discussão· .......................................... 66 Perguntas e respostas ............. . •........... . ..... • .....•..... 67 Workshop ... . . . .........• . ....••.....•.....•.....•. , ...• . .... 68 Teste' ................ •.....•......•. .... • ..... •• ....•..... 68 Exercidos .......•.....• . ..... . .... . .. .... .. .... .. • .... ..... 69
Sumário
Dia 4
Herança: obtendo algo para nada
o que é herança?
7'
. . ....... . .. . ...................... . .. • . . ...... 71 Por q ue herança? . . . .. . ............ . .. ... . ........... . . • .. ... . .. 74 " É um" versus " tem um": aprendendo quando usar herança ' ...... • •....... 75 Aprendendo a navegar na teia emaran hada da herança ' .. . . . .. .... •.. . .... 77 Mecânica da herança ... . . ......... . . . . ............ ... . ••• .. . .. 79 Métodos e atributos sobrepostos' ... .. .. . .......... . ..... . . .. . . . .. 8 1 Novos métodos e atributos · . . ........... ... • ..... • .. . ... • . . .. .. . 84 Métodos e atributos recursivos .... .. ..• . . ... •.. ........ . .• . . ..... 84 T ipos de herança .. . .. . ... .... . . . . ..... . ... • ...... • .. . . ... . . . . .. 84 Herança para impl ementação' .. ... .. . . .. ... ... .• .... .• . • . . . • . . . .... 85 Prob lemas da herança da implementação' .. . . . . .... .. . . . . . . . ..• . .... 85 Herança para d iferença' . .. .... .. . ...... . ... ••. . ... . ... . ........ 86 Especialização' ...... . .. .... .. . . . .. .. .... . . .. . . . .... .•.. . .... 87 Herança para substituição de tipo ......••. .. . . • ..... . . . .. .•....... 90 Dicas para a herança eficaz ' .. .... .. . . . .... . . .. • ... . . • .... .•... . .. . 92 Resurno ..... . .. . ... . . ...... . . ... . . ... .... . . . ... . ..... .•. ... .. 94 Como a herança atende aos objetivos da 00 .. ... . .. . . .... ... . . ... . . .. . 94 Perguntas e respostas .. ....... . ....... . . .. . . . . . • .... • • . . . .• .. . .. . 96 Workshop . ............... . . . ............. . ..... .• ..... . ...... 96 Teste' ......... . . . . ............ . . •..... • ..... •• ..... • .... .. 97 " .......... . ..... . .............•... . . . ...... • ...... 97 E' xerclcl0s Dia 5
Herança: hora de escrever algum código
99
Laboratório I: herança simples ' . . ......... • .... .. ..... . ..... . ... . .. 99 Expos ição do prob lema ' ......... ..... . ............... .•. .. .. . . 100 So luções e di scussão ' ... . .. . .. . ...... . .... . .. .. . . . .... . ....... 101 Laboratório 2: usando classes abstratas para herança planejada . ... . . .. . . .. . 102 Exposição do problema' . . .. . ..... . . .... .. .. .. . ...... .. . .. . .... 105 Soluções e di scussão' ......................... . . ..... . . .. . . . . . 105 Laboratório 3: conta em banco - praticando a herança simples' . .. . ....... 107 Urna conta genérica . ...... . . . .. .. ..... . ........ . . . ....• . .. . .. 107 A conta poupança' ... . . . ........... . . .. . .. . .. .. . . .... .•. . .... 107 Urna conta com vencimento programado' . .. .. .•. .. .. . • .. . . . . .... . . 107 Con ta com cheques' . . . . . . . .. . ..... .. .. .. . . . ..... . .... . .• .. ... 107 Conta com cheque especial ' . . . . . . . ........ . . . . . ... . . . . . . .•. .... 108 Exposição do problema' . .. . ..... . . .. .... . . .• . •. .. • .• . . . •• ... .. 108 Expos ição estendida do problema' ...... . ..... . . . .. . ............. 110 Sol uções e discussão ' .... . . . ..... . ................. . .... . ..... I I I Laboratório 4: estudo de caso - "é um ", " tem um" e j ava.uti l.Stack· .... .... 1 17 Expos ição do prob lema · .. . ... . .. . ................. . ........... I 18 Soluções e discussão' . ... . .. . ........ . ... ............. . . . . . . . . I 18 ResUlllo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . • . . . . . . • . . . . . . . . . . 119 Pergurllas e respostas . . . . . . . . . . . . . . . . . . . . • . . . . . . . . . . . . . . . . . . . . . . I 19
XII
Aprenda Progra mação Ori entada a Obj eto s em 2 1 Dia s
Workshop' ...... •• . .•.•• . . • •• . .. . •.• .. ••.• . . • •••. .• •••• .. .... 120 Teste' ....... ..................... . . .•• .. .••• .. . . •........ 120 .' . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . • . . . . . • • . . . . . . . . 120 Exe rCIClOS Dia 6
Polimorfismo: aprendendo a prever o futuro
121
Po linl orfi smo ....................... ....... . .. .. • . . .. •.. . ..... Po limorfismo de incl usão' ....... . .......... o • • • • • • • • • • • • • • • • • • • • Po limorfi smo paramétrico' .. . . .. . . ..... . .........••..... . ........ Métodos paramétricos' ......•.....•.....•.....•............... Tipos paramétri cos ' . .. . . .. .• ....... ....•......•..... •.. .. . . . .
122 126 13 1 13 1 133
Sobreposição ............... • •.... • ... . . . •....•• ....• •.• .. .... 134 Sobrecarga' . . .. . . . .. .... .. . . • .... . •... .. •.• . .. ..•.. . . .. . . .. . . 135 Conversão' .. . . . .. .... .... . .. .. .. ... .. •.•. .. . .• . . . .• ... . . . . 137
Poli morfis mo eficaz' . .. ..................... . .. . ............ . . . Armadi lhas polim órficas ' . . .. .. . . . . . ........................ .. . . . Armadilha I : mover comportamentos para cima na hierarquia ' ...... .. . . . Armadil ha 2: sobrecarga de desempenho ' ..................... . .... Armadil ha 3: vendas······ · ············ ·················· · ···· Advertênc ias' ........ . . . .. .... .. . . . .. . .. . ..• . .. .. .. . . .. . .. .... Como o polimorfismo atende os objctivos da 00 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ResunlO .................................•.....•.....•....... Perguntas e respostas .... • . . .. . •......•.....•................... Workshop' ............ . .....• . ..................... . . . •• . .... Teste' .... ......... . .. .... .. .............. . . .... .. . •. . .... . ' ExerCICIOS ............ . . . .. . ...........•.• . . . . ..... . •...... Dia 7
Polimorfis mo: hora de escrever algum código Laboratório \: aplicando poli morfismo' .. .... . ... . .• .. . . •• .. . . .. . . . . . Exposição do problem a · ............... . .... .. .. . .............. Soluções e discussão' .. . . .. . . . .... ................... ... .. . . . .
137 139 140 140 141 142 142 143 144 145 145 145
147 147 154 155
Laboratório 2: conta de banco - aplicando po limorfismo em um exemplo con hec ido' . . .................................... . .. . . .. ...... 156 Expos ição do problema' . .. .. . . .. .. ................. ...... .. . . . 157 Soluções e d iscussão' ....... . ................................. 159 Laboratório 3: conta de banco - usando polimorfismo para escrever código à prova do fut uro' .... . . . .. .. .... . .... .. ....................... Exposição do problema ' ....................................... Sol uções e d iscussão" ........................................ Laboratório 4: estudo de caso - estruturas condic ionais Java e pol imorfismo' Corrig indo uma estrutura condic ional ............................. Exposição do problema ' .. . .................................... Soluções e discussão ' .... . .... . ..... .. .. ... . •.. . .. .•.. .. .•. ... Res Ulllo ..... ............... . . . .. .. . . .• . . .. •• . .. . . ..... ......
160 162 163 165 167 169
17 1 172
Sumário
XIII
I
Pergu ntas e respostas ....• . . . . •• . .. .. •. .. ..•. . . . .... .. .... ... ... 173 Workshop' ...... ....... . .. . ••. .. . .... . . ••• .. .••• .. . . •........ 173 T este ········ ·········· · ················ ·· ·· ···· ·· ········173 Exercícios ........ .... .. . •.....•.....•.....•.....•......... 173 SEMANA 1
Em revisão
174
SEMANA 2
Aprendendo a aplicar 00
175
Introdução à UML
177
Introdução à Unified Mode ling Language ...... .. .. .... ..... . ........ Modelando s uas c lasses· .. . . .. . . . .. . ........•.....•.....•...... . . Notação bás ica de classe' .............. . .. . . .. ........ • ........ Notação avançada de classe . . . . .. . ........................ .. . . . Modelando suas c lasses de acordo com seus objeti vos ....• . . . .••. ... . . Modelando um re lacionamento de c lasse' ......... . ..... .. .. .. , .. . ... Dependência' . ... .. . . . .. .. . . .. .. ...............•... .. ..... . . Associação' ....... .......... . . .. . . .. .. . .....••....••..... . . Agregação ' ........••.....•..... •... ..•.... .•... .. .. ....... Conl pos içãO ........• . ....• •. ....•.....•.....••.... .. ....... General ização' ......•...... • . . .. .. •....•......... . .. . .. . .... Reun indo tudo ......... . .... ..•. .. .. •• . ..•. . . . .. ... .. ..• . . .... ResUlllo ............. ..•... . .... .. . . .. ... .... . . . .. . . . . •• . .... Perguntas e res postas ..... •.. . . . •• . .... . . . . . . ..... . ..... . ....... Workshop' ............ .•. ...• . .....•.....•.....•.....•....... Teste ' ................. . .. . ..... . ...........•.....•....... ,. xerCICIOS ...... . ....... . ....................•..... .. .. . ... E
177 179 179 181 18 1 183 183 184 186 187 188 189 190 191
Introdução à AOO (Análise Orientada a Objetosl
193
Dia 8
Dia 9
191 191 192
O processo de desenvolv imento de software " ..................... . . . . 194 O processo iterativo ' .... .. . .... .. . . ...... ............ .•.... . . 195 Uma metodo logia de alto nível ' ......................... . . . . .... 197 AOO (A náli se Orientada a Objetos)- .. .. . .. ................ . . . .... . . 198 Usando casos de estudo para descobrir o liSO do sistema ' . .... . . . . ...... 199 C ri e uma lista pre liminar de casos de uso ................ . . .. ...... 20 I Construindo o modelo de domínio' . . .................. .... •...... 2 13 E agora?' .. ... ...... ................. ........ ...•. ....•...... 2 14 Resull'o ............ . . . .............•.....•...........•...... 2 15 Perguntas e res postas .....••.... ... . . ............. . .••.. .. •..... 2 16 Workshop' ............. . .• . . .. •. .... ..... . .... .. . •. . .. ...... ·2 16 T este················· ········· ··········· · ·· · ············ 2 16 Exercícios ............ .. .......................•.....•..... 2 17 Dia 10 Introdução ao POO (Projeto Orientado a Objetos) 219 POO (Projeto Orientado a Objelos)' ...............• . .. .. •. . . .. ..... 220
XIV
Aprenda Progra mação Orientada a Obj eto s em 21 Dia s
Como você apl ica roo (Projeto Orientado a Objeto)?' . . .. .. . . .. .. ...... 221 Passo I: gere uma lista inic ial de objetos· ............• .. . . ......... 222 Passo 2: refine as responsabilidades de seus objetos ......... • ......... 223 Passo 3: desenvolva os pontos de imeração ...............•........ ·231 Passo 4: detal he os relacionamentos entre os objelos ........ . ...... . .. 232 Passo 5: construa seu modelo· .. . .................... .. •.• . ..... 233 ReSUlllO .......... . .............................• •. ••.• • . . ... 234 Perguntas e respostas ... . .... . ... . . . .. .. .. .••. . ... . .... •.• . ..... 234 Workshop' .. .. . . ..... . . .. . . . •. .. . . •• . . ... . . . . .. ..... ......... 235 Teste' ........... .. . . .. . .... . ..............•.....•.... . . . . 235 Exercíc ios ...................... . .. . ........•.....•........ 236 Dia 11 Reutilizando projetas através de padrões de projeto
237
Reutili zação de projeto' ....................... .. .. ........... . . . 238 Padrões de proj eto . . .. . . . .. ..... . . ..............•.....•... .. . . . 238 O nom e do padrão' . ... .. .. • . .. . .. . . .. .. . ..... . ..... • ... .. . . . 23 9 O problelna ........... ... . •...... ..... . .... .... ... .. ....... 23 9 A solução' ... .... . . . . .. . . . . ....... . . . .. •• .. . . •.. . ... ....... 239 As conseqUênc ias' .. . . . . . . . . ... . . . .. . ... ..• . .. . . .. . . .. . .. .... 239 Rea lidades do padrão' ...... . . .. . ... . . • . . . . ... ... .. .. . . .. ....... 240 Padrões por exem plo' .......... .•. .. ..• ... .•. .... .. .... .• ....... 240 O padrão Adapter' .... . ..... . .....•.....••.... . •.... .• ....... 24 1 O padrão Proxy ........ . ...•.....••.....•........... . .... . .. 245 O padrão Iterator ........... •. .... .. .. . . .. .......• . ... ••..... 247 Apossando-se de um padrão ' .... . . • .... •. ..... . .. . .. . . . .. . • . . .... 254 ReSUlno ....... . .. . .. . .. . . . . . .•. . . .. .. . . . •.•. . . . ..... . •...... 255 Perguntas e respostas ........... . .... .. ..... .. .... •. .... . •...... 255 Workshop ' ..... . ..... ... ...........•.....•.....•......... . . . . 256 Teste' ..............•.. .. . . . . ...... ...•............•...... 256 " los ................. . . . ... ... ... .. ..... . .. . . . . ...... 256 Exerc lc Respostas do teste ' . . .. .... .. . . . .. . . . . .•• .... .• ..... . . . . . . .. . . . . 259 Respostas dos exercíc ios ' ..... . . . ... ... ... ... .•.. .. .. . . . . . . ...... 260 Dia 12 Padrões avançados de projeto
263
Mais padrões por exemplo' .. . .. . .......••..... . .. . . .. ..... .. ..... 263 O padrão Abstracl Factol)' ········ ·· · · ·· ····· ·· · · · · ·· · · · ··· ····264 O padrão Sing leton ' . . . . .. . .. . ... ... .... . .. . . . . . . •.• .. . •• ..... 269 O padrão Typesafe Enum ..... .. •. ..... .. . .. . ..... . . . ...• .. .... 275 Armad ilhas do padrão' ...........•.....•.....•.....•.....•...... 280 Resumo' ................ ....•.... ..•.... .• .....•.....••..... 281 Perguntas e respostas ..... • ......•.....•..... . ..... . •.... .. ..... 28 1 Workshop' .............. . ................. . ••. . .. •• •. . .. ..... 282 Teste ' ............. .. . . . . . . ... ... . . . ... . • .. . .. .•.. .. .. .... 282 Exercfcios ..... . . • . . . . ... . . . . . . .. . . .• . . .. •• . .. . . ..... . . .. .. 282
Sumário
Respostas do teste' .......... , , •........... . . . . . .... .... .. ...... 284 Respostas dos exercíc ios' ..................... .. .•.• .. . . ......... 285 Dia 13 00 e programação da interface com o usuário
POO e a interface com o usuârio' ........................... .. ..... A importância das Uls desacop ladas .......................... . ..... Como desacoplar a UI usando o padrão Mode l View Controller ... ......... o Inodelo' ............ ........ ... . ......................... o modo de visualização' ...................................... O controlador ' .... . .. . . .. .• ...........•......•.....•........ Problemas com o MVC············ ....... . •...........•.• . ..... Uma ênfase nos dados' . . .. . . . .... . •... .. •.• . .. •.•. . . ... . . . . . . Acoplamento forte' . .... .... . .. .. .• . . . .. •.• . .. •.• . ... •. .. . . . . Ineficiência' ............. .. .. .. .. .... . ... .. .. ........... ... Resumo ' ...... . . . .. . . . ... .... ..........•.....•.......... . . . . Perguntas e respostas .. ... .. .. • . .. . .. . . .. .. . ...........•........ Workshop' .............. ....•.....• ........... . ......... ... .. Teste' ....... ... . .. . .. . .. . ..... . ...... . • .... •.. . ... ....... Exercícios ................................ .. .... . . .. . .. .... Dia 14 Construindo software confiável através de testes
289 289 290 293 294 297 30 I 303 304 304 305 305 305 306 307 307 313
Testando software 00··········································3 14 Testes e o processo de desenvolvi mento de software iterat ivo' .... .. •. . .... 3 14 Fonnas de teste' ...................................... . . •. . .... 3 17 Teste de unidade· ................................ .... . •...... 3 17 Teste de integração' ....•..........• .. .... .. ....•..... . ....... 3 18 Teste de sistema ' . . .... .. ................•.....•..... .. .. .... 318 Teste de regressão ... .................. ..•.....•......•...... 3 19 Um gu ia para escrever cód igo confiáve l' ..... . .. ... .... .• .... . •. • .... 3 19 Comb inando desenvolvimento e teste' ....• .... .• .. . . ••. ... .. . . . . . 3 19 Escrevendo código excepc iona l ' .. . .. . . .. .. .. . .• . .. . •. . . .. •.•.... 335 Escrevendo documentação eficaz' ............ .. .....•............ 336 Resumo ' ........ . .. . . .. .... . ............•............•.... . . 339 Perguntas e respostas .......... . .. . ...•............•.....•...... 339 Workshop' .............•.....•..... . •........................ 340 Teste······ ························ ·· ·· ·· ··· · · ·· ·· · ·······341 Exercícios .... ...... . ... . .. .•.. .. .... . . . . .. ... .... .. ••..... 341 SEMANA 2
Em revisão
342
SEMANA 3
Reunindo tudo: um projeto 00 completo
345
Dia 15 Aprendendo a combinar teoria e processo
347
Jogo Vin le-e-u m' .................... . ...... . • .... . •... . ... .... 347 Por quê vi nte-c-um? ................... - .... ••. . .. . • . ... ...... 348
XVI
Aprenda Progra mação Ori entada a Obj eto s em 2 1 Dia s
Ded aração da visão' ............ .. .. . . ..•. . . . .... . . .. .. ...... Requis itos de sobreposição ' ....... . .... . . ••• .. .•.• .. . . ......... Análise inicial do jogo vinte-e-um ..................•... .. •......... As regras do jogo vinte-e-um ............ .......•.....•......... Criando uma lista pre liminar de casos de uso' .......•... .. .......... Plancjando as iterações ' ......... . ................ . .. ...•.•. ..... Iteração I : jogo bás ico' .................... ... ••.. . . .... . . .. .. Iteração 2: regras' ... ................ . . .••. . ... . .. .. •.• . ... .. Iteração 3: aposta' ................••. . ..• . . . .. . ..... .... . . ... Iteração 4: interrace com o usuário ' . ..•....•......•.....•.... . . . . Iteração 1: jogo bás ico .................................•........ Análise do jogo vinte-e-um' . . . . .....•.....•.....•.......... . . . . Projeto do jogo vinte-e-u m .. . . . .... . ..• . .. •.• ... ••• . ... ••. ... . . A implementação' ............ . .. . .. . . .. .•. .. . ..• . .. ..•. ..... Resumo ' ........... . . .. . .. . . .... . ..• . .. . .• .. . . ••. .. .•..... . . Perguntas e respostas .. . . . •. .. . . .. . ..............• . ....•... ..... Workshop' ................. - •.....•.....•.....•.............. Teste' ............. . .. . . - - ..... - ..... . ......•..... .... .... Exercícios ................................... . ........ . ....
348 349 349 350 353 353 354 355 355 355 356 356 360 365 380 381 381 381 381
Dia 16 Iteração 2 do jogo vinte-e-um: adicionando regras Regras do jogo vinte-e-um .........................•.....•....... Análise das regras' ...... ..... ........................ . - ...... Projeto das regras' ..........• . ...................•. ... ••..... Implementação das regras' .... . .• ... -o • • • • • • • • • • • • • • • • • • • • • • • • . Teste' ......... . .. .........•. .. . .... .. •.•. ... ..... .• ...... ResUlno .......................... .. ...........•..... . •...... Pergun tas e respostas ... .... ... ...... .•.....•.....•......... . ... Workshop ' .............•.. . .......•... ...•............•......
383 383 384 388 395 409 409 410 4 10
Teste·············· · · ····· ·· ··· · · ············ · ·· · ·· ·· ····· 4 11 Exercícios ... . . . .. .. .... . . . .. .. ............ . . .. . . ... ....... 4 1 I Dia 17 Iteração3dojogo vinte-e-um: adicionando aposta 413 Aposta no jogo vinte-c-um . .. .. . . .. .. . . . .. .. . .......•..... ....... 413 Análise da aposta' ...... ........ ................ . ........... ·4 14 Projeto da aposta .................. . •.....•.. . . . ... . . . .•..... 4 17 Impl ementação da aposta .. . ..... . ...... . . .. . .. . . . •.• .. . ••..... 420 A implementação de Bank (Banco) ......... . .. ... ... ......•..... · 421 Um pequeno teste: um objeto falsificado' .......•.....•.....•...... 42 7 Resumo ' ..................... .... ........•.....•.....••..... 428 Perguntas e respostas ...............................•........... 429 Workshop' .............. ..... ............. . ••. ... ••• ... ...... 429 Teste' ..... ........ .. . . . . . . ... . . . ... . . . . •.. . .. .•. . .. ...... 429 Exercicios .... . . .• . . . . ... ... . .. .. . . .• . . ..••.. . . . .. .. . ...... 429
Sum ário
XV II
I
Dia 18 Iteração 4 do jo go vinte-e-um: adicionando uma GUI 431 Apresentação do jogo vinte-e- um ' ....................... .• ........ · 431 Oti mizaçõcs da linha de comando· ...... ..... . ........... • ......... 432 Análise da GU I do jogo vinte-e-um ' ....•.....•........... · 433 Casos de uso da GUI .... ...... ........ . . .• .. . . •• . . .. ... . .. . . ·433 Modelos visua is de GU I .. ...... ..... .. .. .••. .. . •• .. .. •.•. ..... 436 o
••••••••
Projeto da GUI do jogo vinte-e-um······ ·· · ······················ 437 Cartões CRC da GU I ....................•....•......•........ 437 Estnltura da GU I ········· · ········ ·· · ·· · ·· · ················· Refazendo' .. . ........................•.....•..... .... . . . . . Diagrama de classes da GU I . . .... . ... .......• . ..•. •. .. o • • • • • • • • Imp lementação da GU I do jogo vinte-e-um . . ..... ... .•.. . . . •• . ..... Im plementando YCard, YDeck c Ca rdV icw· .. ......... . .. . . . ... . . . . Implementando Pl ayerVicw' .. . . .. . ..... . ..............•........ Implementando Opt ionYicw e OptionViewCont ro ller' ....... .•. ... .. . . Implementando G UIPlayer . .. . . .. . .. . .................• . .......
438 439 440 440 440 444 445 445 Reunindo ludo com BlackjackGUI ······················ · · ······ ·448 Resurl10 ...... ..... . .. . .. . ........••. .. . . •• . . .. . . . .. .... ... .. 449 Perguntas e respostas .... ........ . .... • . . .. .•• . .. . •.•. . .• . . ..... 450 Workshop' ............ . . . ... . • . .. .•••........ .. . ............. 450 Teste' .......... .. .............••.....•.....•.... . • ....... 450 . . E' XCrcICIOS . . . . . . . . . . . . . . . . . . . . . . • . . . . . . • . . . . . • . . . . . • . . . . . . · 45 1 Dia 19 Aplica ndo uma alternativa ao MVC 453 Uma GU I alternativa do jogo vinte-e-um ····· · ·· · ····· ··· ··· · ······· 453 As camadas do PAC· .... ... ..............•.....•.... .•. .. .. .. 454 A filosofia do PAC , .... ... . .. . ...........•.....•............. 454 Quando usar o padrão de projeto PAC · ............................ 455 Ana lisando a GU I PAC do jogo vinte-c-um .... ................ .... . . . 455 Projetando a GU I PAC do jogo vinte-e-um ................. . . . •...... 455 Identificando os componentes da camada de apresentação' .... . . . . .. . . . . 456 Projetando os com ponentes da camada de abslração' ...........•...... 457 Projetando ri camada de con trole' .........................•...... 458 Usando o padrão Faclory para evi tar erros comuns' ..... • ..... . .... . . 458 Im plemen tando a GU I PAC do jogo vinte-e-um' .... . ..... . ......•.... ·460 Implementando YCard e V Hand ................. . .. ... ... .• .... · 460 Implementando VBetting Player · ..............•.•. . .• .• .. . ••..... 462 Im plementando VBlackj ackDea ler' ...... . .................••.... · 464 Implementando GU IPlayer ............•.....•.. ...•. ....•..... · 465 Reun indo tudo com O Contro le' ........•.....•.....• ..... .• .... ·465 Resurl10 ............................•......•.... .. ..... . ..... 468 Perguntas e respostas .. .... ...... ..... ..... . . . •• . . .. •• . . .. . .. . .. 468 Workshop' ............ . .... . . . .... .. ... ... .•.. . .. .•.. .. . ..... 468
XVII I
Aprend a Progra m ação Ori entada a Objetos em 21 Dia s
Teste ' ....... , , .. , . , ... , , , ... .. .. . ...•. . . . .... .. .. .. . ..... 468 Exercicios .......... ........... .... . . ••• .. .•.• .. . . ......... 469 Dia 20 Oivertindo-se com o jogo vinte-e-um
471
Di vertindo-se com O polimorfismo" ............. .... •. .... •....... ·47 1 Criando um jogador ................ . .. . ... . .. .••. . ..• .. . ..... 471
O jogador segu ro' .............. .. .• .. ..•• .... •.. . ........... 472 Adicionando SafePlayer na GU I .................••..... . ........ Aperfe içoamento' ...............•.....•.....•............... POO e sim ulações' .. . . ............... ....•......•.....•.... . . .. Os jogadores do jogo vinte-c-um ' .... .. .. . . . •...........•.• . ..... Resurno .... .... . ............ .... . •... .. •.• . .. ..•. . . . .. . . .... Perguntas e respostas .. .... .. . . . .....•.. . .. •.•. .. •.• . . . .•... . . . . Workshop' ......... ....• . .. .. .... . ..... . ..................... Teste' ...... . . . .. . . . ... . . . .......... .•.....•......... ..... Exercícios ..... ......... . • . .. . ..•.....•.....•..............
472 473 474 474 479 480 480 480 480
Dia 21 O último quilómetro 483 Amarrando as pontas ....... . ..... . ............................. 483
Refazendo o projeto do jogo vinte-e·um para reut ilização em ou tros sistcnl as ............ .............. ......................... 484 Identificando as vantagens que a Poo tTouxe para o sistema do jogo vi nte·e·um ................................................. 489 Rea lidades do setor e POO ..................................... 491 Resulllo .................... . .• .. ..• . ..... .... . ...... . •...... 49 1 Perguntas e respostas . ..... ...... ....••.....•.....•.....•...... . 491 Workshop' ............ . .....•......•.....•.....•............. 492 Teste' ...... . .. .. . ... .. .. .•.....•.....•........... .•. . .... 492 Exercíc ios .... .• .. .... .. . ............ . . ..• .. ..• • .... ....... 492 SEMANA 3
Apêndices
Em revisão
493 495
Apêndice A Respostas 497 Dia 1 Respostas do teste ' . . ................. .. •.. . . ...... .... .. . . 497 Respostas do teste' ............. . ..........•.....•.....•...... 497 Dia 2 Respostas do teste e dos exercícios' ...............•.....•..... ·499 Respostas do teste' ........................•...........•...... 499 Respostas dos exercícios' ..........................•• . ... •..... 50 I Dia 3 Respostas do teste e dos exerc ícios' ...... .. . ..•. . . .•.. .. ....... 50 1 Respostas do teste' .. .... ....... ........ ... . •. .... ............ 50 I Respostas dos exercic ios' ....................•.....•........... 503 Dia 4 Respostas do teste e dos exercícios' .........•......•........ . .. 505 Respostas do teste' ...... . .... . ............•......•........... 505
Sumário Respostas dos exercicios' ....•• . .. . ... . .. .•. . . . ..... . .......... 507 Dia 5 Respostas do teste' . . .............. . . ••• .. .•.• .. . . ......... 508 Respostas do teste' ............... . ..... . .....•.....•......... 508 Dia 6 Respostas do teste e dos exercícios' ......•.....•.....•......... 508 Respostas do teste' ...... .. ......... . ... . .....•............... 508 Respostas dos exercic ios' ...... . ................•... .. •.. . ..... 5 10 Dia 7 Respostas do teste' .............. .. . .. •• ... •• . . . .. . .. . . . ... 5 11 Respostas do leste' ... . .... . .. . ..... . .. . .••. . ... . .. .. •. .. ..... 51 1 Dia 8 Respostas do teste e dos cxercícios' .... . .... . . . .. ..... ... .. . ... 5 12 Respostas do teste' .. .. . . .. . .... . .......•......•.....•.... . . . . 5 12 Respostas dos exercícios' ........... . .. . .• . ...........•........ 5 13 Dia 9 Respostas do teste e dos exercícios' ..... . ••... .. •.. ... . .. .. . . . . 5 15 Respostas do teste' .. .... .. . . . ......... .. •.• ... ••• .. . .•• . . . . . . 5 15 Respostas dos exercícios' .............. . .. . . . .. . . .•. .. ..•. .. . . . 5 17 Dia 10 Respostas do teste e dos exercícios' .... .. •.• .. .• •• ... . •. .... . . 5 17 Respostas do teste' .. . . . .. .. .... . .......... .... . . ....•.. ...... 5 17 Respostas dos exercícios' . ......... .......•.... .•.. ...•........ 5 19 Dia I I Respostas do teste e dos exercícios ' .. . ...•.....••.... .. .. . .... 520 Respostas do teste' ............................ .. .... . ... . .... 520 Respostas dos exercícios' ............... . . .. • . . . .. . . . . . . . . ..... 521 Dia 12 Respostas do teste e dos exercíc ios · .... .. •. . . . ... . .. .. . . . ..... 523 Respostas do testc' .................... . . .. ..... . ..... . ....... 523 Respostas dos exercícios' . . .. . ............••.....•.....•....... 524 Dia 13 Respostas do teste e dos exercíc ios· . . ....••.....•.....•....... 528 Respostas do teste' . ....... ...... ......... . ..... .. ... . . •...... 528 Respostas dos exercíc ios' ... . .. . .. . .. . ....... . .. .. .• .. . . ••..... 529 Dia 14 Respostas do teste e dos exercícios' .... . . . ... . . . . •• .. . . •...... 53 1 Respostas do teste' . . .. .... .. .......... ... ..• .. . .• ..... . .. . . .. 53 1 Respostas dos exercíc ios' . .. . .... .. . . .. . ... . .. .... . .....•.... .. 532 Dia 15 Respostas do teste e dos exercícios' . .. . .. . • .. ..........•...... 533 Respostas do teste' .. .. ... .. . . ....... .....•.......... ......... 533 Respostas dos exercícios' ... . . . ...... . .. .. ..• ..... . ... . . . ...... 533 Dia 16 Respostas do leste e dos exercícios' .... . . . .• . . . . . ... . . . .... ... 534 Respostas do teste' .. . . . .. .. . .. . .. ..... . .. . • . . . . . . . .. . . .... . . . 534 Respostas dos exercícios' . .... .............. . .. . . .. .....•...... 534 Dia 17 Respostas do teste e dos exercícíos ' .. . .....•.....•.... .•...... 53 6 Respostas do teste' .... . ........ . .... . .....•.....•.....••..... 536 Respostas dos exercícios' ................... . ..... .• .... .. ..... 536 Dia 18 Respostas do teste e dos exercícios ' ....... . . .• .... •• .... .• .... 540 Respostas do teste' ...... . .......... . .... . . . •• . ... •• .... • , .... 540 Respostas dos exercícios' ...............•... . •.... .. ..... . ..... 540 Dia 19 Respostas do teste e dos exercíc ios' . . ...... , ......•..... • ..... 543 Respostas do teste' ....... . ................• . ..... . .....•..... 543
xx
Aprend a Programação Ori entada a Objetos em 21 Dia s
Respostas dos exercicios' .................•. . . . .... . . ... . ...... Dia 20 Respostas do teste e dos exercíc ios' .... . ••• .. .•.• .. . . ......... Respostas do leste' ............... . ...........•... .. • ......... Respostas dos exercíc ios' ................•.....•.....•......... Dia 2 1 Respostas do teste e dos exercíc ios' . . ... . .....•..... . ...... . .. Respostas do leste ' ........... . ..... . ..........•... .. •.. . ..... Respostas dos exercícios' ............... . .•• ... •• . . . . .... . . . ...
544 548 548 548 553 553 554
Apêndice B Resumo do Java 555 O Java Developer's Kit: J2SE 1.3 SDK ............ . . . •. . ... ... .. . . .. 555 Configuração do ambiente de desenvolvimento' ......•.....•.... . . . . 556 Panorama das fe lTamen tas do S DK ..... . . .. . .... .. .. . .....•..... ... 557 Co mpilador Java: javac' .... . . . .. . .............. •. .... ... .. . . . . 557 Interpretador Java : java ' .. . .. . . .. .. . . .. . . .. ........... . •• . . .. . . 558 Uti litário de com pactação de arqui vos Java: jar· . . . .. .... ... . ••. .... . 558 Documentação Java e o gerador de documentação: javadoc .... .. ... .. . . 559 Cercadinho Java: seu primeiro programa Java .. .... . ..........•..... . . 560 Compi lando e executando ' ..................................... 561 Cri ando um arqui vo .jar . .... . .......... . .• ......•.....•.... ... 562 Gerando j avadoc' ........... •. .... • ...... . ..... . .. . .. . ....... 563 Mecãnica da li nguagem Java' ... . ••.. .. . •• . .... . . .. .. .. . . . . . . .. ... 564 Classe Java simples ........ ..•.. .. ..• . .. .... . . ..... . .•.• ..... 564 Ti po de Dados· ............ . •• .... . . ..... ..... . ..... . ....... 565 Variáve is' ...... . ....• . ...• . .....•.....•.....•.....•...... . 566 Constantes ............ . ... •. .. .. . . .. . ..•.....•..... • ....... 568 Operadores' .. . .. . ....... .. .......•.....•..... .. .... . • .. . ... 568 Estruturas condic iona is' . .. . . ..... . .. . . . ............... . •• .. . . . 570 Laços ou estruturas de repetição" . .. . .. . . .... .. ......... . • ...... 57 1 C lasses e interfaces - blocos de construção da linguagem Java ' .... . .. . . . . 57 1 Usando classes já ex istentes' . . .. . .. . . .. . .... .. ..........•...... 572 Cri ando suas próprias classes ....... ............... . .. . .. . ...... 572 Interfaces' . .... . .. . . .. . .. . .. . .... ...... . ............ . .. ... . 575
C lasses internas e classes internas anónimas' .... . • . .... . .... . .. , .... 577 Resulno ... . .. . .. . ........ .. ....... . .. . .. . • .. . . . ... . . . . .... . . 579 Apêndice C Referência da UML 581 Referênc ia da UM L ............ . ... . . . •.... . . ...........• . .... . 581 Classes' .............. . .... . .......... .... • .....•.....•...... 581 Objeto ............... ..... . ......• .. . . .• .....•.....••..... 58 1 Visibilidade " ......... . .....•.....•..... . ..... . •.... .. ..... 581 Classes e métodos abstralOs ' .... .. .............. . . .. •• .. .. .• .... 582 Notas· ...................... . . .. .. ... . ... . • .. . . ... . . . .. •.... 582 Estereótipos ' ............ . .... . .. ... . . .• .... •• .... .•. ... . .... . 583 Relacionamentos ' ....• . . . .. ........... . ... . . . • .. . . .. ..... . ..... 583
Sumário
Dependência· ..... ...•. . . . •• . .. . . ..... .•. ... .... .. . .• . ... ... Associação ' ...... . . . .. .. . ••. .. . .... . . •.• .. .•.• .. . . ......... Pa pé is' ...... ... . . ..... . •............... .. • ..... •... ... ... Mu ltipl icidade ........... . •.....•..... • .....•.....•......... Agregação ........•... ..•... ..••. . ... . .....•............... COln posição ........... ...... ... .•. .......... . .. ...•.•. ..... General ização' ...................•• .... •• ... •• . . .. .... . . .. .. Diagram as de in leração' ...... . .•. . . . . ... .. .••. . ... . ....• .• . ... .. Diagramas de colaboração· ............ . . ..• . . .. .. . . ... .... . . ... Diagramas de seqUência' . .. . .... .. ......•......•.....•.... . . . .
583 584 584 584 585 585 585 586 586 586
Apêndice O Bibliografia selecionada 587 Aná lise, projeto e metodologias' . . . .. . ........•.....•.......... . . . . 587 Programaçl'lo com C++ . .... .. . . . .. .. . . .. .. .. . • . .. ••• .. . .•• .. .. . . 588 Padrões de projeto ........ ............ .... . . . .. . .• .. .. .. •. .. ... 588 Principios e teoria gera l da 00 .. . . .. .. ............ . .•• .. . . . •.... . . 588 Teoria " Hard Core" (mas não deixe isso assustá- lo!) . . ....•....••....... 588 Programaçllo com Java' ................... .......•.....• ........ 589 Miscelânea ' ........... ................ .........•.....•....... 589 Snl alltalk ................... . ..... ...... . ... ................. 589 Teste' .......................................... .... ........ 589 Apêndice E Listagens do código do jogo vinte-e-um 591 blackjack.core· ..................... ................... . •...... 592 blackjack.core.lhreaded ..... . ................ ............ •. ..... . 6 19 blackjack.exe .... ............••...........•.. ...•..... • .. ..... 62 1 blackjack. players ........ . .. . .•......•.. ...•..... •. ... ..•...... 627 blackjack. ui ........... . •.. .. .... . .. . ............... ... •...... 635 blac~ack.u i.mvc · ...... . . .• . . ... ... . . .... . . ... . .. .. .. ... ....... 636 blackjack. ui.pac ... . .. . .. .. . ... ... ... ... .. . . .• .. . . •• . ... ....... 649 (ndice Remissivo
669
Introdução Este livro adota uma estratégia prática para e ns inar programação orienlada a objclos (POO). Em vez de ensinar POO em um níve l acadêmico, este li vro apresenta lições e exemplos acessíveis e amigáveis, para pennitir que você comece a aplicar POO imediatamente. Em vez de tenlar ensi-
nar cada detalhe teórico, este li vro destaca os assuntos que você precisa conhecer para poder aplicar POO em seus proj etas diários - e não perder seu tempo se degladiando em algum debate teórico.
o objetivo deste li vro é fornecer a você uma base sólida sobre programação o rientada a objelos. Após 21 dias, você deverá ter lima boa idéia dos concei tos bás icos da POO. Usando essa base, você pode começar a apli car co nceitos de POO em seus projetas diários, assi m como cont inuar a construir seus conhec imentos de 00, através de estudo adicional. Você não aprenderá tudo a respeito de POO em 21 dias - isso simplesmente não é possível. En tret8llto, é possíve l constru ir uma base sólida e começar com o pê direito. Este livro o ajuda a fazer exatamente isso. Dividimos este li vro em três partes. A Semana I apresenta os três princípios da POO (também conhec idos como os três pilares da POO). Esses três princípios fonnam a base da teo ria da orientação a objetos. O entendimento desses três princípios é abso lutamente fundamen ta l para entender POO. As lições da semana estão divid idas entre apresentação da teoria e o fornec imento de experiência prática, através de laboratórios. A Semana 2 apresenta o processo de desenvolvimento de software 00. Embora as lições do Capítulo I, "llllrodução à programação orientada a objetos", sejam im portantes, mandá-lo programar se m qualquer outra instrução é como dar a você madeira, uma serra, um martelo e a lguns pregos, e dizer para construir uma casa. A Semana 2 mostra como aplicar as ferramentas apresentadas nas Iiçôes da Semana I. A Semana 3 o conduz em um estudo de caso completo, de um jogo de cartas 00. Esse estudo permitirá que você percorra um ciclo de desenvolvimento 00 inte iro, do iníc io ao fim , assi m como prat ique a escrita de algum cód igo. Esperamos que esse estudo de caso ajude a esc larecer a teoria da 00 e torne as co isas mai s concretas. Também existem vários apêndices no fina l do livro. De impo rtânc ia espec ial é o "Resumo do Java", no Apêndice B, e a bibliografia selecionada, no Apêndice D, O Apêndice B serve como um g uia exce lente para a linguagem de programação Java. A bibliogra fia mostra os rec ursos que você desejará consultar, quando conti nuar seus estudos de POO. Esses recursos certamente fo ram va liosos para escrever este livro.
Sobre os exemplos Todos os exemplos de código-fonte foram escritos em Java. A Iguma ex periência em Java ajudará; entretanto, o Apêndice B deve ajudá-lo a ter ve locidade, caso você esteja enferrujado ou mlnca tenha visto a linguagem antes. Desde que você tenha algum conhec imento de programação, os exemp los 5<10 os mai s acess íveis. Os recursos e truques especia is do Java foram particularme nte evitados nos exemplos.
o que você precisa saber para usar este livro Este li vro presum e alguma experiência anterior em programação e não lenta ensinar programação básica. Este livro pega o conhec ime nto que você já tem e mostra como pode usá-lo pa ra escrever so fi ware orientado a objclos. Ou escrever software orientado a objelos me lhor. Isso não quer dizer que você precisa ser um guru de programação para ler e enlender este livro - um curso de programação introdutório ou simplesmente a leitura de um li vro de programação é todo o con hec imento que você deve precisar.
Para poder tirar total prove ito dos exemplos e exercícios, você também precisará de um computador com acesso à Internet. A escolha do ambiente operacional e do editor fi cam completamente por conta de seu gosto pessoal. O único requisi to é que você possa fazer download, instalar e executar Java. O Apêndice B o conduz pelo processo de obtenção de um SDK Java. Finalmente, você precisa de determinação, dedicação e uma mente aberta. A programação orientada a objetos não é fáci l e você demorará mais de 21 dias para dominar, mas aqui você pode ter um início bom e sólido. O mundo maravilhoso da
roo está à espera ....
SEMANA
1
Definindo 00 I Introdução à programação orientada a objetos 2 Encapsulamento: aprenda a manter os detalhes • consIgo mesmo 3 Encapsulamento: hora de escrever algum código 4 Herança: obtendo algo para nada
5 Herança: hora de escrever algum cód igo
6 Polimorfismo: aprendendo a prever o futuro 7 Polimorfismo: hora de escrever algum código
Panorama Os próximos sete dias fornecerdO a você uma base sólida sobre programação orientada a objelos. O Dia I descreve os fu ndamentos da 00 (orientado a objetos). Você aprende a respeito de orientação a objelos a parti r de uma perspectiva histórica e vê como a 00 evolu iu a partir de linguagens de programação existentes. Você também aprende a terminologia básica, ass im como as vantagens e armadi lhas da programação orientada a objetos. Os dias 2, 4 e 6 apresentam os três pitares da programação orientada a objelos: encapsulamenlo, herança e polimOlfismo. Esses capítul os não apenas expli cam os fundamentos da programação
orientada a obj elos, mas como e quando usá-los, assi m como os erros a serem evitados. Os dias 3, 5 e 7 fornecem laboratórios correspondentes a cada um dos três pilares. Cada capitulo de laboratório fornece experiência prática que lhe penn ite se fami liarizar-se com os pi lares apresentados nos dias 2, 4 e 6. Após concluir a primeira semana, você deverá ter um entend imento com pleto do quê constitui um programa orientado a objetos. Você deve poder identificar os três pilares da 00 e aplicá-los em seu cód igo. Testes e exercícios seguem a lição de cada dia, para ajudá-lo a entender melhor os assuntos abordados. As respostas de cada pergunta dos testes e exercícios aparecem no Apêndice A.
SEMANA
1
DIA Introdução à programação orientada a objetos Embora as linguagens orientadas a objetos j á existam desde a década de 1960, os últimos 10 anos têm vi sto um cresci mento sem paralelo no uso e na aceitação de tecnologias de objeto, por todo o selar de software. Embora tenham começado como algo secundário, slIcessos recentes, como Java, CORBA e C++, têm impulsionado as técnicas orientadas a objetos (00) para novos níveis de acei tação. Isso não é por acaso. Após anos presa nos meios acadêmicos e tendo de lutar uma árdua batalha contra as práticas entrincheiradas, a programação orientada a objetos (POO) amadureceu até o ponto onde as pessoas são finalmente capazes de perceber as promessas que a técnica contém. No passado, você tinha de convencer seu chefe a permit ir o uso de uma linguagem orientada a objetos. Hoje, muitas empresas obrigam seu uso. É seguro dizer que as pessoas estão finalmente ouvindo. Se você está lendo este livro, fina lmente foi convencido. Provavelmente é alguém com um nível intcnnediário de experiência em programação. Se você conhece C. Visual Basic ou FORTRAN, já estava nas imediações, mas decidi u que precisa dar uma séria olhada na programação orientada a objetos e torná-Ia parte de seu conjunto de habilidades. Mesmo que você tenha alguma experiência com uma linguagem orientada a objetos, este li vro pode aj udá-lo a sol id ificar seu entendimento de 00. Mas não entre em pânico, caso você não esteja fa miliarizado com uma linguagem 00. Embora este livro use Java para ensinar conceitos de
Dia 1
00, um conhecimento prévio de Java não é necessário. Se você fica r confuso ou predsar de um lem brete da sintaxe, basta consultar o Apênd ice B, " Resumo do Java". Se você prec isa de 0 0 para se manter no mercado, termi nar seu projeto ma is recente ou satis· fazer sua pró pria curiosidade, então veio ao lugar certo. Embora nenhum livro possa ensinar tudo que há em re lação a ao, este li vro promete fornecer uma base sól ida em 00. Com essa base, você pode começar a praticar POO. E, mais im portante, a fundamentação fornece rá a base d urável q ue você precisa para con tinuar a aprender e, fin al mente, dominar esse parad ig· ma de programação. Hoj e você aprenderá •
Programação orientada a objetos em um contexto hi stórico
•
A base da programação ori entada a objetos
•
As vantagens e obj eti vos da programação orientada a objetos
• As fa lác ias e armadilhas comuns associadas á programação ori entada a objetos
Programação orientada a objetos em um contexto histórico Para entender o estado atual da POO. você deve conhecer um pouco da história da programação. Ninguém cOllcebeu a POO da noite para o dia. Em vez disso, a POO é apenas outro estágio na evolução natural do desenvolv imento de soft ware. Com o passar do tempo, se tomou mais fác il identifi car as práticas que funcionam e as que comprovadamente falh am. A POO com bina práti· cas com provadas e testadas o mais efi cientemente possível.
00 é a abreviatura de orientado a objelOs. 00 é um lemlO gera l que incl ui qualquer esti lo de desenvolvimento que seja baseado no conceito de 'objeto ' - urna entidade que exibe características e comportamentos. Você pode aplicar uma estratégia orientada a objetos na programação, assim como na análise e no projeto.
Novo TERMO
Você também pode di zer que 00 é um estado da mente, uma maneira de ver o mundo todo em termos de objetos. Simplesmente, a 00 contém tudo que pode ser denominado como ori entado a objetos. Você vai ver o tenno 00 muitas vezes neste livro.
Precursores da POO Atualmente, quando usa um computador, você tira proveito de 50 anos de refi namento. Antiga· mente, a programação era engenhosa: os programadores introduziam OS programas diretamente na memória princi pal do computador, através de bancos de chaves (sw itches). Os programado-res escreviam seus programas em linguagem binária. Ta l programação cm linguagem binária
Introdução à programação orientada a objetos
5
era extremamente propensa a erros e a falta de estrutura tornou a manutenção do código praticamente impossível. Além disso. o cód igo da linguagem binária não era muito acessível. Quando os com putadores se tomaram mais comuns, linguagens de nível mais alto e procedurais começaram a aparecer; a primeira foi FORTRAN. Entretanto, linguagens procedurais posteriores, como ALGO L, tiveram mais inO uência sobre a 00. As linguagens procedurais permitem ao programador reduzir um programa em proced imentos refinados para processar dados. Esses procedimentos refinados definem a estrutura global do programa. Chamadas seqi.lenciai s a esses procedi mentos geram a execução de um programa proced ural. O programa termi na quando acaba de chamar sua li sta de procedi mentos. Esse paradigma apresentou diversas melhorias em relação à Iinguagem binária, incluindo a adição de uma estrutura de apoio: o procedimento. As funções menores não são apenas mais fáceis de entender, mas também são mai s fáceis de depurar. Por outro lado, a programação procedural limita a reutilização de código. E, com muita freqüência, os programadores produziam código de espagueti - cód igo cujo caminho de execução se assemelhava a uma ti gela de espagueti. Finalmente, a natureza voltada aos dados da programação procedural causou alguns problemas próprios. Corno os dados e o procedimento são separados, não existe nenhum encapsulamento dos dados. Isso ex ige que cada procedimento saiba como manipularcorretamente os dados. Infelizmente, um procedi mento com comportamento errôneo poderia introduzi r erros se não manipulasse os dados corretamente. Uma mudança na representação dos dados ex igi a alterações em cada lugar que acessasse os dados. Assim, mesmo urna pequena alteração poderia levar a uma cascata de alterações, por todo o programa - em outras palavras, um pesadelo de manutenção. A programação modular, com uma linguagem como Modula2, tenta mel horar algumas das defi ciências encontradas na programação procedural. A programação modu lar divide os programas em vários componentes ou módulos constitui ntes. Ao contrário da programação proccdural, que separa dados e proced imentos, os módulos combinam os dois. Um módulo consiste em dados e procedi mentos para manipu lar esses dados. Quando outras partes do programa precisam usar um módulo, elas simplesmente exercitam a interface do módu lo. Como os módu los ocultam todos os dados internos do restante do programa, é fácil introduzir a idéia de estado: um mód ulo contém informações de estado que podem mudar a qual quer momento. Novo
TERMO
o estado de um objeto é o significado combinado das variáveis irllemas do objeto.
Novo
TERMO
Uma variável inferna é um valor mantido dentro de um objeto.
Mas a programação Illodular sofre de duas deficiências próprias importantes. Os módulos não são extensíveis, sign ificando que você nào pode fazer alterações incrementa is cm um módulo sem abrir o código a fo rça e faze r as alterações diretamente. Você também não pode basear um módulo em outro, a não ser através de delegação. E, embora um módulo possa defin ir um ti po, um módulo não pode compart ilhar o tipo de oulro módulo.
Dia 1
Nas linguagens modulares e procedurais, os dados estruturados e não estruturados têm um
'tipo'. O tipo é mais fac ilmente pensado como o fonna lo da memória para os dados. As linguagens fortemente ti padas exigem que cada objeto tenha um tipo específico e definido. Entretanto, os tipos não podem ser estendidos para criar outro tipo, exceto atraves de um est ilo chamado 'agregação'. Por exemplo, em C, podemos ter dois tipos de dados relacionados: typedef struct {
'Int a; int b; } aBaseType; typedef struct {
aBaseType Base ; i nt c; } aDerivedType õ Nesse exemplo, aDeri vedType é baseado em aBaseType, mas uma est rutura de aDerivedType não pode ser tratada di retamente como uma estrutura de aBaseType. Uma só pode faze r referência ao mem bro Base de uma estrutura aDeri vedType. Infel izmente, essa organização lcva a código que possu i muitos blocos case e if7else, pois o aplicativo deve saber como manipular cada módulo que encontra . Finalmente, a programação modu lar também é um híbrido procedural que ainda div ide um programa em vários proced imentos. Agora, em vez de atuar em dados brutos, esses procedimentos manipulam módulos.
Programação orientada a objetos A roo dá o próximo passo lóg ico após a programação modular, adicionando herança e polimorfismo ao módulo. A roo estrutura um programa, dividindo-o em vários objetos de alto nível. Cada objeto modela algum aspecto do problema que você está tentando resolver. Escrever listas seqUencia is de chamadas de procedimento para dirigir o flux o do programa não é mais o foco da programação sob a 00. Em vez disso, os objetos interagem entre si, para orientar o fluxo global do programa. De certa forma , um programa 00 se torna uma si mulação viva do problema que você está tentando resolver.
Uma estratégia de POO para software usando objetos Imagine que você tivesse de desenvolver um programa 00 para implementar um carrinho de compras on-l ine ou um tenninal de ponto de vendas. Um programa 00 conterá os objetos item, carrinho de compras, cupom e caixa. Cada um desses objetos vai interagir uns com os outros para orientar o programa . Por exemplo, quando o caixa totalizar um pedido, ele veri ficará o preço de cada item.
Introdução à programação orientada a objet os
7
Definir um programa em termos de objetos é uma maneira profunda de ver o software. Os objelOS o obrigam a ver tudo, em nível concei tuai, do que um objeto faz: seus comportamentos. Ver um objelo a partir do nível concei tua i é um desvio da observação de como algo é feito: a implementação. Essa menta lidade o obriga a pensarem seus programas em termos nat urais e reais. Em vez de modelar seu programa como um conj unto de procedimentos e dados separados (termos do mundo do computador), você modela seu programa em objetos. Os objetos permitem que você modele seus programas nos substant ivos, verbos e adjet ivos do domínio de seu problema. Novo TERMO Novo TERMO
A implcmcntaç(70 define como algo é feito. Em tennos de programação, implcmcnlaç(jo é o código.
o dOll/inio é o espaço onde um prob lema reside. O domínio é o conjunto de conceitos que representam os aspectos im portantes do problema que você está tentando resolver.
Quando recua e pensa nos termos do problema que está resolvendo. você ev ita se emaranhar nos detalhes da implementação. Ê claro que alguns de seus objetos de alto nível precisarão interagir com o computador. Entretanto, o objeto isolará essas interaçôes do restante do sistema. (O Dia 2, "Encapsulamento: aprenda a manter os detalhes consigo mesmo", explorará melhor essas vantagens.)
NOTA
Em term os do carri nho de compras, ocultação de implementaçáosigní' ica que o caixa não vê dados brutos ao t otali zar um pedid o. O caixa não sabe procurar, em certas posições de memória, números de item e outra variável para um cupom. Em vez disso, o caixa interage com objetos item. Ele sabe perguntar quanto custa o item.
Neste ponto, você pode definir ohjelo fo nnalmente: Novo TERMO Um objeloé uma construção de software que encapsu la estado c comportamento. Os objetos permitem que você modele seu software em lennos reais e abstrações. Rigorosamente fa lando, um objeto é urna instância de lltna classe. A próx ima seção apresentará o concei to de classe. Assim como o mundo real é constituído de objetos, da mesma forma o é o software orientado a objelos. Em uma linguagem de programação 00 pura, tudo é um objeto, desde os tipos mais básicos, como inteiros e lógicos, até as instâncias de classe mais complexas; nem todas as li nguagens orientadas a objeto chegam a esse ponto. Em algumas (como o Java), primi tivas como int c fl oat, não são tratadas como objetos.
Dia 1
o que é uma classe? Assi m como os objetos do mundo real, o mundo da POO agrupa os objelos pelos seus comportamentos e atributos comuns. A biologia classifica todos os cães, gatos, elefantes e seres humanos como mamíferos. Caraclerislicas comparti lhadas dâo a essas criaturas separadas um senso de comunidade. No mundo do software, as classes agnlp.1.1ll objetos relacionados da mesma maneira. Uma classe define todas as característ icas com uns a um ti pode objeto. Especificamente, a classe defi ne todos os atribulos e comportamentos expostos pelo objeto. A classe define a quais mensagens seus objelos respondem. Quando Ulll objetoquer exercer o comportamento de outro objeto, ele não faz isso diretamente, mas pede ao outro obj eto para que se mude, normalmente baseado em alguma informação adiciona l. FreqUentemente, isso é referido como 'envio de uma mensagem' . Novo TERMO Uma c/asse define os atributos e comportamen tos com uns compartilhados por um tipo de objelo. Os objetos de certo tipo ou classificação compartil ham os mesmos comportamen tos e atributos. As classes atuam de forma muito parecida com um cortador de mal · de ou biscoito. no sentido de que você usa uma classe para criar ou instanciar objetos. Novo TERMO Atributos são as caracteristicas de uma classe visiveis externamente. A cor dos olhos e a cor dos cabelos são exemplos de atributos. Um objeto pode expor um atribula fornecendo um link direto a alguma variável interna ou retornando o valor através de um método. Novo TERMO ComporramelJ/o é uma ação executada por um objeto quando passada uma mensagem ou em resposta a uma mudança de estado: é algo que um objeto faz. Um objelo pode exercer o comportamenlo de outro, executando uma operação sobre esse objeto. Você pode ver os termos chamada de método, chamada de função ou passar lili/a mensagem , usados em vez de executar uma operação. O que é importante é que cada urna dessas açôes om ite o comportamento de um objcto. Passagem de mensagem, operação, chamada de método e chamada de função: o quê você usa, freqüen temente depende de seus conceitos anteriores.
Pensar em termos de passagem de mensagem é uma maneira muito orientada a objetos de pensar. A passagem de mensagem é dinâmica. Conceituai mente, ela separa a mensagem do objeto. Tal mentalidade pode ajudar a pensar a respeito das interaçOes entre objetos. linguagens como C++ e Java têm heranças procedurais, onde as chamadas de função são estáticas. Como resultado, essas tinguagens freqüentemente se referem a um objeto realizando uma chamada de método a partir de outro objeto. Uma chamada de método estâ fortemente acoptada ao objeto. Este livro normalmente usará chamada de método. devido à sua forte ligação com Java. Entretanto, podem existir ocasiões em que o termo mensagem é usado indistintamente.
Introdução à programação orientada a objetos
9
Reunindo tudo: classes e objetos Pegue um objeto item, por exemplo. Um item tem uma descrição, id, preço unitário, quantidade e um desconto opcional. Um item saberá calcular seu preço descamado. No mundo da POO, você diria que todos os objetos item são instâncias da classe Item. Uma classe Item poderia ser como segue :
public class Item ( private private private private private
double double i nt Stri ng Stri ng
unit_pri ce i discount; Iluma porcentagem de desconto que se ap l ic a ao preço quantity ; desc r i pt i on i i d;
public Item( String id , Stri ng descri ption, int quantity , doub l e price ) { this.id • id; th i s.description • descript i on; O) I this.quantily • quan t ity;
if( quan tity
>-
I else { l his.quantity • O;
I this.unit_price • pri ce i
I publi c double getAdju stedTotal() { double tota l · unit_price * quantity; double total - discount • total * discount; double adj usted_to t al • total - total_d isco unt; return adjusted_total
j
I
II
apl ic a uma porce ntagem de desconto no preço publ i c void setOiscount( double di scount ) I if( dis count u 1. 00 ) { this .discoun t • discount;
I else { t his.discount • 0.0 ;
I
Dia 1
I publi C double getD1scount() return discount:
t
I publl c int getQuantityO return quantity;
1
I public vo 1d setQuantity( int quantity ) { if( quantity >- O ) { this.quantity z quantitYi
I I pub l ic String getProductIDO { return idi
I public String getDescription() ( return descriptionj
I
I Métodos como public Item( String id, String descr1ption. 1nt quantity . double pr1ce ) são chamados COI1Slr/llo r es. Os construtores inicializam um objeto durarue sua criação. são métodos usados para inicializar objctos durante sua instanciação. Você chama a criação de objetos de il/Slallciaçt7o porque ela cria uma instância do objeto da classe.
Novo
TERMO
NOTA
COI/Slr/IIores
No construtor e por todo o exemplo Item, você poda notar o uso de th i s. thi s é uma referência que aponta para a instância do objato. Cada objato tem sua própria referência para si mesmo. A instância usa essa referência para acessar suas próprias variáveis e métodos.
Métodos como setDfscountO, getDescript1onO e getAdjustedTota l O são lodos comportamentos da classe I tem que retomam o u configuram atributos. Quando um caixa quer totali zar o carrinho, e le simplesmente pega cada ite m e envia ao objeto a mensagem getAdj us tedT ota 1 O .
Introdução à programação orientada a objet os
11
unit_price, di scou nt, quantity, description e id são todas variáveis internas da classe Item. Esses va lores compreendem o esladQ do objelo. O estado de um objeto pode variar com Otempo. Por exemplo, ao fazer compras, um consumidor pode aplicar um cupom ao item. Aplicar um cupom ao item mudará o estado do item, pois isso mudará o valor de discount. Os métodos como getAdjustedTota 10 e getoiscountO são chamados de ace:"SQres, pois eles pennitem que você acesse os dados internos de um objeto. O acesso pode ser direto, como no caso de getDiscount(). Por outro lado, o objeto pode realizar processamento antes de retornar um valor, como no caso de getAdjustedTotal O. Os ace.~.wres dão acesso aos dados internos de um objeto. Entretanto, os (lcessores ocultam o fato de os dados estarem em uma variáve l, em uma combinação de variávei s ou serem calculados. Os aces:wres permitem que você mude ou recupere o valor e têm 'efeitos colaterais' sobre o estado interno.
Novo
TERMO
Os métodos como setDi scount() são chamados de l11ulanles, pois eles permitem que você altere o estado interno do objeto. Um mutante pode processar sua entrada como qui ser, antes de alterar o estado interno do objeto. Pegue setD; scount () , por exemplo. setDi scount () garante que o desconto não seja maior que 100%, antes de aplicá- lo. Novo
TERMO
Os
1II1I/(lI1les
pennitem que você altere o estado interno de um objeto.
Ao serem executados, seus programas usam classes como a I tem para cdar ou instanciar os objetos que compõem o aplicativo. Cada nova instância é uma duplicata da últ ima. Entretanto, uma vez instanciada, a instância transporta comportamentos e controla seu estado. Então, o que inicia sua vida como clone poderia se comportar de maneira muito diferente durante sua existência. Por exemplo, se você criar dois objetos item a partirda mesma classe I tem, um objeto item poderá ter um desconto de 10%, enquanto o segundo pode não ter desconto. Alguns itens também são um pouco mais caros que outros. Um item poderia custar USS 1.000, enquanto outro poderia custar apenas US$I,98. Assim, embora o estado de um item possa variar com o passar do tempo, a instânc ia ainda é um objeto de Item. Considere o exemplo da biologia; um mamífero de cor cinza é tão mamífero quanto outro de cor marrom.
Fazendo os objetos trabalhar Considere o método main() a seguir: publiC static void main( String II cria os itens Item mil k • ,e. Item( Item yogu rt • 'e. Item{ Item bread • 00' Item( Item soap • ,e. Item(
[] al"9S ) I "dairy-Oll", "I Gallon Hilk", 2, 2.50 ); "dairy-032", ·Peach Yogurt", 4,0.68 ) ; "bakery-023" , "Sliced Bread" , 1,2.55 ); "household -21", "6 Pack Soap· , 1,4.51 ) ;
Dia 1
II aplica cupons milk.setOiscount( 0. 15 ). II obtêm preços ajustados double milk_price • milk.getAdjustedTotal(); doubl e yogurt_price • yogurt.getAdjustedTotal(); doubl e bread_price z bread.getAdjustedTotal(). double soap_price a soap.getAdj ustedTotal(). I I imprime recibo System.out.println( "Thank Vou For Vour Purchase." ). $ystem. out.println( "Please Come Again!" ): System . out.println( milk.getOescription() + "\t $" + System.out.print l n( yogurt.getOescription() + "\t $" + System.out.print l n( bread.getOescript ion() + ., \ t $" + + ., \ t $" + System . out.println( soap.getOescr i pt ion()
milk_pri ce yogurt_price bread_price soap_price
); ); ); );
II calcu l a e imprime total double total· milk_price + yogurt_price + bread_price + soap_price. System . out.p r intln( "Total Price \t $" + total ); }
Esse método mostra como poderia ser um pequeno programa que usa objetos de Item. Primeiro, o programa instancia quatro objetos de Item. Em um programa real, ele poderia criar esses itens quando um usuário navegasse em um catálogo on-li ne ou quando um caixa percorre os amlazéns. Esse programa cria vários itens, aplica descontos e, em segu ida, imprime um recibo. O programa rea liza toda a imeração do objeto enviando várias mensagens para os itens. Por exemplo, o programa apl ica um desconto de 15% no leite (milk), enviando a mensagem setDiscount () para o item. O programa total iza os itens primeiro enviando a cada item a mensagem getAdjustedTotal O. Finalmente, esse programa envia um recibo para a tela. A Figura 1.1 ilustra o exemplo de saida. FIGURA 1.1
Imprimindo I/m recibo.
É im portante perceber que toda a programação foi feita em termos de objetos de Item e os comportamentos expostos pelos métodos de I tem - os substantivos e verbos do domínio do carri nho de compras.
Introdução à programação o rientada a objetos
13
Relacionamentos de objeto
o modo como os objetos se relacionam é um com ponente muito importante da POO. Os objetos podem se relacionar de duas maneiras im portantes. Primeiro, os objetos podem existi r independentemente uns dos outros. Dois objetos de Item p0dem aparecer no carrinho de compras si multaneamente. Se esses dois objetos separados prec isarem interagi r, eles interagirão passando mensagens um para o outro. Novo TERMO Os objetos se comunicam uns com os outros através de mensagens. As mensagens fazem com que um objeto realize algo. ' Passar uma mensagem' é o meSmo que chamar um mélodo para mudar o estado do objeto ou para exercer um com portamento. Segundo, um objeto poderia conter outros objetos. Assim, como os objetos compõem um programa em POO, eles podem compor outros objetos através da agregação. A partir do exemplo Item, você poderia notar que o objeto ite m contém muitos outros objetos. Por exemplo, o objeto item também contém uma descrição e uma ido A descrição e a id são ambas objelos de Stri ng. Cada um desses objetos tem uma interface que oferece métodos e at ributos. Lembre-se de que, na POO, tudo é um objeto, mesmo as partes que compõem um objclo! A comunicação funciona da mesma maneira entre um objetoe os objetos que ele contém. Quando os objetos precisarem interagir, eles fa rão isso enviando mensagens uns para os outros. As mensagens são um importante conceito de 00. Elas pennilem que os objetos permaneçam independentes. Quando um objeto envia uma mensagem para OUITO, geralmente ele não se preocupa com a maneira como o objetoescol he transportar o comportamento solici tado. objeto solic itante se preocupa apenas que o comportamento aconteça.
°
Você vai aprender mais a respeito de como os objetos se relacionam, na próxima semana.
A definição de objeto está aberta ao debate. Algumas pessoas não defi nem um objeto como uma instância de uma classe. Em vez disso, elas definem tudo em termos de ser um objet o: a partir desse ponto de vista, uma classe é um objeto que cri a outros objetos. Tratar uma classe como um objeto é importante para conceitos como metaclasse. Quando este livro encontrar uma discordância na terminologia, escolheremos u ma definição e ficaremos com ela. Muito freqüentemente, a escolha será pragmática. Aqui, optamos por usar a definiçâo de objeto como instância. Essa é a definição UML (Unified Modeling Language) e a mais encontrada no set or. (Você vai aprender mais a respeito de UMl posteriorm ente.) Infelizm ente, a outra é uma definição orientada a objetos mais pura. Entretanto, você não a encontrará muit o freqüentemente e o conceito de m etaclasse está fora dos objetivos deste livro.
Dia 1
Como a programação orientada a objetos fundamenta o passado Assi m como outros paradigmas lentam acentuar as vantagens e corrigir as ralhas dos paradigmas anteriores, a POO rundamenta a programação proced ural e mod ular. A programação modular est rutura um programa em vários módulos. Do mesmo modo, a POO d ivide um programa em vários objetos interativos. Assim como os mód ulos ocultam representações de dados at rás de procedi mentos, os objetos encapsulam seu estado por trás de suas interraces. A POO empresta esse concei to de encapsulamento di retamente da programação modular. O encapsul amento direre muit o da programação procedura l. A prog ramação procedu ral não encapsul a dados. Em vez d isso, os dados são abertos para todos os procedi mentos acessarem. Ao contrário da program ação procedural , a programação orientada a obj etos acopla rortemente dados e com portamentos ao objeto. Você va i aprender mais a respei to do encapsulam ento nos dias2e3. Em bora os objetos sejam conceitua lmente semelhantes aos módulos, eles di rerem de várias maneiras importantes. Primeiro, os módulos não suportam extensão prontamente. A programação orientada a objetos introduz o conceito de herança para elimi nar essa deficiência. A herança permite que você estenda e mel hore suas classes raci lmenle. A herança também permite que você classi fi que suas classes. Você vai aprender mais a respeito da herança no Dia 4, " Herança: obtendo algo para nada" e no Dia 5, " Herança: hora de escrever algum cód igo" . A POO também acentua o concei to de polimorfi smo, q ue aj uda a construir programas fl exíveis, que não resistem à mudança. O poli morfi smo acrescenla essa fl exibilidade limpando o sistema de tipagem li mitado do módu lo. Você vai aprender mais a respeito do poli morfi smo no Dia 6, " Polimorfismo: aprendendo a prever o ruturo" e no Dia 7, " Poli morfismo: hora de escrever algum código". A POO certamente não invento u o encapsulamento e o polimorfi smo. Em vez disso, a POO combina esses conceitos em um só lugar. Peg ue a noção da POO de objetos e você reun irá essas tecno logias de uma maneira jamais reita.
Vantagens e objetivos da 00 A programação orientada a objetos define se is objeti vos sobrepostos para desenvolvimento de software. A POO se esmera em produzir software que tenha as seguintes características: 1. Natura l
2. Confiáve l 3. Reut ilizável 4. Manuteníve l
Introdução à programação orientada a objet os
15
5. Extensível 6. Oportunos Vamos ver como ela funciona para atender cada um desses obj etivos.
Natural A POO produz software natural. Os programas naturais são mais intel igíveis. Em vez de progra· mar em termos de regiões de memória, você pode programar usando a terminolog ia de seu pro-blema em particu lar. Você não precisa se aprofundar nos detalhes do computador enquanto projeta seu programa. Em vez de ajustar seus programas para a linguagem do mundo dos computadores, a 00 o libera para que expresse seu programa nos termos de se u problema. A programação orientada a objetos pennite que você modele um prob lema em um nível funci onal e não em nível de implementação. Você não precisa saber como um so ftware func iona, para usá-lo: você simplesmente se concentra no que ele faz.
Confiável Para criar soft ware úti I, você precisa criar software que seja tão confiável quanto outros produtos, como geladeiras e televi sões. Quando fo i a última vez que seu microondas quebrou? Programas orientados a objetos, bem projetados e cuidadosamente escritos são confiáveis. A nat ureza modular dos objetos penn ile que você faça alterações em uma parte de seu programa, sem a fetaroutras partes. Os objetos isolam oconhec imenlo ea responsabilidade de onde pertencem. Uma maneira de aumentar a confiabilidade é através de testes completos. A 00 aprimora os testes, permitindo que você isole conheci mento e responsabilidade em um único lugar. Tal isolamento pennite que você teste e val ide cada componente independentemente. Uma vez que tenha va lidado um componente, você pode reutilizá-lo com confiança.
Reutilizável Um construtor inventa um novo tipo de tijo lo cada vez que constrói um a casa? Um engenheiro eletricista inventa um novo tipo de resistor cada vez que projeta um circuito? Então, por que os programadores continuam ' reinventando a roda?' Uma vez que um problema esteja resolvido, você deve reutili zar a solução. Você pode reutili zar prontamente classes orientadas a objetos bem feitas. Assim como os módulos, você pode reutil izar objetos em muitos programas diferentes. Ao contrário dos módu los, a POO introduz a herança para penni tir que você estenda objetos ex istentes e o polimorfi smo, para que você possa escrever código genérico. A 00 não garante código genérico. C riar classes bem feitas é urna tarefa difici l que exige concentração e atenção à abstração. Os programadores nem sempre acham isso fácil.
Dia 1
Através da POO, você pode modelar idéias gerais e usar essas idéias gerais para reso lver problemas específicos. Embora você vá construi r objelos para resolver um problema específico, freq üentemente construi rá esses objetos específicos usando partes genéricas.
Manutenível
o ciclo de vida de um programa não tennina quando você o di stri bui. Em vez disso, você deve manter sua base de código. Na verdade, entre 60% e 80% do tempo gasto trabalhando em um programa é para manutenção. O desenvolvi mento representa apenas 20% da eq uação! Um código orientado a objetos bem projetado é manutenível. Para corrigir um erro, você simpl esmente corrige o problema em um lugar. Corno uma mudança na implem entação é transparente, todos os outros objetos se benefici arão automaticamente do aprimoramento. A ling uagem natural do cód igo deve permitir que outros desenvolvedores também o entendam.
Extensível Ass im como você deve manter um programa, seus usuários exigem o acréscímo de nova fu ncionalidade em seu sistema. Quando você construir uma biblioteca de objetos, também desejará estender a func ional idade de seus próprios objetos. A POO trata dessas real idades. O soft ware não é estático. Ele deve crescer e mudar com o passar do tempo, para permanecer útil. A POO apresenta ao programador vários recursos para estender cód igo. Esses recursos incluem herança, polimorfismo, sobreposição, delegação e uma variedade de padrões de projeto.
Oportuno O ciclo de vida do projeto de software moderno é freqüentemente medido em semanas. A POO ajuda nesses rápidos c iclos de desenvolvimento. A POO dim inui o tempo do ciclo de desenvo lv imento, fornecendo software confiável, reutilizável e facilmente extensíve l. O software nat ura l simplifica o projeto de sistemas complexos. Embora você não possa ignorara projeto cuidadoso, o software natural pode otim izar os ciclos de projeto, po is você pode se concentrar no problema que está tentando resolvcr. Quando você divide um programa em vários objetos, o desenvolvi mento de cada parte pode ocorrer em paralelo. Vários desenvolvedores podem trabalhar nas classes independentemente. Tal desenvolvimento em para lelo leva a tempos de desenvolvimento menores.
Armadilhas Quando você aprende 00 pela primeira vez, existem quatro annadilhas que precisa evitar.
Introdução à programação orientada a objetos
17
Armadilha 1: pensar na POO simplesmente como uma linguagem Freqüentemente, as pessoas equ iparam linguagens orientadas a objetoscom a poo. O erro surge ao supor que você está programando de maneira orientada a objetos si mplesmente porque usa uma linguagem orientada a objetos. Nada poderia estar mais distante da realidade. A POO é muito mais do que simp lesmente usar uma linguagem orientada a objetos ou conhecer ceno conjunto de definiçõcs. Você pode escrever código horrivelm ente não orientado a obj etos em uma linguagem orientada a objetos. A verdadeira POO é um estado da mente que exige que você veja seus proble mas como um grupo de objetos e use encapsu lamento, herança e polimorfismo corretam ente. Infeli zmente, muitas empresas e programadores supõem que, se sim plesmente usa rem Lima linguagem orientada a objetos, se beneficiarão de todas as vantagens que a POO oferece . Quando fa lham, elas tentam culpar a tecnologia e não o fato de que não trei naram seus funcionários corretamente, ou que agarraram um conceito de programação popular sem entender realmente o que ele sign ificava.
Armadilha 2: medo da reutilização Você deve aprender a reutilizar cód igo. Aprender a reutilizar sem culpa freq Uentemerlle é uma das lições mais dificeis de aprender, quando você escolhe a POO pela primeira vez. Três problemas levam a essa dificu ldade. Primeiro, os programadores gostam de criar. Se você ol har a reutilização de modo errado, ela parecerá afastar algumas das alegrias da criação. Entretanto, você precisa lembrar que está reutilizando panes para criar algo maior. Pode não parecer interessante reutilizar um componente, mas isso permitirá que você construa algo ainda melhor. Segundo, muitos programadores sofrem do sentimento de 'não escrito aq ui'- signi fi cando que eles não confiam no softw are que não escreveram. Se um software é bem testado e atende sua necessidade, você deve reuli lizá-Io. Não rejeite um componente porque você não o escreveu. Lembre-se de que reutilizar um componente o liberará para escrever outro software maravilhoso.
Armadilha 3: pensar na 00 como uma solução para tudo Embora a POO ofereça muitas vantagens, ela não é a solução para tudo no mundo da programação. Existem ocasiões em que você não deve usar 00. Você ainda precisa usar bom senso na escolha da fe rramenta correta para o trabalho a ser feito. Mais importante, a POO nào garante o sucesso de seu projeto. Seu projeto não terá sucesso automaticamente, apenas porque você usa uma li nguagem 00. O sucesso aparece somente com planejamento, projeto e cod ificação cuidadosos.
Dia 1
Armadilha 4: programação egoísta Não seja egoísta quando programar. Assim como você deve aprender a reutil izar, também deve aprender a com partil har o cód igo que cria. Compart ilhar significa que você encorajará outros desenvo lvedores a usarem suas classes. Entretanto, compartilhar também signifi ca que você tomará fác il para outros reutili zarem essas classes. Lembre-se dos outros desenvolvedores quando você programar. Faça interfaces limpas e inteligiveis. Mais importante, escreva a documentação. Documente suposições, parâmetros de métodos, documente o máximo que você puder. As pessoas não reuti lizarão o que não podem encontrar o u entender.
A próxima semana Na próx ima semana, você continuará s ua introdução a roo, aprendendo a respeito dos três pi lares que fonnam a base da tcoria da POO: encapSlllamenlo, herança e polimorfismo. Cada pilar será dividido em duas lições. A primeira lição apresentará o pi lar e a teoria subjacente. A segunda lição fornecerá experiência prática com os conceitos apresentados no dia anterior. Essa estratégia espelha a estratégia de preleção/laboratá rio, usada com sucesso por muitas universidades e escolas. Você com pletará todos esses laboratórios usando a linguagem de programação Java da Sun Mi · crosystem. Você pode obter gratuitamente todas as ferramen tas usadas neste livro, através da World Wide Web. O Dia 3, assi m como o Apênd ice B, " Resumo do Java", no fi nal do livro, o cond uzirão na obtenção e config uração de seu ambiente de desenvolvi mento.
NOTA
Por que Java 7 Existem dois m otivos para se usar Java como fer ramenta de ensino. Primei ro, o Java o abstrai perfe itamente dos det alh es da m áq uin a e do sist em a opera cional. Em vez de ter de se preocupar com alocação e desa locação de memória, você pode simp lesmente se concentrar no aprendizado dos objetos. Finalm ente, aprender boas práticas orientadas a o bjetos em Java é prático. Você pode pegar o conhecimento e fazer um trabalho. Al gumas li nguagens silo mais orientadas a objetos do que o Java. Entretanto, é f ácil faze r o Java fun cionar.
Resumo Hoje, você fez um passeio pela programação orientada a objetos. Você começou vendo a evolução dos principa is paradigmas de programação e aprendeu alguns dos fundamentos da roo. Agora, você deve entender as idéias conceitua is por trás da 00, como o que é uma classe e como os objetos se comunicam.
Introdução à programação orientada a objet os
19
Definições são importantes, mas nunca devemos perder o rumo do que estamos tentando fazer usando 00, nos atendo ao 'como' do que est ivennos fazendo. As seis vanlagens e objetivos resumem o que a programação orientada a objetos espera cumprir: 1. Natural
2. Confi ável 3. Reuti li zável
4. Manuten ível 5. Extensível 6. Oportuna
Você nunca deve perder esses objetivos de vista.
Perguntas e respostas P o q ue posso fer para dominar a POO? R Li vros como este são um a boa maneira de começar em sua jornada para dominar a 00. É importante construir uma base sólida; uma que você possa desenvolver. Uma vez que tenha uma base, você precisa começar a praticar 00 ativamente. O verdadeirodomínio só vem com a experiência. Comece como um desenvolvedorem um projeto 00. Conheça profundamente o assunto. Quando você se tornar mais fam il iarizado com a 00, comece a se envolver na análise e no projeto de seus trabalhos. Também aj uda encontrar um mentor. Encontre alguém que esteja desejoso de passar al gum tempo com partilhando sabedori a. Instruir-se com os outros é a maneira me lhor e mais rápida de aprender POO. Finalmente, continue seu estudo pessoa l. Leia livros, artigos, participe de conferências. Você sempre pode absorver mais informações.
Workshop As perguntas e respostas do teste são fornecidas para seu melhor entend imento. Veja as respostas no Apêndice A, "Respostas".
Teste I. o que é programação procedural? 2. Qual vantagem a programação procedural tem cm relação à programação não estruturada? 3. O que é programação modu lar?
Dia 1
4. Quais vantagens a programação modul ar tem em relação à programação procedural?
S. Liste uma deficiência da programação procedural e da programação modular. 6. O que é programação orientada a objetos? 7. Quais são as seis vantagens e objetivos da programação orientada a objetos? 8. Ex pliq ue um dos obj etivos da programação orientada a objetos. 9. Defina os seguintes lermos: Classe Objeto Comportamento 10. Como os obj etos se comunicam ent re si? I I. O que é um construtor? 12. O que é um acessor? 13. O que é um mutante? 14. O que é thl S?
Exercícios Alegre-se! Para hoj e, você não tem exercícios escritos. Em vez disso, dê um passeio.
SEMANA
1
DIA
Encapsulamento: aprenda a manter os detalhes consigo mesmo Esperamos que o Dia I, " Introd ução ã programação orientada a objetos", tenha aguçado seu interesse e que você provavelmente tenha mui tas perguntas. Conforme você pode imag inar, existe muito mais na programação orientada a objetos do que algumas de fi nições simples. Ao adotar uma estratégia de 00 para desenvolvimento de software , você não pode sim plesmente sair programando. Em vez di sso, você deve ler um planejamento cuidadoso e lima boa base nas importantes teorias ex istentes por trás da POO . Infelizmente, não há uma maneira práti ca de se tornar
um especialista em POO cm poucos anos, imagine em 21 dias! Em vez di sso, você precisa voltar e perguntar, "o que estou lentando fa zer?" Você está tentando se tornar um especia lista teórico ou um profissional prático? Você sabe, é preciso ser um pouco mai s prático se quiser aprender 00 suficientemente para faze r seu trabalho. Felizmente, você não precisa de um doutorado para entendere apl icar 00 em seus projetas de soft ware eficientemente. O que você precisa é de uma mente aberta e o desejo de aprender - ou desaprender, em muitos casos. Hoje e no restante da semana, você dará uma o lhada prática nas teorias subjacentes a POO: as ferra mentas de 00. Essas teorias devem lhe dar uma base suficiente para começar a experimen· tar a POO. Entretanto, o domínio não virá rapidamente. Assim como qualquer outra habilidade, seus conheci mentos de POO me lhorarão e aumentarão apenas com estudo e prática.
Dia 2
Hoje você vai aprender: Sobre os três pilares da programação orientada a objetos • Como apl icar encapsulamento efi cazmente • Como programar de forma abstrata • Como os ti(X)s abstratos de dados formam a base do encapsulamento • A diferença entre interface e implementação • Sobre a importância da responsabi lidade • Como o encapsulamento ati nge os objetivos da 00 •
Os três pilares da programação orientada a objetos Para edi fi car seu entendimento c domínio de 00, você deve primeiro ter uma base sólida a partir da qual possa expandi r sua com preensão. Primeiro, você precisará identi ficar, definir e explorar os conceitos básicos da 00. Somente quando você tiver uma boa base das teorias básicas de 00 é que poderá aplicá-la corretamente no software que escrever. Tal discussão o leva naturalmente aos três conceitos que devem estar presentes para que uma I inguagem seja considerada realmente orientada a objetos. Esses três conceitos são freqüe ntcmente referidos como os Irês pilares da programação orientada a objetos. Novo
TERMO
Os Irês pi/ares da programação orienlada a objetos si'lo: eI1CapSIlItl/lIeIllO, herança e poIiIllOlfi:m/O.
Como a 1>00 é baseada neles, os três pilares são semelhantes a uma torre de bl ocos: remova o bloco inferior e tudo mais virá abaixo. O encapsulamento, que você abordará hoje, é uma peça extremamente importante do quebra-cabeça, pois ele forma a base da herança e do polimorfismo.
Encapsulamento: o primeiro pilar Em vez de ver um programa como um a única entidade grande e monolít ica, o encapsul amento permite que você o divida em várias partes menores e independentes. Cada parte possui im plemetllaçi'lo e realiza seu trabal ho independentemente das outras partes. O encapsul amento mantém essa independência, ocultando os detalhes internos ou seja, a im plementação de cada parte, através de uma interface externa. Encapsulamento é a característica da 00 de ocultar partes independentes da implementação. O encapsulamento penn ite que você construa partes ocultas da implementação do soft ware, que at injam uma funcionalidade e ocultam os detalhes de im plementação do mundo exterior.
Novo
TERMO
Encapsu lamento: aprenda a mante r os det alhes consigo mesmo
NOTA
23
Se você não estiver famil iarizado com o termo encapsulamento, ta lvez reconheça os termos módulo, componente ou bean. Você pode usar esses termos em vez de 'softwa re encapsulado'.
Uma vez encapsu lado, você pode ver uma entidade d e software como lima caixa preta. Você sabe o que a caixa preta fa z, po is conhece sua interface externa. Conforme a Figura 2.1 ilustra, você si mplesmente envia mensagens para a caixa preta. Você não se preocupa com o que acontece dentro da caixa; você s6 se preocupa com o fa to d e que isso aconteça.
FIGUR A 2 .1
Uma caixa preta.
INTERFACE
Men,egem
Mensage m
INTERFACE
Uma il/lelje/ce lista os serviços forn ecidos por um componente . A interface é um contrato com O mundo exterior, que define exatamente o que uma entidade externa pode fazer com o objeto. Uma interface é o painel de controle do objcto.
NovO TERMO
Uma interface é importante, pois ela d iz o que você pode fazer com o componente. O mais interesse é o q ue uma interface nAo informa: como o componente fará seu trabalho. Em vez disso, a interface oculta a implementação do mundo exterior. Isso li bera o componente para alterações na sua implementação a qualquer momento. As mudanças na implementação não mudam o código que usa a classe, desde q ue a interface permaneça inalte rada. As alterações na interface necessitarão de mudanças no código que exerce essa interface.
Dia 2
Talvez você esteja familiari zado com o t ermo d e programação APl (Int erfa ce d e Programa Aplica ti vo). Uma interface é semel hante a APl para um objeto. A interface lista t odos os métodos e argumentos que o objeto entende.
Novo
TERMO
A implementação define como um componente realmente fomece um serviço. A im-
plementação defin e os detalhes internos do componente.
Um exemplo de interface e implementação Consi dere a classe Log a seguir: pub li c class Log { public void debug( String message ) { pri nt ( "DEBUG ". message ) i }
publiC void info( String message ) { print( "IN FO ", message }i }
public void warn ing( Stri ng message ) { pr i nt( "WARN ING ", message )i }
public void er ror( Str ing message } I print{ "ERROR ", message }i }
pub lic void fatal( Stri ng message ) I print( "FATAL ", message ) ; System .exit( O ) i }
private void print ( String message , Str ing severity ) { Sys tem.ou t. println{ severity + ": " + message )j }
}
A classe Log contém objctos para relatar mensagens de depuração, infonnativas, de aleT1a e de erro, durante a execução. A interface da classe Log é constituída de todos os compoT1amentos di spon íveis para o mundo exterior. Os eomponamentos disponíveis para o mundo exterior são conhecidos como interface pública. A interface pública da classe l og inclui os seguintes métodos:
public public publ i c public public
void void void void void
debug( St ring message ) i nfo( String message ) warn ing( String message ) er ror ( String message ) fatal ( String message )
Enca p sulamento: aprenda a manter os deta lhes con sigo m esm o
25
Tudo mais na de fin ição da classe, além dessas ci nco declarações de método, é implementação. Lembre-se de que a implementação define como algo é fei to. Aqui, o 'como' é o fa to de que log imprime na tela. Entretanto, a interface oculta completamente o 'como'. Em vez disso, a interface define um contrato com o mundo exterior. Por exemplo, publ'i c vo'id debug ( String message ) é uma maneira de dizer ao mundo exterior que, se você passar uma Stri ng, ela reportará uma mensagem de depuração. O que é importante notar é o que a interface não diz. debug O não diz que impri mi rá na tela. Em vez disso, o que é feito com a mensagem é deixado para a implementação. A implementação p0deria escrever na tela, descarregar em um arquivo, gravar em um banco de dados ou enviar uma mensagem para um cliente monitorado pela rede.
Público, privado e protegido Você pode ter notado que a interface pública não incl ui private void print( Stri ng message , String severity ). Em vez disso, a classe log restringe o acesso a pri nt O . O que aparece e o que não aparece na interfa ce pública é governado por diversas palavras-chave. Cada li nguagem 00 define seu próprio conjunto de palavras-chave, mas fundamen talmente essas palavras-chave acabam tendo efeitos semelhantes. A maioria das linguagens 00 suporta três níveis de acesso:
• Público - Garante o acesso a todos os objetos. • Protegido - Garante o acesso à instância, ou seja, para aquele objeto, e para todas as subclasses(mai s informaçõcssobre subclasses no Dia4, "Herança: obtendo algo para nada"). • Pri vado - Garante o acesso apenas para a instância, ou seja, para aquele objeto. O níve l de acesso que você escolhe é muito im portante para seu projeto. Todo comportamento que você queira tornar visível para o mundo, precisa ter acesso público. Tudo que você quiser ocultar do mundo exterior precisa ter acesso protegido ou privado.
Por que você deve encapsular? Quando usado cuidadosamente, o encapsulamento transforma seus objetos em componentes plugáveis. Para que outro objeto use seu componente, ele só precisa saber como lIsar a interface públ ica do componente. Tal independência tem três vantagens importantes: •
Independência sign ifica que você pode reutilizar o objeto em qualquer parte. Quando você encapsular corretamente seus objetos, eles não estarão vinculados a nenhum programa em particular. Em vez disso, você pode usá-los sempre que seu uso fize r sentido. Para usar o objeto em qualquer lugar, você simplesmente exerce sua interface. • O encapsulamento penn ite que você tome transparentes as alterações em seu objeto. Desde que você não altere sua interface, todas as alterações pennanecerão transparentes para
Dia 2
aq ueles que estiverem usando o objeto. O encapsulamento penn ite que você atualize seu componente, forneça uma implementação mais eficiente ou corrija erros - tudo isso sem ter de tocar nos outros objetos de seu programa. Os usuários de seu objeto se benefi c iarão automaticamente de todas as alterações que você fi zer. •
Usar um objeto encapsu lado não causara efeitos colaterais inesperados entre o objeto e o restante do programa . Como o objeto tem implementação independente, ele não tera nenhuma oll1 ra interação com o restante do programa. além de sua interface.
Agora, você está em um ponto onde podemos falar sobre algumas generalidades a respeito do encaps ulamento. Você viu que o encapsulamento pennite escrever componentes de so ftware com implementações independentes. As três características do encapsulamento eficaz são: •
Abstração
•
Ocultação da implementação
•
Divi são de responsabilidade
Vam os ver mais profundamente cada característica, para aprender a melhor maneira de obter o encapsulamento.
Abstração: aprendendo a pensar e programar de forma abstrata ,
Embora as linguagens 00 estimulem o encapsulamento, elas não o garantem. E fáci l construir cód igo dependente e frágil. O encapsu lamento efi caz vem apenas com um projeto cuidadoso. abslração e experiência. Um dos primeiros passos para o encapsulamento eficaz é aprender como abstrair software e os conceitos s ubjacentes eficientemente.
o que é abstração7 Abstração é o processo de simplificar um probl ema difici!. Quando começa a resolver um problema, você não se preocupa com cada detalhe. Em vez disso, você o simplifica, tratando apenas dos detalhes pertinentes a uma solução. ,
Imagine que você tenha de escrever um simu lador de fluxo de tráfego. E poss ível que você modele classes para sinais de trânsito, veículos, cond ições da pista, auto-estradas, ruas de mão dupla, ruas de mão única, condições climáticas etc. Cada um desses elementos afetaria o fluxo do tráfego. Entretanto, você não modelaria insetos e pássaros no sistema, mesmo que eles possam aparecer em uma via real. Além disso, você omitiria tipos específicos de carros. Você simplifica o mundo real e inclui apenas as partes que realmente afetam a sim ulação. Um carro é muito importante para a simulação, mas o fato de ser um Cadillac ou fazer com que o carro controle seu nível de combustível é supérfl uo para a simulação de tráfego. A abstração tem duas vantagens. Primeiro, ela permite que você resolva um problema faci lmente. Mais importante, a abstração o ajuda a obter reuti lização. Muitas vezes, os componentes de
Encapsulamento : aprenda a mante r os det alhes consigo m esm o
27
software são demasiadamente especializados. Essa especial ização, combinada com uma interdependência desnecessária entre os componentes, torna diflci l reutilizar um código existente em outra parte. Quando possivel, você deve se esrorçar por criar objetos que possam resolver um domínio inteiro de problemas. A abstração penni te que você resolva um problema uma vez e depois use essa solução por todo o domínio desse problema.
NOTA
Embora seja desejável escrever código abstrat o e evitar uma especialização dem asiada, é duro escrever código abstrato, especial mente quando você começa a praticar a POO. Existe um a li nha tênue entre muita e pouca especialização. A linh a pOde ser discernida apenas com a experiência. Entretanto, você p recisa saber desse poderoso co nce ito.
Dois exemplos de abstração Considere dois exemplos. Primeiro, imagine pessoas em fil a em um banco, esperando por um caixa. Assim que um caixa se toma disponível, a pri mei ra pessoa da fil a avança para a janela aberta. As pessoas sempre deixam a fil a na ordem de que o pri meiro a entrar é o pri meiro a sai r (FI FO): essa ordem é sempre mantida. FIG URA 2 .2
Uma fila em
Banco de 00
11111 IXII/co.
ENTRADA
IL _
_
Clie nte 1
Ce ixe 1
Cliente 2
C.iu 2
A _• ~ I ~
Cliente 5
3
SAlDA
• C. ix.3
Dia 2
Segundo, considere um estabelecimento de sanduíches do tipofaslfood. Quando um novo sanduíche fi ca pronto, ele é colocado atrás do últi mo sanduíche que está no escaninho; veja a Figura 2.3. Desse modo, o primeiro sanduíche ret irado também é o mais antigo. FIFO é o esquema do restaurante.
FIGURA 2 .3 Sandlliches fic(lndo P"OIlfO.f.
Embora cada um desses exemplos seja específico, você pode encontrar uma descrição genérica que funcione em cada situação. Em outras palavras, você pode chegar a uma abstração. Cada domínio é um exemplo de fila do tipo primeiro a entrar, pri meiro a sair. Não importa quais tipos de elementos apareçam na fil a. O que importa é que os elementos entram no fina l da fil a e saem dela a pan ir da fre nte, confonne ilustrado na Figura 2.4.
FIGURA 2 .4 UII/tI abSfra(((io de
ambos os domínios.
ENTRADA
• ••••
Abstraindo os domínios, você pode criar uma fila lima vez e reutilizá- Ia em qualquer problema que modele um dom íni o onde exista uma ordenação FIFO de elementos.
Abstração eficaz Neste ponto, você pode formular alg umas regras para a ahstraçào eficaz: •
Trate do caso geral e nào do caso especifico.
•
Ao confrontar vários problemas diferentes, procure o q ue for comum a todos. Tente ver um conceito e não um caso específico.
• Não se esqueça de que você tem um problema a resolver. A abstração é valiosa, mas não descuide do problema na esperança de escrever cód igo abstrato. •
A abstração pode não estar prontamente aparente. A abstração pode não saltar à sua frente na pri meira, segunda ou terceira vez que você resolver um problema que está s ujeito a ser abstraído.
Enca psulamento: aprenda a manter os deta lhes consigo mesmo
•
29
Prepare-se para a fa lha. É quase impossíve l escrever uma abstração que funcione em todas as situações. Você verá por que, posterionnente ainda hoje.
AL ERTA
NeTA
Não caia na paralisia da abstração. Resolva os problemas que você encontrar primeiro. Veja a abstração com o um bónus e não com o o objetivo final. Caso contrário, você vai se deparar com a possibilidade de prazos fina is perdidos e abstração incorreta . Existem ocasiões para abstrair e ocasiões em que a abstração não é apropriada. Uma boa regra ger~1 é abstrair algo que você tiver implementado três vezes de m aneira análoga. A medida que você ganhar experiência, aprenderá a escolher a abstração mais rapidamente.
Nem sempre você pode reconhecer oportunidades para uma abstraçâo. Talvez você tenha de resolver um problema várias vezes, antes que uma abstração se torne aparente. Às vezes, diferentes situações ajudam a encontrar uma abstração efi caz e, mesmo então, a abstração pode precisar de alguma conversão. A abstração pode demorar a amadurecer.
A abstração pode tomar um componente encapsulado mais reutili zável, pois ele está personalizado para um dominío de problemas e não para um uso específi co. Entretanto, há ma is coisas importantes quanto ao encapsulamento do que a simples reutilização de componentes. O encapsulamento também é importante porocul tar os detalhes internos. O tipo abstrato de dados é um bom lugar para ver em seguida, na busca do encapsulamento eficaz.
Guardando seus segredos através da ocultação da implementação A abstração é apenas uma característ ica do encapsulamento eficaz. Você pode escreve r código abstrato que não é encapsu lado. Em vez di sso, você também precisa ocultar as implementações internas de seus objetos. A ocultação da impl ementação tem duas vantagens: •
Ela protege seu objelo de seus usuários.
•
Ela protege os usuários de seu objeto do próprio objeto .
Vamos explorar a primeira vantagem -
proteção do objeto.
Dia 2
Protegendo seu objeto através do T AD (Abstract Data Type - Tipo Abstrato de Dados)
o
Tipo Abstrato de Dados (TAD) não é um conceito novo. Os TADs,junto com a própria 00, cresceu a partir da li nguagem de programação Si mula, introduzida em 1966. Na verdade, os TADs são decididamente não 00; em vez disso, eles são um subconj unto da 00. Entretanto, os TADs apresentam duas característ icas interessantes: ahstração e tipo. É essa idéia de tipo que é importante, pois sem ela, você não pode ter um verdadeiro encapsulamento.
NOTA
o verdadeiro encapsulamento é imposto em nivel de lin guagem, através de construções internas da linguagem. Qualquer outra fo rma de encapsulamento é simplesmente um acordo de cavalheiros, que é fa cilmente malogrado. Os programadores o contornarão porque podem fazer isso!
Um TAD é um conj unto de dados e um conjunto de operações sobre esses dados. Os TAOs pernlitem que você defina novos tipos na linguagem , ocultando dados internos e o estado, atrás de uma interface bem defin ida. Essa interface apresenta o TAO como uma unica unidade atómica.
Novo TERMO
Os TA Os são uma maneira excelente de introduzir encapsulamento, pois eles o li beram de considerar o encapsulamento sem a bagagem extra da herança e do polimorfismo: você pode se concentrar no encapsulamento. Os TAOs também pennitem que você explore a noção de tipo. Uma vez que o tipo seja entendido, é fácil ver que a 00 oferece uma maneira natura l de estendcr uma linguagem, defin indo tipos personalizados do usuário.
o que é um tipo? Quando programar, você criará diversas vari áveis e atribuirá valores para elas. Os ti pos de fi nem as di ferentes espécies de va lores que estão disponíveis para seus programas. Você usa tipos para construir se u programa. Exemplos de alguns tipos comuns são inlegers (inteiros), longs (inteiros longos) e fl oats (rea is). Essas defin ições de ti po informam exatamente quai s espécies de tipos estão disponívei s, o que os ti pos fa zem e o que você pode fazer com eles. Usaremos a seguinte definição de tipo: Os tipos definem as diferen tes espécies de valores que você pode usar em seus programas. Um tipo define o domín io a panir do qual seus valores válidos podem ser extraídos. Para inteiros posit ivos, são os números sem panes fracionarias e que são maiores ou iguais a O. Para tipos estruturados, a definição é mais complexa. Além do dom inio, a definição de tipo inclu i quais operaçõcs são válidas no tipo e quais são seus resultados.
Novo TERMO
Enca psulamento: aprenda a manter os detalhes consigo mesmo
NOTA
31
a tratamento formal de tipo está fora dos objetivos de um livro sobre paa para iniciantes.
Os tipos são unidades atómicas da computação. Isso signifi ca que um tipo é uma unidade independente. Pegue o inteiro, por exemplo. Quando soma dois inteiros, você não pensa sobre a adi ção de bits individuais; você pensa apenas a respeito da adição de dois numeroso Mesmo que os bits representem o inteiro, a linguagem de programação apresenta o inteiro apenas como um númcro para O programador. Pegue o exemplo Item do Dia I. A criação da classe Itemadiciona um novo tipo em seu vocabulário de programação. Em vez de pensar a respeito de uma id , uma descrição e um preço de produto como entidades separadas, provavelmente regiões desconectadas da memória ou variáveis, você pensa simplesmente em termos de I tem. Assim, os tipos permitem representar estruturas complexas em um nível mais simp les e mai s conceituai. Eles o protegem dos detalhes desnecessários. Isso o libera para trabalhar no nível do problema, em vez de trabalhar no nível da implementação. Embora seja verdade quc um tipo protege o programador dos detalhes subjacentes, os tipos oferecem uma vantagem ainda mais importame. A definição de um tipo protege o tipo do programador. Uma defi nição de tipo garante que qualquer objeto que interaja com o tipo, o faça de maneira correta, consistente e segura. As restrições impostas por um tipo im pedem os objetos de terem uma interação inconsistente, possivelmente destrutiva. A deçlaração de um tipo impede que o tipo seja usado de maneira não projetada e arbitraria. Uma declaração de tipo garante o uso correio. Sem uma definição clara das operações pennitidas, um tipo poderia interagir com outro tipo da maneira que quisesse. FreqUentemente, tal interação indefi nida pode ser destrutiva. Pense novamente no Item do Dia I. Imagine que tivéssemos alterado um pouco a definição de I tem: public class Unencapsulatedltem {
II· ·· public double public double publ ic int public String pub l ic String
unft_pri ce ; discount ; !!lITI(I porcentagem de desconto a ser aplicada no preço quantity; description; id;
I Você notará que Iodas as variáveis internas agora estão publicamenlc di sponívci s. E se alguém escrevesse o programa a segui r, usando o novo Unencapsu l atedItem: publ i c static void main( String {] args ) { Unencapsulatedltem moni t or z new UnencapsulatedItem( ·e l ectronics~012· .
Dia 2 "17\" SVGA Monitor ", 1•
299 .00 ) ; monitor .discount · 1. 25 ;
II inválido, o desconto deve ser menor que 100%!
double pri ce • mon itor.getAdj ustedTot al(); System.out. pr i nt ln( "Incorrect Tota l: $" + prlce ) ; monitor.se tO lsco unt{ 1.25 );
II invál ido I I entretanto , o configurador capturarli o erro
pr i ce • monitor.get Ad justedTotal O; System . out .println{ "Correct Total:$" + price ) ; }
A Figura 2.5 mostra o que acontece quando você executa o método mainO. 2.5 Um 10/(// illwílido. FIGURA
Abrindo o tipo Unencapsu 1a ted I tempara acesso liberado, outros podem chegar e deixar uma instância de Une nca psu1ated I t emem um estado inválido. Nesse caso, ma i n () cria um Unencapsu 1atedltem e, em seguida, aplica diretamente um desconto inválido. O resultado é um preço negati vo ajustado! Os TADs são ferramentas de encapsul amento valiosas, pois eles permitem que você defina novos tipos da linguagem que são seguros de usar. Assim como novas palavras são acrescentadas no idioma inglês a cada ano, um TAD permite que você crie novas palavras de programação, onde você precisa expressar lima nova idéia. Uma vez defi nido, você pode usar um novo tipo como qualquer outro. Assim como você pode passar um inteiro para um método, também pode passar um TAO para um método. Isso é conhecido como sendo um objeto de primeira classe. Você pode passar objetos de primeira classe como parâmetros.
Enca psulamento: aprenda a manter os deta lhes consigo mesmo
33
Novo
TERMO
Um objelO de primeira c/o.çse é aquele que pode ser usado cxatamcnte da mesma ma· neira que um tipo interno.
Novo
TERMO
Um objelo de segllnda c/asse é um tipo deobjelO que você pode definir, mas não ne· ccssariarncnte usar, como faria com um tipo interno.
Um exemplo de TAO Vamos considerar o exemplo da fila abstrata apresentado anteriormente. Ao implementar uma fila, você di spõe de várias escolhas de implementação. Você pode implementar a fila como uma Iista encadeada, uma li sta duplamente encadeada, um vetor ou uma matriz. Entretanto, a imple· mentação subjacente não muda o comportam ento defin ido da fi la. Independentemente da impIe· mentaçào, os itens ainda entram e saem de acordo com a ordem FIFO. A fila é uma fo rte candidata para um TAO. Você já viu que não precisa conhecer a im plementa· ção subjacente para usar a fil a. Na verdade, você não quer ter de se preocupar com a impl emen· tação. Se você não transformar a fila em Ulll TAO, cada objeto que preci sa de uma fila precisará rei mplementar a estrutura de dados. Cada objeto que quiser manipular os dados na fi la precisará entender a implementação e entender como fazer para interagir com ela corretamente. Você já viu os perigos do uso não projetado! Em vez disso, você deve construi r a fila como um TAO. Um TAD de fi la bem encapsulado ga· rante acesso consistente e seguro aos dados. Ao sentar·se para projetar um TAO, você precisa perguntar·se o que o TAD faz. Neste caso, o que você pode fazer com uma fi la? Você pode: • Colocar elementos na fil a: enqueue • Remover elementos da fi la: dequeue • Consultar o estado da fi la • Ver o elemento da frente sem removê· lo: peek Cada um dos métodos se transfo rmará em uma entrada na interface públ ica de Queue. Você também preci sa nomeara TAO. Neste caso, o nome do TAO é Queue. O TAO é definido como segue: pub li c inte rface Queue { public vo i d enqueue( Object obj ); pub l i c Object dequeue(); pub l ic boolean i s Empty(); pub l ic Obj ect peek();
I Note que a interface da fila não diz nada a respeito de como a fila contém seus dados internos. Note também que a interface não fornece acesso li berado a qualquer dos dados internos. Todos os detalhes fi cam ocultos.
Dia 2
Em vez disso, agora você tem um novo tipo, uma fila. Agora, você pode usar esse tipo em qualq uer um de seus programas. Como se trata de um objeto de primeira classe, você pode usara fi la como parâmetro. Você pode tratar a abstração como uma unidade, pois todas as partes são independentes. Isso é poderoso; pois permite ao programador ser mai s expressivo. Em vez de pensar em termos de ponteiros e listas, o programador pode pensar em um nível muito mais alto: em termos do problema a ser resolvido. Quando o programador d iz fila, a palavra inclui todos os detalhes de uma lista e de UIll ponteiro, mas também permite que ele ignore esses deta lhes e pense em uma estrutura de dados FIFO de a lto nivel.
Conforme você verá. à medida que continua r. um tipo pOderia ser compos to de outros ti pos. através de restrições. Embora isso oculte os det athes. t ambém aumenta sua capacidade de se expre ssar. Os tipos que contêm outros tipos podem abranger muitos conceitos. Po r exemplo, quando você programa e diz i nt. o significado é muito simples; você simplesmente declarou um único inteiro. Entret anto, quando você diz Queue, sua declaração é muito mais expressiva. Hâ muit o mais ocorrendo dentro de Queue do que dentro de int.
Vamos considerar a interface mais um pouco. Note que essa interface é muito genérica. Em vez de dizer que essa é uma fil a de inteiros ou hamburgers, a interface simplesmente coloca e reti ra objetos da fi la. Em Java, você pode tratar todos os objetos como Objec t . Entretanto, cada linguagem fornece seu próprio mecanismo semel hante. Declarando os parâmetros desse modo, você pode colocar qualquer objeto que queira na fila. Assim, essa defi nição torna o tipo Queue Íltil em muitas sit uações diferentes.
ALERTA
As interfaces genéricas têm seus próprios perigos. Uma Queue de inteiros é muito exata. Você sabe que cada element o em Queue é um intei ro. Entret anto, uma Queue de ob jetos é fraca m ente tipada . Quando você extrai um elemento, pode não saber qual é seu tipo .
Para um encapsu lamento realmente efi caz, existem mai s algumas características que você prec isará tratar. Tocamos no aspecto da ocultação da implementação. Mas, e quanto ao outro lado da moeda - proteger os usuários de seus objetos?
Protegendo outros de seus segredos através da ocultação da implementação Até aqui, você vi u que uma interface pode ocultar a implementação subjacente de um obj eto. Quando oculta a implementação atrás de uma interface, você protege seu objeto de uso não pro-
Enca p sulamento: aprenda a manter os detalhes consigo mesmo
35
jetado ou destrutivo. Proteger seu objeto de uso não projetado é uma vantagem da ocultação da implementação. Entretanto, existe outro lado na história: os usuários de seus objetos. A ocullação da implementação leva a um projeto mais flex ível , pois ela impede que os usuários de seus objetos se tomem fortemen te acoplados à implementação subjacente dos objetos. Então, não apenas a ocultação da implementação protege seus objelos, como também protege aqueles que usam seus objetos, esti mulando um cód igo fracamente acoptado. Novo TERMO
o códigofracamente acoplado é independente da implementação de outros compo-
Novo TERMO
o código fortemel1te acoplado é fortemente vinculado à implementação de outros
nentes. componentes.
Você poderia estar se perguntando, "para que serve cód igo fracamente acoplado?" Quando um recurso aparece na interface pública de um objeto, todo mundo que usa o recurso se toma dependente do fato de ele existir. Se o recurso desaparecer repenti namente, você preci sará alterar o código quc tiver se desenvolvido de forma dependente a esse comportamento ou atri buto. Novo TERMO Código dependeme é dependente da existência de detcnninado tipo. O código dependente é inevitável. Entretanto, existem graus para a dependência aceitável e para a superdependência. Existem graus para a dependência. Você não pode eliminar a dependência totalmente. Entretanto, você deve se esforçar para min imizar a dependência entre objetos. Nonnahnente, você limita tal dependência programando uma interface bem defi nida. Os usuários s6 podem se tomar dependentes quanto ao que você decide colocar na interface. Entretanto, se alguma implementação do objeto se tornar partc da interface, os usuários do objelo poderão se tornar dependentes dessa implementação. Tal código fortemente acoplado el imina sua li berdade de alterar a implementa~ ção de seu objcto. Uma pequena alteração na implementação de seu objcto poderia necessitar de uma cascata de alterações por todos os usuários do objeto.
o encapsulamento e a ocultação da implem entação não são m ágica. Se você precisar alterar uma interface, precisará atualizar o código que é dependente da interface antiga. Ocult ando os deta lhes e escrevendo software para uma interface, você cria software que é fracamente acoplado.
o cód igo fonem ente acoplado anula o objetivo do encapsu lamento: criarobjelos independentes e reutilizáveis.
Dia 2
Um exemplo real de ocultação da implementação Um exemplo concreto de ocu ltação da implementação esclarecerá esta lição. Considere a seguinte defin ição de classe: publlc cl ass Customer { II ... vários mêtodos de cliente • • • publlc I tem (} items; Iles te array contêm t odos os itens selecionados }
Um Customer contém itens selecionados. Aqui, Customer torna o array I tem parte de sua interface externa: publ1c static void main( String [] args ) { Customer customer • new Customer {) ;
II . . . seleciona alguns itens .. . II preço dos itens double total · 0.0; for( lnt 1 • O; 1 < customer.items.length; i++) { Item i tem· customer.items[i] ; total · total + item.getAdjus tedTotal(); } }
Esse método main() pega um cliente, adiciona alguns itens e tota liza o pedido. Tudo funciona, mas o que acontece se você qu iser mudar a maneira corno um Customer contém itens? Suponha que você qu isesse introd uzi r uma classe Bas ket. Se você mudar a implementação, precisará atualizar todo o código que acessa diretamente o array Item. Sem ocultação da implementação, você perde sua liberdade de melhorar seus objetos. No exemplo Cus temer, você deve tomar o array Item privado. Forneça acesso aos itens através de acessores.
NeTA
A ocu ltaçào da implementação tem seus inconven ientes. Existem ocasiões em que você poderia precisar saber um pouco mais do que a interface pode informar. No mundo da programação, você d eseja rá uma caixa preta que funcione dentro de det erminada tolerãncia o u que use a quantidade certa de precisão. Você poderia saber que precisa de inteiros de 64 bits, pois está tratando com números m uito grandes. Ao definir sua interface, é importante não apenas fornecer uma interface, mas também documentar esses tipos de detalhes especificas sobre a implementação. Entretanto, assim como em qualquer o utra parte da interface pública, uma vez que você declara um comportamento, não pode aiterá·lo.
Encapsu lamento: aprenda a manter os deta lhes consigo mesmo
37
A ocuhação da implementação permite que você escreva cód igo q ue é independente e fracamente acoplado com outros componentes. O código fracamente acopladoé menos frág il e mais flexível para alterar. Um código flexive l facil ita a reuti lização e o aprimoramento, pois as alterações em uma parte do sistema não afetará outras partes não relacionadas.
DI CA
Co mo você obtém ocultação da implementação eficaz e cód igo fracamente acoplad07 Aqui estão algumas dicas: • Só permita acesso ao seu TAO através de uma interface baseada em método. Tal interface garante que você não exponha informações sobre a implementação. o
o
o
Não forneça acesso involuntário a estruturas de dados internas, retornan do ponteiros ou referências acidentalmente. Após alguém obter uma referência, a pessoa pode fazer tudo com ela. Nunca fa ça suposições sobre os outros tipos que você usa. A não ser que um comportamento apareça na interface ou na documentação, não con te com ele. Cuidado enquanto escrever dois tipos intimamente relacionados. Não programe acidentalmente em supOSições e dependências.
Divisão da responsabilidade: preocupando-se , . , . com seu propno negocIo A ocultação da implementação evol ui naturalmente para uma discussão sobre a divisão da responsabilidade. Na seção anterior, você viu como poderia desacoplar cód igo ocultando detalhes da implementação. A ocu ltação da implementação é apenas um passo na direção da escrita de cód igo fracamente acoplado. Para ter realmenle código fracamente acoplado, você também deve ter uma di visão da responsabilidade correta. Di visão da responsabilidade correta signifi ca que cada objeto deve executar uma função - sua responsabilidade - e executá- la bem. A divisão da responsabilidade correta também sign ifica que o objeto é coesivo. Em outras pa lavras, não faz sent ido encapsul ar muitas funções aleatórias e variáveis. Elas precisam ter um forte vincu lo conceituaI entre si. Todas as funções devem trabalhar no senti do de uma responsabil idade comum.
Dia 2
NOTA
A ocultação da implementação e a responsabilidade andam lado a lado. Sem ocu ltação da implem entação, a responsabi lidade pode faltar em um objet o. É de responsabilidade do objeto saber como fazer seu trabalho. Se você deixar a implementação aberta para o mundo exteri or. um usuári o poderá começar a atuar diretamente na implem entação - dupl icando assim a responsabil idade. Assim que dois objetos começam a fazer a mesma tarefa, você sabe que não t em uma divisão da responsabilidade correta. Quando você observar a exist ência de lógica redundante, precisará refazer seu código. Mas não se sinta m al; refazer o trabalho é um a part e esperada do ciclo de desenvolvimento 00. A medida que seus projetos amadu recerem, você encontrará muit as oportunidades para melhorá-los.
Vamos considerar um exemplo rea l de div isão da responsabilidades: o relacionamento entre gerente e programador. Imagine que seu gerente venha até você, forneça as especificações de sua parle em um projeto e, em seguida, o deixe trabalhar. Ele sabe que você tem um trabalho a faze r e que sabe como razero melhor trabalho possível. Agora, imagine que seu chefe não é tão esperto. Ele explica o projeto e pelo que você será responsável. Ele lhe garante que está lá para facilitar seu trabal ho. Mas, quando você começa, ele puxa uma cadeira! Pelo resto do dia, seu chefe fica em cima de você e fornece instruções passo a passo, enquanto você cod ifica. Embora o exemplo seja um tania extremo, os programadores programam dessa maneira em seu cód igo, o tempo todo. O encapsu lamento é como o gerente efi ciente. Como no mundo real, conhecimento e responsabi lidade precisam ser delegados para aqueles que sabem como fazer o trabalho da melhor ronna possível. Muitos programadores estruturam seu código como um chefe autoritário trata seus funcionários. Esse exem plo é fac ilmente transportado para os termos da programação. Vamos considerar um exemplo assi m:
public class Badltem { private double unit _pricei private double adjusted_pr1ce i private double discounti II uma porcentagem de desconto para aplicar no II preço private i nt quantitY i private String descriptioni pri vate St ring i d i publ ic Badltem( St ring id o String descri ption, int quantity . doub l e pri ce ) { this . id • 1d i this.descriptlon • descriptioni i l( quantity
>-
O) {
Encapsulamento: aprenda a manter os detalhes consigo mesmo
this.quantity • quantity; }
else { this.quantity • O; }
this.unit_price
z
price;
}
public double getUnitPrice() { return unit_price; }
II aplica uma porcentagem de desconto no preço public void setOiscount( double discount ) ( if( discount <- 1. 00 ) { this.discou nt : discount ; } }
publiC double getOiscount() I return discount; }
publiC int getQuantity() ( return quantity; )
public void setQuantity( int quantity ) { th i s.quantity - quantity ; }
publiC String getProductID() I return id; }
public String getDescription() { return description; }
publ i C double getAdjustedPrice() { return adjusted_price ; }
publ iC vo i d setAdjustedPrice( double pr ice) I adjusted_price • price; } }
39
Dia 2
BadItem não contém mais a responsabilidade de calcular o preço ajustado. Então, como você gera um preço ajustado? Considere o método mainO a seguir: public static voi d ma i n( String [] args ) I II cr i a os itens BadItem milk = new BadItem( ~dairy-Ol1". "1 Gallon Milk
R ,
2, 2 .50 );
II
apl ica cupons milk. set Oi scou nt( 0.15 ) ;
II
obtêm os preços aju s tados double milk_price • milk.getQuantity() * milk.getUnitPrice() ; double milk_discount = milk.getDiscount() * milk_price ; milk.setAdjustedPrice( milk_price - milk_discount ); System . out.println( "Y our milk costs : \t $" + milk.getAd j us tedPriceO )i
I Agora, em vez de si mplesmente solici tar a Item seu preço ajustado, você precisa se comportar como o gerente ineficiente. Você preci sa dizer ao objeto item o que fazer, passo a passo. Ter de chamar varias funções para calcular o total ajustado, retira a res ponsabilidade do item e a coloca nas mãos do usuário. Retirar a responsabil idade dessa maneira é tão rui m quanto expor implementações internas. Você acaba com responsabil idade duplicada por todo o seu cód igo. Cada objeto que desejar calcular o total aj ustado precisará repeti r a lógica encontrada no método ma ioO. Ao escrever s uas interfaces, você preci sa certificar-se de que não esteja si mplesmente apresentando a implementação através de um conjunto de nomes diferente. Lembre da fila - você não quer métodos chamados addObjec tToLi s tO , updateEndl i stPoi nterO etc. Esses tipos de comportamentos são es pecíficos da implementação. Em vez disso. você ocul ta a implementação, através dos comportamentos enqueueO e dequeueO . de nível mais alto ( mesmo que, internamente, você possa atua li zar ponteiros c adicionar o objeto a uma li sta). Em te rmos de BadI tem, você não deseja rá ter de chamar um método ca 1culateAdjustedPri ce O, antes de poder recuperar o preço aj ustado através do método getAdjustedPri ce (). Em vez d isso, getAdjustedPri ce () deve saber efetuar o cálculo. Quando você tem objetos que não dividem corretamente a responsabil idade, acaba com cód igo procedural , centrado nos dados. O método mai n para calcular o preço aj ustado é mui to procedural o Um método maio que instruísse um obj eto Queue em cada passo de seu processo enqueue() seria procedura l. Se você simplesmente enviar uma mensagem para um objeto e confia r que ele faça seu trabalho, esse é o verdadeiro desenvolvimento orientado a objetos. O encapsulamento esta completamente ligado à ocultação de detal hes. A responsabilidade coloca o con hecimento de certos detalhes onde eles pertencem . É importante que os objetos tenham apenas uma o u um pequeno numero de responsabilidades. Se um objeto tiver responsabilidades
Encapsu lamento: aprenda a manter os deta lhes con sigo mesmo
41
demais, sua implementação se tomará mui to confusa e dificil de manter e estender. Para alterar uma responsabil idade, você correrá o risco de alterar outro comportamento inadvertidamente, se um objeto conti ver muitos comportamentos. Ele tam bém central izará mu ito conhecimento, que estaria melhor espalhado. Quando um objeto fica grande demais, ele quase se toma um programa completo e cai nas armadi lhas procedurais. Como resultado, você se depara com todos os problemas que encontraria em um programa que não usasse nenhum encapsulamento. Quando você verifi car que um objeto executa mai s de uma responsabi lidade, precisará mover essa responsabilidade para seu próprio obj eto.
A LE RTA
A ocultação da implementação é apenas um passo para o encapsu lam ento efi ciente. Sem divisões de responsabil idade corre tas, você simplesmente acaba com uma lista de procedimentos.
Neste ponto, você pode melhorar a definição de encapsulamento. NovO
TERMO
Encapsulamel1to efelil'o é abstração mais ocu ltação da implementação mais res-
ponsabilidade.
Retire a abstração e você terá um cód igo que não é reutilizável. Retire a ocultação da implementação e você fi cará com um código fortemente acopladoe frágil. Retire a responsabilidade e você ficará com um cód igo centrado nos dados, procedural, rortemente acoplado e descentralizado. Sem todas as três partes, você não pode ter um encapsulamento efet ivo, mas a falta de responsabilidade o deixa com a pior situação de todas: programação procedural em um ambiente orientado a objetos.
Dicas e armad
do encapsulamento
Ao se aplicar encapsu lamerHo, existem várias di cas a seguir e armadilhas a ev itar.
Dicas e armadilhas da abstração Ao escrever uma classe, você pode ter problemas, se tentar trabalhar de forma abstrata demais. É impossível escrever lima classe que satisfaça todos os usuários e cada situação. Imagine que você tivesse de escrever um objeto pessoa para um sistema de fo lha de pagamento de uma empresa. Esse objeto pessoa vai ser muito diferente de um objelo pessoa no simulador de nuxo de tráfego que discutimos anteriormente.
Dia 2
A abstração pode ser perigosa. Mesmo que você t enha abstraldo algum elem ento, ele poderá não funci onar em todos os casos. É muito difícil escrever uma classe que sat isfaça as necessidades de todos os usuári os. Não caia na fi xação da abstração - resolva seus problemas prim eiro l
T udo se resume a faze r o s uficienle para resolver o problema imediato. Inclui r todos os detalhes necessários para o objeto pessoa funcionar nos dois contextos seria muito dispendioso. Isso pode provocar lodos os problemas que você viu hoje, devido à responsabil idade embaralhada. Embora você possa ligar seu objeto pessoa às duas situações, ele não será mais um objeto pessoa abstrato. Você perde toda a simplificação que a abstração oferece.
ALERTA
Não coloqu e em uma classe mais do que o necessári o para re solver o problema. Não tente resolver todos os problemas; re solva o problema imediato. Somente então você deverá procurar meios de abstrair o que fez .
•
E claro que ex istem ocasiões onde um problema é complexo, como um cálculo dificil ou uma simulação com plicada. Estamos falando de com plexidade do ponto de vista da responsabi lidade. Quanto mais responsabilidades um objeto assume, mais complexo ele será e mais dificil será mantê-lo.
DIC A
Lembre-se de que ad icionar uma nova classe em seu sist ema é o m esmo que cri ar um novo tip o. Ter essa noção em m ente ajuda a f ocalizar o que você está realmente f azendo. Ao fa lar sobre seu problema, você verá que estará falando em term os dos objetos e das interações e não de dados e métodos.
Finalmente, a verdadeira abstração só pode vir com o tempo. A verdadeira abstração norm almente nasce a part ir de usos reais c não do fato de um programador se sentar e decidi r criar um obj elo reuti lizáve l. Como diz o ditado, a necessidade é a mãe da invenção. Os objetos funcionam da mesma maneira. Normalmente, você não pode sentar-se e escrever um objeto abstraIO realmente reutilizável, logo na primeira vez. Em vez disso, os objetos reutilizáveis nonnalmente são derivados de um cód igo amadurecido, que foi posto à prova e que enfrentou muitas alterações. •
A verdadeira abslração também vem com a experiência . E um objelivo a ser buscado no domínio
da POO.
Enca p sulamento: aprenda a manter os deta lhe s con sigo m esm o
43
Dicas e armadilhas do TAO A transformação de um TAD em uma classe é especifica da linguagem. Entretanto, ex istem algumas considerações independentes da li nguagem que você pode faze r a respeito das classes. A maioria das linguagens 00 fomece palavras-chave que o aj udam a defi nir classes encapsuladas. Primeiro, existe a própria defin ição de classe. A classe é como o TAD, mas com alguns recursos importantes, que você vcrá nos próximos dias. Dentro de uma classe, normalmente você tem métodos e variávei s internas-os dados. O acesso a essas variáveis e métodos é fomecido por funções de acesso. Tudo na interface do TAD deve parecer fazer parte da interface pública do objeto.
Os TADs não são diretament e análogos à classe da 00. El es não têm herança e recursos de pOlimorfismo. A importância desses recursos se t ornará evidente quando você estudar o Dia 4 e o Dia 6, ~ Po l imorfismo : aprendendo a prever o futuro ~ .
Dicas da ocultação da implementação o que expor e o que ocultar em sua interface nem sempre é fácil de decidir. Entretanto, podemos tecer algumas considerações independentes da linguagem sobre o acesso. Apenas os métodos que você pretende que outros usem devem estar na interface püblica. Os métodos que apenas o tipo usará devem estar ocul tos. No exemplo da fi la, dequeue () e enqueue() devem estar na interface públ ica. Entretanto, você deve ocultar métodos auxiliares, como updateFrontPointerO e addToLis tO . Você sempre deve ocultar as variáveis internas, a não ser que elas sejam constantes. Achamos que elas não devem estar apenas ocultas, mas também acessíve is apenas para a própria classe. Você vai expl orar esse conceito mais detidamente, no Dia 4. Abrir variávei s internas para acesso externo expõe sua implementação.
NeTA
Você pode abrir variáveis internas para uso extern o apenas se sua linguagem tratar desses valores com o trata métodos. O Delphi e o Borland C++ tratam de variáveis i ntern as dessa maneira. Se usuários externos puderem acessar métodos e va lores sem saber que estâo mexendo em um valor, então estará corre to abri·los. Em t al linguagem, uma variável interna exposta seria igual a um método que não recebe parâmetros. Infelizment e, não são m u itas linguagens 00 q ue Irata m valores e mét odos da m esma maneira.
finalmente, não crie interfaces que apresentam apenas a representação interna com um nome di ferente. A interface deve apresentar comportamentos de alto nível.
Dia 2
Como o encapsulamento atende os objetivos da programação orientada a objetos
o Dia
I mostrou que o objetivo da programação orientada a objetos é prod uzir softwa re:
I. Natural
2. Confiáve l 3. Reuti lizáve l 4. Manutenivel
5. Extensível 6. Oportuno
o encapsulamento atende cada um desses objetivos: •
•
• •
•
•
Natural: o encapsulamento permite que você divida a responsabil idade da manei ra como as pessoas pensam naturalmente. Através da abstração, você fica li vre para modelar o problema em termos do próprio problema e não em termos de alguma implementação específica. A abstração permite que você pense e programe no geral. Confiável: isolando a responsabilidade e ocultando a implementação. você pode validar cada componente individual. Quando um componente for va lidado, você poderá usá- lo com confiança. Isso possi bili ta testes de unidade completos. Você ainda precisa realizar testes de integração, para cert ifi car-se de que o software constru ído funciona corretamente. Reutil izável: a abstração fornece código flexíve l e utilizável em mais de uma situação. Manutenível: o cód igo encapsu lado é mais fácil de manter. Você pode fazer qualquer al teração que queira na implementação de uma classe, sem dan ificar código dependente. Essas alterações podem incluir mudanças na implementação, assim como a adi ção de novos métodos na interface. Apenas as alterações que violam a sernâmica da interface exigirão mudanças no código dependente. Extensivel: você pode mudar implementações sem danificar cód igo. Como resultado, você pode fazer melhorias de desempenho e mudar func ionalidade sem dani fi car o código existente. Além di sso, como a implementação fica oculta, o cód igo que usar o componente ser:l atualizado automaticamente, para tirar proveito de todos os novos recursos que você introduzir. Se você fi zer tais alterações, certifique-se de fazer os testes de unidade novamente! Danificar um objeto pode ter um efeito de dominó por todo o código que use o objeto. Oportuno: divid indo seu sofuvare em partes independentes, você pode dividi r a tarefa de criar as partes entre vários desenvolvedores, acelerando assim o desenvolvi mento.
Uma vez que esses componentes estejam construidos e validados, eles não precisarão ser reconstruidos. Assim, o programador fica livre para reutil izar func ionalidade, sem ter de recriá-Ia.
Enca p sulame nto: aprenda a m anter os deta lhes con sigo m esm o
45
Advertências Você pode estar pensando, "mas eu não preciso de 00 para abstrair e encapsular meu cód igo". Quer saber? Você está certo - você não precisa de 00 para ter código encapsulado. Os próprios TA Ds não são 00. Ebastante possível ter encapsulamento em praticamente qualquer linguagem . Entretanto, há um problema. Em outros tipos de li nguagens, você freqüente mente precisa criar seus próprios mecanismos de encapsulamento. Como não existe nada na linguagem que o obrigue a respeitar seus padrões, você precisa estar atento. Você precisa se obrigar a seguir suas diretrizes. Você também terá de recriar suas diretrizes e seu mecanismo para cada programa que escrever. Isso está bem para um desenvolvedor. Mas e para dois desenvo lvedores? Dez? Uma empresa inte ira? Quanto mais desenvolvedores são adicionados, mais difici l é ter todos na mesma página. Uma verdadeira linguagem 00 fornece um mecanismo para encapsulamento. Ela impõe o mecanismo de um modo que você não consegui ria sozinho. A li nguagem encapsula os detalhes do mecanismo de encapsulamento do usuário. Uma linguagem 00 fornece algumas palavras-chave. O programador simplesmente usa as palavras-chave e a linguagem cuida de todos os detalhes. Ao trabalhar com os recursos fornecidos pela linguagem, a linguagem apresenta a todos os programadores o mesmo mecan ismo consistente.
Resumo Agora que você entende de encapsulamento, pode começar a programar com objetos. Usando encapsulamento, você pode tirar prove ito das vantagens da abstração, da ocultação da implementação e da responsabilidade em seu código diário. Com a abstração, você pode escrever objetos que são útei s em várias situações. Se você ocultar corretamente a implementação de seu objeto, estará livre para faze r quaisquer mel horias que queira em seu código - a qualquer momento. Finalmente, se você divid ir corretamente a responsabilidade entre seus objetos, evi tará lógica duplicada e cód igo procedural. Se você fec har este li vro agora e nunca mais consultá-lo, terá aprendido novas hab il idades de 0 0 sufi cie ntes para escrever componentes independentes . Entretanto, a história da 00 não termina com o encapsulamento. Cont inue a ler e você aprenderá como tirar proveito de todos os recursos oferec idos pela POO.
Perguntas e respostas P Como \'ocê sabe quais mClodos d cvc incluir em um a inlerface? ,
R E si mples saber quais métodos deve incl uir. Você precisa incluir apenas os métodos que tornam o objeto úti l; os métodos de que você precisa para que ou tro objeto faça seu trabalho.
Dia 2
Quando você começar a escrever uma interface , desejará prod uzi r a menor interface q ue ai nda satisfaça suas necessidades. Tome sua interface o mais si mples possível. Não incl ua métodos que você ' poderia ' prec isar. Você pode adicioná-los quando rea lmente precisar de les. Conheça certos tipos de métodos de conven iência. Se você fize r um obj eto conter outros objetos, normal meme desejará evitar a criação de métodos que si mplesmeme encaminham uma chamada de método para um dos objetos conti dos. Por exempl o, digamos que você tenha um objeto ca rrinho de compras que contenha itens. Você não deve adicionar um método de conveniênc ia no carrinho, que consultará um item para sabe r seu preço e retomá-lo. Em vez disso, você deve ter um método que permi ta obter o item. Quando você tiver o item, poderá solici tar o preço em si. P Você mencionou as palavras-chave publ i c, protected e private. Existem outros modificadores de acesso?
R Cada linguagem define seus modificadores de acesso de sua própria maneira. Entretanto, a maioria das linguagens 00 define esses três níveis. A linguagem Java também tem um modificador de acesso do pacote padrão. Você especifica esse nível omi tindo um modificador. Esse nível restringe o acesso a apenas as classes do mesmo pacote. I)ara mais informações sobre pacotes, consulte o Apêndice Bt "Resumo do Java". P Os modificadores de acesso também têm o papel de mecanismo de segurança?
R Não. Os modificadores de acesso só restringem o modo como OUlros objelos podem interagir com detenninado objeto. Os mod ificadores não têm nada a ver com a segurança do computador.
Workshop As perguntas e respostas do teste são fornecidas para seu melhor entendimento. Veja as respostas no Apêndice A, ·' Respostas".
Teste I. Como o encapsulamento at inge os objetivos da programação orientada a objetos? 2. Defina abstração e dê um exemplo demonstrando abstração. 3. Defi na implementação. 4. De fin a interface. 5. Descreva a diferença ent re interface e implementação. 6. Por que a divisão clara da responsabil idade é importante para o encapsulamento eficaz?
Enca p sulamento: aprenda a manter os deta lhes consigo mesmo
47
7. Defina tipo. 8. Defina TAO (Tipo Abstrato de Dados). 9. Como você obtém ocultação da implementação efi caz e código fracamente acoplado? 10. Quais são alguns dos perigos inerentes à abstração?
Exercícios ,
I. Considere a estrutura de dados de pilha clássica. Urna pilha é uma estrutura ,. Ult imo a entrar, pri mei ro a sair" (LI FO). Ao contrário de urna fi la FI FO, você só pode ad icionar e remover elementos a parti r da mesma extremidade de uma pilha . Assim como em um a fila , urna pilha permite que você verifique se e la está vazia e esco lha o prime iro elemento que pode remover. Defi na um TAO para a classe pilha. 2. Pegue o TAO de pil ha do Exercicio I e esboce uma implementação. Quando terminar, defina uma segunda implementação. 3. Exam ine os exercicios I e 2. A interface q ue você projetou no Exercício I era adequada para as duas implementações formuladas no Exercicio 2? Se ass im foi, quai s vantagens a interface forneceu? Caso contrário, o que falto u na interface original?
SEMANA
1
DIA Encapsulamento: hora de escrever algum código Ontem, você aprendeu tudo sobre encapsulamento. Ao in iciar as lições de hoje, você deverá ter uma boa idéia do que é encapsu lamento, como aplicá-lo efi cazmente e em qua is erros comuns , deve prestar atenção. Neste ponto, o que você não tem é experiência prática COlU a técnica. E claro que você conhece a Icoria, mas nada melhor do que se aprofundar no cód igo. Para o restante do d ia, você completará vários laboratórios que deverão cimentar as lições do Dia 2.
Hoje você aprenderá:
• Como con fi gurar o amb iente Java • Sobre as classes básicas •
Como implementar o encapsulamento
•
A respeito do pacote de primitivas Java
laboratório 1: configurando o ambiente Java Você usará a linguagem de programação Java para com pletar todos os laboratórios desta se· mana, assi m como o projeto fi nal. Para programar em Java, você precisa obter uma versão do Java 2. Para completar estes laboratórios, você precisa ter pelo menos a versão 1.2 do SDK.
Dia 3
Se você ainda não tem, terá de fazer download e instalar um kit de desenvolvimento agora. Existem mu itos kits de desenvolvimento diferentes disponíveis. Entretanto, você pode obter facilmente a versão mai s recente, a parti r do endereço http: //www.javasoft. com/ j2se/ . A Sun s uporta três platafonnas pri ncipais: Solaris, Linux e Wi ndows. Quando este livro estava
sendo produzido, a versão mais recente da Sun era Java 2 SOK, Standard Edi tion, v 1.3. A IBM também o ferece vários kits de desenvo lvimento, no endereço http ://www.i bm.com/java/jdk/index.html. Além das plataformas suportadas pela Sun, a IBM também fornece suporte a várias plataformas, como OS/2, AS/400 e A IX . Cada kit de desenvo lvimento vem com instruções de instalação adequadas. Siga essas instruções para instalar o kit em sua máqui na de desenvo lvimento. Você também pode cons ultar o Apênd ice B, " Resumo do Java", para obter mai s ajuda. Você também pode optar por usar um popular IDE Java , como Forte, JBuilderoll Visual Age for Java. Estes exem plos e laboratórios também funci onarão nesses ambientes. Este livro pressupõe uma fam il iaridade básica com programação, mas você não precisa de um conhecimento profundo de Java para completar estes laboratórios. Se você precisar de alguma ajuda para conhe<::er os fundamentos da linguagem Java, consulte o Apêndice B.
Exposição do problema Na verdade, você precisa do boi antes do carro. Antes de poder programar, você precisa obter e configurar um am biente de desenvolvimento Java. Se você ai nda não fez isso, obtenha um kit de desenvolvi mento Java e siga os passos de li neados no Apêndice B para instalá·lo. Uma vez instalado, o apêndice o conduzi rá na configuração do caminho das classes, ass im como na compilação e execução de seu primeiro programa Java. Quando você concluir este labof'cltório, saberá se s ua in stalação de Java fun ciona. Você também saberá tudo q ue precisa para compilar e executar seus programas Java.
laboratório 2: classes básicas É muito importante que você se recorde das lições dos dias 1 e 2, enquanto escreve s uas primeiras classes. No Dia I, você aprendeu alguns fu ndamentos sobre classes e objetos. O Dia 2 mostrou como você podc se benefi ciar do encapsulamento para produzir objetos bem definidos. A biblioteca de classes Java contém um rico conjunto de estruturas de dados clássicas, como listas e tabelas hashi ng. Considere a classe OoubleKey da Listagem 3.1.
Enca psulamento: hora de escrever algum código
LISTAGEM 3 .1
51
OoubleKey.java
public class OoubleKey { private String keyl , key2:
II
um construtor sem argumentos public DoubleKeyD ( keyl • "key l "; key2 • "key2":
I
II
um construtor com argumentos public DoubleKey( String keyl , Str ing key2 ) { this.keyl • keyl: this.key2 • key2;
I
II
acessor pub l ic String getKeylO ( return keyl :
I
II
mutante public void setKeyl( St r ing keyl ) { th i s . keyl • keyl :
I
II
acessor public String getKey2() { return key2 :
I
II
mu tante public void setKey2( St ring key2 ) { this.key2 = key2:
I
II
igua l e código hashi ng omitidos por brevidade
I Quando coloca um objeto em qualquer implementação de java .ut; 1 .Map. você pode especificar qualquer objeto como chave para esse objeto. Quando precisa recuperar um objeto, você simplesmente usa a chave para recuperar o valor. Ooubl eKey permite que você use hashing em duas chaves Stri ng, em vez de em uma.
Dia 3
Você notará que Ooubl eKey tem dois construtores: public OoubleKey() I keyl • "keyl-j key2 • "key2 M ; J
public OoubleKey( String keyl . String key2 ) { this.key l = keyl; this . key2 • key2 i J
Os construtores aparecem em duas formas: aqueles sem argumentos (construtores lI oargs) e aq ueles com argumentos. Novo TERMO Conslr Ufores l10args são construtores que não recebem nenhum argumento.
NOTA
Construtor noargé u m termo Java. O equivalente em C++ é construtorpedrão.
Os construtores sem argumento instanciam um objeto COm valor padrão. enquanto aqueles que aceitam argumentos usam os argumentos para in icializar o estado interno dos objetos. pub 1i c Ooub 1eKey () é um exemplo de construtor noarg. enquanto pub 1i c Ooub 1eKey ( Stri ng key l. String key2 ) aceita argumentos. Confonne o Dia 1 ensinou, métodos como public St ring getKeyl O e publ ic Stri ng get Key20 são conhecidos como acessorcs, pois eles pcnn item que você acesse os valores internos do objeto.
O mundo Java reconhece dois tipos de acessores: de con figuração e de obtenção. Os de con figuração permitem que você con fi g ure u ma variável d e instância. enquanto os de obtenção permitem que você leia uma variável de instância. A Sun Microsyst em s desenvolveu uma convenção de atribuição de nomes em torno de acessores de configuração e de obt enção, conheci da com o JavaBean Design Patterns. JavaBeans é uma maneira padronizada de escrever se us componentes. Se seus componentes obedecem a esse padrão, você pode conectá-Ias em qualquer IDE compatível com JavaBean. Tal IDE poderia permitir a construção visual de seus programas, usando os beans. As convenções de atribuição Java são simples. A convenção JavaBean para atribuição d e nomes para acessores de obt enção e de configuração é: public void set
( value ) publ1c get() o nde é o tipo da variável de instância e é o nome da vari ável de inst ância.
Enca ps ulamento : ho ra de escrever algum código
53
Tome como exemplo u m objeto Pessoa. Um objeto Pessoa tem um nome. Os acessores de obtenção e de config uração do nome poderiam ter o seguint e fo rmato: pub l ic void setName( String name ) publi c String getName()
Finalmente, você chama métodos como publ ic void setKey l ( String keyl ) e publ ic void setKey2( String key2 ) de mutantes, pois eles permitem alterar o estado interno do objeto. Doub1 eKey demonstra o uso correto do encapsulamento. Empregando uma interface bem definida, DoubleKey oc ulta sua implementação do mundo exterior. Doubl eKey também é bastante abstrata. Você pode reutili zar Doub l eKey onde precisar usar hash ing com duas chaves String. Finalmente, Doub l eKey di vide corretam ente a responsabilidade, fornecendo apenas os métodos necessários para atuar como uma chave.
Exposição do problema No Di a 2, você viu um Banco 00. No Banco 00, os clientes entram em uma fi la, enquanto esperam por um caixa. Mas não se preocupe, você não vai escrever uma classe Queue. A linguagem Java tem bastante suporte interno para estruturas de dados clássicas. Em vez disso, você vai programar uma classe de conta - a linguagem Java ainda deixa o programador com alguns trabalhos a fazer. Seja essa uma conta corrente, uma conta poupança ou uma conta de mercado financeiro, todas elas têm algumas caracteristicas compartilhadas. Todas as contas têm um saldo. Uma conta tam bém permitirá que você depos ite valores, saque valores e consulte o saldo. Hoje, você vai escrever uma classe de conta. O Laboratório 2 vem completo, com uma classe Te 11 er. A classe Te 11 er tem um método ma in () que você usará para testar a impl ementação de sua conta. A classe l eller espera uma interface pública específica. Aqui estão as regras: • •
Você deve chamar a classe conta de Account. A classe deve ter os dois construtores a seguir: pub 1i c Account () pub1; c Aceount( double in Hial_deposH ) O construtor noargs configurará o sa ldo inicial como 0.00. O segundo construtorconfigurará o saldo inicial como i nitlal_deposit .
•
A classe deve ter os três métodos a seguir. O pri mei ro método credita na conta o va lor de funds: publlc vo"id depositFunds( double funds ) O método seguinte debita na conta o valor de funds:
Dia 3
publ ic doub l e withdrawFunds( doub l e fun ds ) Entretanto, wi thdrawFund s () não deve permiti r um saque a descoberto. Em vez disso, se funds for maior que o saldo, apenas debita o resto do saldo. wi thdrawFunds() deve retornar a real quant idade sacada da conta.
O terceiro método recupera o saldo corrente da conta: pu bll c double ge tBalan ee () Além dessas regras, você pode adicionar quaisquer outros métodos que possa considerar útei s. Entretanto, certifique-se de implementar cada um dos métodos exatamente como listado anteriormente. Caso contrário, o ca ixa não poderá fazer seu trabalho! Uma vez que você lenha terminado de escrever a classe Aceoun t , ce rtifique-se de compi lar as classes Account e Te 11er. Uma vez fei to isso, execute o ma i n de Te 11er, d igitando ja '.Ia Te 11 er. Se você fe z seu trabalho corretam ente, deverá ver a saída ilustrada na Figura 3.1.
FIGURA 3 .1
A M/ida correia de rel/er.
A próxima seção discuti rá as soluções do Laboratório 2. Não prossiga até concluir o Laboratóri o 21
Soluções e discussão A Li stagem 3.2 apresenta uma possível implementação de Accoun t.
Enca psulamento: hora de escrever algum código
LISTAGEM 3.2
55
Account .java
public class Account {
II dados privados private double ba lancei
II const rutor public Account( double in it_deposit ) { balance· init_depos iti
I publi C Account() { II n!o precisa fazer nada.
balance terá O como padrão
I
II deposita dinhe iro na conta publ ic void deposltFunds( double amount ) { balance· balance + amount ;
I
II consu l ta o sal do pub l ic double get8a l ance() I return balance:
I
II saca fundos da conta publi c double withdrawFunds( double amount ) I if(amount > balance)1 amount • balance i
II ajusta o valor
I balance· balance - amount i return amounti
I I A classe Account ilustra os conceitos importantes existentes por trás do encapsulamento. Account é bastante abstrata. Ela funcionará como a base para muitos tipos diferen tes de contas. A
classe Account ocu lta sua implementação atrás de uma interface bem definida. Finalmente, a classe Account mostra uma divi são correta de responsabilidades, pois contém todo o conhecimento de como debi tar e cred itar no saldo da conta. O conhecimento de como executar essas tarefas não 'vaza' para fora do objeto.
Dia 3
Entretanto, Accounl não é perreita; ainda há espaço para melhoria. Por questão de brevidade, essa solução de cl asse Accounl pula a validação do argumento. além da verificação de saque a descoberto si mples. Para usar no mundo real, você precisaria incluir código para validar todos os parâmetros dos métodos.
laboratório 3: o encapsulamento o Di a 2 abordou três características do encapsulamento eficaz: • Abstração • Ocu ltação da implementação • Divisão da responsabilidade Cada característica é urna habi lidade importante a ser domi nada, enq uanto você projeta e escreve suas classes. Você precisa aplicar todas as três características pam ter objetos bem encapsulados. Vamos apli car essas três características em um jogo de cartas. Primeiro, vamos aplicar a abstração. Lembre-se de não abusar da abstração. Você ai nda tem um problema para resolver e não pode resolver todos os problemas. Assim , você deve pri meiro tentar resolver os problemas que conhece! O quê você pode dizer genericamente a respeito de jogos de carta jogados com um baralho de pôq uer padrão? Um bom lugar para começar é no próprio maço de cartas. Um baralho padrão contém 52 cartas. Você pode embaralhar um maço, assim como escolher uma carta do baral ho, em qualq uer posi ção. Do mesmo modo, você pode retomar uma carta para qualquer posição no maço. Qualquer outra extração é apenas uma especial ização da escolha de uma carta a partir de qualquer parte do maço. O que você pode dizer a respeito das cartas em si? Todas as cartas compartilham lim a estrutura comum. Cada carta tem um naipe: ouros, copas, espadas ou paus. Cada carta também tcm um valor: 2 a 10, va lete, dama, rei ou ás. A única direrença de uma carta para outra é o valor desses dois atributos. Levado a um extremo, você poderia tentar descrever cada tipo de maço de cartas, sejam elas cartas de beise bol ou de tarô. Novamente, quando você começa a abstrair, precisa cert ificar-se de não abstrair em dem asia. E quanto a ocultação da implementação? A não ser que você roube enquanto joga baralho, nunca verá o que está no maço, até receber uma carta. Você também não insere cartas que não fazem parte do baral ho. Final mente, e quanto à responsabilidade?
Enca psulamento : ho ra de escrever algum código
57
No mundo real, as cartas em si não fazem muito. Uma carta simplesmente exibe seu nai pe e seu valor. Uma cana tem um estado: face para cima ou face para baixo. Do mesmo modo, um baralho não fa z muito no mundo real. Em vez disso, o carteadoré aque le que embaralha e distribui as cartas. O bara lho simplesmente contém as cartas de jogo. No mundo dos computadores, uma cana conterá seu naipe, valor e estado. Em um programa simples, uma cana também saberá como ser apresentada. Um baral ho criará e conterá as cartas. Finalmente, o caneador saberá embaral har as cartas e distribuir uma carta.
NeTA
Posteriormente, você aprenderá a importância de separar a exibiçâo de seu modelo/dados. Entretanto, para nossos objetivos aqui, você pode misturar os dois.
Exposição do problema Use as classes de projeto de descrição de carta de pôquer para representar as cartas, o maço de cartas e o caneador. EllIão, você deve escrever um pequeno método mai n() , que instancie o carteador e seu maço de canas, embaralhe as cartas e, em seguida, imprima o baral ho. Este laboratório deixa a você muita margem de movimento, enquanto projeta suas cartas, o baralho e o carteador. Ao pensar nas classes que você criará, certi fi que-se de considerar a ocultação da implementação e a divisão da responsabilidade. Coloque a responsabi lidade apenas onde ela pertence e, quando você a colocar, certifique-se de que ela não 'vaze'.
NeTA
Veja java.lang.Math.randomH para a geração de nú meros aleatórios. A fu nção randomO será útil pa ra embaralhar o maço. Você pOde obter a documentação completa das APls Java no endereço http ://www.javasoft.com/. Por exemplo, (i nt) fMath.randomO * 52) fornecerá um número entre O e 51.
A próxima seção discutirá as soluções do Laboratório 3. Não prossiga até completar o laboratório 3!
Soluções e discussão A Listagem 3.3 apresenta uma possivel classe Ca rdo LISTAGEM 3,3
Card. j ava
public cl ass Card { private int ranki
Dia 3
LISTAGEM 3.3
Ca rd. java (con t inuaç ão)
private i nt suit; priva te boolean face_up ;
II constan tes usadas pa ra i nstancia r II na i pes publ ic sta tic publ i c static publ i c static public sta ti c II valores pu bl ic static publi c static publi c static publi c static publ ic st atic publ ic st atic publ ic stat ic pub l ic static pub l ic stat i c pub l ic static pub l i c st at i c publi c stati c publ ic stati c
final i nt OIAMONOS final i nt HEART S fina l i nt SPAOES fi nal CLUBS
'o,
fi na l fi na 1 fi nal fi nal fi na1 fina l fi nal fina l fina l f inal f inal f i na1 fi na 1
in t i nt in t i nt in t
'o,
'THREE "O FOUR FIVE
SI'
• • • •
4·o
3·o 6; 5;
• 2; • 3; • 4 ·o
• 5·o
,.
• 6; • o
SEVEN i nt EIGHT • 8·o i nt NINE • 9 ;
,o,
TEN
i nt JACK i nt QUEEN KING i nt ACE
,o,
• 10 ; • 74 ·o • 8 1; • 75 ; • 65;
II cria uma nova ca rt a - usa apenas as cons tantes para i ni cia l iza r pu blic Card( i nt suit, int rank ) I II Em um programa rea l . você preci sari a fazer a val i dação dos argumentos. this.suit • sui t; this.rank • rank; }
publi c int getS uit () { ret urn s uit; }
publ ic i nt getRank() { return rank; }
pub l iC voi d faceUp() { face_up • true; }
publ i c voi d fa ceOown() I
Enca p sulamento : ho ra de escrever algum código
LISTAGEM 3 .3
59
Ca rd. java (continuação)
}
pub ll C boolean l sFaceUp() { return face_up; }
public String display() { St ring di spl ay ; H( ra nk > 10
} I
display • Str ing .valueOf( (char) rank } ; } el se ( di splay • String . valueOf( rank ); }
switch ( suit ) ( case OIAMONOS: return dlsp lay case HEARTS: return displ ay case SPAOES: retur n display default: return display
+
String.val ueOf( (char) OIAMONOS ) ;
+
St r ing. val ueOf( (char) HEARTS } ;
+
St r ing.val ueOf{ (char) SPAOES } ;
+
Str i ng.va l ueOf( (char) CLUBS };
} }
}
A defin ição da classe Card começa defini ndo várias constantes. Essas constantes enumeram os valores e naipes de carta váli dos. Você notará que, uma vez instanciado, não é possível mudar o valor da cm1a. As insI!incias de Card são imutáveis. Tornando a carta imutável , ninguém poderá alterar erroneamente o va lor de uma carta. Novo
TERMO
Um objelo imlltável é aquele cujo estado não muda, uma vez construido.
A classe Card é responsável por conter seu nai pe, assim corno o valor. A carta também sabe como retornar urna representação de String de si mesma. A Li stagem 3.4 apresenta uma possível implementação de Oeck.
LISTAGEM 3.4
Oeck.java
public class Oeck { private java.util . LinkedList deck : publiC OeckO { bulld CardsO; }
public Card get( int index) { if( index < deck .size() ) { return (Card) deck.get( index ) : }
return null : }
publ iC void replace( int index. Card card ) { deck.set( index . card ) ; }
pub l ic int size() ( return deck.size(): }
public Card removeFromFront() ( if( deck.size() > O ) ( Card card • (Card) deck.removeFirst() ; return card; }
return null; }
public void returnToBack( Card card ) { deck.add( card ); }
private void buildCards() { deck · new java. util.LinkedListO ; deck.add( new Ca rd( Card .CLUBS . Card .TWO )} : deck.add( new Card( Card.CLUBS . Card. THREE ) }; deck.add( new Card( Card.CLUBS . Card.FOUR )} ; deck.add( new Card( Card.CLUBS. Card.F IVE )}: /1 a definição compl eta foi cortada por brevidade /1 veja a l istagem compl eta no código -fonte } }
Enca p sulamento : ho ra de escrever algum código
61
A classe Deck é responsáve l por instanciar as cartas e, em seguida, fornecer acesso a elas. A classe Oeck forne ce mélodos para recuperar c rclomar as cartas. A Li stagem 3.5 apresenta a implementação de Oeal er. LISTAGEM
3.5
Oea 1er . java
publ i c class Dealer
I
private Deck deck i publiC Dea l e r ( Deck d deck =d i
li
I public vo i d sh uffle() 1 Il torna o array de cartas aleatório int num_ca rds • deck.size()i for( in t i .. O; i < num_c ards; i ++ ) I int index " (int)( Math.random() * num_cards )i Card ca rd_i .. ( Card ) deck.get( i ) ; Card card_ index a ( Card ) deck.get( index )i deck.rep lace( I. card_index ) i deck.replace( index , card i )i
I I public Card dealCard() I if( deck.size() > O ) I return deck. remove FromFront() ;
I return nu ll;
I I A classe Dealer é responsável por embaral har o maço e di stribuir as cartas. Essa imp lementação de Oeal er é honesta. Outra implementação de Deal er poderia dar as cartas a partir do final do baralho! Todas as três classes têm uma divisão da responsabilidade clara. A classe Card representa cartas de pôquer, a c lasse Deck contém as cartas e a classe Oea ler distribui as cartas. Todas as três classes também ocultam sua implementação. Nada sugere que a classe Oeck tenha realmente uma Unked Us t de cartas.
Dia 3
Embora Ca rd possa definir várias constantes, isso não com promete a integridade de sua implementação, pois a classe Ca rd está livre para usar as constantes como qu iser. Ela também está livre para mudar os valores das constantes a qualquer momento. O método bui l dCards() de Deck destaca uma deficiênc ia da ocultação da implementação. Você poderia instanciar cartas com números de 2 a 10 em um laço for. Se você examinar as constantes, verá que 1WOa TEN contam de 2 alO, seq üencialmente. Tal laço é muito mais simples do que instanc iar cada carta individualmente.
Entretanto, tal suposição o vincul a aos valores correntes das constantes. Você não deve penn itir que seu programa se torne dependente de determinado valor, escond ido na constante. Em vez disso, você deve usar a constante cegamente, chamando Cardo TWO , Card. THREE, etc. Você não deve faze r quaisquer tipos de suposições sobre o valor. Card poderia redefini r os valores das constantes a qualquer momento. No caso de bui 1dCards () , é fác il cair na tentação de usar os valores das constantes di rctamcnle. Aqui, o contrato entre Card e o usuá rio das constantes de Card são os nomes das constantes e não seu valor subjacente. O Dia 12, "Padrões avançados de projeto", apresentará urna solução um pouco mais elegante do que esse uso de constantes.
laboratório 4: estudo de caso - os pacotes de primitivas Java (opcional) o Laboratório 4 é um laboratório opcional. Embora a conclusão do laboratório
dê a você mais idéias sobre a programação orientada a objetos, sua conclusão não é necessária para ter êxito nos próx imos dias.
Cada linguagem orientada a objetos tem suas próprias regras para detenninar o que é e o que não é um objeto. Algumas linguagens orientadas a objetos são mais 'puras' que outras. Uma linguagem puramente orientada a objetos, como a Smallta lk, considera ludo um objeto, até mesmo operadores e primitivas. Novo
TERMO
Uma linguagem orientada a objetos pllra suporta a noção de que ludo é um objeto.
Em uma linguagem puramente orientada a objelos, tlldo- classes, primitivas, operadores e até blocos de código - é considerado um objeto. A linguagem Java tem suas próprias regras para determinar o que é e o que não é um objcto. Na linguagem Java nem tudo é um objeto. Por exemplo, a linguagem Java declara diversos valores de primiti vas. As primitivas não são consideradas objetos na linguagem Java. Essas pri mitivas compreendem boolean, cha r, byte, short, i nt, 10ng, float c double.
Enca ps ulamento: hora de escrever algum código
63
Novo TERMO Uma linguagem or ielllada a objelos não considera tudo um objclO. As primitivas oferecem algumas vantagens em relação os objetos. Para usar uma primitiva, você não precisa instanciar uma nova instância usando new. Como resultado, usar uma primi tiva é muito ma is eficiente do que usar um objeto, pois ela não sofre da sobrecarga associada aos objetos. Por outro lado, às vezes você achará o uso de primitivas limitante. Você não pode tratar pri mitivas como objctos. Isso sign ifi ca que você não pode usá-Ias em lugares que exigem um objelo. Considere java . uti 1. Vector, da colcção de classes genéricas. I)ara colocar um valor no vetor, você precisa chamar o método add () do vetar: public boolean add( Object o ) i Para armazenar um va lor no vetor, o valor deve ser um obj eto. Colocado de maneira simples, se você quiser colocar lima pri mitiva em um vetor, estará sem sorte. Para contornar essas fa lhas, a linguagem Java tem vários pacotes de primitivas, incluindo Boolean, Characte r, Byte, Ooub l e, Fl oa t, Integer, l ong e Short. Essas classes são chamadas de pacotes, pois elas contêm, ou possuem , um valor de pri miti va. Novo TERMO Um pacole é um objeto cujo único propósito é conter outro objcto ou primiti va. Um pacote fornecerá qualquer número de metodos para obter e manipularo valor possuído. Vamos considerar a interface pública de Boolean, que está delineada na Listagem 3.6. LISTAGEM3.6
java.1ang.Boolean
publ i C final cl ass Boolcan impl ements Se r ial iz able I publi c Boolean( boolean value ) ; publ i c Boolean( String s l i publiC s t atic final Bool ean FAlSE; public static final Boolean TRU Ei publi c static final ClASS TYPEi publiC static boolean getBoolean( St ring name }i publi c s tati c Boolean valueOf( String s )i publ iC public pub l ic pub l i c
I
boolean booleanValue() ; boolean equals( Object obj li int hashCode(); Stri ng toSt r i ng( )i
Dia 3
NOTA
Final se ref ere ao conceito de impedir que cla sses descendentes alterem esse elemento, quando herdadas. A herança será d iscutida no Dia 4, -Herança: obtendo algo para nadaM. Impfements está rela cionado à constr uçâo de 'interface' especial da linguagem Java, d iscutida no Apêndice B, ' Aesumo d o Java' .
Internamente, o pacote Boo l ean conterá uma primitiva booleano Ass im , para passar um valor boa 1ean para um vetor, você prec isaria primeiro instanciar um pacote Boo1 ean. possuir a primitiva boo 1ean e passar esse pacote para o vetor. A interface Boo 1ean introduz outros recursos das linguagens orientadas a objetos: métodos de classe e va ri áveis de classe. Até agora, todos os métodos e variáveis que você viu eram métodos de instância e variáveis de instância. Isto é, cada variável e ca da método está ligado a alguma instância de objeto. Para chamar o m étodo o u acessar a variável. você deve ter uma instância do objeto. O fat o de que você precisa de uma instância é freqüentemente lógico. Considere o mé· todo booleanvalueO de Boolean. O m étodo booleanValueO é um método de inst ância. O valor que o método retorna dependerá do est ado interno das instâncias de Boo I ean individuais. Uma inst ância pode possuir o va lor true, out ra pode possuir o valor fal se. O valor retornado dependerá do valor que a instância contém int ernamente. Agora, considere o método get8001 ean() de Boolean. Esse ê um método de classe. Se você estudar a definição de getBooleanO, notará a palavra-chave static. Na linguagem Java, a palavra-chave statle declara que o m étodo ou variável é um método ou var iável de classe. Ao contrário das variáveis e métodos de instância, os métodos e variáveis de classe não estão v inculados a nenhuma instância. Em vez disso, você acessa métodos de classe através da própria classe. Assim, para cham ar getBoo leanO , você nâo precisa de uma instância (contudo, você ainda poderia chamar o método como se ele fo sse um método de instância). Em vez disso, você pode simplesmente chamar Boolean.getBoolean(). A resposta de getBoolean() não depende do estado de nen huma instância. Po r isso, ele pode escapar impun emente, sendo declarado como um método de classe.
Variáveis de classe sâo variáveis que pertencem à classe e não a uma instância específi ca . As variáveis de classe são compartilhadas entre todas as inst âncias da classe.
Novo TERMO
Métodos de classe sâo métodos que pertencem à classe e não a uma instância especifica. A operaçâo executada pelo método não é dependente do estado de qualquer instância.
Novo TERMO
As variáveis de classe funcionam da mesma maneira. Você não precisa de uma instância para acessá-Ias. Entretanto, elas também t êm outro uso. Como a variável é mantida no nfvel da classe, todas as instâncias compartilham a mesma variável (e, se for pública, todos os objetos poderão compartilhá-Ia). As variáveis de classe diminuem os requisit os de memória. Considere publ i e statle final Boolean FAlSE'. Essa é uma constante que possui o valor fa I se. Com o ela ê estática, todas as instâncias compartilham essa mesma constante. Cada instância não precisa de sua própria cópia. Considere a classe, Counte
Encapsu lamento : hora de escrever algum código
65
publlC class CountedObject { private static i nt instances;
,u
Cri a novo CountedObject */ public CountedObj ectO { i nstances++ : I oubl ic s tat ic int oetNumberi ns tancesO I return instances: I oublic static void mainr Strino n aros) I CountedObiect obi = null: for{ int i = O: i < 10: i++ I I obi • new CountedObiect(): I Svstem.ouLorintlnl "Instances created: " + ob:i .oetNumberInstances II \: /I note oue isso também funcionarêi Svstem.ouLorintlnl "Instances created: • + CountedObiect.oetNumberlnstancesO \: I I CountedObject declara uma variável de classe chamada instances. Ela também declara um método de classe para recuperar o valor, getNumberInstances (). Dentro do constr utor, o valor é incrementado sempre que uma instância é criada. Como todas as instâncias compartilham a variável, a variável i nstances atua como u m contador. À medida que cada objeto é criado, ela incrementa o contador.
O método ma i n () cria 10 instâncias. Você notará que pOde usar uma instância para fazer a chamada de getNumberlnstances{) ou a própria classe. Se você vai ou não declarar um método ou variável estática é uma decisão de projeto. Se o método ou variável for indepen dente do estado de qualquer instância, provavelmente é uma boa idéia torná-lo um método ou variável de classe. Entretanto, você não pode declarar variáveis e métodos que são d ependentes da instância, como estáticos, como o método booleanValueO de Boolean.
Você pode ter feito algumas observações a respeito de Boo I ean. Se você estudar a interface, notará que não há meios de mudar a valor bool ean possuído, uma vez q ue tenha inslanciado a instância de Boolean! Como você não pode mudar seu valo r, diz-se que as instâncias de Boolean são imutáveis. Existem ocasiões em que usar um objeto imutável é fundamental. Se você estiver fami liarizado com linhas de execução, um objcto imutáve l é inerentemente seguro quanto a linha de execução, pois seu estado nunca pode mudar.
Dia 3
Entretanto, existem ocasiões em que os objetos imutáveis causam mais danos do que trazem vantagens. No caso dos pacotes de pri mitivas, a sobrecarga da instanc iação de um pacote para cada primiti va pode se tornar dispendiosa.
Exposição do problema Para o Laboratório 4, você precisa criar um pacote de primit iva Bool ean mutável. No mínimo, esse pacote deve pennitir que você o btenha e configure o va lor possuído. O pacote também deve fornecer do is construtores: um construtor noargs e um construtor que recebe o va lor inicia l do pacote. Sinta-se livre para ad icionar quaisquer outros métodos que considere conven iente. Entretanto, não se esqueça de seguir as regras do encapsulamento eficaz. Talvez você ache interessante examinar a discussão do Apêndice B sobre a palavra-chave sta tic. se decidir fornecer todos os métodos oferecidos pelo pacote de primitivas Bool ean.
ALERTA
A próxima seção discutirá as soluções do Laboratório 4. Não prossiga até com· pletar o Laboratório 4!
Soluções e discussão A Li stagem 3.7 apresenta uma possível solução para o Laboratório 4 . LISTAGEM
3.7
MyBoolean.java
publi c class MyBoo l ean {
II
alguma s cons tantes, por conveniência publi c static final Class TYPE K Boolean.TYPE;
private boolean va l ue;
II
construtor sem argumento - tem false como padrão publi c MyBooleanO { val ue • fal se ; }
II
configura o valor in ic ial como value publi c MyBool ean( boo lean value ) { t his. val ue • vai ue; }
publ ic boolean booleanVa lueO {
Enca psulamento : ho ra de escrever algum código
LISTAGEM 3 .7
67
MyBoolean.java (continuação)
return value : }
publiC void setBooleanValue{ boolean va l ue ) I this . value value: E
}
II para getBoolean e valueOf. podemos simplesmente delegar para Boolean II você vai aprender mais sobre delegação no Capítulo 4 public static boolean getBoolean( St ring name ) I return Boolean.getBoolean( name ); }
public static MyBoolean valueOf( St ring s ) { ret urn new MyBoolean( Boolean.ge tBoolean( s ) ) ; }
II definições de hashCode . equals e toString omit i das por brev i dade }
MyBoolean mantém a interface públ ica encontrada em Boolean, com três exceçôes:
• MyBoo1ean adiciona um mutante: pub 1i c voi d setBoo 1eanVa 1ue ( boa 1ean va 1ue ). Esse mutante permite que você mude o valor dos pacotes. • MyBool ean redefi ne val ueOf () de modo que retorne uma instânc ia de MyBoolean, em vez de Boolean. • MyBool ean remove as constantes TRUEe fAlSE. Agora que MyBoolean é mutante, esses vaIares não se tornam constantes adequadas, pois seus valores podem ser alterados por qualquer um, a qualquer momento. A solução do Laboratório 4 também fornece um vislumbre do Dia 4, "Herança: obtendo algo para nada". Métodos como va 1ueOfO demonstram a delegação. A so lução de cód igo-fonte completa do Laboratório 4 também proporciona urn a visão da herança e da sobrecarga, através dos métodos toString() hashCode() e equals(). I
Perguntas e respostas P No Laboratório 3, você escreveu, "Essa implementação de Deal er é honesta. Outra implementação de Dea1er poderia distribuir as cartas a partir do final do baralho! " O que você quer dizer com outra implementação? R Você pode dizer que os métodos shu ffl eC) e dea 1Ca rdO constitucm a intcrface pública de Dea ler. A classe Dea 1er apresentada é honesta. Ela di stribui as cartas a partir do in ício
Dia 3
do baral ho. Você poderia escrever outro carteador, chamado OishonestDealer, que tivesse a mesma interface públ ica. Entretanto, esse carteador poderia distribuir as cartas a partir do final do baralho. Você chama esse carteador de outra implementação, porque ele reimplementa uma interface igual àquela encontrada em Oea 1er. Entretanto, essa classe implementa a funciona lidade oculta no método de forma ligeiramente dife rente.
P O encapsulamento pode ser prejudicial? R Na verdade, o encapsu lamento pode ser prejudicial. Imagine que você tenha um componente que efetue cá lculos matemáticos. Suponha que você prec ise manter determinada prec isão, quando concluir seu cá lculo. Infelizmente, o componente pode encapsu lar completamente a quantidade de precisão que mantém. Você poderia acabar com um valor incorrelo, se a implementação usasse uma precisão diferente daquela que precisa. Você pode acabar com erros estranhos, se alguém alterar o componente. Ass im, o encapsulamento pode ser prejudicial, se você prec isar de um controle preciso sobre as maneiras pelas quais um objeto manipula seus pedidos. A única defesa é a boa documentação. Você deve documentar todos OS detalhes e s uposições importantes da im plementação. Uma vez documentados, você não poderá fazer alterações fa cilmente cm quaisqucr dctalhcs ou suposições documentados. Assim como no componente matemático, se você fizer uma alteração, correrá o risco de prejud icar todos os usuários desse objeto.
Workshop As perguntas e respostas do teste são fornecidas para seu melhor entendimento. Veja as respostas no Apênd ice A, " Respostas".
Teste I. Examine a c lasse Account do Laboratório 2. Qual(is) rnétodo(s) é(são) mutante(s)? Qual(is) rnétodo(s) é(são) acessor(es)?
2. Quais são os dois tipos de construtores? A partir das sol uções de laboratório, encont re um exemplo de cada ti po de construtor. 3. (Opcional) Boolean, conforme discut"ido no Laboratório 4, declara três variáveis públicas. Neste caso, o uso de variáveis públ icas é aceitável. Você pode explicar por que está certo usar variáveis públ icas nesse caso? 4. (Opcional) Como você pode to mar a solução do Laboratório 3 ma is e fic iente?
Enca ps ulamento : ho ra de escrever algum código
69
5. Por que você acha que a solução do Laboratório 3 não criou uma classe Card separada para cada nai pe? 6. No Laboratório 3, você explorou a divisão da responsabilidade. Quais vantagens a divi· são da responsabil idade proporciona às classes Ca rd, Deck e Deale r?
Exercícíos I. (Opcional) Pegue o Laboratório 2 e abstraia Ooub leKey ainda mais. Refaça o projeto de Doubl eKey de modo que ela possa aceitar qualquer tipo de objeto como chave - não ape· nas urna String. Para que sua nova classe Ooub 1eKey funcione, você precisará alterar a definição dos métodos equal s () e hashCode{). Esses métodos foram omitidos por brevidade nas soluções im· pressas. Entretanto, os métodos estão disponíve is no código· fonte completo das soluções. 2. (Opcional) No Laboratório 3, as instâncias de Card sabem como se apresentar. Entrctan· to, a classe Oeck não sabe como se apresentar sozinha. Refaça o projeto de Oeck de modo que as instâncias de Oeck saibam como se apresentar.
SEMANA
1
DIA Herança: obtendo algo para nada Nos três últimos dias, você se concentrou em aprender sobre o primeiro pilar da programação orientada a objelos: encapsulamento. Embora o encapsulamento seja um conceito fundame nta l na POO, há mais na história do que apenas suportar TADs e módulos si mples. Na verdade, a POO ofereceria muito pouco cm relação ao estilo de programação antigo, se ludo que ela fizesse fosse oferecer encapsulamento simples. É claro que a POO oferece muito mais. A POO va i além, adicionando dois outros recursos: herança e polimorfismo. Você vai passar os próximos dois dias considerando a herança, o segundo pilar da POO. Hoje você vai aprender:
•
O que é herança
•
Os diferentes tipos de herança
•
Algumas das arm adi lhas da herança
•
Dicas para a herança eficaz
•
Como a herança atende aos objetivos da 00
o que é herança? Ontem, você viu como o encapsulamento penniteescreverobjetos bem definidos e indepcnden· teso O encapsu lamento permite que um objeto IIse outro obj eto, através de mensagens. O liSO é apenas uma das maneiras pelas quais os obj etos podem se relacionar na POO. A POO também fornece uma segunda maneira de relacionamento entre os objetos: herança.
Dia 4
A herança permite que você baseie a defi nição de uma nova classe em uma classe previamente ex istente. Quando você baseia uma classe em outra, a definição da nova classe herda automaticamente todos os atributos, comportamentos e implementações presentes na classe previamente existente. Novo TERMO Herança é um mecanismo que penn ite a você basear uma nova classe na definição de uma classe previamente existente. Usando herança, sua nova classe herda todos os atributos e comportamentos presentes na classe previamente existen te. Quando uma classe herda de outra, todos os métodos e atributos que aparecem na interface da classe previamente ex istente apareceri\o automaticamente na interface da nova classe. Consi dere a classe a segui r: pub li c class Empl oyee { private String f1rst_name õ private String last_name õ priva t e doub le wage õ public Employee( String first_name . Stri ng la st_name. double wage ) { th is .first name • f i rst_name õ this.las t name • last_name õ t his.wage • wage õ
I public double getWage() { return wage õ
I pub lic String getFirstName() I return fi rst_name õ
I publ iC String getLastName() I return last_name õ
I I Instâncias de uma classe como Emp 1oyee podem aparecer em um aplicat ivo de banco de dados de fo lha de pagamento. Agora, suponha que você precisasse modelar um func ionário comissionado. Um funcionário comiss ionado tem um sa lário-base, ma is uma pequena comissão por venda. Além desse req uisito sim ples, a classe Corrmi ssi onedEmployee é exatamente igual à classe Empl oyee. Afinal, um objeto COfllTri ss i onedEmployee é um objeto Employee. Usando-se o encapsulamento di reto, existem duas maneiras de escrever a nova classe COIIIlli ss i onedEmpl oyee. Você poderia si mplesmente repetir o código encontrado em Emp loyee e ad icionar
Herança: obtendo algo para nada
o cód igo necessário para controlar comissões e calcular o pagamento. Entretanto, se você fizer isso, terá de manter duas bases de código separadas, mas semel hantes. Se você precisar corrigir um erro, terá de fazê-lo em cada lugar. Assi m, simplesmente copiar e colar o cód igo não é boa uma opção. Você precisará tentar outra coisa. Você poderia ter uma variável employee dentro da classe Corrrni ss ionedEmpl oyee e delegar todas as mensagens, como getWageO e getfirstNameO, à instância de Employee. Novo TERMO
Delegaçao é o processo de um objeto passar uma mensagem p.1ra outro objeto, para
atender algum pedido.
Entretanto, a delegação ainda o obriga a redefinir tooos os métooos encontrados na interface de Emp 1oyee para passar tooas as mensagens. Assim, nenhuma dessas duas opções parece satisfatória. Vamos ver COmO a herança pode corri gir esse problema: pub li c cla ss CommissionedEmployee extends Employee private doub le commission; private int units;
{
II o custo por unidade II controle do número de unidades vendidas
publiC CommissionedEmployee( String firs t_name. String last_name. doub le wage. double commission ) ( super( fi rs t_name , last_name , wage ); II chama o construtor original II para inicializar corretamente 11 0 valor da comissão this.commission • commission ; }
publ ic double calculatePay() I return getWage() + ( commission * un i ts ) ; }
public void addSales( lnt units ) { thiS.units • this.units + units ; }
public void resetSales() { units • O; } }
Aqu i, CommissionedEmp l oyee base ia sua definição na classe Empl oyee já ex istente. Como Commi ss i onedEmp Ioyee herda de Emp 1oyee. getFi rs tName O, getLas tName O, getWage O. first_name. l ast_name e wage se tomarão tooos parte de sua defi nição.
Dia 4
Como a interface pública de Empl oyee se toma parte da interface de Conmi ssionedEmployee, você pode enviar para ConmissionedEmpl oyee qualquer mensagem que poderia enviar para Employee. Considere o método ma inO a seguir, que faz exatamenle isso: publ i c static yoid ma;n(String [) args ) 1 Commiss1onedEmployee c • new Coomi ss i onedEmp I oyee ("Mr. " • ·Sa1es" •5. 50 . 1. 00) ; c.addSa l es(5); System.out.println( KFirst Name: " + c.get FirstNameO ) ; System.out.println( "last Name : · + c.get l astNameO ) ; System.out.pri ntln( "Base Pay:$ • + c. getWageO }; System.out.pri ntln( "Total Pay:$ " + c .calcula t ePayO ) ; J
A Figura 4. I ilustra o que você verá após a execução desse cód igo. FIGURA 4 .1
S(lÍ(/a ge/'(/da a punir de COIIlfIiss ionedflllp I oyee.
Por que herança? Conforme você viu no últ im o exemplo, às vezes o relacionamento de liSO do encapsulamento simples não é suficiente. Entretanto, há mais na herança do que si mplesmente herdar urna interface pública e impl ementação. Conforme você verá posteriormente no dia de hoje, a herança permite à classe que está herdando redefi nir qualquer comportamento de que não goste. Tal recurso permite que você adapte seu soft ware, quando seus req uisitos mudarem. Se você precisar faze r uma alteração, bastará escrever uma nova classe, que herde a antiga funcio nalidade. Então, sobreponha a funcionalidade que precisa mudar ou adicione a func ional idade que está faltando e pronto. A sobreposição é interessante. pois permite mudar a maneira como um objeto funciona sem tocar na defin ição origina l da classe! Você pode deixar seu código bem testado e validado intacto. A sobreposição funciona mesmo que você não tenha o cód igo-fonte original de uma classe. A herança tem outro uso mui to importante. No Dia I, "Introdução à programação orientada a objelos", você viu como uma classe agrupa objetos relacionados. A herança pemlite que você agrupe classes relacionadas. A POO sempre se esforça por produzir software natural. Assi m como no mundo real, a POO pennite que você agrupe e classifi que suas classes.
Herança: obtendo algo para nada
•
"E um" versus "tem um" : aprendendo quando usar herança Para apresemar os mecanismos de herança, a primei ra seção abordou o que é conhec ido como herança de implemelltação. Conforme você viu, a herança de implementação perm ite que suas classes herdem a implementação de outras classes. Entretanto, somente porque uma classe pode herdar de Qlura não significa que isso deve ser feito! Então, como você sabe quando deve usar herança? Felizmente, existe uma regra gera l a ser seguida, para evitar uma herança incorreta. Quando você está considerando a herança para reut ilização ou por qualquer outro moti vo, prec isa primeiro perguntar-se se a classe que está herdando é do mesmo tipo que a classe que está sendo herdada. O fato de pensar em termos de tipo enquanto se herda é freqUentem ente re rerido como teste 'ê um ' . ,
Novo
TERMO
E 1111/ descreve o relacionamento em que uma classe é considerada do mesmo tipo de outra.
Para usar 'é um ' , você diz a si mesmo, " um objeto ConmissionedEmployee 'ê um' Empl oyee". Essa declaração ê verdadeira e você saberia imed iatamente que a herança é válida nessa situação. Agora, pare e considere a interface Ite rator Java : public interface I terator 1 publ ic boolean hasNext(); pub l ic Object next()j public void remove()j J
Digamos que você qui sesse escrever uma classe que implementasse essa interrace. Se você lembrar do Dia 2, poderá perceber que uma implementação de Queue poderia ser útil na const rução de sua interrace Iterator. Você poderia usar toda a implementação de Queue previamente existente, para conter os elementos da interface Ite ra tor. Quando você precisa verificar hasNext () ou remove () , pode simplesmente chamar o método Queue correto e retornar o resultado. Nesse caso, a herança fornecerá um modo rápido de implementar lim a interface Iterator. Entretanto, ames de começar a codificar, não se esqueça do teste 'é um '. " Uma interface Itera tor 'é uma' Queue". Claramenle essa declaração é fa lsa. Esq ueça-se de herdar de Queue!
NO TA
Uma Queue pode 'ter uma' interface Iterator que saiba como percorrer os elem entos.
Dia 4
Existi rão muitas situações onde o teste 'é um ' fa lhara, quando você quiser reuti lizar alguma implememação. Fel izmente, existem outras maneiras de reutil izar implementação. Você sempre pode usar composição e delegação (veja o quadro a seguir). O teste 'tem um' salva o dia. NovO TERMO
Tem U/II descreve o relacionamento em que uma classe contém uma instância de outra classe.
NovO TERMO
Composiç(io significa que urna classe é im plementada usando-se variáveis intemas (chamadas de variáveis membro), que contêm instâncias de outras classes.
Composição é uma form a de reutiliza ção que você já viu. Se você não puder herdar, nada o impede de usa r instãncias da outra classe dentro da nova classe. Quando você quiser usar os recursos de o utra classe, use simplesmente uma i nstância dessa classe como uma de suas partes constituintes. É claro que você sofre as limitações apresenta· das anteriormente. Considere novamente o exemplo Queuej lterator. Em vez de herdar de Queue, a interface Itera tor pode simplesm ente criar uma instância de Queue e armazená-Ia em uma va riável de instância. auando a interface Itera tor precisa recuperar um elemento ou verificar se está vazia, ela pode simplesmente delegar o trabalho para a instância de Queue, como demonstrado na Figura 4.2. FIGURA 4 .2
InstAncia de lteflllQl'
Uma illll!lface Iterator delegando chamadas de miüooo para Queue. HasNextU
IsEmptyU
InslAncia de allelle
Quando usa composição, você esco lhe cuidadosamente o que vai usar. Atra vés da delegação, você pode expor alguns ou todos os recursos de seus objetos constituintes. A Figura 4.2 ilustra como a interface I terator direciona o m étodo hasNext () para o método isEmptyO de Queue.
É importante indicar que a delegação difere da heran ça de duas maneiras importantes: 1. 2.
Com a herança. você tem apenas uma instância do objeto. Existe apenas um objet o indivisível, pois oque é herdado se torna uma part e intrinseca da nova classe. A delegação geralment e fornece ao usuário apenas o que está na interface publ ica. A herança norm al dá mais acesso aos detalhes internos da classe herdada. Vamos falar a respei to de t al acesso em d etal hes, no final da lição de hoje.
Herança: obtendo algo para nada
77
Aprendendo a navegar na teia emaranhada da herança Os conceitos de 'é um ' e composição mudam a natureza da discussão sobre herança da ambiciosa reutil ização da implementação para inter-relacionamentos de classe. Uma cl asse que herda de outra deve se relacionar com essa classe de alguma maneira, para que os relacionamentos ou hierarquias de herança resultantes façam sent ido. Uma hierarquia de herança é um mapeamento do tipo árvore de relacionamentos que se ronnam entre classes como resultado da herança. A Figura 4.3 ilustra uma hierarquia real extraída da linguagem Java.
Novo TERMO
FIGURA 4 .3
Fermat
Um exemplo de hierarqllia de j ovo. text o
tmefemm
MluageF"""' 1
Num"-rf"ormM
Simpleo.tlForrnet
ChoiceForm.1t
Ded.... IForm.t
A herança defin e a nova classe, afilha, em termos de uma classe antiga, a progenitora ou mãe.
Esse relacionamento filh a-progenitora ou filha-mãe é o relacionamento de herança mais simples. Na verdade, todas as hierarqu ias de herança começam com uma progenitora e uma fil ha. Novo
TERMO
Novo TERMO
A classe filha é
a classe que está herdando; também conhecida como subc lasse.
A classe progenitora ou mãe é a classe da qual a fi lha herda diretamente; ela também
é conhec ida como superclasse.
A Figura 4.4 ilustra um relacionamento progen itora/filha. NumberFonnat é a progenitora das duas filhas Cho l ceFo nnat e Dec ima lFonna t .
Dia 4
FIGURA 4 .4 Uma progellitor a com
NumlMlrl'ormat
\'{;rias filhas.
ChoiceFonnet
OeclmalFormat
Agora que você já viu mais algu mas definições, pode refinar a defini ção de herança. Novo TERMO
Herança é um mecanismo que pemlite estabelecer relacion amentos ' é um ' en tre
classes. Esse relacionamento também penn ite que uma subc lasse herde os atributos e comportamentos de sua superclasse.
NO TA
Quando uma filha herdar de uma progenitora, a filh a obterá todos os atributos e comportamentos que a progenitora possa ter herdado de outra classe.
Confonne você viu, para que a hierarquia de herança faça sentido, deve ser possivel faze r na filha tudo que é possivel fazere m sua progen itora. É isso que o teste 'é um' rea lmente testa. A uma mha s6 é permit ido aumentar a funciona lidade e adicionar funcionalidades. Uma fi lha nunca pode remover funcio nalidade.
Se você verificar que uma filha precisa remover funci onalidade, isso será uma indicação de que ela deve aparecer antes da progenitora na hierarquia de herança!
Assim como pais e filh os da vida real, as fi lhas e progen itoras da classe serão semelhantes entre si. Em vez de compartilhar genes, as classes compartilham in formações de ti po.
NOTA
Como na vida rea l dos filhos, uma classe pode ter apenas uma progenitora flsica. Tudo depende de como a linguagem im plementa herança. Algumas l inguagens permitem que uma classe tenha mais de uma progenitora. Isso é conhecido como herança múlt ipla. Algumas linguagens restringem a filha a uma progenitora. Outras l inguagens, com o Java, permitem apenas um a progen itora por implem entação, m as fornecem um m ecanismo para herdar múltip las interfaces (m as não a implem entação, apenas as assi naturas de método).
Herança: obtendo algo para nada
Assim como as filhas reais, as classes fil has podem adicionar novos com portamentos e atributos a si mesmas. Por exemplo, uma fil ha real pode aprender a tocar piano. mesmo que a mãe nunca tenha aprendido. Do mesmo modo, uma filha pode redefinir um comportamento herdado. For exemplo, a mãe pode ter sido má aluna de matemática. A filha pode eSlUdar mais e se tornar uma boa aluna de matemática. Quando você quer adicionar novo comportamento em uma classe, pode fazer isso adicionando um novo método na classe ou redefinindo um comportamento ant igo.
Mecânica da herança Quando uma classe herda de outra, ela herda im plementação, comportamentos e atributos. Isso signi fi ca que todos os métodos e atributos disponíveis na interface da progen itora aparecerão na interface da filha. Uma c lasse construída através de herança pode ter três tipos importantes de métodos e atributos: •
Sobreposto: a nova classe herda o método ou atributo da progenitora, mas fornece uma nova definiçã o.
o
Novo: a nova classe adiciona um método ou atributo completamente novo.
o
Recursivo: a nova classe simplesmente herda um método ou at ributo da progenitora.
A maioria das linguagens 00 não permite que você sobreponha um atribu to. Entretanto, o atributo sobrepost o foi i ncluído aqui para serm os completos.
Primeiro, vamos considerar um exemplo. Em seguida, exploraremos cada tipo de método e atributo.
public class TwoDimcns1onal Po int ( private double x_coord; private double y_coordi publiC TwoDimensiona l Po i nt( double x,double y ) ( se tXCoo rdinate( x ); se tYCoordinate( y ) i }
public double getX Coordi nate() { retu rn x_coord; }
public void setXCoordinate ( double x ) { x coo rd = Xi }
Dia 4
pub l ic double getYCoordinate() I relurn y_coord ; }
public void setYCoo rd ina te( double y ) 1 y_coord • y; }
public String toString() ( return "I am a 2 dimensional poinl.\n " + ~My x coo rdinate is: " + getXCoordinateO + "\ n" + "My y coo rdinate is: " + getYCoordinateO: } }
pub li C cla ss ThreeDimensionalPoint extends TwoDimensionalPoint ( priva te double z_coord : public ThreeOimensionalPoint( double x. doubl e y. double z ) ( super( x.y) ; II inicializa os atributos herdados II chamando o construtor progenitor setZCoordinate( z ) ; }
publiC double getZCoord inale() { return z_coord: }
}
public void setZCoordinate( double z ) { z-coord • z'
.
publiC Stri ng toString() I return "I am a 3 dimens i onal "My x coordinate is: "My Y coordinate is: "My z coordinate is:
point. \ n" + .. + gelXCoordinate() + U\n" + .. + getYCoordinate() + "\n" + .. + getZCoordinate();
}
}
Aqui, você tem duas classes ponto que representam pontos geométricos. Você poderia usar pontos em uma ferram enta de traçado de gráficos, em um modelador visual ou em um planejador de vôo. Os pontos têm muitos usos práticos. Aqui, TwoOimens ionalPoint contém coordenadas x e y. A classe define métodos para obter e configuraras pontos, assi m como para criar uma representação de String da instânc ia do ponto.
Herança: obtendo algo para nada
ThreeDimensionalPoinl herda de TwoDimensionalPoint. ThreeDimensiona lPoi nl acrescenta a coordenada z, assim corno um método para recuperar a valor e para con fi gurar o valor. A classe também fornece um método para obter uma representação de String da instância. Como Thr eeDimensional Poinl herda de TwoDimensiona l Poi nt, ela também tem os métodos contidos dentro de TwoDimensionalPoint. Esse exemplo demonstra eada tipo de método.
Métodos e atributos sobrepostos A herança perrnite que você pegue um método ou atributo previamente existente e o rede fina. A redefinição de um método permite que você mude o comportamento do objeto para esse método. Um método ou atributo sobreposto aparecerá na progenitora e na filha. Por exemplo, ThreeDimensionalPoint redefine o método toStringO que aparece em TwoDlmenslonalPoint:
II de TwoDimensionalPoint pub l ic String toString() { return "I am a 2 dimensional poinl. \n" + "My x coordinate is: • + getxCoordinateO + "\n" + "My Y coordinate is: • + getYCoordinateO: }
TwoOimens iona I Point define um método toSt r i ng() que identifi ca a instância como um ponto bidimensional e impri me suas duas coordenadas. Th reeOimensionalPoint redefi ne o método toSt ringO para identi fi car a instância como um ponto tridimens ional e imprime suas três coordenadas:
II de ThreeOimensional Point public String toString() { return "I am a 3 dimensional "My x coordinate is: "My Y coordinate is: "My z coordinate is:
point.\n" + " + getXCoordinate() + "\n" + " + getYCoordinate() + "\n" + " + getZCoordinate();
}
Considere o método mainO a seguir: publ ic s tati c void main( String [] args ) { TwoDimensionalPoinl two : new TwoDi mensionaIPoint(1.2); ThreeDimens ionalPo int three = new ThreeO imensionalPo i nt(1.2.3); Syslem.out. pr int ln (two.toString(» : Syslem.out. pr int ln(three . toStri ng(» ; }
A Figura 4.5 ilustra o que você verá após executar o método mai nO.
Dia 4
FIGURA 4 .5
TestaI/do o lIIétodo toString() sobreposto.
Conforme você pode ver na Figura 4.5, ThreeDimensionalPoint retorna sua representação de String sobreposta. Sobrepor um método também é conhecido como redefinir um método. Redefinindo um método, a filha fomece sua própria implementação personalizada do método. Essa nova implementação fomeccrá um comportamento novo para o métooo. Aqui, ThreeDimensionalPoint redefine o comportamento do métooo toStri ngO, para que ele seja correlamente transfonnado em um objeto String. Novo
TERMO
Sobrepor é O processo de uma filh a pegar um método que aparece na progenitora e
reescrevê·lo para mudar o comportamento do método. A sobreposição de um méto· do também é conhecida como redefinição de um método.
Então, como o objelo sabe qual definição deve usar? A resposta depende do sistema 00 subjacente. A maioria dos sistemas 00 procurará primei ro a defini ção no objeto para o qual é passada a mensagem . Se uma defi nição não for encontrada lá. o ambiente em tempo de execução percorrerá a hierarquia, até que uma defi nição seja encontrada. É importante perceber que é assim que uma mensagem é manipulada e que é por isso que a sobreposição fu nciona. A definição da filha será a prim eira a ser chamada, poi s é a primeira encontrada. O mecanismo é igual para métodos e atributos recursivos, que veremos posteriormente. A Figura 4.6 ilustra a propagação de método entre os objetos ponto para LIma chamada de get XCoordi nate (). Uma chamada de método para getXCoord i na te () percorrerá a hierarq ui a até encontrar LIma defi ni ção para o método.
I
FIGURA 4 .6
Pl'op(/gaç(10 de mellsagem emre
(Irl"
d' •
-,·1
os objeto.f pomo. D
5
5
D
,
;/ ~
I
o
. . .J;:;
L ___
• L I
Herança: obtendo algo para nada
83
Ao considerarmos a sobreposição de um método ou atribulo, é importante perceber que nem todos os métodos e atributos estão disponíveis para sua filha sobrepor. A maioria das linguagens orientadas a objelos tem alguma noção de controle de acesso. As palavras-chave controle de acesso defi nem exatamente quem pode ver e acessar métodos e atributos. Genericamente, esses níveis de acesso caem em três categorias, conforme discutido brevemente no Dia 2:
• Privado: um nível de acesso que restringe o acesso apenas à classe. • Protegido: um nível de acesso que restringe o acesso à classe e às filhas. •
Publico: lIm nível de acesso que permite o acesso a todos e a qualquer um.
Os métodos e atributos protegidos são aqueles aos quais você deseja que apenas as subclasses tenham acesso. Não deixe tais métodos püblicos. Apenas aq ueles com amplo conhecimento da classe devem usar métodos e atributos protegidos . Você deve tornar privados todos os atributos não-constantes e qualquer método destinado unicamente à própria classe. O nível privado impede que qualquer outro objeto chame o método, exceto quanto ao próprio objeto. Não torne protegidos os métodos privados, apenas para o caso de alguma subclasse querer acessá-los algum dia. Use o nível protegido apenas para os métodos que você sabe que uma subclasse deseja usar. Caso contrário, use o nível privado ou público. Tal prática rígida significará que talvez você tenha de voltar ao seu cód igo posteriormente e mudar o nível de acesso de um método. Entretanto, isso leva a um proj eto mais conciso do que um que abra tudo para uma subclasse.
NOTA
Vollar e mudar niveis de acesso pode parecer uma prática ruim. Entretanto, as hierarqui as de herança nunca devem acontecer por acidente. Em vez disso, as hierarquias devem ser desenvolvidas naturalmente, enquanlO você programa. Não há vergonha em refazer suas hierarquias com o passar do tempo. A POO real é um processo iterativo. Contudo, lembre-se de que torna r tudo privado é uma regra geral . Existem casos em que esse consel ho não fun cionará a seu fav or . Tudo depende do que você estiver programando. Por exemplo, se você vender bibliotecas de classe genéricas sem fornecer o cód igo-fonte, provavelmente deverá ter o n ível protegido como padrão, para que seus clientes possam usa r herança para estender suas classes. Na verdade, existem ocasiões em que você d esejará projetar um a subclasse com a herança em mente. Em tal caso, fa z sentido estabelecer um protocolo de herança. Um protocolo de herança ê uma estrutura abstrata, vislvel apenas através dos element os protegidos da classe. A classe progenitora chamará esses métodos e a classe filha poderá sobrepor esses métodos para aumenlar o comportamento. Você vai ver um exemplo assim, amanhá.
Usando essas definições e regras, é fácil ver que os métodos e atributos protegidos e publicos são os mai s importantes para a herança.
Dia 4
Novos métodos e atributos Um novo método ou atributo é um método ou atributo que aparece na fil ha, mas não aparece na progen itora. A filha acrescenta o novo método ou atributo em sua interface. Você viu novos métodos no exem plo ThreeDimens ional Poi nt . ThreeDimensiona I Poi nt acrescenta os novos métados getlCoordinate() e setZCoordinateO. Você pode adicionar nova funcionalidade na interface de sua fi lha, adicionando novos métodos e at ributos.
Métodos e atributos recursivos Um método ou atri buto recursivo é definido na progenitora ou em alguma outra ancestral, mas não na fi lha. Quando você acessa o método ou atributo, a mensagem é enviada para cima na hierarqu ia, até que uma definição do método seja encontrada. O mecan ismo é igual àquele apresentado na seção sobre métodos e atributos sobrepostos. Você viu mélodos recursivos no código-fonte de TwoDi mens i ona I Poi nt e ThreeDi mens i ona 1Poi nt. getXCoord1 na te () é um exemplo de método recursivo, pois ele é definido por TwoDimens ional Point e não por ThreeDimensiona 1 Poi nt. Os métodos sobrepostos também podem se comportar de forma recursiva. Embora um método sobreposto apareça na filha, a maioria das linguagens orientadas a objetos fornece um mecanismo que permite a um método sobreposto chamar a versão da progenitora (ou de algum outro ancestral) do método. Essa capacidade permite que você enfat ize a versão da superclasse, enq uanto define novo comportamento na subclasse. Na linguagem Java, a palavra-chave super dá acesso à implementação de uma progeni tora. Você terá a chance de usar super nos laboratórios do Dia 5, " Herança: hora de escrever algum cód igo".
NOTA
Nem todas as tinguagens fornecem a palavra-chave super. Para essas linguagens, você precisará tomar o cuidado d e inicia lizar corretamente qualquer código herdado. Não referen ciar co rretamente as classes herdadas pode ser uma fonte sutil de erros.
Tipos de herança Ao todo, existem três maneiras principais de usar herança: I. Para reutilização de implementação
2. Para diferença 3. Para substituição de tipo
Herança: obtendo algo para nada
Esteja avisado de que alguns tipos de reutilização são mais desejáveis que outros ! Vamos explorar cada uso em detalhes.
Herança para implementação Você j á viu que a herança possibil ita que lima nova classe reuti lize implementação de outra c lasse. Em vez dc recortar e colar código ou instanciar e usar um componente através de composição, a herança torna o código automaticamente disponível, como parte da nova classe. Como mágica, sua nova classe nasce com funcionalidade. A hierarquia Emp 1oyee e a mal gui ada Queue/ Itera tar demonstram a reutil ização de implementação. Nos dois casos, a filha reut ili zou vários comportamentos encont rados na progen itora.
DI CA
Lembre-se de que, quando programa com herança de implementação, você está preso à imp lementação que herda. Escolha as classes que herdará com cuidado. Você precisará ponderar as vantagens da reutilização em relação a todos os faiaS negativos de reutilizar algumas implementações. Entretanto, uma classe corretamente definida para heran ça fará bastante uso de métodos protegidos refinados. Uma classe que herda pode sobrepo r esses m étodos protegidos para alterar a implementação. A sobreposição pode diminuir o impact o da herança de uma implementação mal feita o u inadequada.
Problemas da herança da implementação Até aqui, a herança de im plementação parece excelente. Cu idado, contudo - o que parece lima técnica útil na superficie se mostra uma prática perigosa no uso. Na verdade, a herança de implementação é ti fonna mais de fici ente dc herança e nonnalmenle você deve evitá-Ia. A reutilização pode ser fácil , mas, conforme você verá, ela tem um alto preço. Para entender as fal has, você precisa cons iderar os tipos. Quando uma classe herda de oulra, e la assume automaticamente o tipo da classe herdada. A herança de tipo correto sempre deve ter precedência, ao se projetar hierarquias de c lasse. Você verá os motivos posteriormente, por enquanto, assuma isso como verdade. Dê uma o lhada no exemp lo Queue / lterator novamente. Quando Iterator herda de Queue, ela se torna um a Queue . Isso significa que você pode tratar Iterator com o se fosse do tipo Queue. Como Iterator também é uma Queue, ela tem toda a fu ncionalidade que estava presente na Queue. Isso significa que os métodos como enqueueO e dequeue() também fazem parte da interface pública de Iterator. Superficia lmente, isso não parece ser um problema, mas dê uma o lhada melhor na definição de Iterator. Uma interface Iterator simplesmente defi ne dois métodos, um para recuperar um elemento e outro para testar se restam quaisquer elementos no I terador. Por defin ição, você não pode adicionar itens em um Iterador; entretanto, Queue define o método enqueue() justamente
Dia 4
para um caso assim. Em vez disso, você só pode remover elementos. Você não pode pegar um elemento e, ao mesmo tempo, deixá-lo dentro da interface I terator. Novamente, Queue define o método peek () ,j ustamente para esse caso. Esimples ver que usar Queue como uma base herdada para I terator não é uma boa escol ha; isso fornece comportamentos que si mplesmente não pertencem a uma interface Iterator.
NOTA
Algumas linguagens permitem que uma classe simplesmente herde implementação, sem herda r as informações de tipo. Se sua linguagem permite tal herança, então o exemplo Queue/I terator não é muito problemático. Entretanto, a maioria das linguagens não permite a separação de interface e implement ação, durante a herança. Das linguagens que fazem a separação, algumas fazem isso automaticamente. Outras ainda, como C++, permitem a separa ção, mas exigem que o programador a solicite explicitamente. Tal linguagem exige que o programador projete e solicite a separação explicitamente, enquanto codifica a classe. Obviamente, pode ser muito fácil igno rar o fato de qu e você precisará separa r a implementação e o t ipo, se não tomar cuidado.
Este livro usa uma definição de herança simples. A discussão sobre herança pressupõe que ela inclui implementação e interface, quando uma classe herda de outra.
Uma herança pobre é o monstro de Frankenstein da programação. Quando você usa herança un icamente para reutil ização de implementação, sem quaisq uer outras considerações, freqUen temente pode acabar com um monstro construido a partir de partes que não se encaixam.
Herança para diferença Você viu a herança para diferença no exemplo de TwoOimens i onal Point e ThreeOimensional Po int. Você também a viu no exemp lo Employee . A programação peta diferença permi te que você programe especificando apenas como uma clas-
se filha difere de sua classe progenitora. Novo
TERMO
Programa(:t;o por diferença significa herdar uma classe e adicionar apenas o código
que tome a nova classe diferente da classe herdada.
No caso de Th reeOimens i ona 1Po int, você vê que ela difere de sua classe progen itora pe lo acréscimo de uma coordenada Z. Para suportar a coordenada Z, ThreeOimens i onal Poi nt adiciona dois novos métodos para configurar e recuperar o atributo. Você tam bém vê que ThreeOimens f ona1Point redefine o método toStringO. A programação por di ferença é um conceito poderoso. Ela permite que você adicione apenas o cód igo necessário o suficiente para descrever a diferença entre a classe progenitora c a classe fi lha. Isso permite que você programe através de incrementos.
Herança: obtendo algo para nada
Um cód igo menor e mais fácil de gerenciar toma seus projetos mais simples. E como você programa menos li nhas de código, teoricamente deve introduzir menos erros. Assi m, quando programa pela diferença, você escreve código mais correto em um espaço de tempo mais curto. Assi m como a herança de implementação, você pode fa zer essas al terações incrementais sem al terar o código existente. Através da herança, ex istem duas maneiras de programar pela diferença: adicionando novos comportamentos e atributos. e redefini ndo comportamentos c atributos antigos. Cada caso é conhecido como especialização. Vamos ver deta lhadamente a especial ização.
Especialização Especialização é O processo de uma classe filha ser projetada em tennos de como ela é diferent e de sua progenitora. Quando tudo estiver dito e feito, a definição de classe da filha incluirá apenas os elementos que a tomam diferente de sua progenitora.
Novo
TERMO
Uma classe fil ha se especializa em relação à sua progenitora, adicionando novos atributos e métodos em sua interface, assim como redefinindo atributos e métodos previamente existentes. A adição de novos métodos ou a redefinição de métodosjá existentes permite que a filha ex presse comportamentos que são diferentes de sua progenitora. Não se confunda com o tenno especiali zação. A especialização permite apenas que você adicione ou redefina os comportamentos e atributos que a fil ha herda de sua progeni tora. A especialização, ao contrário do que o nome possa sugerir, não permite que você remova da fi lha comportamentos e atributos herdados. Uma classe não obtém herança se letiva. O que a especial ização faz é rest ri ngir o que pode e o que não pode ser um ponto tridimensional. Um ThreeOimensiona 1Point sempre pode ser um TwoDimensiona l Point. Entretanto, é incorreto dizer que um TwoDimensi onal Poi nt sem pre pode ser um ThreeDimensional Point.
Em vez di sso, um ThreeD imensiona 1Poi nt é uma especialização de um TwoDimensional Poi nt e um TwoOimens ionalPoint é uma genera li zação de um ThreeDimensionalPoint. A Figura 4.7 ilustra a di ferença entre general ização e especial ização. Quando você percorre uma hierarquia para baixo, você se especial iza. Quando você percorre uma hierarquia para cima, , você generaliza. A medida que você generaliza, mais classes podem cair sob esse agrupamento. , A medida que você espec iali za, menos classes podem satisfazer todos os critérios para serem classi ficada s nesse nível.
Dia 4
FIGURA 4 .7
Quando percorre /filia hierarquia para cima. \'ocê gellerali: a. Qllalldo
perco,.re lima hierarqllia para boi.to. !"ocê especia!i:a.
Como você vê. especialização não significa uma restrição de fun ciona lidade, ela significa restrição da categori zação de tipo. A especialização não precisa acabar com ThreeOi mens; ona 1Poi nt. Na verdade, ela não precisa necessariamente nem mesmo começar com TwoOi mens i ona 1Poi nt. A herança vai ter a profundidade que você quiser. Você pode lIsar herança para formar estruturas de hierarq uia de classe comp lexas. A noção de hierarquia introd uzida anteriormente leva a mais dois termos novos: Gnceslral e descendente.
Apenas porque você pode ter hierarquias complicadas não significa que deve tê-Ias. Você deve se esforçar po r ter hierarquias pouco profundas e não hierarquias demasiadamente profundas. À medida que uma hierarquia se aprofun da, ela se torna mais difícil de manter.
NOTA
Novo
TERMO
Dada alguma filh a, uma ances/ral ê uma classe que aparece na h ierarqu ia de classes antes da progenitora. Conforme a Figura 4.8 il ustra, Format é uma ancestral de Oeci-
mal Format .
Novo TERMO Dada lima classe, toda classe que aparece depois dela na hierarquia de classes é uma descendellle da c lasse dada. Conforme a Figura 4.8 ilustra, Oec imal Format é uma
descendente de Fermat.
FIGURA 4 .8
Formal
OecimolFormat é desceI/deli/e de Formot. NumberFo rmat
-'ChoiceFormlll
OecimalFormlll
Herança: obtendo algo para nada
89
Digamos que tivéssemos a hierarqu ia de herança de classes mostrada na Figura 4.9. Dizemos que OneDl mensional Point é a progenitora de TwoDimensional Point e ancestral de ThreeDimen s i onalPoint e de FourOimensionalPoint. Também podemos dizer que TwoDimensionalPoint, ThreeDi mens i ona 1Poi nt c FourOi mens i ona 1Pai nt são todas descendentes de OneOimens i ona 1Pa int. Todos os descendentes com pan il ham os métodos e atributos de seus ancestrais. FIGURA 4 .9
hierarqllia de pallfo.
II
I "".~~~M I..~___~ 1 I. TwoOI......w .....PoI'"
4
TII ...Di......1.... IPo!m
1'41
1 F......oimI_.. I~
I
Podemos fa zer mais algumas declarações interessantes sobre a hierarq uia de classes. OneDimen sianalPo int é a raiz e FaurOi mensionalPaint é uma folha. Novo TERMO A c/eu·se raiz (também referida comum ente como classe base) é a classe superior da hierarquia de herança. A Figura 4.9 mostra que OneOimens i ona 1Poi nt é uma classe raiz. NovO TERMO Uma c/(I.u efolha é um a classe sem filhas. Na Figura 4.8, Decimal Format é uma classe fol ha. É imponantc notar que as descendentes refl etirão as alterações feitas nas ancestrais. Digamos que você encontre um erro em TwoOimens i ona 1Po int. Se você corrigir TwoOimensional Point, todas as classes de ThreeOimens i ona 1Poi nt até FourOi mens i ona 1Poi nt tirarão provei to da alteração. Assim, se você corrigi r um erro ou tomar lima implementação mais efi ciente, todas as classes descendentes da hierarquia tirarão proveito disso.
Herança multipla Em todos os exemplos você viu herança simples. Alg umas implementações de herança permitem que um objeto herde direta mente de mais de uma classe. Tal implementação de herança é conhecida como herança múltipla. A hera nça múltipla é um aspecto controverso da POO. Atguns dizem que ela só torna o software mais diflcil de entender, projetar e manter. Outros têm grande confia nça nela e dizem que uma linguagem não está completa sem ela. De qualquer modo, a herança múltipla pode ser valiosa, se usada cuidadosa e corretamente. Existem vários problemas introduzidos pela herança múlti pla. Entretanto, uma discussão completa do que a herança múltipla pode e não pode fazer está fora dos objetivos deste dia.
Dia 4
Herança para substituição de tipo
o tipo fina l de herança é a herança para substituição de tipo. A substituição de tipo penni te que você descreva re lacionamentos com capacidade de subst ituição. O que é um relacionamento com capacidade de substituição? Considere a classe li ne:
publi c class l i ne ( private TwoOimens i ona lPoi nt pI : private TwoOimensionalPoint p2: publiC Li ne( TwoDimensionalPoi nt pI, TwoDimensionalPoint p2 ) ( thi s.p l • pI: th is.p2 • p2: )
publ ic TwoDimensionalPo in t getEndpo i nt l() { return pI: }
publi c TwoDi mensionalPoint getEndpo i nt2() { return p2: }
publ ic double getDistanceO I doub l e x • Math.pow( (p2.ge tXCoordinate( ) pl.getXCoordinate (» , 2 ); doub l e y • Math.pow( (p2.getYCoordinate() - pl . getYCoordinate(», 2 ) ; doub l e di st ance • Math. sqrt( x + y ) ; return di stance : }
public TwoDi mensional Po i nt getMidpoint () I double new_x • (p l . ge t XCoordinate() + p2 .getXCoordinate() ) / 2; double new_y • (p l .ge t YCoord in ate() + p2 .getYCoordinate() ) / 2: ret urn new TwoOimensionalPoi nt ( new_x . new_y ) : } }
Li ne recebe dois objetos TwoDimensiona l Poi nt como argumentos e fornece alguns métodos para recuperar os valores, um método para calcular a distância entre os pontos e um método para calcular o ponto médio. Um relacionamento com capacidade de substi tuição significa que você pode passar para o construtor de l ine qualquer objelo que herde de TwoDimensional Point.
Herança: obtendo algo para nada
91
Lembre-se de que, quando uma filha herda de sua progen itora, você diz que a filha 'é uma' progenitora. Assim, como um objclo ThreeO imens iona 1Poi nt 'é um ' objeto TwoDimensional Poi nt, você pode passar um objeto ThreeDimensiona 1Poi nt para o construtor. Considere o método mainO a seguir: publi c s ta tic void main( Str ing [] args } I ThreeDimensionalPoint pi = new ThreeDimensionalPoint( 12, 12, 2 }; TwoDimensiona l Point p2 z new TwoDimensionalPoint( 16 , 16 ); Line 1
z
new Line( pi, p2 );
TwoOimens i ona lPoint mi d = 1. ge tM idpoint() ; System.out.println( "Midpoint: (" + mid.getXCoordinate() + " •" +
mid.getYCoordinate() +
"l" ); System.out.println(
"Distance:" + l.getOistanceO );
I Você notará que o método principal passa um objeto TwoDi mens ional Poi nt e um objeto Th reeDimens iona 1Poi nt para o construtor de Li ne. A Figura 4.1 OiI ustra o que você verá, se executar o método mainO . FIGURA 4 .10
Tes/(mdo re/aciollamelllos com capacidade de .mbslillliç{i o.
NOTA
Tente imaginar as possibilidades que os relacionament os co m capacidade de substituição oferecem a você. No exemplo da linha, eles poderiam possibilitar uma ma neira rápida de trocar de um modo de visualizaçâo em 3D para um m odo de visualização em 20 em u ma GU!.
Capacidade de conexilo é um conceito poderoso. Como você pode enviar a uma filha qualquer mensagem que pode ser enviada para sua progenitora, é passiveI tratá-Ia como se ela pudesse ser
Dia 4
substituída pela progenitora. Esse é o motivo pelo qual você não deve remol/er comportamentos ao criar uma fil ha. Se você fizer isso, a capacidade de conexão será invalidada. Usando a capacidade de conexão, você pode adicionar novos subtipos em seu programa, a qual· quer momento. Se seu programa fo r feito para usar uma ancestral, ele saberá como usar os novos objetos. O programa não precisará se preocupar com o tipoexato do objeto. Desde que tenha um relacionamento com capacidade de substituição com o ti po que espera que possa usar.
ALERTA
Novo
TERMO
Saiba que os relacioname ntos com capacidade de substituição s6 podem ir até um n(vet acima na hierarquia de herança. Se você programar seu objeto para aceitar determinado tipo de objeto, não poderá passar para ele a progenitora do objeto esperado. Entretanto, você pode passar para ele qualquer descendente. Pegue como exemplo o construtor de Une: public Line( TwoDimensionalPoint pl,TwoDimensionalPoint p2 ) Você pode passar para o construtor um objeto TwoDemenslonalPoint ou qualquer descen dente de TwoDimens i ona 1Po i nt . Entretanto, você não pode passa r para o construtor um objeto OneDimensionalPoint, pois essa classe aparece antes de TwoDimensionalPoint na hierarquia .
Um slIbtipo é um tipo que estende outro tipo através de herança.
A capacidade de substituição aumenta sua oportunidade de reutil ização. Digamos que você tenha escrito um pacote para conter objetos TwoDirnens i ona 1Poi nt. Devido a capacidade de conexão, você também pode usar o pacote para qualquer descendente de TwoOirnens ional Poi nt. A capacidade de subst ituição é importante, pois ela pennite que você escreva código genérico. Em vez de ter várias instruções case ou testes if/else para ver que ti po de ponto O programa eSlava usando, você si mplesmente programa seus objetos para tratar com objetos do tipo TwoOimen· si ona 1Poi nt.
Dicas para a herança eficaz A herança vem com seu próprio conjunto de problemas de projeto. Embora seja poderosa, na verdade a herança fornece a corda para você se enforcar, quando usada incorretamente. As dicas a seguir o ajudarão a usar a herança eficazmente: Em geral, use herança para reutilização de interface e para defin ir relacionamentos de substituição. Você também pode usar herança para estender uma implementação, mas 50-mente se a classe resultante passar no teste 'é um ' . • Em geral, prefira a composição em vez da herança para reut ili zação de implementação simples. Use herança apenas se você puder apl icar o teste 'é um ' na hierarqu ia resultante. Não use herança para reutilização de implementação ambiciosa. • Scmpre use a regra 'é um '. •
Herança: obtendo algo para nada
As hierarquias de herança corretas não acontecem sozinhas. Freqlientemente, você descobrirá hierarquias à medida que prosseguir. Quando isso acontecer, refaça seu cód igo. Em o utras ocasiôes, você precisará projetar deliberadamente suas hierarquias. De qualquer modo, existem alguns princípios de projeto a seguir:
• Como regra gera l, manten ha suas hierarquias de classe relativamente rasas. •
Projete cuidadosamente a hierarq uia de heranças e remova as características comuns das classes base abstratas. As classes base abstraIas pennilem que você defina um método sem fornecer uma implementação. Como a classe base não especi fica uma implementação, você não pode instanciá-Ia. Entretanto, o mecanismo abstraio obri ga uma classe que esteja herdando a fornecer uma implementação. As classes abstratas são valiosas para herança planejada. Elas ajudam o desenvolvedor a ver o que elas precisam implementar.
Ne TA
Se sua linguagem não fornece um mecan ism o abstrato. cri e métodos vazios e documente o fat o de que subclasses devem implementar completamente esses métodos.
•
As classes freqUentemente compartilham código COmum. Não há sentido em ter várias cópias de código. Você deve remover o código comum e isolá-lo em uma (mica classe progenitora. Entretanto, não o coloque muito acima . Coloque-o apenas no primeiro nível ac ima de onde ele é necessário.
•
Simplesmente não é possível planejar sempre suas hierarquias com pletamente. As características com uns não aparecerão até que você escreva o mesmo código algumas vezes. Quando você ver característ icas com uns, não tenha medo de refazer suas classes. Esse trabalho é freqílentemente referido como refazer.
O encapsulamento é tão importante entre progenitora e filha quanto entre classes não relacionadas. Não relaxe quanto ao encapsulamento, quando você estiver herdando. A práti ca de usar uma interface bem definida é tão vál ida entre progenitora e fi lha quanto entre classes completamente não relacionadas. Aqui estão algumas dicas que o aj udarão a ev itar a quebra do encapsulamento, quando você herdar: •
Use interfaces bem defin idas entre a progen itora e a fi lha, exatamente corno as usaria entre classes.
•
Se você adicionar métodos especificamente para uso por subclasses, certifique-se de torná-los protegidos, para que apenas a subclasse possa vê-los. Os métodos protegidos permitem que você ofereça às suas subclasses um pouco mai s de controle, sem abrir esse controle para toda classe.
•
Em geral , evite abrir a implementação interna de seu objeto para s ubclasses. Uma subclasse pode se tomar dependente da implementação, se você fi zer isso. Tal acoplamento tem todos os problemas delineados no Dia 2.
Aqui estão alguns segredos fi nais para a herança eficaz:
Dia 4
•
Nunca se esqueça de que a substituição é o objet ivo número um. Mesmo que um objeto deva ' intuit ivamente' aparecer em uma hierarquia, isso não quer dizer que ele deve aparecer. Apenas porque é possível ou porque sua intuição clama, não sign ifica que você deve fa zer isso.
• Programe pela di ferença para manter o código fácil de gerenciar. •
Sempre prefira a composição à herança para reutilização de implementação. Geralmente é mais fácil alterar as classes envolvidas na com posição.
Resumo Existem do is tipos de relacionamentos fornecidos pela POO : um relacionamento de /(so entre objetos e um relacionamento de herança entre classes. Cada relacionamento fornece urna forma de reutili zação. Entretanto, cada um vem com s uas próprias vantagens e problemas. A simples instanc iação e uso freqUentemente limitam a nexibilidade de uma classe. Através da reutili zação simples, não há meios de reutil izar ou estender uma classe. Em vez disso, você fica com uma instanciação simpl es o u recorte e colagem. A herança supera essas defi ciências, fornecendo um mecan ismo interno para a reutil ização segura e eficiente do código. A reuti lização de implementação proporciona a você um modo rápido e grosseiro de usar cód igo previamente existente em s uas novas classes. Ao contrário da operação de recorte e colagem simples. existe apenas uma cópia do cód igo para manter. Entretanto, simplesmente herdar para reuti lizar é imprevidente e lim ita seus projetos. A implementação para diferença permite que você programe suas novas classes em termos de como el as d ife rem da classe original. Você só programa os atributos que diferenciam a filha da progenitora . Finalmente, a herança para substituição penni te que você programe genericamente. Com a substituição, você pode trocar de s ubclasses para a progen itora a qualquer momento, sem dani fica r seu código. Isso permite que seu programa seja flexíve l para futuros requisitos.
Como a herança atende aos objetivos da 00 A herança preenche cada um dos objelivos da POO. Ela ajuda a prod uz ir so ftware que é: i. Natura l
2. Confiáve l
3. Reutili zável
4. Manuteníve l 5. Extensível
6. Oportuno
Herança: obtendo algo para nada
Ela atinge esses objetivos, como segue: o
Natural: a herança permite que você modele o mundo mais naturalrnente. Através da herança, você pode fonnar hierarquias de relacionamento complexas entre suas classes. Como seres humanos, nossa tendência natural é querer categorizar e agrupar os objetos que estão em tomo de nós. A herança pennite que você traga essas tendências para a programação. A herança também abrange o desejo do programador de evitar trabalho repetitivo. Não faz sentido fazer trabalho redundante.
•
Confiável: a herança resulta em código confiável. A herança si mplifi ca seu código. Quando programa pela diferença, você adiciona apenas o código que descreve a diferença entre a progen itora ea filha. Como resultado, cada classe pode ter código menor. Cada classe pode ser altamente especializada no que faz. Menos código significa menos erros. A herança permite que você reuti li ze código bem testado e comprovado, como a base de suas novas classes. A reutilização de cód igo com provado é sempre mais desejável do que escrever novo código. finalmente, o mecanismo de herança em si é confiável. O mecani smo é incorporado à linguagem, de modo que você não precisa construir seu próprio mecanismo de herança e cert ifica r-se de que todo mundo segue suas regras. Entretanto, a herança n1l0 é perfeita. Ao usar subclasses, você deve estar vigi lante com relação à introdução de erros sutis destruindo inadvertidamente dependências não expostas. Prossiga com cuidado, quando herdar.
•
Reutilizável: a herança auxilia a reuti lização. A própria natureza da herança permite que você use classes antigas na construção de novas classes. A herança também perrn ite que você reuti lize classes de maneiras nunca imaginadas pela pessoa que escreveu a classe. Sobrepondo e programando pela diferença, você pode alterar o comportamento de classes existentes e usá-las de novas maneiras.
o
Manuten ível: a herança auxil ia a manutenibil idade. A reuti !ização de cód igo testado significa que você terá menos erros em seu novo código. E quando você encontrar um erro em uma classe, todas as subclasses tirarão proveito da correção. Em vez de se aprofundar no código e adicionar recursos diretamente, a herança permite que você pegue código previamente existente e o trate como a base da construção de uma nova classe. Todos os métodos, atributos e infornlaçõcs de tipo se tomam parte de sua nova classe. Ao contrário do recorte e colagem, ex iste apenas uma cópia do código original para manter. Isso ajuda na manutenção, diminuindo a quantidade de cód igo que você precisa manter. Se você fosse faze r alterações diretamente no código existente, poderia danificar a classe base e afetar partes do sistema que usem essa classe.
196 •
Dia.
Extensíve l: a herança torna a extensão ou especialização de classe possível. Você pode pegar uma classe antiga e adic ionar nova funcionalidade a qualquer momento. A programação pela diferença e a herança para capacidade de conexão estim ulam a extensão de
classes. •
Oportuno: a herança o aj uda a escrever código oportuno. Você já vi u como a reutilização simples pode di mi nuir o tempo de desenvolvimento. Programar pela diferença signi fi ca que existe menos cód igo para escrever; portanto, você deve terminar mais rapidamente.
Capacidade de substituição sign ifica que você pode adicionar novos recursos, sem ter de alterar muito o cód igo já existente. A herança também pode tornar os testes ma is fáceis, pois você só precisa testar a nova
funcionalidade e qua lquer interação com a funci onalidade antiga.
Perguntas e respostas P Hoje, foram listados três motivos separados para usar herança. Esses motivos precisam ser mutuamente exclusivos ou posso combiná-los? Por exemplo, quando eu herdo pela diferença, parece que também poderia herdar para implementação. R Não, os moti vos por trás da herança não precisam ser mutuamente exclusivos. Você poderia usar herança e acabar satisfazendo cada um dos mot ivos. P Herdar para reutilização de implementação parece ter uma conotação negath'a. A reutilização mio é um dos principais motivos para se usar programação orientada a objetos? R A relllilização é apenas um dos objet ivos da roo. A roo é uma estratégia de programação que permite modelar as soluções para seus problemas de uma maneira mais natural: através de objetos. Embora a reut ilização seja importante, você não deve simplesmente buscá-Ia, ignorando os outros objetivos da 00. Lem bre do exemplo lterator/Queue. Aque le era um model o natural de uma interface Iterator? É claro que não! Além disso, a herança para reutilização de implementação é apenas uma maneira de obter a reutiIização. FreqUentemente, a delegação é a melhor maneira de obter reuti lização de impl ementação simpl es. A herança si mplesmente não é a ferram enta correta, se seu objeti vo é apenas reut ili zar uma implementação. A herança é a ferramenta correta quando você quer programar pela diferença Oll estabelecer capacidade de substit uição de tipo.
Workshop As perguntas e respostas do teste sâo fornecidas para seu melhor entend imento. Veja as respostas no Apêndice A, "Respostas".
97
Herança: obtendo algo para nada
Teste I. Quais são algumas das limitações da reutilização simples? 2. O que é herança? 3. Qua is são as três formas de herança? 4. Por que a herança de implementação é perigosa?
5. O que é programação pela diferença? 6. Ao herdar uma classe, pode-se ter três tipos de métodos e atributos. Quais são esses três tipos dc atributos e métodos? 7. Quai s vantagens a programação pela diferença oferece?
8. Considere a hierarqu ia da Figura 4.11 , extraida da segurança do Java. FIGURA 4 .11
P•• mluion
A hierarquia Permi ss i on.
AU~.m;ss;on
B..icP,rm;uion
Un •...,r..edf'"miuion
Stc:urifvl'ermlulon
Se você voltar sua atenção para a classe Pcnni ss i on, quais classes são suas !ilhas? Quais são descendentes? Consi dera ndo a hierarquia intci ra, qual classe é a classe raiz? Quais classes são classes fol has? Fina lmente, Permission é uma ancestral de SecurityPermission? 9. O que é herança para substitui ção de tipo? 10. Como a herança pode destruir o encapsulamento? Como você pode impor o encapsulamento ao usar herança?
Exercícios I. Dada a defini ção da classe a seguir, quais problemas poderiam ocorrer, se ela fosse herdada? publ lc class Point ( public Point( i nt x, i nt y) I
Dia 4
,
this . x • x: this.y "' y;
publ1c Po1nt getlocation{) { return new POint( x, y ):
,
public void move( int x, i nt y ) { thi s.x • x; this.y ., y;
,
public void setlocation( int x, int y ) { this.x • x·• thiS.y .. y:
,
pub l ic void setlocation( Point p ) { th;s.x = p.x ; this.y .. p.y :
,
,
public ;nt X; public int y;
2. Como você evitaria esses problemas?
SEMANA
1
DIA Herança: hora de escrever algum código A herança é uma ferramenta poderosa. Hoje, você vai explorar o uso dessa nova ferramenta , através de varios exercícios práticos de laboratório. No fina l da liçãOde hoje, você deverá se sentir um pouco mais à vo ntade com a teoria apresentada no Dia 4. Hoje você aprenderá:
•
Corno usar herança enquanto programa
•
Como as c lasses abstraIas o ajudam a planejar a herança
•
Sobre a impOrléincia do relacionamento
o
Como a linguagem Java pode ter violado os relacionamentos ' é um' e ' tem um '
'e um' e ;tem um '
laboratório 1: herança simples A Li stagem S. l apresenta a classe base MoodyObject personificada. LISTAGEM 5 .1
MoodyObject.java
public cla ss MoodyObject (
II retorna o humor protected String getMood() I
Dia5
1 100
LISTAGEM 5 .1
MoodyObject.java (continuação)
return "moody" i }
II
pergunta ao obje to como ele se sente public voi d queryHood() I System.out.prin t ln("I feel " + ge tMood O + " today!")i }
}
MoodyObject define um método público: queryMood (). queryMood () imprime o humor do objeto na lin ha de comando. MoodyObject também declara um método protegido, getMood (). queryMood () usa getMood () internamente para obter o humor que coloca em sua resposta . As subclasses podem si mplesmente sobrepor getMoodO para especializar seu hum or. Se uma subclasse qui sesse mudar a mensagem escrita na linha de comando, ela precisaria sobrepor queryHood ().
Exposição do problema Neste labomtório, você criará duas subclasses: SadObject e HappyObject. As duas subclasses devem sobrepor getHood() para fornecerem seu próprio humor especialmente personal izado. SadObject e HappyObject também devem adicionar alguns métodos próprios. SadObject deve adicionar um método: pub l i c voi d cry(). Do mesmo modo, HappyObject deve adic ionar um método: pub 1i c voi d 1augh (). 1augh () deve escrever ' hahaha' na linha de comando. Do mesmo modo, cry() deve escrever ' boa hoo' na linha de comando. A Listagem 5.2 configura um driver de teste que você deve com pilar e executar quando tiver concluído a escri la de HappyObject e SadObj ect. LISTAGEM 5.2
MoodyO riv er.java
pub li c class MoodyOriver { public f i nal static void ma in( String [] args ) { MoodyObject moodyObjec t c new MoodyObject() i SadObject sadObj ec t • new SadObject (); HappyObje ct happyObjec t " new HappyObject( ) ; System. out.print ln( -How does the moody objec t feel today?M ) i moodyObjec t . queryMood()i Sys t em. out . pr1ntl n( "" l i Sys t em. out . print ln( "How does the sa d object feel today?M )i sadObjec t.queryMood( ); Ilnote que a sobrepos i çáo muda o humor SadObject.cry() ; System.ouL prin t ln( ,,- ) ;
Herança: ho ra de escrever algum códig o
LISTAGEM 5.2
101
MoodyDriver. java (continuação)
System.out.print ln{ NHow does the happy object feel today?" ); happyObjec t . queryMood(); Ilnote que a sobreposi ção muda o humo r happyObject . laugh() ; Sys tem.out .print l n( •• l ; J
J
ALERTA
A próxima seção discute as soluções do Laboratório 1. Não prossiga até concluir o Laboratório 1.
Soluções e discussão As listagens 5.3 e 5.4 apresentam uma solução para o laboratório. LISTAGEM 5.3
HappyOb j ect.java
publ ic cla ss HappyObject extends MoodyObject (
Ilredefine
o humor da cla sse protected String getMood() I return "happy·; J
II
especialização pu bl ic vo i d la ugh () { Sys tem. ou t. pri nt 1n(" hehehe ... hahaha . .. HAHAHAHAHAHA! ! ! ! ! ") ; J
J
LISTAGEM 5.4
SadObject .java
public class SadObjec t ex tend s MoodyObject {
II
redefine o humor da cl asse protected String getMood() { return "sad";
J
II
especialização publ ic voi d cryO I
Dia 5
1 102
LI STAGEM
5.4
SadObject.java (continuação)
System.out . println{"'wah' ' boo hoo' 'weep ' 'sob' ' weep'"}; } }
Quando você executar o driver de teste, deverá ver uma saída semelhante a da Figura 5.1. 5.1 A saida correra de FIGURA
MoodyDriver,
De interesse é a chamada de queryMood(). Quando você chama queryMood () em SadObject. " I
fee l sad today!" é impresso na tela. Do mesmo modo, HappyObject imprime, "1 fccl happy today!" Tal comportamento pode parecer surpreendente, pois nenhuma classe redefi ne queryMood O. Você precisa ver queryMoodO detalhadamente. Internamente, queryMoodO chama getMoodO para obter o hum or. Como as subclasses redefinem getMoodO, queryMoodO chamará a versão fi lha de getMood (). Esse comportamento é um exemplo do processo ilustmdo na Figura 4.6 do Dia 4.
laboratório 2: usando classes abstratas para herança planejada Ex isti rão ocasiões em que você desejará desenvolver lima classe especificamente para que outros possam herdar dela. Quando você desenvolve algumas classes relacionadas, pode encontrar código que é comum a todas as suas classes. A boa prática diz que, ao ver código comum, você o coloca em uma classe base. Quando você escreve essa classe base, planeja para que outras classes herdem dela. Entretanto, uma vez que você tiver term inado de mover o código, poderá notar que não faz sentido instanciar a classe base di retamente. Embora a classe base contenha cód igo comum, que é
Herança: ho ra de escrever algum código
103
muito valioso para subclasses, ela pode não ter nenhum valor para instanciação e uso direto. Em vez disso, só fa z sentido usar as subclasses. As subclasses se especializam em relação à classe base e fornecem o que está faltando. Considere a classe Employee: publ i c cl ass Employee { private String first_name ; private String last_n ame; private double wage ; publi c Employee ( String first_name . St ring last name . double wage ) ( this.first_name· f i rs t _name : this.las t _name • last_name : this.wage • wage;
I publ iC doub l e getWage() { return wage:
I pub l iC String getFirstName() ( return fir st_name:
I public String getLastName() ( return l ast_name:
I publ ic double calc ulatePay() ( II Não sei como fazer isso ! return O;
I publiC Stri ng print Paychec k() { String full_name " l ast_name + " , " + fi r st_name ; ret urn ( "Pay : " + full name + " $" + ca l culatePay() );
I I Você pode usar Empl oyee como classe base de Conmi ss i onedEmp loyees, HourlyEmployees e Sal ariedEmp loyees. Cada subclasse sabe como calcular seu pagamento. Entretanto, o algoritmo usado para ca lcular pagamento vai variar de acordo com o tipo de funcionário. Quando cri amos essa hierarqu ia, imaginamos que cada subclasse preci saria definir seu próprio método cal cul atePayO .
Dia 5
Há um pequeno problema: Empl oyee não tem nenhuma regra para calcular seu pagamento. Não faz sentido executar calc ulatePayO para um objeto Employee. Não ex iste nen hum algoritmo para calcular o pagamento de um runcionário genérico. Uma solução é não definir cal culatePayO na classe base. Entretanto, não defi ni r o método na classe base seria uma decisão inreliz. Isso não modela um runcionário muito bem . Cada runcionário saberá calcular seu pagamento. A única diferença é a implementação real do método ca 1culatePayO. Assim, na verdade, método pertence à classe basco
°
Se você não definir ca 1cul atePayD na cl asse base, não poderá tratar os ru ncionários genericamente. Você perderá a capacidade de conexão de subtipo para o método ca 1cul atePay (). Outra so lução é simplesmente codificar um retorno en latado. O método poderia simpl esmente retornar wage . Um retorno codificado não é uma so lução muito limpa. Não há garantias de que outro dcsenvolvcdor vá se lembrar de sobrepor o método, ao desenvol ver uma nova subclasse. Além disso, não raz nenhum sentido instanciar um objeto Emp loyee. Felizmente, a 1'00 oferece um tipo especial de classe, desl'inada especificamente à herança planejada: a classe abstrata. Uma classe abstrata é mui to parecida com qualquer outra definição de classe. A de fi nição da classe pode defin ir comportamentos e atributos, exatamente como uma classe normal. Entretanto, você não pode instanciar diretamente uma classe abstraIa, pois uma classe abstrata pode deixar alguns métodos indefin idos. Novo
TERMO
Um método declarado, mas não implemelllado, é chamado de melOdo absl NIIQ. Somente classes abstraIas podcm tcr métodos abstratos.
Em vez di sso, você pode instanciar apenas as descendentes da classe abstrata que realmente implementam os métodos abstraIaS. Vamos ver uma classe Employee abstraIa: public abstract cl ass Empl oyee ( ••••
publiC abstract double calculatePay{) ; II o re s tante da definição permanece igual
I A classe abslrata Emp 1oyee de fi ne um método ca1cu 1a tePay () ; entretanto, ela o deixa inde fi nido. Agora, fica por conta de eada subclasse implementar realmente o método. HourlyEmp l oyee é uma dessas subclasses: pub lic cla ss Hou r ly Employee extends Emp l oyee { private i nt hours ;
Ilcontrola o número de horas trabalhadas
public HourlyEmpl oyee ( Str i ng f lrst_name, String last_name. double wage ) { supe r ( flrst _name. l ast_name. wage };
Herança: hora de escrever algum código
105
}
publiC double calculatePay() I return getWage{) * hours; }
publi c void addHours( i nt hours ) I this . hours • thiS.hours + hours; }
public void resetHours() { hours • O; } }
Declarando métodos abstratos, você obriga suas subclasses a se especial izarem em relação à classe base, fornecendo uma implementação para os métodos abstratos. Tornando uma classe base abstrata e criando métodos abstratos, você planeja com antecipação o que a subclasse deve redefi nir.
Exposição do problema No Laboratório 1, você criou uma classe MoodyObject. Todas as subclasses redefinem getMood (). Para o Laboratório 2, altere um pouco essa hierarquia. Torne o método gctMood()abstrato. Você também precisará atualizar a classe MoodyDriver para que ela não tente mais instanciar MoodyObject diretamenle. Você nào terá de fazer quaisquer alterações em SadObject ou HappyObject, pois elas já fornecem uma implementação de getMoodO.
A próxima seção discute as soluções do Laboratório 2. Não prossiga até concluir o Laboratório 2.
Soluções e discussão As li stagens 5.5 e 5.6 apresentam as definições de MoodyObj ect e MoodyDri ver reestruturadas. LI STAGEM
5.5
MoodyObject .java
public cla sse abstrata MoodyObjec t I
II retorna o humor protected abstract Stri ng getMood();
II pergunta ao objeto como ele se sente public void queryMood() I
Dia5
1 106
LISTAGEM 5 .5
MoodyObject .java (cont inuação)
System.out.println{"I feel N + getMood{) + N today!N); } }
LISTAGEM 5 .6
MoodyDriver . java
public class MoodyOriver ( publi c final stat i c void main{ String [] args ) ( IIMoodyObject mo • new MoodyObject{); l lnão pode instanciar MoodyObject SadObject so· new SadObject(); HappyObject ho • new HappyObject() ; IISystem.out.println( "How does the moody object feel today?" ); Ilmo.queryMood(); II Sys tem.out.println{ "" ); System. out.println( "How does the sad object fee l today?" }; so.queryMood(); II note que a sobrepos i ção muda o humor so.cryO; System.out . println( MM ); System.out.print l n{ NHow does the happy obj ect feel today?M ); ho.queryHood(); II note que a sobreposição muda o humor ho.laughO; System.out.println( N_ ); }
}
As al terações são muito simples. MoodyObject define um método getMood () abstrato e deixa por conta de suas subclasses fornecer a implementação real. Quando O método queryMood () precisa recuperar o humor, ele simplesmente faz uma chamada ao método abstrato. O uso de classes abstratas define o contrato que as subclasses devem atender para utilizar a classe base. Como descnvolvedor, quando você ver uma classe base abstrata, saberá exatamente o que preci sa especiali7..ar ao herdar. Você pode especializar os métodos abstratos. Entretanto, você sabe que definindo os métodos abstratos, sua nova classe se encaixará na hierarquia corretamente. Quando uma classe base tem muitos métodos, pode ser confuso descobri r quais deles devem ser sobrepostos. As classes abstratas fornecem uma dica.
Herança: ho ra de esc rever algum có digo
laboratório 3: conta em banco a herança simples
107
praticando
Agora é hora de testar seus conheci mentos de herança. Vamos voltar ao Banco 00 e vero que a herança pode fazer para o sistema de contas do banco. O Banco 00 oferece aos seus clientes algumas escolhas de contas: uma conta poupança, uma conta com cheques, uma conta com vencimento programado e uma conta de cheque espec ial.
Uma conta genérica Cada tipc de conta pcrmitc ao usuário depositar e sacar fundos, assi m como veri ficar o saldo corrente. A conta básica genérica não permite saque a descoberto.
A conta poupança A conta poupança especial iza a conta genérica do banco, aplicando juros no saldo, quando instru ída a fazer isso. Por exemplo, se um depositante tem um saldo de US$I.OOO e a taxa de juros é de 2%, após o pagamento dos juros, o saldo será de USS 1020:
balance
E
balance + (balance * inte rest_rate)
A conta poupança não pennite saque a descoberto.
Uma conta com vencimento programado A conta com venc imento programado também aplica juros ao saldo. Entretanto, se o titular da conta fizer qualquer saque do capital investido, antes do prazo de vencimento, ° banco deduzi rá uma porcentagem do saque. Ass im, por exemplo, se o depositante sacar US$I.OOO antes do prazo de venci mento e houver urna multa de 5% sobre o valor sacado, o saldo da conta diminuirá US$IOOO. Entretanto, o depositante receberá apenas US$950. Se a conta estiver no venc imento, o banco não pena lizará os saques.
balance
E
balance - withdraw amount
mas
amount_given_to_depositor • amount - (amount * penalty_rate) a conta com vencimento programado não permite saq ue a descoberto.
Conta com cheques Ao contrário das contas poupança e com vencimento programado, a conta com cheques não aplicaj uros ao saldo. Em vez disso, a conta com cheques pcnni te que o depositante emi ta cheques e faça transaçõcs na conta através de ATM (caixa elelrânico de auto-atendimento). Entretanto, °
Dia 5
banco limita o número de transações mensais a algum número fixo. Se o depositante ultrapassar essa quota mensal , o banco cobrará uma taxa por transação. Assim, por exemplo, se o depositante tiver direito a cinco transações gratuitas por mês, mas fi zer oito transaçôes a uma taxa de US$I por transação, O banco cobrará do deposi tante uma taxa de US$3: fee • (total _t ransac t ions - month l y_quota) * per_transac ti on_fee A conta com cheq ues não permite saque a descoberto.
Conta com cheque especial Finalmente, a conta com chcquc especial perm ite ao depositante sacar dinheiro além do saldo da conta. Entretanto, nada é de graça. Periodicamente, o banco apl icará uma taxa de j uros no caso de qualquer sa ldo negativo. Assim , por exemplo, se o depositante acumular um saldo negativo de US$I.OOO a uma taxa de 20%, poderá pagar uma taxa de US$200. Depoi s da aplicação da taxa, seu saldo será de - US$ 1200: balance· balance + (balance * i nteres t_rate) Noteque o banco só calculajuros em contas com saldo negativo! Caso contrário, o banco acabaria di stribuindo dinhei ro. O Banco 00 não está no ramo de distribuição de dinheiro. Nem mesmo para desenvolvedores. Ao contrario da conta com cheques, a conta com cheque especial não coloca um Iimite no nlllnero de transaçôes mensais. O banco estimularia os saq ues - eles poderiam cobrar juros!
Exposição do problema Sua tarcfa é formular uma hierarquia de herança e implementar as contas confonne definido anteriormente. Você deve criar as seguintes classes de conta: Ban kAccount • Sa vi ng sAccount • TimeMaturityAccount • CheckingAccount • OverdraftAccount •
BankAccount é a classe base. Ela contém as tarefas comuns a todas as contas. Essa é a única dica hierárquica que você terá 1Parte do laboratório é você experimentar as hierarquias de herança. Existem vá rias si mpl ifi cações que você pode fazer. Para cálcul os de taxas, venc imento programado e juros, supon ha que outra pessoa observará o calendário. Não programe esse tipo de funcionalidade em suas classes. Em vez disso, forneça um método para outro objelo chamar. Por exemplo, SaV l ngsAccount deve ler um método addlnterest (). Um objeto externo cha mara o método. quando for hora de calcular osjuros. Do mesmo modo, Checki ngAccoun t deve expor um método access FeesO. Quando chamado, esse método calculará todas as laxas e as ap licará no saldo.
Herança: hora de escrever algum código
109
Não se atrapalhe com detalhes desnecessários. lembre-se de que você está completando este laboratóri o para ganhar experi ência prática com herança e não para escrever o sist ema de con ta mais robusto possivel. Assim, não se preocupe com a validação da entrada (a não ser que queira fazer isso). Você pode supor que todos os valores dos argumentos serão sempre vál idos.
NOTA
o Dia 4 abordou brevemente o uso de super. super não é um conceito dificiL Considere a seleção a seguir: publlc COfTlTlissionedEmployee( String first_name, String last_name, double wage, double cOfTlTlission ) 1 super(first_name, last_name,wage l; II chama o construtor origi nal
II this.cOfTlTlission
para inicializar corretamente
• cOfTlTlission;
I Quando você chama super dentro de um construtor, isso permite que o construto r da progenitora seja chamado. Ê claro que você deve fornecer todos os argumentos exig i· dos pelO construtor da progenitora. A maioria das linguagens, Java inclulda, exige que, se você chamar super dentro do construtor, então deve fazer isso antes de tudo. Na verdade, se você não chamar super, automaticamente a linguagem Java t entará chamar superO sozinha. super permite que você destaque o código da progenitora, que de outro modo seria simplesmente sobrescrito. No caso dos constru tores, super permite que a fil ha chame o construtor de sua progenitora. Chamar corretamente o construtor da progenitora é algo que você não deve desprezar. Você precisa garantir que a classe seja inicializada corretamente. Você também pode usar super dentro de um método. Imagine uma classe VeryHappyObject, publ 1c clas s VeryHappyObject extends HappyObject {
II
redefi ne o humor da cl asse protected String getMoodO { String old_mood· super.getMood(); return "very" + old_mood;
I I VeryHappyObject SObrepõe getMood(). Entretanto, super.getMood() permite que VeryHappyObject chame a versão da progenitora de getMood (). VeryHappyObject especializa o método getMood () de sua progenitora, realizando algum processamento extra no valor retornado por super .getMood (). Assim, mesmo que uma filha sobreponha o método de sua progenitora, a filha ainda poderá destacar o código existente na progenitora.
Dia 5
Assim como em um construtor, se você usar super .~todo>() para cha mar um método, então d eve fornecer t odos os argumentos que o método possa exigir. Você achará super útil neste laboratório.
Pare agora e complete o laboratório, caso se sinta à vontade. Se você precisar de um pouco mais de ajuda, cOlu inue a ler.
Exposição estendida do problema Se você ainda se acha perdido, estas interfaces devem aj udá· lo um pouco. Essas interfaces repre· sentam apenas unta maneira de completar o laboratório. BankAccount contêm os seguintes métodos: pub li c void depositfunds( double amoun t ) pub l ic double getBa l ance() public double withdrawfunds( double amount ) protected void set8a l ance( double newBalance ) Savi ngsAccount deve conter os seguintes métodos: public void addlnterest() public void setlnterestRate( double interestRate ) public double getlnteres tRate() TimedMaturityAccount contém os seguintes métodos: public public public public
boolean isMature() void mature() double getfeeRate() void set feeRate( double rate)
Ti medMatu ri tyAccount precisará redefi nir wi thdrawfunds () para verifi car o venci mento e apli· car todas as taxas necessárias. Checki ngAccount contém os seguintes métodos: pub li c pub li c public public public public
void accessfees() double getFee() void setfee( double fee ) int getMonth l yQuota{) void setMonth l yQuota( int quota) int getTransact ionCount()
Checki ngAccount precisará sobrepor wi thdrawfunds () para controlar o número de transaçõcs.
Herança: ho ra de escrever algum código
"1
OverdraftAccount contém os seguintes métodos: publl c void chargelnterest() public double getCreditRate() publ i c void setC redltRate( doub le rate) Overd raftAccount pode precisar sobrepor withd rawFunds (), caso 6ankAccount coloque cheq ues a descoberto no método. Talvez você também queira iniciar sua hierarquia com a classe Account, que desenvolveu para o Laboratóri o 2 no Dia 3. A (mica mudança que você deverá fazer é no método wi thdrawFund s{). Você provavelmente deve colocar proteção contra saq ue a descoberto nos métodos withdrawFunds ().
A próxima seção discute as soluções do Laboratório 3. cluir o Laboratóri o 3.
Não prossiga até con-
Soluções e discussão A Figura 5.2 ilustra a hierarquia de herança resultante de conta.
FIGURA 5 .2
Bel'llAccounl
ii hierarquia de conta elll baIlCO.
S8"; nll'''' """"nl
OvtIrd •• flAccounl
Ch", kln ll 4 CCOllOl
Ti mflMalll. 11'1 Acooun I
É im portante ter essa hierarquia em mente, enquanto você considera as soluções a seguir. A Listagem 5.6 apresenta uma possivel implementação de 6ankAccount. Essa classe base contro-
la o sa ldo e man ipula depósitos e saq ues. LISTAGEM
5.7
BankAccount.j ava
publl c cla ss 6ankAccount {
II dados privados private double ba lance;
1 11 2
Dia5
LISTAGEM 5.7
BankAccount.java (continuação)
II
construtor public BankAccount( double initoeposit ) { setBalance( initDeposit ): }
II
deposita dinheiro na conta public void depos1tFunds( double amount ) { II a classe base não ap l ica regras ãs contas II não valida a entrada setBalance( getBalance() + amount ): }
II
consulta o saldo public double getBalanceO { return balance; }
II
configura O saldo protected void setBalance( double newBa l ance ) { balance · newBalance: }
II
saca fundos da conta pub l ic double withdrawFunds( double amount } { if( amount >'" balance) { amount • balance: }
setBalance( getBalance() - amount ); return amount; }
}
Savi ngsAccount, na Listagem 5.8, herda diretamente de Ban kAccount. Sav i ngsAccount especializa BankAccount, adicionando métodos para obter e con figurar a taxa de juros, assim corno um método para apl ica r juros no saldo da conta. LISTAGEM 5.8
Savi ngsAccount.java
publiC class SavingsAccount extends BankAccount {
II
dados privados private double interestRate:
II
Cria novo SavingsAccount public SavingsAccount(double initBa l ance , doub l e interestRate }I supere initBalance ) : setInterestRate(interestRate l:
Herança: hora de esc rever alg um có dig o
LISTAGEM 5 .8
11 3
Savi ngsAccou nt. j ava (continuação )
}
II
calcula e soma juros na conta public voi d addInterest() { double bala nce = getBa l ance( ) ; double rate z get InterestRate() ; doubl e interest • ba la nce * rate ; double new balanc e
z
balance
+
i nte rest;
set Bala nce{ new_balance ); }
II
con figura a taxa de juros pu bli c vo i d set Intere stRate( double i nterestRate ) { this.interestRate z interestRate ; }
II
cons ulta a taxa de juros publ ic doub l e getInterestRate() { return interestRate; } }
Ti meMa t urityAccount, na Listagem 5.9, herda de SavingsAccount, pois juros podem ser aplicados em seu saldo. Entretanto, ela especializa sua progenitora, defin indo métodos para configurar o nível de vencimento e taxas. Interessante é o fa to de que essa classe redefine o método wi thd rawFunds (). Através de uma chamada a super .wi thdrawFunds () , esse método ai nda usa a funciona lidade original; ent retanto, ela acrescenta as verificações necessárias para ver se prec isa acessar uma taxa para a transação. Se ass im fo r, ela acessa a taxa c retorna o valor do saque, menos a taxa. LISTAGEM 5 .9
Ti meMaturi tyAccount .java
class Ti medMaturityAcco unt extends SavingsAccount {
II
dados pri vados private boolean ma ture; private doub le feeRate;
II
Cria novo TimedMa tu ri tyAccou nt public TimedMaturityAccount( double i nitBalance. double interestRate. double fee Rate ) I super ( in itBalance . in tere stRate ) ; set FeeRate ( feeRate ); }
Dia5
1 11 4
LISTAGEM
5.9
TimeMaturityAccount.java (continuação)
II
sobrepõe withdrawFunds de BankAccount public double withdrawFunds( double amou nt ) I super.withdrawFunds( amount ): if( !isMature() ) I double charge ~ amount * getFeeRa t e() : amount • amount - charge : }
return amount: }
II
verifica o vencimento public boo l ean isMature() I return matu re : }
II
faz vencimento publ ic void mature() { mature· true ; }
II % de
taxa para saque anteC i pado pub l ic doub l e getFeeRate() { return feeRate: }
II
configura % de taxa para saque antecipado public voi d setFeeRate( doubl e rate) I feeRate • rate: } }
Checki ngAccoun t, na Listagem 5. IO, herda diretamente da classe base BankAccount. Essa classe adiciona os métodos necessários para configura r a taxa por transação, configurar a quota mensal, recon fi gurar a conta da transação e consultar o número de transações corrente. Essa classe também sobrepõe o método wi thdrawFunds () para controlar o número de transações. Assim corno TimedMaturityAccount, ChecklngAccount ainda usa a lógica original, chamando super .w ithdrawFundsO. LISTAGEM
5.10 CheckingAccount.java
publiC cl ass CheckingAccount extends BankAccount I
II
dados privados private int monthlyQuota : private int transact ionCount: private double fee :
II
Cria novo CheckingAccount
Herança: ho ra de esc rever alg um cód igo
LISTAGEM 5.10
11 5
Checkl ngAccount . java (continuação)
publiC ChecklngAccount( double initOeposit , int trans, double fee } { supere in ltOeposit }: setMonthlyQuota( trans l : set Fee( fee ): J
II sobrepõe withdrawFunds de BankAccount public double withdrawFunds( double amount ) { transactlonCount++ : return super .withdrawFunds( amount l: J
II acessa as taxas se ultrapassou o l imite de
tran saç~o
public void accessFees() ( int extra a getTransactionCount() - getMonthlyQuota(): if( extra> O ) { double total_fee • extra * getFee{) j double balance · ge tBalanceO total fee: setBalance( balance ); J
transactionCount
a
O:
J
II al guns métodos de obtenç ão e con figura ção public double getFee() { return fee; J
public void se tFee( double fee ) ( this. fee • fee; J
publi c int getMonthlyQuota() { return monthlyQuota : J
public vo id setMonthlyQuota( int quota) { monthlyQuota a quota: J
publi C int getTransactionCount() { return transa ctionCount: J
J
Finalmente. Ove rdra ftAceou nt, na Listagem 5.1 I, herda diretamente de BankAceount . Entretanto, ela também adiciona métodos para configurar a taxa de juros de saque a descoberto e para aplicar quaisquer taxas de juros.
Dia 5
LISTAGEM 5 .11
OverdraftAccount .java
public class Ove rdraftAccount extends BankAccount I
II
dados privados private double creditRate;
II
Cria novo OverdraftAccount public OverdraftAccount( double initOeposit. double rate) ( super( 1nitOeposit ) j setCreditRate( rate ) j
I
II
cobra j uros sobre qualquer di nh ei ro emprestado public void chargelnterest() { double balance a ge tBalance()j if(balance < O ) I double charge • balance * getCreditRate()j setBalance( balance + charge ) õ
I I
II
consulta a taxa de crédito pub l ic double getCreditRate() ( return creditRate õ
I
II
configura a taxa de crédito public void setCreditRate( doub le rat e) { creditRate • ratei I
II
saca fundos da conta public double withdrawFunds( double amount ) ( setBala nce(getBa l ance() - amount )j return amountj
I I Cada uma dessas classes espec ial iza sua progenitora de uma maneira ou de outra. Algumas, corno SavingsAccount, sim plesmente adicionam novos métodos. Outras. como Checki ngAccount, Ove rdraftAccount e TimedMaturi tyAccount, sobrepõem o comportamento padrão da progenitora, para aumentar a funcionalidade. Este laboratório o expõe aos mecanismos da herança, assim como à herança para reuti lização de im plementação e à programação pela diferença.
Herança: hora de escrever algum código
117
Embora não seja mostrada aqui, você também pode usar a capacidade de conexão, pois a classe com um BankAccount se relaciona a todas as contas. Qualquer um que sai ba como atuar na classe base BankAccount pode sacar, deposi tare emitir um cheque nos fu ndos de qualquer tipo de conta. Você vai explorar a capacidade de conexão com detalhes por todo o Dia 6, "Polimorfi smo: aprendendo a prever o futuro", e Dia 7, "Polimorfismo: hora de escrever algum código".
laboratório 4: estudo de caso "tem um" e java.util.Stack
Ilé um"l
Quando se é ini ciante em 00, pode ser tentador ver a li nguagem Java como um exemplo de projeto orientado a objetos perfeito. Você pode di zer a si mesmo, "se a linguagem Java faz isso, deve estar correto". I nfel izmente, colocar confiança inquestionável em qualquer implementação 00 é perigoso. Vamos rever a estrutura de dados de pilha clássica. Você pode colocar itens em uma pilha, extrair itens de uma pilha e olhar o primeiro elemento da pilha, sem removê- lo. Talvez você também quisesse ver se a pi lha está vazia. A linguagem Java tem uma classe Stack. A Listagem 5.12 ilustra a interface.
LtSTAGEM 5. 12 java.utll.Stack publ lC class Stack extends { public boo lean empty() ; publi c Object peek(); public Object pop (); public Object push( Obj ect item ) ; public int search( Object o ) ; }
Você notam que a linguagem Java ol im iza um pouco a definição clássica de pi lha. A linguagem Java acrescenta um método searc h(). O método push () tam bém retorna o item que você coloca. Entretanto, há um problema maior. A classe Java Stack também herda de Vector. De um ponto de vista, essa é uma deci são inteligente. Herdando de Vector, St ack obtém toda a implementação contida em Vector. I)ara implementar Stack, tudo de que você precisa é envolver os métodos de Stack para chamarem internamente os metodos herdados de Vector corretos. Infelizmente, a classe Java Stack é um exem plo de herança pobre. Stack passa no teste 'é um'? "Um objeto Stack 'é um ' Vector"? Não- o teste fal ha. Vector tem todos os tipos de métodos para colocar elementos em Vecto r e removê-los. Um objeto Stack só permite que você coloque elementos no topo da pilha. A classe Vecto r permite que você insira e remova elementos em qualquer parte. Aqui, a herança pennite que você interaja com a classe Stack de maneiras indefin idas para uma pilha.
Dia 5
Exposição do problema Stack passa no teste ' tem um'. "Um objeto Stack 'tem um' objeto Vector". Para este laboratório, escreva uma nova versâo de Stack que empregue o tipo correto de reut ilização de implementação.
ALE R TA
A próxima seção discute as soluções do laboratório 4. Não prossiga até con· cluir o laboratório 4.
Soluções e discussão A Listagem 5. 13 ilustra uma possivel implementação de Stack
LISTAGEM 5.13 A New Stack Implementation public cl ass Stack ( private java.u til .Arraylist list : pub l ic StackO( list • new java.uti l .Arrayl ist() : }
public boolean emptyO ( return l ist.isEmptyO : }
publiC Object peek() I if( !emplyO } I return li st .get( O }; }
return nu ll : }
publi C Object popO ( if( !emplyO } I return list.remove{ O ): }
return nul l; }
publiC Object push( Object item ) ( lis t . add( O, item l : return item: }
Herança: ho ra de esc rever algum có dig o
11 9
LISTAGEM 5.13 A New Stack Implementation (continuação) pub l iC int sea rch( Object o ) { in t index · l i st. indexO f ( o ,; if( index ! - - 1 ) { return index + 1; J
return - 1; J J
Se este laboratório nos ensi nar algo, é que não devemos depositar toda nossa fé em qualquer fo nte de 00. Nada é perfe ito.
Resumo Hoje, você completou quatro laboratórios. O Laboratório I permi ti u que você experi mentasse a herança simples. Após concluir o Laboratório 1, você deve ter entendido o mecan ismo básico da herança. Laboratório 2 permitiu uma maior exploração da herança, através da classe abstrata e da herança planejada. O Laboratório 3 deve ter solidifi cado as lições do Dia 4. Os laboratórios I e 2 permiti ram ver métodos e atributos redefinidos, novos e recursivos. Você também viu como, mesmo se sobrepuser um método. ainda pode usar a im plementação da progen itora.
a
a Laboratório 4 ilustrou a importância de considerar os testes 'é um ' e 'tem um ', enquanto se formam hierarquias de herança. Às vezes, a melhor ação é não herdar. Conforme o Dia 4 enfatiza, a com posição é freqUentemente a fomla mais limpa de reutilização. A herança só fa z sentido do ponto de vista relacional ou 'é um ' . Se dois objetos não estão relacionados pelo tipo, então eles não devem herdar. A implementação compart ilhada não é motivo s ufi ciente para herdar. Dentro de um sistema ou aplicati vo, você sem pre deve planejar a máxima herança possível. Entretanto, q uando programa para um apl icativo especí fi co, você está limitado a esse programa. Com o passar do tempo, você trabalhará em muitos programas diferentes. Quando você começar a notar que está programando as mesmas coisas repetidamente, as oportunidades para herança começarão a se apresentar. Você sempre deve estar atento a essas hi erarqu ias de herança descobertas. Você deve refaze r seu código, quando descobrir essas novas hi erarquias.
Perguntas e respostas P No Laboratório 4, você mostrou como até a linguagem Java comete erros de 00. Quando eu examinar as APls Java ou outras fontes de exemplos de 00, como pod erei ter certeza de que o que estou vendo é ' boa ' ao?
Dia 5
R É dificil dizer o que constitui a ' boa' 00 e a ' má' 00, mesmo após ter mui ta experiência em 00. A me lhor coisa que você pode fazer é aplicar o que aprendeu e nunca tomar um exemplo como garant ido. Encare cada exemplo ponderadamente e, se a lgo não parecer correto, discuta isso com seus colegas: obtenha uma segunda opinião.
Workshop As perguntas e respostas do teste são fornecidas para seu melhor entendimento. Veja as respostas no Apêndice A, ·'Respostas".
Teste I. A partir das so luções de laboratório, dê um exemplo de método redefinido, de método recursivo e de novo método. 2. Por quê você declararia uma classe como sendo abslrata? 3. No Laboratório 4, você explorou os relacionamentos 'é um ' e 'tem um'. Antes mesmo de aprender a respeito de herança, você viu relacionamentos ' tem um '. Quais os relacionamentos ' tem um" você viu nos laboratórios do Dia 3? 4. Como esses laboratórios preselVaram o encapsulamento entre a classe base e as subclasses? 5. A partir das soluções, encontre um exemplo de es peciali zação. 6. Como as soluções do Laboratório 3 e do Laboratório 4 adotam lima estralégia diferente para reutil ização de implementação?
Exercícios Não há exercícios hoje. Faça seus laboratórios!
SEMANA
1
DIA Polimorfismo: aprendendo a prever o futuro Até aqui, você aprendeu sobre os dois primei ros pilares da programação orientada a objelos: encapsulamento e herança. Como você sabe, o encapsulamento permi te construir componentes de soft ware independentes e a herança pennite reutilizar e estender esses componentes. Entretanto, ainda falta algo. O so ft ware está sempre mudando. Se os usuários exigem nova runcional idade, erros aparecem ou o software precisa ser integrado em novos ambientes, a única constante é a mudança. O cic lo de vida do so ft ware não tennina quando você di stribui um prod uto. Você precisa de sof1ware que possa se adaptar às necessidades futuras. Não seria ótimo, se você pudesse
escrever so ftware 'à prova do futuro '? Um software à prova do futuro se adapta aos requ isitos futuro s sem alteração. Um software fi prova do futuro permi te que você faça alterações e adicione novos recursos facilmente. Feliz· mente, a POO entende que um software de s ucesso não é estático. Assim , a POO usa o conce ito de polimorfi smo para perm itir que você escreva esse soft ware à prova do fu turo. Você pass
o que ê polimorfismo
• Quai s são os diferentes tipos de poli morfismo e o que eles o ferecem para seus objetos • Dicas valiosas para o polimorfismo eficaz
122
Dia 6
•
A respeito de algumas armadil has do polimorfismo
•
Como o polimorfismo atinge os objetivos da 00
Polimorfismo Se o encapsulamento e a herança são os socos um e dois da POO, o polimorfismo é o soco para nocaute seguime. Sem os dois primeiros pilares, você não poderia ler o polimorfi smo, e sem o
polimorfismo, a POO não seria eficaz. O polimorfismo é onde o paradigma da programação orientada a objelos rea lmente brilha e seu domínio é abso lutamente necessário para a roo eficaz. Polimorfismo significa muitas fo rmas. Em termos de programação, o paI imorfi smo penn ite que um único nome de classe ou nome de método represente um código diferente, selecionado por algum mecanismo automáti co. Assim, um nome pode assumir muitas formas e como pode representar cód igo difercnte, o mesmo nome pode representar muitos comportamcntos di fe rentes.
Polimorfismo: ter mu itas ronnas . Em tennos de programação, mui tas rorm as significa que um único nome pode representar um código direrente, selecionado por algum mecanismo automático. Assi m, o polimorfismo pennite que um unico nome expresse muitos componamentos direrentes.
Novo
TERMO
De sua própria maneira, o polimorfismo é o di sturbio das multiplas personalidades do mundo do somvare, pois um único nome pode expressar muitos comportamentos direrentes. Toda essa conversa sobre expressar 'm uitos componamentos di rerentes' pode parecer um pouco abstrata. Pense no termo abrir. Você pode abrir uma porta, uma caixa, uma janela e uma conta no banco. A palavra abrir pode ser aplicada a mu itos objetos direrentes no mundo real. Cada ohjeto interpreta 'abrir' de sua própria maneira. Entretanto, em cada caso, você pode simplesmente dizer 'abrir', para descrever a ação. Nem todas as li nguagens suponam poli morfismo. Uma linguagem que suporta polimorfismo é uma lingl/agem polimórfica. Em contraste, uma lingl/agem monomórfica não suporta polimorfismo e, cm vez disso, restringe tudo a um e apenas um comportamento estáti co, pois cada nome é estaticamente vinculado ao seu cód igo. A herança rornece o aparato necessário para tornar certos tipos de polimorfi smo poss íveis. No
Dia 4, "Herança: obtendo algo para nada", você viu como a herança perm ite fonnar relacionamentos com capacidade de substituição. A capacidade de conexão é extremamente imponante para o poli morfismo, pois ela pennitc que você trate um tipo específico de objeto genericamente. Considere as classes a segui r:
publl C cla ss Persona l ityObject ( public St r ing speak() { return - I am an object."; } }
Po lim o rfismo: aprendendo a prever o futuro
123
pub1i c c1ass Pessimi sticObject extends Personali t yObject ( public St r ing speak() { return "The glass i s half empty . ":
I I pub1ic class Opt imisticObject extends Personal ityOb ject ( pub1ic String speak() { re t urn "The gl ass is ha 1f fu ll. ":
I I publiC class IntrovertedObject extends PersonalityObj ect ( publlc St ring spea k() { return "h i ... ";
I I publi C cl ass ExtrovertedObject extends PersonalityObject ( public Stri ng speak() { return "He ll o. blah blah blah. did you know that bl ah blah blah. M: I
I Essas classes fo rmam uma hierarquia de herança muito si mples. A classe base, Persona1i tyObject, declara um método: speak(). Cada subclasse redefi ne speak () c retorna sua própria mensagem, baseada em sua personalidade. A hierarquia forma relacionamentos com capacidade de substituição entre os subtipos e seus progenitores. Considere O método mal n() a segui r: public static void main(S tring [] args ) ( Persona1 1tyObject personality new PersonalityObject(); Pess1misticObject pessimistic " new Pessimi sticObject() : OptimisticObject optimis t ic " new OptimisticObject( ): IntrovertedObject introverted new IntrovertedObject(); ExtrovertedObject extroverted " new Ext rovertedObject(): II a capacidade de substi tuição perm ite fazer o seguinte Personal ityObject [] personal ities " new Personal ityObject [5] : personali t ies [O] • personal ity: personalities (1 ] • pessimistic; personalities [2] • opt imi st i c: personalities [3] • introve rted: persona 11 t i es [4] • ext roverted; E
E
I I o polimorfismo faz com que PersonalityObject pareça ter I I muitos comportamen tos diferentes
Dia 6
II
l embre -se - o polimorf i smo é o distúrbio das múlti plas Ilpersonalidades do mundo 00 System.out.prin t ln( "Pe r sonaIHyObject[O] speaks: " + pe r sona 1 i t ies[O] . speak(»; System.out.prin tl n( "Pe r sona I HyObject[ l] speaks: + persona l it i es( l). speak(» : System . out.print ln( "Persona I HyObject [2] s peaks: + persona l i ties [2] . speakO) : System .out . prlntln( "PersonalityObject[3] speaks: " + persona 1 i t i es [3) . speak O) : System.out.println( "PersonalityObject[4} s peak s : " + personalities[4}.s peakO) : M
M
J
Os primeiros dois terços do método mai n{) não aprese ntam nenhuma nov idade. Conforme você viu no Dia 4, a capacidade de substituição permite tratar um objeto genericamente. Ent retanto, o trecho a seguir é onde o exemplo se torna interessante :
1/
o po l imorfismo faz com que PersonalityObject pareça ter l/muitos comportame ntos diferentes II lembre - se - o polimorf i smo é o distúrb i o da s mú l ti plas Ilpersonalidades do mundo 00 System.out.println( "Pe r sonaI H yObject[O] speaks: " + pe r sonal i t ies[O] . speak(» ; System.out.print ln( "Pe r sona htyObject[ l] speaks: + persona 1H ies[ I] . speakO) : System.out.print ln( "Per sona lityObject [2] s peaks: + persona I i ties (2) . speak() : System.out.println( "Per sonalHyObjec t (3] speaks: " + persona 1 i ties (3) . speak O) : System .out . println( "PersonalityObject(4) s peak s : ~ + persona l ities(4] . speakO) ; M
M
A Figure 6. 1 ilustra a saída.
FIGURA 6 .1
Demolls'raçeio do compor/mllell/O polimó/fico.
Po lim o rfismo: aprendendo a prever o futuro
125
Com base na saída, parece que o método speak() de Personal i tyObject tem muitos comportamentos diferentes. Mesmo que PersonalHyObject defina speakO para imprimir " 1 am an object", Pe rsona 1i tyObject está ex ibindo mais de um comportamento. Mesmo que o array s upostamente contenha instâncias de Persona 1i tyObject, cada membro do array se comporta de forma diferente, quando o método principal chama o método speak (). Esse é o dilema do comportamento poli mórfico; Personal 1tyObject, o nome, parece ter muitos comportamentos. Novo
TERMO
NOTA
persona 1it i es é um exem plo de variável polimórfica. Uma vari(Í11ef polimórfica é uma variável que pode conter muitos tipos diferentes.
Em uma linguagem tipa da, as variáveis polimórficas estão restritas a conter va lores específi cos. Em uma linguagem dinamicamente tipada, uma variável polimórfi ca pode conter qualquer valor.
o exemplo anterior explica o mecanismo, mas ele poderia não representar adequadamente o espírito do polimorfismo. Afi nal, você sabe exatamente o que o array contém. Em vez disso, imagine que você tenha um objelo cujo método recebe um objeto Persona1i tyObject como parâmetro: public void makeSpeak( PersonalityObject obj ) I System.out. prin t ln( obj.speak() );
I Os relacionamentos com capacidade de subst itu ição permitem que você passe uma instância do objeto Personal i tyObject ou qualquer descendente dessa classe para o método makeSpeak (), como um argumento. Assi m, ao criar descendentes especializados de Persona 1 i tyObject, como Ext rovertedObject, você não precisa mudar a lógica do método para que ele use instâncias das novas classes como argumento. Em vez disso, você pode simplesmente instanciar ExtrovertedObject (ou qualquer descendente) e passar a instância para o método. O polimorfis mo entra em ação quando o método makeSpeak() é chamado e lhe é passado um obj eto como argumento. O polimorfi smo garante que o método correto seja chamado através do argumento de Persona 1i tyObject, chamando o método do objeto com base no tipo da c lasse real do argumento, em vez do tipo da classe que o método makeSpeakO pensa que está usando. Assim, se você passar um objeto ExtrovertedObject, o polimorfismo garantirá que a definição de speak () de ExtrovertedObject seja chamada e não aquela encontrada na classe base. Como resultado, makeSpeak() apresentará mensagens diferentes na tela, dependendo do tipo de argumento que for passado. Você pode apri morar o polimorfismo para adicionar nova funci onal idade em seu sistema, a qualquer momento. Você pode adicionar novas classes, que tenham funcionalidades jamais imag inadas quando O, programa foi escrito pela pri meira vez - tudo isso sem ter de mudar seu códi go já existente. E para isso que serve o software á prova do fu turo.
Dia 6
Este exemplo é apenas a ponta do iceberg polimórfi co. Na verdade, o exem plo representa apenas uma das muitas formas do polimorfis mo. O próprio polimorfi smo correto é poli mórfi co! Infeli zmente, ai nda há pouco consenso na comunidade de 00 quando se trata de polimorfi smo. Em vez de entrar na controvérsia, este livro apresentará quatro forma s de polimorfismo. O entendimento dessas q uatro fonnas comuns deve dar-lhe a base de que você precisa para começar a aplicar o polimorfismo. Hoje você aprenderá sobre: I. Polimorfismo de inclusão
2. Po limo rfismo paramétrico 3. Sobrepos ição 4 . Sobrecarga
Polimorfismo de inclusão o pol imorfi smo de inclusão, às vezes chamado de poli morfismo puro, permite que você trale obj etos relacionados genericamente. Você viu o poli morfismo de inclusão pela prime ira vez, no início do dia. Considere os mélodos a seguir:
I
publ 1c v01d makeSpeak( Pess1mist1cObject ob j ) System.out.println( obj.speak() ) ; } publ1C v01d makeSpeak( OptimisticObject obj ) System .out . println( obj.speak( ) );
I
}
public v01d makeSpeak( Introve rtedObject obj ) ( System.out.println( obj . speak() ): }
pub l ie void makeSpeak( ExtrovertedObject obj ) ( System.out.println( obj . spe ak() ) : }
Pess1m1sticObject, OptimisticObjeet . IntrovertedObjec t e Ext rovertedObject estão todos rel acionados, pois lodos eles são objetos Persona 11 tyObj eet. A capaci dade de substituição e o polimorfi smo de inclusão pennitem que você escreva um método para manipu lar lodos os tipos de objctos Persona1 1t yObject: pu bl 1c void makeSpeak( PersonalityObjec t obj ) System.out.p rin tln( obj.speak() ) ; }
I
Po lim o rfismo: aprendendo a prever o futuro
127
A capac idade de substit uição permite que você passe qualq uer objelo Persona I i tyObject para o método e o polimorfi smo gara nte que o metodo correto seja chamado na instância. O polimorfismo chamará o método com base no tipo verdadeiro da instância (Optimi s t icObjec t. IntrovertedObject . ExtrovertedObject ou Pess imisticObject) e não cm seu tipo aparente (Persona 1i tyObject). O pol imorfis mo de inc lusão é lltil porque di minui a quantidade de código que precisa ser escrito. Em vez de ter de escrever um método para cada ti po concreto de Personal i tyObject, você pode simplesmente escrever um método que manipule todos os tipos. O polimorfismo de incl usão e a capacidade de substituição permitem que makeSpeak() funcione cm qualquer objeto que 'sej a um ' Persona 1ityObj ect. O po limorfismo de inclusão torna mai s fácil adicionar novos subtipos em seu progra ma, pois você não precisará adicionar um método especificamente para esse novo tipo. Você pode simplesmente reut ilizar makeSpeak{). O polimorfismo de inclusão também é interessante porque faz parecer que as instâncias de Personal i tyObjec t exibem muitos comportamentos diferentes. A mensagem apresentada por makeSpeak () será di ferente de acordo com a entrada do método. Através do uso cuidadoso do p0li morfismo de inclusão, você pode mudar o comportamento de seu sistema, introduzindo novas subclasses. A melhor parte é que você pode obter esse novo comportamento sem ler de alterar nen hum cód igo já existente.
O polimorfismo é o mot ivo pelo qual você não deve associar automaticamente herança com reutili zação de implementação. Em vez disso, você deve usar herança principal mente para permitir um comportamento polimórfico através de relacionamentos com capacidade de substituição. Se você defin ir corretamente os relacionamentos com capacidade de subst ituição, a reutil ização será automát ica. O poli morfismo de inclusão perm ite que você reutil ize a classe base, qualquer descendente, ass im como os métodos que usam a classe base.
Agora, você provavelmente já entende o mecani smo, mas por que desejaria usar pol imorfi smo de inclusão? Considere a hi erarquia a seguir: publi c abstract class Baselog {
II al gumas constantes úte is ; não s. preocupe com a s i ntaxe private private private private private
f inal f ina l f i nal f inal f inal
static static stat i c s tatl c s t atlc
String String St r ing St r ing St r ing
DEBUG INfO WARNING ERROR fATA L
• "DEBUG'" •
- MIN fO'" •
• "WARNING" ; • 8ERRQR"; • 8fATAl'" •
java.tex t . Da teformat df • java .text.Oate Fonmat.getDateTimeInstance( ) ;
Dia 6
public void debug( St ring message ) { log( message, DEBUG, getDate() ); } public void info( Stri ng message ) { log( message , IN FO . getoa teO , ; } public voi d warn;ng( St ring message ) ( l og( message , WARNING. getDate() ) ; }
publ ic void error( String message ) ( log ( message , ERROR , ge tDate() ); } publi c voi d f atal ( String message ) ( log( message , FATAL, getoateO ); }
II
cr ia uma indicaçao de tempo protected String getDateO { java.u t il. Date date" new java.util.DateO; return df.format( date ); }
II
permite que as s ubclasses def i nam como e onde vão gravar o l og protected abstract vo i d l og( St ring mess age, String leve I . St r i ng t ime ,;
}
BaseLog é um log abstraio que define a interface pública de um log, assim como alguma implementação. Ba se Log é abstraIo porq ue cada desenvol vedor precisa personalizar o modo como o log é gravado. Todo desenvolvedor de Baselog deve definir o método logO. Tomando a classe abstrata, você garante que todo desenvolvedor implemente as subclasses corretamente. Tal estratégia permite que você rellti Iize o projeto do log entre muitos aplicali vos diferentes. Quando um novo aplicativo aparecer, você poderá simpl esmente fornecer a implementação necessária para esse apli cat ivo. Não há necessidade de criar um novo projeto de logo Em vez disso, você pode reutilizar o projeto de log delineado em Baselog, fornecendo implementações personali zadas. publi c cla ss Filelog extends Baselog { private ja va .i o.Pri ntWriter pw ; public Fi l elog ( Stri ng filename )throws java.i o. IOExcepti on { pw· new java.io . PrintWriter( new java.i o. FileWriter( f ilename ) , ; } protected void log( St r ing message , String leveI . Stri ng ti me) { pw . pr in tln( l eve l + "; " + t ime + + message ); M;
M
Po lim o rfismo: aprendendo a prever o futuro
129
pw. flush(); }
public void close() ( pw.c1ose() ; }
pu bl i c class Screenlog ex tend s Baselog ( protected void log( St ring message , String leve l , String time) ( System. out.println( l eve l + "; " + ti me + ": " + message ) ; } }
Fi 1elog e Screenlog herdam de Baselog e implementam o método 1og(). Fi 1elog grava em um arqu ivo, enquanto Screenlog escreve na tela. Lembrando do exemplo Emp l oyee do Dia 4, é razoável suspe itar que exi sta urna classe que sa iba corno recuperar objetos Emp10yee a partir de um banco de dados: pub1ic cla ss Empl oyeeDatabaseAccessor ( private Baselog erro r_log ; public Empl oyeeDa t abaseAccessor( Basel og l og ) throws InitDBException { error_log • l og; try ( II a conexão com o banco de dados I ca t ch( DBExcept ion ex ) I error_l og. fata l ( "cannot access databa se: " + ex.ge tMessage() ); throw new Ini tDBException( ex.getMessage() ); }
}
public Emp10yee ret riev eEmp1oyee( St r ing first name ,String la st name ) throws EmployeeNotFoundException { try { II tenta re cuperar o fu ncionãrio return nu11; } catch ( Emp1oyeeNotFoundExcept ion ex ) { error_1og.warning ( "cannot loca te employee: " + 1ast_name + " , " + f irst_name ,; throw new EmployeeNotFoundException( 1ast_name, first name , ; }
}
II etc. , cada método usa error_l og para registrar erros }
Dia 6
A classe EmployeeDatabaseAccessor recebe um objeto Base l og como argumento em seu constru-
tor. Uma instância usará esse log para gravar todo e qualquer evento importante. Considere o método mainO a seguir: public static void ma i n( Stri ng [] ar gs ) { Baselog 10g • new Screenlog (); Emp l oyeeDatabaseAccessor eda = new EmployeeOa ta baseAccessor ( 10g ) ; Employee emp· eda. retri eveEmployee ( MEmployee" ,
"Mr .~
)
}
Concebivelmente, o método mai n() poderia passar qualquer subclasse de Ba se l 09 para Empl oyeeDatabaseAccessor. Um aplicat ivo poderia fazer o mesmo. EmployeeDatabaseAc cessor é à prova de futuro - no que di z respeito ao registro. Talvez no futuro você precise de um arq uivo de log que funcione a cada 24 horas ou um que crie um nome de arquivo usando a data. Ta lvez outro log faça registros em um manipulador de erros, que receba informações da rede. Quem pode ter certeza? Entretanto, com O polimorfismo de inclusão, você está pronlo. Sem pol imorfismo de inclusão, você pred saria de um construtor para cada tipo de log que quisesse fazer o accessor usar. Entretanto, isso não pára aí. Você também precisaria trocar cód igo dentro do accessor. para que ele soubesse qual log deveria usar. Um objeto EmployeeOataba seAccessor que não usasse polimorfi smo, mas quisesse suportar mui tos logs di fe rentes, poderia ser como segue: publ i c class Empl oyeeOatabaseAccesso r { pr ivate Flle log f il e_log; pr ivate ScreenLog screen_l09; priva te i nt log_type ; / / a I gumas cons tantes ' útei s' private f i nal static i nt FILE_LOG "O ; private final static i nt SCREEN_LOG " 1; pu bl iC EmployeeData ba seAccessor{ File log 109 ) throws InitDBException { f ile_l og • 109 ; 109_type • FI LE_lOG ; inH(); }
publiC EmployeeDataba seAccesso r( Sc reen Log 10g ) th rows Ini tOBException { sc reen_l og • log ; l 09_type • SCREEN_LOG: i nH{): }
publ i C Employee retrieveEmployee ( St r i ng f i r st_name . String la st_name )
Po lim o rfismo: aprendendo a prever o futuro
13 1
throws EmployeeNotFoundExcept ion I t ry { II tenta recuperar o funcionár io return nu11; ) catch( EmployeeNotFoundExcept ion ex ) I if( 1og_type "" FIlE_lOG ) { file_1og.warning( ·cannot locate employee: " + la st_name + ", " + first_name ); J else if ( log_type ,,~ SCREEN_lOG ) I screen_1og.warning( "ca nnot locate employee: " + last_name + ", " + first_name ); }
throw new EmployeeNotFoundException( last name , first name ); } }
private vold lnlt () throws InitOBException { try { II ini cializa a conexão com o banco de dados ) catch( OBException ex ) { if( log_type •• Fl LE_lOG ) { file_log . fatal( "cannot access database : • ex.getMessageO ); ) else if ( log_type ... SCREEN_lOG )1 screen_log.fat al( ·cannot access database: ex.getMessageO ) ;
+
M
+
}
throw new In itOBException( ex.getMessage(} ); }
}
II etc. Cada método usa error_log para registrar erros }
Você precisará atual izar Emp 1oyeeDa tabaseAccessor sempre que quiser adicionar suporte a um novo log. Agora, qual versão você gostaria de manter?
Polimorfismo paramétrico o polimorfis mo paramétrico permite que você crie métodos e tipos genéricos. Assim como o polimorlismode incl usão, os métodos e tipos genéricos pennitem que você codifique algo uma vez e faça isso traba lhar com muitos tipos diferentes de argumentos.
Métodos paramétricos Embora o polimorfismo de inclusão arete o modo como você vê um objeto, o polimorfi smo paramétrico areia os métodos. O polimorfismo paramétrico penn ite que você programe métodos
Dia 6
genéricos retirando a referência de declarações de tipo de parâmetro até o momento da execução. Considere o método a segui r: lnt add(lnt a, i nt b) add() recebe dois intei ros e retoma a soma. Esse método é muito explícito; ele recebe dois inteiros como argumentos. Você não pode passar dois números reais para esse método ou dois objelOS matriz. Se você tentar, obterá um erro em tempo de com pilação. Se você quiser somar dois números reais ou duas matrizes, então deve criar métodos para cada ti po: Matrix add_matrix(matrix a , matrix b) Rea l addJeal (real a , rea l b) etc., para cada tipo que você queira somar. Seria conveniente se você pudesse evitar ter de escrever muitos métodos. Primeiro, ter de escrever muitos métodos torna scus programas maiores. Você precisará separar um método para cada tipo. Segundo, mais código leva a mais erros e a mais manutenção. Você não quer tornar a manutenção mais difícil do que precisa ser. Terceiro, ter de escrever métodos separados não modela • add{) naturalmente. E mais natural pensar apenas em tennos de add{) e não de add matrix() e add_real ().
O polimorfismo de inclusão apresenta uma solução para o problema. Você poderia declarar um ti po chamado addab le, que teria um método que soubesse como somar-se a outra instância de addable.
O t ipo poderia ser como segue: publ i c abs tract class Addable ( public Addable add(Addable) ; J
O novo método seria como segue: Addable add_addable(Addable a , Addable b) Retu rn a.add(b)
NOTA
Às vezes, o exemplo ant erior é referido com o polimorfismo de funçAo.
Está tudo bom, tudo bem. Você s6 precisa escrever um método para somar, entretanto o método funciona apenas para argumentos de Addabl e. Você também precisa certi fica r-se de que os objetosAddabl e passados para o método sejam do mesmo tipo. Tal requ isito é propenso a erros e contrário ao que a interface implica. De q ualquer modo, você realmente não resolveu o problcma
Po lim o rfis mo : aprendendo a preve r o futuro
133
original. Você ai nda precisará escrever métodos para cada tipo que queira somar, que não seja do ti po Addabl e. Nem tudo que você desejará somar será Addable.
É aí que o polimorfismo paramétrico entra em ação. O polimorfis mo paramétrico permi te que você escreva um e apenas um método para somar todos os tipos. O pol imorfismo paramétrico retarda a declaração dos tipos dos argumentos. Considere o método reescri to para ti rar proveito do polimorfismo paramétrico: ,dd([T] "
[T] b) , [T]
[T] é um argumento exatamente igual a a e b. O argumento [T] espec ifica o tipo de argumento de a e b. Declarando um método dessa maneira, você adia a defini ção do tipo dos argumentos até o momento da execução. Você também notará que a e b devem ter o mesmo [T].
Internamente, o método pode ser como segue: [T] ,dd([T] " return a + b j
[T] b)
O polimorfis mo não é mágico. Ele ainda espera que o argumento tenha determ inada estrutura. Neste caso, qualquer argumento que você passe deverá definir + para esse tipo.
NOTA
Determinada estrutura pode ser a presença d e certo método ou operador corretamente definido.
Tipos paramétricos Levado à sua conclusão extrema, o poli morfismo paramétrico pode estender seu alcance aos próprios tipos. Assim como os métodos podem ter parâmetros paramétricos, os ti pos podem ser eles próprios paramétricos. Consi dere o TA O Queue defin ido no Dia 2: Queue [T] void enqueue([T]) [T] deq ueue O boolean isEmpty() [T]
,oekO
O Queue é um tipo parametrizado. Em vez de escrever uma classe de fil a para cada tipo que gostaria de enfilei rar, você simplesmente especi fi ca os tipos de elementos que gostaria de enfi leirar, para conter dinamicamente em tempo de execução. Originalmente, você poderia dizer que o objeto Queue era um Queue de Objec t oAgora, oobjeto Queue pode ser um Queue de qua lquer ti po. Assi m, se você quisesse annazcnar objctos Empl oyee, fa ria a seguinte declaração: Queue[Emp l oyeel employee_queue
=
new Queue [Employee] :
Agora, quando você usar Queue, só podem usar enqueue() e dequeue () nas inst,'incias de fu ncionário.
Dia 6
Se tipos paramétricos não forem possíveis, você precisaria escrever uma fil a separada para inteiros, outra para reais e ai nda outra para alien igenas do espaço. Em vez di sso, usando tipos parametrizados, você pode escrever o tipo uma vez, neste caso uma fila , e usa- lo para conter todos os tipos possiveis.
NO TA
Polimorfismo paramétrico soa bem no papet, mas há um problema: suporte. Para aq ueles que estão famitiarizados com Java, os exemplos anteriores podem parecer estranhos. Na versão 1.3, a linguagem Java não tem suporte nativo a tipos parametrizados ou polimorfismo paramétrico em geral. Você pOde imitar tipos parametrizados, mas o preço na efi ciência é bastante alto. Existem algumas ext ensões Java disponiveis para suporte a polimorfismo paramétrico, entretanto nenhuma de las foi oficialmen te sancionada pe la Su n. A sintaxe dos exem plos anteriores é completamente inven tada. Entre tanto, ela dem onstra as idéias adequadamente.
Sobreposição A sobreposição é um tipo importante de pol imorfismo. Você viu como cada subclasse de Pe r sonalityObject sobrepôs o método speakO no início deste dia. Ent retanto, você vi u detalhadamente um exemplo ainda mais interessante de sobreposição e polimorfismo no Dia 5. Especificamente, considere as definiçõcs de classe MoodyObject e HappyObject : publ l c class MoodyObject (
II
retorna o humor protected Stri ng getMood{) ( return "moody " ;
,
II
pergunta ao objeto como ele se sent e public void queryMood() { System , out.println("I feel " + getMood() + " today! ");
,
,
public cl ass HappyObject extends MoodyObject {
II
redef i ne o humor da classe protected String getMood() { return "happy";
,
II
especialização public voi d laugh() (
Po lim o rfismo: aprendendo a prever o futuro
135
Sys tem. out. pr; nt 1n ("hehehe. .. hahaha... HAHAHAHAHAHA!!!!!"); }
}
Aqui, você vê que Ha ppyObject sobrepõe o método getMood() de MoodyObject. O interessante é que adefi nição de MoodyObject de queryMood( ) faz uma chamada a getMood () internamente. Você notará que HappyObjec t não sobrepõe o método queryMood(). Em vez disso, HappyObjec t si mplesmente herda o método como um método recursivo de MoodyObject. Quando você chama queryMood() em HappyObject , o poli morfismo da instância garante a chamada da versão sobrescrita de getMoodO em HappyObject, internamente. Aqui, o pol imorfismo cuida dos detalhes do método a ser chamado. Isso o libera de ter que redefin ir queryMoodO, para que ele chame a versão correta de getMoodO. Posteriormente, você viu como poderia tornar getM oodO abstrato na progenitora: pub1ic abstract c1ass MoodyObjec t (
II retorna o humor pro t ected abst ract St r i ng getMood( ) ;
II pergu nta ao obje t o como el e se sente pub l ic void queryMoodO t System.out.print1n ( "I fee l " + getMoodO + " t oday!M); }
}
Os métodos abstralos são freq Uentemente referidos como métodos adiados, poi s você retarda a defin ição para as classes descendentes. Entretanto, assim como em qua lquer outro método, a classe que define o método abstraio pode fa zer chamadas ao método. Assim como os mélodos sobrepostos, o polimorfi smo garantirá que a versão correta do método ad iado seja sem pre chamada nas subclasses.
Sobrecarga A sobrecarga, também conhecida como polimorfismo ad-llOc, permite que você use o mesmo nome de método para mu itos métodos diferentes. Cada método difere apenas no número e no tipo de seus parâmetros. Considere os métodos a segui r, definidos em ja va.1ang.Math: pub1i c publ i c public pu bl i c
s ta tic s tat ic s tati c s tat ic
int max(i nt a, int b) ; 10n9 max( long a, 10n9 b): fl oat max(floa t a, float b) : double max(doubl e a, doub l e b) ;
Dia 6
Os métodosmax () são todos exemplos de sobrecarga. Você notará que os métodosmax( ) di fe rem apenas no ti po de parâmetros. A sobrecarga é lIt il quando um método não é defi nido por seus argumentos. Em vez disso, o mé·
todo é um conceito independente de seus argumentos. O método transcende seus parâmetros especificas e se aplica a muitos tipos di fe rentes de parâmetros. Peguemos o método max (). max() é um conceito genérico que recebe dois parâmetros e informa qual é ma ior. Essa defini ção não muda se você comparar intciros, n(mlcros com ponto fl utuante, valores duplos ou a ordem da bi· cada de um bando de pássaros. A operação + é outro exemplo de método sobrecarregado. O conceito + é independente de se us
argumentos. Você pode somar todos os tipos de elementos .
Ne TA
Você não pode SObrecarrega r ou sobrepor operad ores na linguagem J ava; entretant o, a l inguagem J ava tem alguma sobreca rg a int erna .
Se a sobrecarga não fosse possível , você teria que fazer o seguinte:
pub l ic publ i c pub1i c publ i c publ i c
s ta t i c s tati c s ta t i c s ta tic stat ic
i nt max_l nt( lnt a. int b } i 10ng max_longe 10ng a. 10ng b ) i f loat max_fl oa t( fl oa t a, floa t b ) i doub1e max_doubl e ( double a , doub l e b } i bi rd max_b i rd ( bi rd a. bi rd b l i
Sem a sobrecarga, você deve dar a cada método um nome exdusivo. Os métodos ma x() não transcenderiam mais seus parâmetros. Max deixaria de ser um conceito abstrato. Em vez disso, você teria de defi nir o método em tennos de seus argumentos. Ter de cscrever o método ma xO dessa maneira não é um jeito natural de modelar o conceito de max (). Isso também fornece ao programador mais deta lhes para ter em mente. ,
E daro que chamar cada método com um nome diferente não é polimórfico. Quando todos os métodos comparti lham o mesmo nome, você obtém comportamento polimórfi co, pois diferen· tes métodos são chamados internamente, dependendo dos ti pos de parâmetros passados. Você pode simplesmente chamar max () e passar seus parâmetros. O poli morfismo cuidará de chama r a método correto internamente. O modo como o polimorfi smo direciona a chamada de método dependc da linguagem. Algumas linguagens solucionam a chamada de método durante a compilação, enq uanto OUlras vinculam a chamada de método dinamicamente, em tempo de execução.
Po lim o rfismo: aprendendo a prever o futuro
137
Conversão Conversão e sobrecarga freq üentemente andam lado a lado. A conversào também pode fazer com que um método pareça como se fosse polimórfico. A conversão ocorre quando um argumento de um tipo é convenido para o tipo esperado, intemamenle. Considere a definição a seguir: public f1 0at add( f10at a, f10at b ); add() recebe dois argumentos float e os soma. O segmento de código a segu ir cria algumas variáveis inteiras e chama o método addO: int iA " 1; int i B • 2: add(iA,iB); ,
Entretanto, o método add() solic ita dois argumentos float. E aí que a conversão entra em ação. Quando você chama add () com argumentos i nt, os argumentos são convcn idos em valores f10a t pelo compilador. Jsso signi fica que, antes que os argumentos i nt sejam passados para add (), primeiro eles são convert idos em valores f1 oat. Os programadores Java reconhecerão essa conversão. Assi m, a conversão faz o método addO parecer polimórfico, pois O método parece funci onar para valores f 1oa t e int. Conforme você vi u na última seção, também seria possivel ter um método add sobrecarregado, da forma: pub1ic int add(1nt a, 1nt b); Nesse caso, add (i A, 1B) não resultaria em conversão. Em vez disso, o método add () corretamente sobrecarregado seria chamado.
Polimorfismo eficaz Assim como todos os outros pil ares, o polimorfismo eficaz não aco ntece por acidente. Existem alguns passos que você pode executar para garantir um pol im orfi smo eficaz. O primeiro passo para o polim orfi smo eficaz é ter encapsulamento e herança eficientes. Sem encapsu lamento seu código se torna facil mente dependente da implementação de suas classes. Não pcnnita que o encapsu lamento seja destruido. Se o código se tornar dependente de alguns aspectos da implementação de uma classe, você não poderá conectar uma subclasse que refaça essa implementação. Um bom encapsulamento é o primeiro passo para o polimorfi smo.
Dia 6
É imponante notar que, nesse con texto, in terface é um pouco dife rent e da noção de interfaces Java, embora sejam semelhantes. Aqui, o termo interface é usado para d escrever a lista de mensagens q ue você pode enviar para um objeto. Todas essas mensagens compreendem a interface pública de um objeto. Uma interface Java também d efine as mensagens que você pode enviar para um o bjeto Java. Quando uma classe Java implementa uma interface, todos os m étodos da int erface se tornam pane da interface públ ica global da classe. Enlretanto, a int erface J av a não é a única maneira de definir as mensagens que você pode enviar para um objeto. Na linguagem Java, qualquer método público d efinido na definição da classe se to rnará pane da interface pú blica do objeto. Isso significa que. se uma classe implementar uma interface e definir métodos públicos adicionais. os dois conjuntos d e métodos se tornarã o parte de sua interface pública. Usar interfaces Java ao programar é considerada uma boa prática, pois isso separa a definição da interface da implementação da classe dessa interface. Quando você separa as duas, muitas classes de outro m odo não relacionadas poderiam implementar a mesma interface. Assim como a herança, os objetos que compartilham u ma interface comum também podem tomar parte em relacio nam entos com capacidade de substituição, mas sem ter de fa zer parte da mesma hierarquia d e herança .
A herança é um fator importante no polimorfi smo de inc lusão. Sempre tente estabelecer relacionamentos com capacidade de substituição programando o mais próximo possível da classe base. Essa prática pennitirá que mais tipos de objelos participem de seu programa. Um modo de estimular a capacidade de substituição é através de hierarquias bem pensadas. Mova as características comuns para classes abstraias e programe seus obj etos para lIsar a classe abstrata e não uma descendente concreta específi ca. Desse modo, você poderá introduzir qualquer descendente em seu programa.
DICA
Para obter um polim orfis mo eficaz, siga estas dicas: • Siga as dicas d o encapsu lamento e da heran ça efi ca zes. • Sempre progra me para a interface e não para a im plementação. Programando para uma interface, você define especifi camente quais tipos de objetos podem participar de seu progra ma. Então. o pOlimo rfismo garantirá que esses o bjetos participem corretamente. • Pen se e programe genericamente. Deixe o polimorfi smo se preocupar com os det alhes especificos. Se você deixar o pOlim orfi smo faz er seu trabalho. não precisa rá escrever muito código. Ele cuidará dos detalhes para você! • Defina a base do polimorfismo estabelecendo e usando relacionamentos com capacidade de substituição. A capacidade de substituição e o polimorfismo garantirão que você possa adicionar novos subtipos em seu programa e que o código corret o será executado, quando esses sUbtipos fo rem usados.
Polimorfismo: aprendendo a prever o futuro
139
• Se sua linguagem fornece uma maneira de separar completam ente a interface e a implementação, favoreça esse m ecanismo em detri mento da herança. Um exemplo d e m ecanism o que permite definir e herdar interface sem i mplement ação é a Java Int erfa ce. Separar os dois permite uma capacidade de substituição mais fl exível, obtendo-se assim mais oportunidad e de polimorfismo . • Use classes abstratas para separar a interface da implementação. Todas as classes não-fo lha devem ser abstratas; programe apenas para essas classes abstratas.
A discussão anterior focalizou muito as linguagens fortemente tipadas, como Java. Em uma linguagem fortemente tipada, você deve deciarar explicitamente o tipo de uma variável. Entretanto, algumas linguagens orientadas a objetos, como Sm alltalk. não têm esse re quisito. Em vez disso, tais linguagens são dinamicamente tipadas. Tipagem dinâm ica significa que você não precisa indicar explicitamente o tipo de uma variável, ao criá-Ia. Em vez di sso, o tipo é determi nado dinamicamente, em tempo de execução. Assim, basicamente, to da variável é polimórfica . O polimorfism o é um pouco mais simples em linguagens dinamicamente tipadas. As variáveis são automaticamente pol imórficas, pois elas podem conter qualquer valor. Desd e que o objeto tenha o m étodo esperado, ele pode trabalha r de forma pOlimórfica. E claro que tudo será destruido, se você tentar chamar um m étodo que não existe ! As l inguagens tipadas são um pouco mais rigorosas. As linguagens di namicamente tipadas permit em que você trate um objeto de forma polim órfica, desde que ele tenha o m étodo em que você está interessado. O objeto não precisa pertencer a uma hierarquia de herança especifica. As linguagens fortemente tipadas exigem que o objeto pert ença a uma hierarquia de herança especifica. Entretanto, os dois casos não são tão diferentes assim. O comportamento é que real· m ente define um tipo; as linguagens tipadas exigem apenas a presença de todos os comporta mentos definidos. Assim, os conceit os por trás do polimorfismo em lingua· gens fortemente tipa das e dinãmicas são os mesmos. Tudo se re duz a um objeto que sa be com o executar algum comportamento. O foco nas linguagen s tipadas é d eliberado. Focalizar diretamente a forte tipagem o obriga a se concentrar no tipo, sem perder os detalhes. Se você pode entender o polimorfism o em uma linguagem tipada, então certamente pode entendê-lo em uma linguagem não tipada. O inverso parece não ser verdadeiro I A escolha de foca lizar o tipo também é pragmática. A maioria das principais l inguagens orientadas a objetos é fortemente tipada.
Armadilhas polimórficas Ao se usar polimorlismo, ex istem três annadil has principai s a serem evitadas.
Dia 6
Armadilha 1: mover comportamentos para cima na hierarquia Mui to freqUentemente, desenvolvedores inexperientes moverão comjX)rtam entos para cima na hierarquia, para aumentar o pol imorfismo. O entusiasmo em tratar tudo de maneira polimórfica pode cegar faci lmente um desenvolvedor e resultar em hierarquias mal projetadas. Se você mover um comportamento para cima demais em uma hierarquia, nem todos os descendentes poderão suportar o comportamento. Lembre-se de que os descendentes nllnca devem retirar funcionalidade de seus ancestrais. Não destrua uma boa herança para tornar seus programas mais pol imórficos. Se você tiver vontade de mover comportamentos para c ima na hierarquia, unicamente para melhorar o polimorfismo, pare. Você está em território perigoso. Se você encontrar limi tações dema is em sua hierarquia, talvez queira revê-I a. Mova elementos com uns para classes abstratas; mova fun cional idade por toda parte. Ent retanto, não mova métodos para cima na hierarq uia, além do nível onde eles são necessários pela primeira vez. Nilo se habitue a mover comportamentos por capricho, simplesmente para adic ionar suporte polimórfico. Certifiq ue-se de que você tenha outro moti vo válido para a mudança. Você pode ter sorte a lgumas vezes. mas a prática o pegará posteriormente, e maus hábitos de programação são dificeis de perder. Ao desenvolver suas hierarquias, é importante considerar a evolução em potencia l das classes, com o passar do tempo. Você pode divid ir a hierarquia em níveis func ionai s. Com o passar do tempo, você pode evoluir sua hierarq uia, adicionando novos níveis funciona is, quando eles forem necessários. Entretanto, você só deve especular com base nos requi si tos fut uros que conhece. Exi ste um número inl1nito de 'e se' indefi nidos. Planeje apenas as eventua lidades que você conhece.
Armadilha 2: sobrecarga de desempenho Tudo tem um preço. O verdadei ro polimorfismo sofrerá certa sobrecarga de desempenho. O polimorfismo não pode competir com um método que conhece seus argumentos estaticamente. Em vez di sso, com o polimorfismo, devem haver verificações em tempo de execução. Para o po limorfi smo de inclusão, a implementação real do objeto para o qual você envia mensagens deve ser determinada em tempo de execução. Todas essas veri ficações levam tempo para terminar e são mais lentas em comparação aos valores que conhecem seus ti pos estaticamente. As vantagens da manutenção e da nexibilidade do programa devem compensar qualquer perda de desempenho. Entretanto, se você estiver escrevendo um aplicativo em que o tempo seja importante, talvez precise tcr cuidado ao usar pol imorfismo. Entretanto, mantenha o desempenho em perspectiva. C rie uma implementação 00 limpa, trace o perfil da implementação e oti mize cuidadosamcnte o desempen ho, onde o traçado do perfil revela problemas.
Po lim o rfismo: aprende ndo a prever o f utu ro
14 1
Armadilha 3: vendas
o polimorfismo de incl usão tem uma de fi ciência. Embora seja verdade que você pode passar uma subclasse para um método que está esperando uma classe base. o método não pode tirar proveito de todos os novos métodos que a subc lasse possa adicionar em sua interracc. Porcxcmplo, a classe Fi 1elog ad iciona um método c1oseO em sua interface. O método retrieveEmpl oyeeO de EmployeeOatabaseAccessor não poderá usá·lo. Um método programado para acei tar a classe base só saberá manipular a interrace da classe base. A Figura 6.2 mostra como a visual ização de uma instância é relat iva. Optar por ver uma instância de Fi 1eLog como se fosse uma instância de BaseLog é corno colocar vendas nos olhos. Você só tem acesso aos métodos declarados em Base Log. É claro que, se você tratar da instância de Fil e· Log como uma instância de Fi 1eLog, obterá toda a funcionalidade definida ou herdada pela classe Fi 1eLog, como aconteceria normalmente. FIGURA 6 .2
Di/erellles l"isõcs do mesmo objero.
Basel og bI '" new FileLogl'file') •.
Baselog debugU InfoU wll m ingU error() fetaltl
FileLog çloseU
InlJl.lnçjll de Filel og
, "-----'= , ,, ,,
'--'
BllseLog II '" new FileLogl'fi le'):
Assim, quando você adicionar novos tipos de fonna polimórfica, seu código antigo não poderá usar nenhum dos métodos novos. Entretanto, o novo cód igo (ou o código atua lizado) está livre para usar qualquer coisa na inlerrace pública. Novamente, essa armad ilha aponta o moti vo pelo qual um descendente nunca deve remover componamentos de seu progen itor. Um método que conte com o polimorfismo de inclusão só saberá como exercer os métodos defin idos com o ti po que estiver programado para manipu lar. Se estiver fa ltando um comportamento, o método não funcionará. Essa armadil ha também mostra que simplesmente trocar para um novo tipo em seu programa já existente, freqUentemente não é tão fácil quanto poderia parecer. No exemplo do Fi 1eLog, você preci sará encontrar um modo de chamara método cl ose(), quando seu programa tiver termina· do com o log.
Dia 6
Advertências Existe uma advenência imponante a ser lem brada ao se considerar o polimorfismo. Cada linguagem implementa o polimorfismo de uma fonna diferen te. Esta discussão esboçou as de fi ni ções teóricas por trás do poli morfismo. A maioria, se não todas as linguagens, supona polimorfismo de incl usão em algum grau. Por outro lado, poucas suponam o verdadeiro poli morfismo paramétrico. A linguagem Java cenamente não supona polimorfi smo paramétrico. C++ fi nge implemen tá-lo. A maioria das linguagens tem alguma forma de sobrecarga e conversão. Entretanto, a implementação exata va i variar de uma linguagem para outra. Assim, quando você começar a programar de fonna poli mórfi ca, lem bre-se da teoria, mas prossiga cuidadosamente. Você não pode evitar as Iim itações de sua Iinguagem de im pl ementação.
Como o polimorfismo atende os objetivos da 00 o polimorfismo atende cada um dosobjetivosda poo. O polimorfismo produz software queé: 1. Natural 2. Confiáve l
3. Reut ilizável
4. Man utenível 5. Extensíve l 6. Oportuno
O polimorfi smo atende esses objet ivos das seguintes maneiras: •
Natural: o po li morfismo permite que você modele o mundo ma is nattlr'dlmente. Em vez de programar para casos especiais, o pol im orfis mo permite que você trabalhe em um nível mais genérico e conceit uaI. A sobrecarga e o polimorfismo paramétrico perm item que você mode le um objeto ou método em nível conceituai do que esse objetoou método faz e não de quais tipos de parâmetros ele poderia processar. O polimorfi smo de inclusão permite que você manipule tipos de objetos, em vez de implementações específicas.
•
Tal programação gencrica c mais natural, pois ela o libera para programar no nível concei tuai do problema e não nas im plementações específicas. Confiável: o poli morfismo resulta em cód igo confi ável.
Po lim o rfismo: aprendendo a prever o futuro
143
Prime iro, polimorfismo sim pl ifica seu código. Em vez de ter de programar casos especiais de cada tipo de objeto que poderia manipular, você escreve simplesmente um caso. Se você não pudesse programar dessa maneira, teria de atualizar seu código sempre que adicionasse uma nova subclasse. Ter de atualizar código é algo propenso a erros. Segundo, o polimorfismo pennite que você escreva menos código. Quanto menos código você escreve, menores as chances de introdução de erros. O polimorfismo também pennite que você isole partes do código das alterações das subclasses, garantindo que elas tratem apenas com os níveis da hierarquia de herança im portantes para sua função. • Reutilizável: o polimorfismo auxilia a reutili zação. Para que um objcto use outro, ele só precisa conhecer a interface do segundo objeto e não os detalhes da implementação. Como resultado, a reutilização pode ocorrer mai s prontamente. • Manutenível: o po limorfismo auxilia a manutenibilidade. Conforme você já viu, o polimorfismo resulta em menos cód igo. Assim , há menos para manter. Quando voeê precisa manter cód igo, não é obrigado a manter grandes estruturas condiciona is. • Extensíve l: o código poli mórfico é mais extensível. O poli morfi smo de inclusão perm ite que você adicione novos subtipos em seu sistema, sem ter de alterar o sistema para usar o novo subtipo. A sobrecarga perm ite que você adicione novos métodos, sem terde se preocupar com conflitos de atribuição de nomes. Finalmente, o polimorfismo paramétrico pennite que você estenda automaticamente suas classes, para suportar novos tipos. • Oportuno: o pol imorfismo oajuda a escrever código oportuno. Se você pode escrever menos cód igo, então pode distribui r seu cód igo mais cedo. Como o polimorfismo o estimula a programar genericamente, você pode adicionar novos tipos quase que instantaneamente em seus programas. Como resultado, a manutenção e a extensão de seus programas ocorre em um ritmo muito mais rápido.
Resumo Polimorfi smo é ter muitas formas. O polimorfismo é um mecanismo que permi te a um único nome representar códigos diferentes. Como um úni co nome pode representar códigos diferentes, esse nome pode expressar muitos comportamentos diferentes. O polimorfismo permite que você escreva código humorado: código que exibe diferentes comportamentos. Para os objetivos deste livro, você aprendeu a respeito de quatro diferentes tipos de polimorfismo: • •
Polimorfismo de inclusão Polimorfismo paramétrico
• •
Sobreposição Sobrecarga
Dia 6
Embora exista certo desacordo em relação ao polimorfismo na comun idade de 00, esses tipos descrevem algumas das fonnas mais comuns de pol imorfismo. Entender esses tipos proporcionará a você uma boa base na teoria de polimorfismo.
O pol imorfismo de inclusão permi te que um objeto expresse muitos comportamentos diferentes, em tempo de execução. Do mesmo modo, o pol imorfi smo paramétrico permite que um objetoou método opere com vários tipos de parâmetro diferentes. A sobrepos ição permi te que você sobreponha um método e saiba que o polimorfi smo garantirá que o método correto sempre será executado. Fi nalmente, a sobrecarga pennite que você declare o mesmo método várias vezes. Cada declaração difere simplesmente no número e no tipo de argumentos. A conversão faz um método parecer polimórfico, convertendo argumentos nos tipos de argumentos esperados pelo método. O polimorfismo permite que você escreva código mais curto e mais inteligivel, que também é mais fle xível para os requisitos futuros.
Perguntas e respostas P Existem três pilares na programação orientada a objctos. Se cu não usar todos os
três, meu software nllo será OO? R No min imo, você sempre deve usar encapsulamento. Sem encapsulamento, você real-
mente não terá herança eficaz, polimorfismo ou 00 em geral. Quanto aos outros dois pilares, você só deve usá- los quando fi zer sent ido. Não use herança ou polimorfismo apenas para que você possa dizer que os uti lizou em seu programa. A ausência de herança e polimorfismo não significa que um programa é necessariamente nào-OO. Entretanto, você precisa dar uma boa olhada em seu programa, para ver se está desperdiçando uma oportunidade de lIsar corretamente os outros pilares. P Por qu e existe tanto desacordo cm relação ao polimorfismo na comunidade de OO? R Há muito desacordo na literatura que ainda precisa ser acertado. Parece que cada autor tem seu próprio vocabulário . Grande parte desse desacordo provém do fato de que cada linguagem im plementa o polim orfismo de sua própria manei ra. Todas essas diferentes implementações têm dividido a comunidade.
O importante é que você entenda os quatro tipos apresentados hoje. Embora eles possam receber nomes diferentes, esses quatro tipos são bastante reconhecidos.
P A linguagem Java vai suportar polimorfismo paramétrico? R Somente a Sun pode responder essa pergunta . Entretanto, atualmentc existe um Java Specificat ion Request (JSR-OOOO 14), que fo i aceito para desenvolvimento. Ele acrescenta tipos genéricos na linguagem de programação Java. Assim, a coisa está a caminho!
Po lim o rfismo: aprendendo a prever o futuro
145
Workshop As perguntas e respostas do teste são fornecidas para seu melhor entendimento. Veja as respostas no Apêndice A, "Respostas".
Teste I. Qua is são os quatro tipos de polimorfi smo?
2. O que o poli morfismo de inclusão perm ite que você faç a? 3. Como a sobrecarga e o polimorfi smo paramétrico mode lam mais naturalm ente o mundo real? 4. Ao programar, por que você deve programar para uma interface, em vez de para uma implementação? 5. Como o polimorli smo e a sobreposição trabalham j untos? 6. Qual é outro nome para sobrecarga? 7. De fi na sobrecarga. 8. Defi na polimorfis mo paramétrico. 9. Quais são as três armad ilhas associadas ao pol imorfi smo? 10.
Como o encapsulamento e a herança afetam o polimorfismo de inclusão?
Exercícios I. Dê um exemplo rea l de situação de programação onde você acha que poderia usar polimorfi smo de inclusão. Pode ajudar pensar em algo que você tenha programado anteriormente, que pudesse ti rar prove ito do pol imorfi smo. 2. Dê um exem pl o de conversão. Explique por que se trata de conversão. 3. Exam ine as APl s Java. Encontre um exempl o de sobrecarga e ex pliq ue-o. Em seguida, encontre uma hierarqu ia de classes que você poderia destacar para polim orfi smo de inclu são. Identi fiq ue a hierarq uia e expliq ue como você pode aplicar polimorfismo de inc lusão nela.
SEMANA
1
DIA Polimorfismo: hora de escrever algum código Ontem, você aprendeu a respeito do polimorfi smo. Você deve ter um bom entendi mento dos quatro diferentes tipos de polimorfismo. Hoje, você ganhará experiência prâtica com o polimorfi smo, alravês de vários exercícios de laboratório. No final da lição de hoje, você deverá se sentir à vontade com a teoria apresentada no Dia 6, " Polimorfismo: aprendendo a prever o futuro", Hoje você aprenderá:
• Como aplicar as diferentes formas de polimorfismo •
Como escrever software à prova do fu turo
•
Como o polimorfi smo pode ajudá- lo a evitar a troca da lógica
laboratório 1: aplicando polimorfismo No Dia 5, o Laboratório 2 apresentou uma hierarquia de funcionári os. A Listagem 7.1 apresenta uma classe base E111> loyee ligeiramente alterada.
LISTAGEM 7.1
Empl oyee .j ava
publi C abstracl class Employee { private String f i rst_name ; private String las t _name ;
Dia 7
1 148
LISTAGEM 7 .'
Empl oyee. java (continuação)
pri va te doub le wage : public Empl oyee ( Stri ng first_name. String la st_name . double wage ) I th is.f irst_name = first name; th is . las t name z last name; this.wage • wa ge : }
public double getWage() { return wage: }
public String getFirstName() { return fir st_name; }
publ iC Str ing getlastName() { return last_name : }
publiC abstract double ca lculatePay( ) ; public vo id printPaycheckO I String full_oame " l ast_name + + first_name ; System. out. pr i nt l n( "Pay: + full _name + " $" + cal cul atePay() ): lO,
"
M
} }
A nova classe Empl oyee tem agora um método abstrato ca 1cul atePay() . Cada subclasse deve defini r sua própria imp lementação de ca 1cu 1atePay (). As listagens 7.2 e 7.3 apresentam duas possíveis subclasses. LISTAGEM 7 .2
Corrm i ss ioned Emp loyee.java
publ i c cl ass CommissionedEmployee extends Employee { priva te double conmi ss i on : II o custo por uni dade private int units: II contro la o número de unidades vendidas publiC CommissionedEmployee ( String first_name . String la st_name . double wage , double conmission ) { supe r ( fir st_name . l ast_name. wage ): II chama o const ruto r origina l para in ic ia lizar corret amente
Polimorfismo: hora de escrever algum código
LI STAGEM
7.2
Colt'lt1i ss ionedEmployee.java (continuação ) th lS.commiss i on
=
commission ;
}
public double calculatePay() ( return getWage() + (commission * units ); }
pub11C void addSales( int units ) { th1s.unlts • thiS.units + un1ts; }
publiC lnt getSales() { return units; }
public void resetSales() { unit s • O; } }
LISTAGEM
7.3
HourlyEmp l oyee.java
public class HourlyEmployee extends Employee ( pri vate int hours; II controla o número de horas trabalhadas publiC HourlyEmployee ( String first_name. String last name . double wage ) { supere first_name . last_name, wage ); II chama o construtor origina l para i nicializar corretamente }
public double calculatePay () { return getWage()* hours ; }
publlc void addHours( int hours ) { this.hours • this.hours + hours; }
public int getHours() { re turn hoursj
149
Dia7
1 150
LISTAGEM 7.3
HourlyEmployee.java (continuação )
I public vo id resetHours() I hours = O;
I I Cada subclasse fornece sua própria im plementação de cal cul atePay(). HourlyEm ployee simplesmente calcula o pagamento mu ltipl icando as horas lIâ balhadas pe la taxa horária. Um objeto Conm; ss; onedEmp 1oyee recebe o sa lário-base, mais um bônus por cada unidade vendida. Cada subclasse tam bem ad iciona alguns métodos próprios. Por exemplo, Hour IyEmp 1oyee tem um método para reconfigurar as horas. Do mesmo modo, Conmiss i onedEmployee tem um método para adicionar vendas. Conforme você aprendeu no Dia 4, " Herança: hora de escrever algum código", Conmi ssionedEmployee e Hourly Employee permitem que as instâncias das duas classes compartilhem um relacionamemo com capacidade de substituição. Você pode usar uma instância de Conmi ss ionedEmp loyee ou uma instânc ia de HourlyEmployee, em lugar de Emp l oyee. Entretanto, o que o per li morfismo permite faze r? Considere a classe Payroll, apresentada na Listagem 7.4. LISTAGEM 7.4
Pay r oll. java
public class payroll I private int total_hours; private int t otal_sales ; private double total_pay; public void payEmployees( Employee (] emps ) I for( int i • O; i < emps . length; i ++ ) { Emp l oyee emp = emps [i]; total_pay +z emp.calculatePay{) ; emp.printPaycheck();
I
I pub li c void recordEmployeeln fo( CommissionedEmployee emp ) ( total_sales += emp.getSales() ;
I publiC void recordEmployeelnfo( Hourly6mployee emp ) I total _hours +- emp.getHours();
Polimorfismo: hora de escrever algum código
LISTAGEM
7.4
151
Payroll. java (continuação)
}
public void pri ntReport() I System.out.pr int ln( "Payroll Report:" System.out.println( "Total Hours: " + System.out.println( "Total Sales: " + System.out.prin t ln( "Total Paid: $" +
); total _hours ) ; total_sales ) ; tota l_pay );
} }
ALERTA
Métodos get e set em demasia indicam um mal projeto 00. Em gera l, você quer so licitar os dados de um objeto muito raramente. Em vez disso, você deve pedir para que um objeto faça algo com seus dados. No exemplo de funcionários, ter ia sido uma 00 melhor se passasse para o ob· jeto Emp 1oyee um objeto Report, onde ele pudesse registrar suas horas. vendas etc. Embora isso fosse uma 00 melhor, teria se desviado do exemplo. A 'boa 00' também é relativa. Se você estiver escrevendo ob jetos genéricos que serão usados em m uitas situações diferentes, talvez que ira adicion ar m étodos get / set. para q ue possa manter a interface da classe fáci l de ger enciar.
Considere o método payEmployees( Employee [] emps ). Os relac ionamentos com capacidade de substituição permitem que você passe qualquer subclasse de Emp I oyee para o método. Geralmente, esse método trata objetos HourlyEmpl oyee e CorrmissionedEmployee como simples instâncias de Employee. O polimorfismo é o que torna esse exemplo interessante. Quando os métodos payEmployeesO dizem total_pay += emp.calculatePay() o polimorfismo faz parecer que Employee tenha muitos comportamentos diferentes. Quando emp.ca I cul atePay() é chamado em um objeto que é realmente um HourlyEmp loyee, ca I cu l atePayO calcu la o pagamento, multipl icando a taxa horária pelo número de horas trabalhadas. Do mesmo modo, quando a instância subjacente é um objeto Conmiss i onedEmployee . calcu l atepay() retorna o salário mais todos os bônus de vendas. payEmployeesO é um exemplo de polimorfismo de incll/são. Esse método fu nciona para qualquer funcionário. O método não precisa de código especial, você não precisará alual izá-Io cada vez que ad icionar uma nova subclasse em seu sistema - ele si mplesmente funciona para todos os objetos Emp I oyee .
Dia 7
Métodos como recordEmployeelnfo( CommlsslonedEmpl oyee emp )
e recordEmployeelnfo( Hour1 yEmployee emp ) demonstram a sobrecarga. A .wbrecarga permite que um método pareça ser polimórfico. Por exemplo, ela permite o segu inte: Payroll payroll • new Payrol1() ; Commi ssioned Employee empl = new Corrmi ssionedEmployee( "Mr. ". "Sales ". 25000.00 , 1000.00);
Hou r1 yEmp1oyee emp2 • new HourlyEmployee( "Mr." , "Minimum Wage" , 6.50 ) ; payroll.recordEmp loyeelnfo( emp2 ) ; payroll . recordEmp10yeelnfo( emp l ) j recordEmp 1oyee I nfo () parece ser pai imórfico. pois pode manipular os dois tipos de funcionário. A sobrecarga é um pouco mais limitada que o polimorfismo de inc lusão. Com o polimorfismo de incl usão, você viu que precisava apenas de um método, payEmpl oyees(), para calcular o pagamento de qualquer objeto Emp 1oyee. Independentemente de quantas subclasses de Emp1oyee você introduza, o método sempre funcionará . Esse é o poder do pol imorfismo de incl usão. Os métodos que empregam sobrecarga não são tão robustos assim. Pegue como exem plo o método recordEmp loyeelnfo(). Sempre que você adicionar uma nova subclasse na hierarquia Empl oyee, terá de adicionar um novo método recordEmp1oyeelnfo() para o novo tipo. Embora alguns métodos extras possam ser aceitávei s para uma hierarquia peq uena, talvez você tenha de refazer sua hierarquia, para que possa escrever um método recordEmpl oyee lnfo() genérico, quando o número de subc lasses de Employee aumentar. A Listagem 7.5 fornece um pequeno método principal que executa os métodos Payroll. LISTAGEM 7.5
Payro 11 Dri ver . ja va
pub li C cl ass PayrollDri ver ( publi c static vO l d maln( String [J args ) (
II cr ia o sistema de fo lha de pagamento Payroll payrol1
=
new Payrol l () ;
II cria e atuallza alguns funcion ários COIlI1lissionedEmp1oyee emp l - new Corrmi ssionedEmployee( "Mr.", "Sa l es ", 25000.00 , 1000 .00),
CommissionedEmpl oyee emp2 • new COIl1lli ssionedEmp1oyee( "Hs." , "Sa les - , 25000 . 00, 1000.00),
empl . addSales( 7 ); emp2. addSa les( 5 );
Po lim o rfi s mo: hora de escrever algum códi go
LISTAGEM 7.5
153
Payro110river . java (continuação)
HourlyEmployee HourlyEmployee emp3 . addHours( emp4.addHours(
emp3 .. new Hour l yEmployee( "Hr . " , "Hinimum Wage" , 6.50 ) ; emp4 .. new Hour l yEmployee( "Hs." , "Hin i mum Wage" , 6.50 ); 40 ) ; 46 );
II usa os métodos sObr eca rregados pay roll.recordEmployeeI nfo( payroll.recordEmployeel nfo( payroll.recordEmployeel nfo( payroll.recordEmployeel nfo{
II coloca os
emp2 emp l emp3 emp4
); ); ); );
fu n cio n ~ ri os
em um array Empl oyee [] emps • new Empl oyee [4]; emps[O] .. emp l; emps [l] " emp2 ; emps [2]
=
emp3 ; emps[3] .. emp4;
payroll. payEmployees ( emps ) i payroll.printReport() j
I I A Figura 7. 1 mostra a saída do método pri ncipal. FIGURA 7.1 A Si/ida correia de Payro 11 Ori ver.
Se você percorrer o código e calcu lar manual mente o pagamento de cada funci onário, verá que payEmp 1oyees () paga o valor correiO. Do mesmo modo, todas as info nnações de funcionário são correlamente gravadas.
Dia 7
Exposição do problema No D ia 5, você trabalhou com objetos MoodyObject. A Lis tagem 7.6 aprese nta uma classe HoodyObject ligeiramente mod ificada. LISTAGEM
7.6
MoodyObject . java
publiC abstract class MoodyObject (
II
retorna o humor protected abstract String getMood() ;
II
pergunta ao objeto como ele se sente publ ic void queryMood() ( System.out.println( " I feel " + getMood() + " today!!") :
I I As listagens 7.7 e 7.8 apresentam duas subclasses: HappyObject e SadObject. LISTAGEM
7.7
HappyObject . java
public class HappyObjec t extends MoodyObject (
Ilredefine
o humor da classe protected String getMood() I return "happy·;
I Ilespecialização public void laugh() { Sys tem . ou t. pri nt l n (" hehehe. .. hahaha. .. HAHAHAHAHAHA!!!!!") ;
I I
LISTAGEM
7.8
SadObject.java
publiC c l ass SadObject extends MoodyObject {
II
redefine o humo r da classe protected Stri ng getMood() { return "sad":
I II
especialização
Polimorfismo: hora de escrever algum código
LISTAGEM 7 .8
155
SadObject.java (conti nuação)
publiC vo ld cry{) ( Sys t em.out.println("'wah' ' boo hoo ' 'weep' ' sob ' ' weep'"); J J
Sua tarefa é praticar o pol imorfismo. Escreva uma classe Psychiatri stObject. Psychiat ri s tObj ect deve ter três métodos. exami ne () deve pegar qualquer instância de MoodyObject e perguntar como ela se sente. Psychi atri stObjec t também deve ter um método observe() sobrecarregado. observe{) deve chamar os métodos cry() ou laughO do objelo. A classe PsychialristObjecl deve tecer um comentário médico para cada comportamento. Certi fi que-se de usar a classe PsychiatristDriver forn ecida, para testar sua solução!
A próxima seção discutirá as soluções do Laborató ri o 1. Não prossiga até concluir o Laboratório 1.
Soluções e discussão A Li stagem 7.9 apresenta uma passivei classe PsychiatristObject. LISTAGEM 7 .9
Psychiatr i stObjec t. java
public cla ss PsychiatristObject {
II usa polimorfismo de inclusão para examinar todos os objetos humor II genericamente pu bl ic vo i d examine(MoodyObject obj ) { Sys tem . out . prlnt ln( "Te11 me, object , how do you feel t oday ?" )i obj . queryMood () ; System . out.print ln() ; J
II usa sobrecarga para observar objetos especificamente. II mas com um método chamado generi camente pub l ic voi d observe( SadObject obj ) ( obj.cry() j Sys t em. out.print ln( "Hnm . .. very . ve ry interest i ng. Some th i ng makes thi s object sad." ); System.out . println()j J
Dia7
1 156
LISTAGEM 7 .9
Psychi atri stObjec t.j ava (continuação)
publiC void observe{ HappyObject obj ) ( ob j . laugh(); Sys tem.out.println( MHmm •.. very . very i nteresti ng. This object seems ve ry System.out .print ln() ;
ha ppy.~
);
J
J examine como ( MoodyObjec t obj ) trata todos os obj etos MoodyObject genericamente. O objeto PsychiatristObject pergunta ao objeto MoodyObject como ele se sente e chama seu método queryMood O. O objeto Psychi atri stObject precisa de um método observe O para cada lipo de objcto MoodyOb ject que gostaria de observar. Após concluir este laboratório, você deverá começar a se sentir à vontade com os mecani smos básicos do polimorfi smo.
laboratório 2: conta de banco aplicando polimorfismo em um exemplo conhecido No Laboratório 2, você vai pegar o que aprendeu no LaboratÓrio 1 e aplicar em um problema li· ge iramente mais compl icado. Este laboratório focaliza a hierarquia BankAccount, apresentada no Dia 5. A hierarquia apresentada aqui continua quase igual àquela apresentada no Dia 5. A única diferença é que agora BankAccount é uma classe abstrata. Você não pode mais instanciar um objeto BankAccount diretamente. Tornar BankAccount abstrata modela mai s precisamente o funcionam ento de con tas bancárias. Quand o você abre urna conta, abre uma conta de cheque ou urn a co nta de mercado fin anceiro. Você não abre uma conta de banco genérica. A Li stagem 7.10 li sta a úni ca mudança na hierarquia. llSTAGEM 7 .10
BankAccount.java
pub li c abst ract cl ass Ba nkAccount { II o restante é igu al J
Polimorfismo: hora de escrever algum código
157
Exposição do problema Neste labormório, você precisa escrever uma classe 8ank. A classe Sank tem vários métodos. As instâncias de 8ank contêm contas. Entretanto, você precisa de uma maneira de controlar a quem penencem as contas. addAccount() permite especificar um proprietário, sem pre q ue você adiciona uma nova conta : publlc YOld addAccount( Str ing name, BankAccount account ); Você pode usar o nome do propri etário para acessar a conta correta posteriormente. totalHol dingsO permite que a classe Bank relate a quantidade total de dinheiro existente no banco: pub l ic doub l e totalHoldings() ; totalHoldingsO deve fazer um laço por todas as contas e totalizar o valor mantido no banco. total Accounts () permite que você consu lte a instância de Bank para ver quantas contas ela poss ui correntemente: publiC int totalAccounts(); deposi tO permite que você deposite fundos em uma conta bancária especifica: public yoid deposite Stri ng name, double amount ); depos i tO é um método de conveniência que o li bera de ter de recuperar uma conta específica, anlesde poder adicionar rundos ne la. Em vez disso, depos i t () pcnn ite que você deposite fundos diretamente através do banco. balanceO permite que você recupere o saldo de uma conta especifica: public double balance( Stri ng name ) Assim como depos i t 0 , ba 1ance() é um método de conveniênc ia. addAccountO armazena uma conta sob determinado nome. Existem várias maneiras de implementar essa fun cionalidade. Entretanto, algumas estratégias são mais fáceis de implementar do que outra s. Para este laboratório, você deverá considerar jaya. ut i I . Hashtab I e. Hashtab I e permite que você armazene e recupere pares chave/Valor. Considere esta APl consolidada: pub1 ic publ ic public publlc
Object get( Object key ); Object put ( Object key, Object ya1ue ); lnt size(); Java . util.Enumeration elementos();
Aqui está um exemplo de Hashtable:
Dia 7
java. ut 11 . Hashtab le tab I e • new java. util . Hashtab I e(); table.put( -LANGUAGE-, "JAVA" ); String name • table.get( -LAHGUAGE " ) ; Esse exemplo armazena ovalar JAVA sob a chave LANGUAGE. Para recuperara valor post eri ormente, você simplesmente chama get() e passa a chave correta . Estudando a APl, você notará que os métodos get () e put () retornam Objecto Assim, se você fosse armazenar uma Stri ng, obteria o valor de volta como um Objecto Na linguagem Java, todos os objetos herdam de Objecto A Hashtable foi escrita para tratar com Object, de m odo que funcionará para tod os os objet os Java. Entre tanto, e se você armazenar um objeto CheckingAccount na Hashtable e quiser tratá-Ia como um objelo Checki ngAccount, após recuperá -lo? Como você fari a isso na linguagem Java? A linguagem Java fornece um modo de transformar uma referência de Object de volta para seu tipo correto. O mecanismo é conhecido como conversão. A instrução a seguir é inválida na linguagem Java: CheckingAccount account • table.get( "CHECKING ACCOUNT" ); Em vez disso, você precisa rá realizar uma conversão, antes de poder armazenar uma referência de Object em uma variável CheckingAccount: CheckingAccount account " (CheckingAccount) table.get( "CHECKlNG ACCOUNT"); Você precisa tomar cuidado enquanto converte. A conversão pode ser perigosa. Por exemplo, a conversão a seguir é inválida: HappyObject o • new HappyObject() ; lable.put( "HAPPY-, o ); (CheckingAccount) table.gel( "HAPPY - ); Quando você converte, deve certificar-se de que o Objecl que está convert endo seja realmente do tipo TYPE . Assim, para este laboratório, quando você recuperar um objeto BankAccount da Hashtable, desejará converter para BankAccount. Como exemplo, considere a converslio a seguir: BankAccount b • (BankAccount) table.get( "ACCOUNTl" }; Se você tentar realizar uma conversã o inválida na linguagem Java, ela lança rá uma exceção ClassCastE)(ception.
Assim como o Laboratório I, o Laboratório 2 fornece um driver para ajudá-l o a testar sua solução. Certifique-se de consultar BankDriver.
A próxima seção discutirá as soluções do laboratório 2. Não prossiga até concluir o Laboratório 2.
Polimorfismo: hora de escrever algum código
Soluções e discussão A Li stagem 7. 11 apresenta lima possível implementação de Bank. LI STAGEM
7.11 Bank.java
publlc cl ass Bank ( private java.utll .Hashtable accounts
=
new java.util.Hashtable();
public void addAccount( String name, BankAccount account ) ( accounts .put( name, account ) ; }
publi C double totaIHoldings() ( double total - 0.0; java.ut il . Enumeration enum = accounts . elements(); while( enum.hasMoreElements() ) ( BankAccount account ~ (BankAccount) enum.nextElement(); total +- account .getBala nce() ; }
return to ta 1; }
public int totalAccounts() ( return accounts.size(); }
publiC voi d deposite Str ing name , double amount l ( BankAccount account - retrieveAccount( name l; if( account !a null ) { account.depositFunds( amount l; } }
public double balancei String name ) { BankAccount aCCQunt * retrieveAccount( name ); if( accQunt !- null ) { return account.getBalance()i }
return 0.0; }
private BankAccount retrieveAccount( String name ) { return (BankAccount) accounts. get{ name l i } }
159
Dia 7
Internamente, essa solução usa java. ut 11 . Hashtab 1e para conter todos os objetos BankAccount. Em vez de forne<::er seu próprio meçan ismo de annazenamento e recuperação, essa implementação tira proveito da reutilização, utili zando as classes fornecidas pe la linguagem Java. ba 1ance (), depos i t (), addAccount () e t ota 1Ho 1di ngs (). todos demonstram o pol imorfismo de incl usão. Esses métodos funcionarão para qualquer subclasse de BankAccount que você possa criar. A pós concl uir este laboratório, você deverá ter uma idéia melhor dos mecanismos de pol imorlis-
mo. No Dia 5, os objetos BankAccount mostraram a conveniência da herança. A herança permitiu que você cri asse subc lasses rapidamente, programando apenas o que era diferente entre as contas. O polimorfi smo sim plifica ainda mais seu código, fornecend o um mecanismo para programação genérica.
laboratório 3: conta de banco usando polimorfismo para escrever código à prova do futuro Por toda a discussão sobre pol imorfis mo, você ouvi u o tenno software â prova do futuro. O que é exatamente soft ware à prova do futuro? Software à prova do futuro é simplesmente um software que se adapta fa ci lmente à mudança nos requisitos. Os requisitos mudam o tempo todo. Quando você começa a escrever um programa pela pri mei ra vez, os requisitos podem mudar, enquanto você aprende mais a respeito do problema que está resolvendo. Uma vez escri to, seus usuários esperarão e exigirão novos recursos de seu software. Se você criar so ft ware à prova do futuro, não terá de reescrevê-lo comp letamente, cada vez que obt iver um novo requisi to. Vamos considerar um exemplo de mudança de requisitos. A Listagem 7. 12 apresenta urna nova classe MoodyObj ect : CarefreeObj ec t. LISTAGEM
7.12
CarefreeObject.java
pub1ic class CarefreeObject extends MoodyObject { II redefine o humor da classe protected Stri ng getMood() { ret urn "carefree j M
}
II especial iz ação pub1ic void whist le{ ) I Sys t em . out. print l n{"whi st1e, whist1e, whistle ... ·); }
}
Po limorfismo: hora de escrever algum código A Li stagem 7. 13 most ra o Psychiatri stOriver atualizado. LISTAGEM 7.13
PsychiatristDriver.java
public class PsychiatristOriver I public sta tic void main( Str ing [] args ) I HappyOb ject happy • new HappyObject(): SadObject sad = new SadObjec t( ) ; Care free Object carefree • new CarefreeObject(); PsychlatristObject psychiatrist z new PsychiatristObject();
II usa polimorfi smo de inclusão psychiatrist . examlne( happy ): psychiatrist.examine( sad ) : psychiatrist.examine ( carefree ) ;
II usa sobrecarga para que possamos observar os objetos psychiatrist.observe( happy ); psychiatrist.observe( sad ); }
}
A Figura 7.2 mostra a saida que você vera ao executar PsychiatristOriver. FIGURA 7 .2
A saída correia de Psychiatri s tOri ver.
161
Dia 7
Aqui , você vê que Psychiatr ;s tObject é à prova do ful uro. A qualquer momento, você pode adi cionar novos objetos MoodyObject, todos com seu próprio comportamento personalizado. PsychiatrlstObject pode simplesmente usar os novos subti pos.
Você pode observar que esse exemplo se concentra no método examineO. O polimorfi smo de inclusão possibilita um software realmente à prova do futuro. Entretanto, se Psychi atri s t Object quiser usar observe () em um novo subtipo, você precisará atualizar Psychiatri stObj ect também.
MoodyObjec t é um exemplo simples. Entretanto, tente imaginar corn o você poderia estender essa idé ia para programas mais complexos!
Exposição do problema Sua tarefa é testemunhar a programação à prova do fut uro em primeira mão. No último laboratóri o, você escreveu uma classe Bank. A classe Bank pode trabalhar com qualquer subtipo de BankAccount. Sua tarefa é criar um novo tipo de BankAccount: a Rewa rd sAcount . Assim como em Savi ngsAccount , a RewardsAccount aplica j uros no saldo. Entretanto, para aumentar o número e o tamanho dos depósitos, o banco gostaria de introduzir um sistema de recompensas. A RewardsAccount cont rola o número de depósitos em relação a certa quantidade de dólares: o nível de depósito para recompensa. Por exemplo, digamos que o nível de depósito para recompensa seja de US$SOO dólares. Sempre que o depositante deposita US$SOO ou mais, ele receberá um poruo de recom pensa.
NeTA
M antenha RewardsAccount simples. Se o nível de depósito para recompensa for de US$500 e o depositante depositar US$500, ele receberá um ponto de recompen sa. Se o depositante depositar US$3.000, ele ainda deverá receber apenas um ponto de recompensa.
Jun to com os métodos definidos por BankAccoun t, RewardsAccoun t também deve fornecer um mecanismo para recuperar e recon fi gurar o numero de pontos de recompensa recebidos. RewardsAccoun t também precisa de um modo para configurar e obter o nível de depósito para recompensa. Para este laboratório, talvez você queira vol tar ao Dia 5 e reler as descrições dc Sav i ngsAccount e BankAccount . Este laboratório também inclui u m RewardsAccountor i ver e um BankOri ver atualizados. Certifique- se de usâ-Ios para testar sua s0lução. Você também desejará ver BankOriver. O BankOri ver demonstra como você pode adicionar um novo tipo de objelo em seu programa, sem ter de atualizar qualquer um dos outros objetos.
Polimorfismo: hora de escrever algum código
ALERTA
163
A próxima seção discutirá as soluções do laboratório 3. Não prossiga até concluir o laboratório 3.
Soluções e discussão A Li stagem 7. 14 apresenta uma possível sol ução para RewardsAccount. LISTAGEM 7.14
Reward sAccount. java
publi c class Rewa rdsAccount extends SavingsAccount { private double min_reward_balance: private int quallfying_deposits: public Reward sAcco unt( double initDeposit. double interest. double min ) { super( i nitDepos it. interest ) : min_reward_balance • min:
I publ ic vo i d depositFund s ( doubl e amount ) { super.deposit Funds( amount ): if( amount >- min_reward_balance )1 qualifying_deposits++: }
I publi c int getRewardsEarnedO I return qualifying_deposits:
I publi c voi d resetRewards() { qual i fyin g_deposi t s • O:
I publ i c doubl e getMi nimumRewardBal ance O { ret urn min_reward_balance :
I publi c vo i d setMinimumRewardBalance( doub le min ) { min reward bal ance : mi n;
I
-
I RewardsAccou nt sobrepõe depos i tFunds O. de modo que pode verificar o saldo e os pontos de recompensa. A classe também adiciona métodos para recuperar o saldo das recompensas, recon fi gurar o saldo, assi m como obter e configurar o nível de depósito para recompensa.
Dia 7
A Listagem 7. 15 apresenta o BankOriver atualizado. LISTAGEM 7 .15
BankOri ver .java
public class 8ankOriver { publlc s t atic void main( String [] args ) { CheckingAccount ca · new CheckingAccount( 5000.00 , 5, 2.50 ); OverdraftAccount oa ~ new OverdraftAccount( 10000.00, 0. 18 ): SavingsAccount sa · new SavingsAccount( 500.00 . 0.02 ): TimedMaturityAccount tma new TimedMaturityAccount( 10000 .00, 0.06. 0.05 ): K
Bank ba nk • new Bank( )j bank.addAccount( "CHECKING", ca ): bank.addAccount( "OVERORAFT", oa ); bank.addAccount( "SAVINGS" , sa }; bank. addAccount ( "TMA" , tma ); Sys tem.out.println( "Total holdings(shou ld be $25500.0): $" + bank.totalHoldings() )i System.out.println( "Total accounts(should be 4): " + Bank.totalAccountsO ) ; RewardsAccount ra • new RewardsAccount( 5000 . 00 , .05 , 500.00 }; bank.addAccount( "REWAROS", ra }; System.out.print ln( "Tota l holdings(should be $30500 .0): $" + bank.totalHoldings() ) ; System .out.println( "Total accounts(shou ld be 5): " + bank. to ta l Accounts () }; bank.depos1t( "CHECKING", 250 . 00 }; double new_balance • bank . balance( "CHECKING " }; Sys t em .out.p r intln( "CHECKING new balance (shou ld be 5250 . 0): $" + new balance ):
I I Para usar a nova classe de conta existem dois passos que você deve dar. Para o primeiro passo, você deve criar seu novo subtipo. Após criar seu novo subti po, você precisa aherar seu programa para criar novas instâncias do objeto. No caso de RewardsAccoun t, você deve atual izar o método mai n () de BankAccount, para criar instânc ias de RewardsAccount. Entretamo, você não precisa mudar mais nada!
Polimorfismo: hora de escrever algum código
165
Quando você est iver escrevendo um programa real, percorrerá os mesmos passos para introduzir novos subtipos em seu programa. Primeiro, você precisará criar o novo subtipo. Segundo, você preci sará al terar seu código para que ele possa criar seu novo subtipo. Mas, é isso. Você não preci sa alterar o restante de seu programa. Posterionnente, você verá maneiras de tomar seus programas tão flexívei s, que poderá nem precisar alterar qualquer código de seu programa para que ele encontre e comece a usar novos subtipos.
laboratório 4: estudo de caso estruturas condicionais Java e polimorfismo A linguagem Java, assi m como muitas outras, fornece um mecanismo de troca. Considere o método day- of- the- week() a seguir: public void day_of_the_week(int day ) { switch ( day ) ( case 1: System.out.pr in tln ( "Sunday· ); break; case 2: System.out.prin tln( "Monday· ) ; break; case 3: System .out.println ( "Tuesday· l; break; case 4: System .out.println( "Wednesday" ); break; case 5: System.out.prin tln( "Thursday" ); break; case 6: Sys t em.out.println( "Friday" l; break; case 7: System.out.print ln{ "Saturday" )i break; default: System.out.p r i ntln{ day + " is not a valid day.· ); break;
I I
Dia 7
o método recebe um parâmetro: um valor inteiro representando o dia da semana. Então, o método testa todos os dias válidos da semana. Se o argumento day corresponder a um dos dias, o método imprimirá o nome do dia. Geralmente fa lando, você usa estnlluras condicionais (case ou i rte1se) para realizar lógica condicionaI. Com a lógica condicional, você procura certa condição nos dados. Se essa condição for satisfeita, você faz algo. Se outra condição for satisfeita, você faz algo inteiramente diferente. Todos osque têm base procedural devem estar familiarizados com tal estratégia de programação. Se você se sente à vontade com estruturas condic ionais, é hora de desaprender um pouco. A lógica condiciona l geralmente é considerada uma má prática de 00. Na verdade ela é tão má, que muitas linguagens 00 não fo rnecem tais estruturas. A lógica cond icional tem uma vantagem; ela o ajuda a detectar um projeto mal feito! As estruturas cond icionais são quase sempre más por natu reza. En tretanto, elas frcqücntemente se insinuam à sua frente , pois aparecem em mu itas formas. Considere um método day_of_the_weekO ligei ramente diferente. public void day_of_the_week( int day ) í iflday"')!
} } } } } } }
System.out.println( "Sunday" ); el se ir ( day == 2 ) I System.out.println{ "Monday" }; else if ( day •• 3 ) { Sys tem.out.print ln( "Tuesday· }; else if ( day •• 4 ) I System.out.println{ "Wednesday" ); else ir ( day •• 5 ) I System.out.print ln( "Thursday· ); else if ( day •• 6 ) I System.out. println( "Fri day" }; else ir ( day =- 7 ) I System . out.print ln ( "Saturday" ) ; else { System.out.printl n( day + " is not a valid day." };
I I Então, o que há de errado com as estruturas condicionais? As estrulUras condicionais são contrárias aos concei tos de 00. Na 00, você não solicita os dados de um objeto e depois faz algo com esses dados. Em vez disso, você sol icita para que um objeto faça algo com seus dados. No caso do metodo day_of_the_weekO, você provavelmente obtem day de algum objeto. Você não deve estar processando dados brutos. Em vez disso, você deve solicitar ao objeto uma representação de string. As estruturas condicionais o obrigam a misturar responsabilidades. Cada lugar que usa os dados terá de aplicar a mesma lógica cond icional.
Po lim o rfi sm o: hora de escrever algum códi go
167
Existem ocasiões em que as cond icionais são absolutamente necessárias. Então, como você detecla ' más ' estruturas condicionais? Existem meios de saber quando uma estrutura condicional 'boa ' fi ca 'ruim'. Se você se encontrar atual izando uma estrutura case ou blocos 1fi el se, sempre que ad icionar um novo subtipo, as chances são de que a estrutura condicional é ' ruim'. Não apenas isso é uma má prática de 00, como também é um pesadelo de manutenção. Você terá que certificar-se deatua lizar cada condi cionai que teste os dados. Exigirá mui to tempo para garantir que você não se esqueceu de atualizar algo!
Corrigindo uma estrutura condicional Considere O método a seguir: pub li c i nt ca l cul ate( Stri ng operação . int operand l. int operand2 ) { if ( operation.equals( "+" ) ) ( return operandl + opera nd 2; ) el se if ( operat1on. eq uals ( "*" ) ) { return operandl *operand2; } el se i f ( operat i on.equals( ~/M ) ) { return operandl loperand2; J el se if ( operatlon. equals( "." ) ) I return ope rand l - ope rand2; ) else ( Sys t em.out.print ln( "invalid oper at ion: • + opera t ion ) ; return O; J J
Tal método poderia aparecer em um problema de calculadora. O método calcul ateO recebe a operação, assim como os dois operandos, como argumentos. Então, ele efetua o cálcul o sol icitado. Sendo assim, como você poderia corrigir o problema? Você pode corrigi-lo com objetos, é claro! Quando você começar a eliminar lógica condicional , comece com os dados que está testando. Transforme os dados em um objeto. Neste caso, você deve criar objetos ad ição, subtração, mult'i plicação e divi são. Todos esses objetos são operações. Assim , todos eles devem herdar de uma classe base comum. Con forme você já viu por todo este dia, a capacidade de substituição e o pol imorfi smo permitirão que você faça algumas coisas inteligentes com esses objetos. Todos os objetos que você deve criar são operações; portanto, você sabe que precisa de uma classe base Operati on. Mas o que uma classe Operation faz? Uma classe Ope rat ion calcula algum valor, dados dois operandos. A Listagem 7. 16 apresenta uma classe Operation.
Dia7
1 168
LISTAGEM 7 .16
Operation.java
publ i c abstract class Operation ( pllblic abstract int cal clll ate ( int operand l. i nt operand2 ):
,
As listagens 7.17, 7. 18, 7.19 e 7.20 apresentam os vários objetos operação. LISTAGEM 7 .17
Add.java
plIbl i c class Add extend s Operati on { publi c int ca l clllat e( int operandl . int operand2 ) I retllrn operandl + operand2 :
,
,
LISTAGEM 7 .18
Subt ract.java
publi C cl ass SlIbtrac t extends Operation I pub l ic i nt calcul ate ( int operand I. i nt operand2 ) I return operandl - operand2 :
, ,
LISTAGEM 7 .19
Mult i ply. j ava
public class Mu l t lply extends Operati on ( public int calc ulat e( int operandl. int operand2 ) { return operandl * operand2;
,
,
LISTAGEM 7 .20
Oivide . ja va
pub l ic cl ass Divide extends Operation ( pub l ic int calcu l ate ( i nt ope rand I. int operand2 ) { return operandl I ope r and2;
, ,
Cada operação implementa o método ca 1cul ateO de sua própria maneira. Agora que você tem um objelo para cada operação, pode reescrever o método cal cu late() original.
Polimorfismo: hora de escrever algum código
169
public int ca l cu late( Operation operation, int ope randI, int operand2 ) ( return operat i on.ca l culate( operandl , operand2 );
I Transfonnando a operação em um objeto. você ganhou muita flexi bilidade. No passado, você leria de atualizaro método sempre que quisesse adic ionar uma nova operação. Agora, você pode simplesmente criar a nova operação e passá- Ia para o método. Você não precisa alterar o método de maneira alguma, para que ele funcione com a nova operação-e le simplesmente func iona.
Exposição do problema A linguagem Java fornece um operador chamado instanceof. O operador instanceof permite que você verifique o tipo subjacente de lima referência. String s • "somestring" õ Object obj = S; System.out.println( (obj ins tanceof String) ), Esse segmento de cód igo imprime true. obj contem uma instância de String. A maioria das linguagens de roo fornece um mecanismo semelhante. Agora, considere a nova classe Payro 11, na Listagem 7.21. LISTAGEM
7.21
Payroll.java
public class Payrol1 ( pr; vate i nt total _hours, pri vate ; nt total_sales; private double total_pay; public void payEmployees( Employee [J emps ) ( for ( int i - O, i < emps.length; ; ++ ) ( Emp l oyee emp z emps[i]; total_pay +- emp.calculatePay(); emp.printPaycheck();
I
I publiC void calculate80nu s( Employee [] emps I I for( int i .. O; i < emps.length, i++ ) I Employee emp • emps[i] ; if( emp instanceof HourlyEmp l oyee ) ( System.out. prin t ln(" Pay bonus to .. + emp. getlastNameO + ", " + emp.getFirstNameO + " SIOO.OO." ); I else if ( emp in stanceo f CommissionedEmpl oyee ) { i nt bonus - ( (Commi ssionedEmployee) emp ).getSales() • 100;
Dia7
1 170
Payroll.java (continuação)
LISTAGEM 7 .21
bonus to • + emp. getLastName() + ~ • " + emp.get FirstNameO + • $" + bonus ):
Sys tem.out. pr in tln(~Pay
I else ( System,ouLprin tln( "unknown employee type
M ):
} } }
public vo1d recordEmployeelnfo( CommissionedEmp l oyee emp ) ( total _s ales +- emp.getSales() : }
publi C vo 1d recordEmployeelnfo( HourlyEmp loyee emp ) ( total_hours +- emp.getHou r s() : }
publ ic void pr1ntReport() ( System.out.println( "Payroll Report:" System.out.pri ntln( -Total Hours: " + System . out.println( MTotal Sa les: " + System.out.print ln( MTotal Paid: $" +
): tota l_hours ): total _sales ); total_pay ):
} }
Essa classe Payroll tem um método ca l cul ateBonus (). Esse método recebe um array de objetos Empl oyee, descobre qua l é o tipo de cada um e calcula um bônus. Os objetos HourlyEmp loyee recebem um bónus fixo de US$ IOO, enquanto os objetos COIl'Illi ss i oned Empl oyee recebem USS IOO por cada venda. Sua tarefa é eliminar a lógica cond icional encontrada em ca l culateBonus(). Comece atacando os dados que o método está testando. Neste caso, ele está testando em um objeto. Então, o que está errado? Em vez de solicitar o bónus ao objeto, o método solicita a ele alguns dados e, em seguida, calcu la um bónus, usando esses dados. Em vez disso, o método deve so licitar os dados ao objeto. Você pode fazer download do código-fonte das classes Payroll, Empl oyee, HourlyEmployee e Commiss i onedEmp l oyee. Também existe um Payroll Dri ver fomecido, para que você possa testar fac ilmente sua solução.
A próxima seção discutirá as soluções do Laboratório 4. Não prossiga até concluir o Laborat óri o 4.
Polimorfismo: hora de escrever algum código
171
Soluções e discussão Para resolver esse problema, você deve adicionar um método cal culateBonus () diretamente em cada objeto Employee. Isso poderia parecer como se você estivesse cai ndo na armadilha I do Dia 6. Entretanto, está correto mover o método para a classe base, pois todas as subclasses sabem como calcular seu bónus. Na verdade, isso já deveria estar na classe há muito tem po. As listagens 7.22, 7.23 e 7.24 apresentam as alterações exigidas. L ISTAGEM
7.22
Employee.java
publiC abs tract class Employee ( publi c abstract double calc ulateBonus() ; II reduz ido por brevi dade. o res tante diz o mesmo J
LISTAGEM
7.23
Hour l yEmployee .java
publi c cla ss HourlyEmployee extends Employee { pub l ic double calcul ateBonus() { return 100 .00 ;
J
II reduzido po r brevidade. o re stante diz o mesmo J
LISTAGEM
7.24
CornnissionedEmployee.java
public class Commissioned Employee extends Employee { publi c double calculateBonus() { return 100.00 * getSales() ; J
II r ed uzido por brevidade . o res tante diz o mesmo J
Com essas alterações, você pode atualizar a classe Payroll, como a Listagem 7.25 demonstra. LISTAGEM
7.25
Payroll.java
publi c cla ss Pay roll { public void calcula t eBonus( Empl oyee [) emps ) { for( in t i z O; i < emps.length; i ++ ) ( Employee emp • emps [i] ; System.out.prin tln("Pay bonus to " + emp.getlastName() + M, " + emp.getFirstName() + " S" + emp.ca l culat eBonus()
Dia7
1 172
7.25
LISTAGEM
Payroll.java (cont inuação)
); ) )
II reduzido por brevi dade . o rest ante diz o mesmo )
Vamos! Acabou-se a lógica condicional irritante!
DICA
Dicas sobre estruturas condicionais: • Evite o use de estrutura s condicionais case ou j f/else. - Considere os blocos if/else grandes com celicismo. - Cuidado com alterações em cascata. Se uma alteraçã o exige muitas mudanças condicionais, talvez você tenha um pro blema. • lnstanceof é um si nal de perigo muito grande. • H /ehe, case e ins tanceof são Nculpados até prova em contrário
DICA
N •
Dicas para a eliminação de estruturas condicionais: • Transforme os dados em objetos. • Se os dados já são um objeto, adicione um m étodo no objeto. • Evite verificações de instanceof; use pOlim orfismo em vez disso.
Resumo Hoje, você com pletou quatro laboratórios. O Laboratóri o I lhe deu a chance de experimentar alguns dos mecanismos básicos do poli morfismo. O Laboratório 2 permi ti u aplicar o que você aprendeu no Laboratório I, em um exemplo mais complicado. O Laboratório 3 deve ter finalmente respondido a pergunta, ;'0 que é exatamente software à prova do fu turo?". O Laboratório 3 resume o motivo pelo qual você desejaria usar polimorfismo. Finalmente, o Laboratório 4 fo rneceu algo para tomar cuidado enquanto estiver programando. El e também mostrou como o polimorfismo pode ser útil, quando utilizado corretamente. Juntos, todos esses laboratórios reforçam as lições sobre polimorfismo. Eles fornecem o que você precisa saber para tirar vantagem do conceito corretamente. Esperamos que, após concluir esses laboratórios, você veja seus programas do ponto de vista do polimorfismo. A verdadeira programação 00 exige uma maneira diferente de pensar a respeito do software. As vantagens da 00 surgem realmente, quando você pode pensar de forma polimórfica.
Po limorfismo: hora de escrever algum código
173
Perguntas e respostas P. Pa rece qu e o polimorfismo de inclu são é mais conveniente do qu e a sob reca rga, pois cu só preciso escr ever um método e fa zer com que ele funcione com muitos tipos diferentes. I'or qu e e u usa ria sobrecarga em vez disso? R. Ex istem ocasiões em que a sobrecarga é uma escolha mel hor. Um método que usa incl usão só funciona se estiver processando objetos relacionados. A sobrecarga perm ite que você reut ilize um nome de método dentre um grupo de métodos, cujos argumentos podem não estar re lacionados. Você não pode fazer isso com a incl usão(embora possa usar uma combinação de incl usão e sobrecarga).
Workshop As perguntas e respostas do teste são fornecidas para seu melhor entend imento. Veja as respostas no Apêndice A, " Respostas" .
Teste I. A part ir das soluções de laboratório, dê um exemplo de método sobrecarregado. 2. Qual problema a sobreposição apresenta? 3. Qua is passos você preci sa dar para alterar o comportamento em uma hierarq uia polimórfica? 4 . A partir dos laboratórios, encontre um exemplo de polimorfismo de inclusão. 5. Como você elimina lóg ica condi cional? 6. Qua l é a vantagem do polimorfi smo de inclusão em relação à sobrecarga? 7. Na 00, qual é o relacionamento entre objetos e dados? 8.
°
que há de errado com as estruturas condici onai s?
9. Qua l é uma boa indicação de que uma estrutura condic ional é ' ruim '?
10.
Ex plique o polimorfismo com suas próprias palavras.
Exercícios Não há exercícios hoje. Faça seus laboratórios!
SEMANA
1
Em revisão Na semana um você não apenas aprendeu os fu ndamentos da programação orientada a objetos, mas também corno e quando deve aplicá-los. Você aprendeu que os três pilares da programação orientada a objetos são o ellcap~' lIfalll elllo, a herança e o polimorfismo. O encapsulamento perm ite que você construa software independente. O encapsulamento é conseguido através de abstração, ocultação da implementação e divisão da responsabi lidade. A herança perm ite que você reutilize c estenda código já existente. Você aprendeu que existem três tipos de herança: para reuti lização de implementação, para diferença e para substituição de ti po. O polimorfismo pennite que um un ico nome represente código dife-
rente. Os quatro tipos diferentes de polimorfismo são: pol imorfismo de inclusão, poli morfismo paramétrico. sobrecarga e sobreposição. O uso dos três pilares da programação orientada a objetos penni te que você crie cód igo que é:
• Natural • Confiável • Reutil izável • Manutenrvel • Extcnsivcl • Oportuno Embora essas inrormações tenham sido apresentadas nos dias 2, 4 e 6, os laboratórios dos dias 3, Se 7 são o que rea lmente reúne tudo. A experiência prática nesses laboratórios aumentou o seu entend imento de como escrever código orientado a objetos que atinge os objetivos mencionados.
SEMANA
2
Aprendendo a aplicar 00 8
Introd ução fi UML
9
Introdução à AOO (Análise Orientada a Objetos)
roo (Projeto Orientado a Objetos)
10
Introdução ao
11
Reutilizando projetas através de padrões de projeto
12
Padrões avançados de projeto
13
00 e programação de interface com o usuário
14
Constru indo softwa re confi ável através de testes
Panorama Na primeira semana, você aprendeu os fundamentos da escrita de cód igo orientado a objelos. Embora esse seja um primeiro passo na criação de um programa orientado a objelos, ainda há muito mais para aprender, antes que você possa começar a codi fi car. Nesta semana, você ultrapassará a si mples codi fi cação e abordará o inteiro processo de desenvolvimento de software. Os passos do processo de desenvolvimento de software que você abordará são: aná lise, projeto, implementação e teste.
A análise orientada a obj etos (ADO) é o primeiro passo no processo de desenvolvimento. A ADO permite que você entenda o problema que está tentando resolver. Após concluir a ADO, você deve conhecer os requisitos de seu programa, assim corno toda a terminolog ia espec ífica do dom ínio.
Após ter analisado o problema, você poderá começar a projetar sua so luçi'lo. O Dia 10 descreverá o projeto orientado a objetos, o processo de pegar o modelo de domínio e criar o mode lo de objetos que você usará durante a implementação. Os dias II e 12 apresentamo alguns atalhos de projeto. O próximo passo é a implementação; escrever o código. É aí que você utiliza as infonnações apresentadas na primeira semana. O teste é o estágio fi nal do processo de desenvolvimento. É importantc tcstar durante todo o está· gio de implementação, assim como no final , para poder garantir um sistema livrede defeitos. Esses assuntos complementarão o conheci mento que você adquiriu na primeira semana e pennitimo que você tenha uma idéia e acompanhe o processo de desenvolvi mento até ter um programa orientado a objetos totalmente desenvolvido.
SEMANA
2
• DIA • Introdução à UML Na semana anterior, você aprendeu as teorias básicas da programação orientada a objelos. Entre·
tanto, si mplesmente conhecer algumas técnicas e defin ições não o preparará adequadamente para aplicá- Ias. Você simplesmente mostra ferramentas a alguém, explica seu uso e propósi to e depois manda essa pessoa construir uma casa? É claro que não! A programação não é diferente. A programação de sucesso só surge com experiência e boa metodologia. Nesta semana, você vai aprender a aplicar corretamente as ferramentas de 00 que viu na semana anterior. Hoje, você va i explorar a UML (Unified Modeling Langllage), assim como a lguns dos aspectos mais elega ntes do relacionamento entre objetos. As lições de hoje fornecen10 a linguagem comum que você usara enquanto aprende a analisar seus problemas e a projetar so luções 00. Hoje você aprenderá: •
Por que deve se preocupar com a Unified Modeling Language
•
Como modelar suas classes usando UML
•
Como modelar os vários re lacionamentos entre classes
•
Como reunir tudo
Introdução à Unified Modeling Language Quando um construtor constró i uma casa. ele nào faz isso aleatoriamente. Em vez disso, o constnllor constrói a casa de acordo com um conj unto de cópias hel iográficas detalhadas. Essas cópias he liográficas dispõem o projeto da casa explicitamente. Nada é deixado ao acaso .
Dia 8
Agora, quantas vezes você ou alguém que você conheça construiu um programa aleatoriamente? Quantas vezes essa prática trouxe problemas para você? A UML (Unified Modelillg Langllage) tenta trazer as cópias heliográ licas para o mundo do soft· ware. A UML é uma linguagem de modelagem padrão. A linguagem consiste em várias notaçôcs grálicas que você pode usar para descrever a arq uitetura inteira de seu software. Os programadores, arqu itetos e anali stas de software usam lingl/agem de modelagem para descrever grafi camentc o projeto do software. Novo TERMO Uma lillguagem de modelagem é uma notação gráfica para descrever projeto de so ftware. A I inguagem também inclui várias regras para distinguir entre desenhos corretos e incorretos. São essas regras que tomam a UML uma linguagem de modelagem e não apenas um pun hado de símbolos para desenho. Uma linguagem de modelagem não é igual a Ll m processo aLi metodologia. Uma melod% giadiz a você como projctar o software. Em vez disso, Lima linguagem de modelagem ilustra o projeto que você criará enquanto segue uma metodologia. NovO TERMO Uma metodologia define um procedimento para projetar software. As linguagens de modelagem capturam esse projeto graficamente. A UML não é a imica linguagem de modelagem. Entretanto, ela é um padrJoamplamente aceito. Ao mode lar software, é importante fazer isso em uma linguagem comum. Desse modo, outros desenvolvedores podem rápida e faci lmente entender seus diagramas de projeto. Na verdade, os criadores da UML reuniram suas três linguagens de modelagem concorrentes - por isso, o U(nified - unificada) em UML. A UM L fornece um vocabulário comum que os desenvolvedores podem usar para transmitir seus projetos. Novo TERMO A VAn é uma linguagem de modelagem padrão. A UML consiste na notação para descrever cada aspecto de um projeto de software.
NeTA
Não é o objetivo deste livro apresentar uma introdução exaustiva da UMl. Em vez disso, este livro apresentará as partes práticas que você pode usar imediatamente para descrever seu software.
É importante notar que uma linguagem de modelagem não diz nada a respeito de como chegar a seu projeto. Metodologias ou processos é que mostram as diretrizes de como analisare projetar software. Novo TERMO Uma melodologia ou processo descreve como projetar software. Uma metodologia freqUentemente contém uma linguagem de modelagem.
Introdução à UML
ALERTA
179
A UMl apresenta um rico conjunto de ferram entas de modelagem. Como resultado, existem muitas informações que você pode colocar em seus modelos. Cuidado para não tentar usar toda notação existente ao modelar. Use apenas notação suficiente para transmitir seu projeto. l embre-se sempre de que o objelivo de seu modelo é transmilir seu projeto. Faça o que precisar para transmiti·lo e fique com isso.
Modelando suas classes Na semana anterior. você viu mui to código. Quando você se aprofunda nele, o código está no nível mais baixo da documentação de seu software. Se seu código funciona, você tem certeza de ter seu projeto documentado. Embora o código seja a documentação mais completa de seu projeto, pode ser extremamente difici l para outros mexerem nele - especialmente se não estiverem familiarizados com o código. A 'documentação' também é úti l para alguém que não conheça a linguagem de implcmcntação. Em vez di sso, você precisa de uma notação que lhe permita documentar seu projeto, para que outros possam entendê-lo imediatamente. Desse modo, outros poderão ver a estrutura de classes de alto nível e apenas se aprofundar nos detalhes, quando isso for necessário. De certa forma, uma notação gráfica o isola dos detalhes, para que você possa exim ir-se de entender a estrutura de alto nível de um programa. Cri ar documentação separada do código exige o comprometimen to de mano t ê-Ia em sincronism o com o código.
Uma maneira pela qual a UML o ajuda a transmitir seu projeto é fornecendo um ri co conjunto de notação para descrever suas classes. Usando essa notação, outros podem ver facilmente as principais classes que compõem o projeto de seu programa. Conforme você verá, a UML penn ite defini r as classes, assim como descrever os relacionamentos de alto nível entre as classes.
Notação básica de classe A UML fornece um rico conjunto de notação para modelar classes. Na UM L, uma caixa representa a classe. A caixa superior sempre contém o nome da classe. A ca ixa do centro contém todos os atributos c a inferior contém as operações. Notas sobre seu modelo aparecem em caixas com cantos dobrados. A Figura 8.1 resume a estrutura básica de classes. NOTA
A UMl faz diferença entre operação e m étodos. Na UMl. uma operação é um serviço que você pode solicitar de qualquer objeto de uma classe, enquanto um método é uma implementação específica da operação. As lições de hoje acompanharão a utilização da UMl.
Dia 8
FIGURA 8 .1
A IIOttlÇ(;O de classe da U/oilL.
-----
._dodoorr,.>
--------
..... ltu' M
,, ,,
,
Dentro do modelo, você pode usar os caracteres ·, # e +, Esses caracteres transmitem a visibilida· de de um atributo ou dc uma operação. O hífe n (-) significa privado, o jogo da ve lha (#) significa protegido e o sinal de adição (+) significa público (veja a Figura 8.2).
8.2 A IIOtaÇtiOda UML
VIsibilidade
FIGURA
.. public...n. , pMteel/rd.". n. , priY' __.".1tr
paro especificar
\'isibilidade,
• public..opr{ , • pMtectocLopr( I , prMte. GP
A Figura 8.3 ilustra a compl eta classe BankAccount dos dias 5 e 7,
BankAccount
FIGURA 8 .3
Uma classe lotalmellle descrita,
, baI ..... : doubPI
• • • •
dlpositFur.do l. mounr : doub ll ) : vo!d g.,elto""" (): doubPI M5e.5. """ ( I : void wirhdrowF uod. (omoun5 : doub .. ): doubPI
Às vezes, uma nota ajudará a transmitir um significado q ue, de outro modo, fi caria perdido ou seria ignorado, como a nota da Figura 8.4, FIGURA 8 ,4
8ank
Um exemplo delOlflado de lIora.
+ .ddAccounl ( I + 100.IHoldings t I .. 100.IAceounls ( )
.. deposit ( I .. balance I)
------ -
o blnco cont6m ,,6.1., COnlal e fornece operaçOes porl m. nipu lar ln'" cont."
Introdução à UM L
18 1
Essas notas são a modelagem aná loga à anotação adesiva do mundo real.
Notação avançada de classe A UM L também de li ne algumas outras notações, mais avançadas. O uso correIO dessa notação o ajuda a criar modelos mais descrit ivos. A UM L o aj uda a ser mais descritivo, permitindo que você am plie o vocabulário da própria linguagem, através do uso de estereótipos. Um eSlere61ipo é um elemento da UML qu e permite que você amplie o vocabulário da própria li nguagem UM L. Um estereótipo consiste cm uma pa lavra ou frase incl uída entre sinais de menor e maior duplos « < »). Você coloca um estereótipo acima ou ao lado de um elemento ex istente.
Novo
TERMO
Por exemplo, a Figura 8. 1 mostra o estereót ipo «Atributo» . Esse estereóti po ilustra onde acrescentar atributos em um retângulo de cl asse. A Figura 8.5 ilustra outro estereótipo que informa lIm pouco sobre a operação. FIGURA 8 .5
Um eS/e/"eólipo qlle qualifica a OperOf(io.
BankAccount <
Final mente, você pode se lembrar que a classe BankAccount foi originalmente defi nida como lima classe concreta. Ent retanto, o Dia 7 redefi ni u a classe BankAccount como uma classe abstrata. A UML fornece uma notação para transmitir que uma classe é abstrata: o nome da classe abstrata é escrito em itál ico. No caso de BankAccount, o nome deve ser escri to em itá lico, conforme ilustrado na Figura 8.6. FIGURA 8 .6
O objelo
BankAccount abSlrato.
BankAccount • b,lance' doubl. + deposilFund. lamounl' doubl, ), vOH:! + IISIS.lance () : doubl, ' "'t8alance (I' void + withdrawfund~ lamaunl: doublel' double
Modelando suas classes de acordo com seus objetivos As duas seções anteriores apresentaram muitas escolhas diferentes de notação. Dadas todas essas opções, como você sabe quais notações deve usar?
Dia 8
Você sempre precisa voltar às perguntas, "o que eu estou tentando transmitir?" e "para quem eu estou telllando transmitir isso?" O objetivo de um modelo é lransm iti r seu projeto o mais eficientemente (e simplesmente) possivel. Talvez seu objetivo seja transmitir a interface pública de uma classe. A Figura 8.7 poderia bastar. Ela transmite adequadamente a interface pública de Bank, scm sobrecarregá-lo com os detalhes de argumentos de método ou atri butos ocultos. Tal notação bastará, se você quiser simplesmente transmi ti r o que outros objetos poderiam fazer com instâncias de Bank.
Bank
FIGURA 8 .7
Uma lIo111Ç(io s imples addAccount () tOlolH oldings ( ) 1010lAccounlS ( ) dapos it ( ) bol8nce ( )
/um/ Bank.
Entretanto, tome a Figura 8.3 como outro exemplo. Essa figura documenta completamente todos os atributos e operações (público, protegido e privado) da classe BankAccount. Você poderia modelar uma classe com esse deta lhe, se quisesse transmitir a defi nição inteira da classe para outro desenvolvedor. Ou talvez., quando você progredi r em sua carreira 00, possa se tomar um arquiteto. Você poderia dar tal modelo para um desenvolvedor, para que ele pudesse criar a c lasse. Então, a resposta da pergunla "como eu se i qua is notações devo usar?" é que isso depende. Qua ndo uma pessoa não-técnica perg unta a você o que faz, você responde de lima mane ira q ue essa pessoa en tenda. Quando um colega pergunla o que você faz, você geral mente dá uma resposta técnica. Mode lar seu projeto não é diferente . Use o vocabulário que fo r apropriado pa ra o q ue você esti ver tentando fazer.
DICA
Dicas para a modelagem eficiente: • Sem pre faça a você mesmo a pergu nta ~o q ue eu estou tentando transmi· tir7~ A resposta o ajudará a decidir exatamente o q ue você precisa modelar. • Sempre faça a você m esmo a pergu nta " para quem eu estou tent ando t ransmitir a informação?" A resposta ditará o modo como você vai modelar. • Sempre tent e produzir o modelo m ais simples que ainda tenha êxito em t ransmitir seu projet o. • Não fique p reso à linguagem d e modelagem . Embora você não deva se r vago demais na semântica, não deve deixar que o fato de seguir a not ação perfeit amente o im peça de concluir seus diagramas. Os perigos de paralisia ao modelar são reais - especiatmente quando você está começando. Não se preocupe se seu m odelo não estiver 100% perfeit o. Preocupe·se somente se seu modelo não transmite corretamente o projeto. • Finalment e, lembre·se de q ue a UM L (ou q ualquer l inguagem de modela· gem) é simplesmente uma fe rramenta para ajudá·lo a transmitir o projeto. Ela não é um instrumento em si mesma. No final do dia, você ainda preci· sará prod uzir código.
Introdução à UML
Modelando um relacionamento de classe As classes não existem no vácuo. Em vez disso, elas têm relacionamentos com plexos entre si. Esses relacionamentos descrevem como as classes interagem umas com as outras. NovO TERMO
Um re/aciollllmelllo descreve como as classes interagem entre si. Na UML, UIl1 re lacionamento é uma conexão entre dois ou mais elementos da notação.
A UML reconhece três tipos de al to nível de relacionamentos de objeto: • Dependência • Associação • Generali zação Embora a UML possa fornecer notação para cada um desses relacionamentos, os relacionamentos não são especificos da UML. Em vez disso, a UML simplesmente fornece um mecanismo e vocabulário comum para descrever os relacionamentos. Entender os relacionamentos, independentemente da UML, é importante em seu estudo de 00. Na verdade, se você si mplesmente ignorar a notação e entender os relacionamentos, estará bem adiantado nos estudos.
Dependência Dependência é o relacionamento mais simples entre objetos. A dependência indica que um objcto depende da especificação de outro objeto. NeTA
Novo TERMO
Especificaç~oé
uma maneira diferemede dizer interface ou comportamen to.
Em um re!acionamel1lo de dependência, um objeto é dependente da especificação de outro objeto. Se a especifi cação mudar, você prec isará atual izar o objelo dependente.
Lembre-se dos laboratóri os do Dia 7. Você pode dizer que Psychiatri stOb ject depende de MoodyObj ect, por dois moti vos. Pri mel ro, o método exami ne () de Psyc hi a t ri s tObj ec t recebe um MoodyOb jec t corno argumento. Segundo, o método examineO chama o método queryMood () de MoodyObject. Se o nome ou lista de argumentos do método queryMood () mudar, você precisará atualizar o modo como Psychi atri stObject chama o método. Do mesmo modo, se o nome da classe MoodyObject mudar, você terá de atualizar a lista de argumentos do método examineO. A Figura 8.8 ilustra a notação UML do relacionamento de dependência ent re Psychiat r istObject e HoodyObjec t .
Dia 8 8.8 Um relaciol/amelllO de dependêllcia simples. FIGURA
+ q .... 'VMoodU : String
+e..mineO
To me not a do que a Figura 8.8 não diz. O elemento Psychiat r istObject não conlém cada m étodo encont rado em Psych1atristObject. O mesm o vale para MoodyObj ect. Em v ez d isso, esse modelo de d ependência contém apenas os recu rsos necessá rios para descrever o rela ci onamento de dependência.
NOTA
Lembre·se d e que a notação UML serve para t ransmi tir info rm ações. Ela não está lá para que você tente usa r cada truque de modelagem do livro de UML !
Através da POO, você sempre tenta minimizar o máximo possível as dependências. Ent retanto, é imposs ível remover todas as dependências entre se us objetos. Nem todas as dependências são criadas de modo igual. As dependências de interface geralm ente estão corretas, enquanto as dependências de implementação quase nunca são aceitáveis. Quando você deve modelar dependências?
DIC A
Nor malment e, você m odela dependências quando quer m ostrar que u m ob jel o usa outro. Um lugar comum onde um ob jeto usa oul ro é através de um argumento de mét od o. Por exemp lo, o métod o exami neO de Psych1at r 1st Object rece be u m HoodyObject com o argumento. Você pode dizer que Psych iatri stObject usa HoodyObj ect.
Associação Os relacionamentos de associação vão um pouco mais fu ndo do que os relacionamentos de dependência. As associações são rel acionamentos estrut urais. Uma associação indica que um objeto contém - ou que está conectado a - outro objeto. Novo
TERMO
Uma associaçdo indica que um objelO contém outro objeto. Nos tennos da UM L, quando se está em um relac ionamento de assoc iação, um objclO está conectado a outro.
Como os objetos estão conectados, você pode passar de um obj eto para outro. Considere a associação entre uma pessoa e um banco, como il ustrado na Figura 8.9. FtGURA 8.9 Vma (lssociarfio /JCSS()(l
e 1/1/1
emre //fila
I
Pessoa
I Empreste de 1>1
Banto
I
bW1CO.
A Figura 8.9 mostra que urna pessoa empresta de um banco. Na notação UML, toda assoc iação tem um nome. Neste caso, a associação é chamada de empresta de. A seta indica a di reção da associação.
Introdução à UML Novo
TERMO
185
o nome da associaçi'lo é um nome que descreve o relacionamento.
Cada objeto em uma associação também tem um papel, confonne indicado na Figura 8.10. FIGURA 8 .10 Os pllpéis IIl1l1SSOCillÇlio.
Novo
TERMO
Na associação, o papel de Pessoa é devedor e o papel de Banco é credor.
Novo
TERMO
o papel da assoc iação é a parte que um objeto desempenha em um relac ionamento.
Finalmente, a multip li cidade indica quantos objelos podem toma r parte em uma associação. Novo
TERMO
A l11uIIIJ)/icidllde ind ica quantos objetos podem tomar parte na instância de uma associação.
A Figura 8.11 ilustra a multiplicidade da associação entre Pessoa e Banco. FIGURA 8 .11 Mulriplicidade.
1
Pessoa
11...
.1
Ba nco
1
Essa notação nos informa que um banco pode ter um ou mais devedores e que lima pessoa pode utilizar O ou mais bancos.
NOTA
DICA
Você especifica suas multiplicidades atra vés de um único numero, uma l ista ou com um asterisco (*). Um único número significa que determinado número de objel os - não mais e não menos - podem participar da associação. Assim, por exemplo, um 6 sig nifi ca que seis objetos e somente seis objetos podem participar da associação. * sign ifica que qua lquer número de objetos pode participar da associação. Uma lista define um intervalo de objetos que podem participar da associação. Po r exemplo, 1..4 indica que de 1 a 4 obletos podem participa r da associação. 3 .. * indica que três ou mais obletos podem participar.
Quando você deve modelar associações? Você deve m odelar associações quando um o blet o con ti ver outro objeto - o relacionamen to rem um. Você tambem pode modelar um a associação quando um objeto usa outro. Uma associação permit e que você modele quem faz o que em um relaci onamento.
186
Dia 8
A UML também defi ne dois tipos de associação: agregação e composição. Esses dois subtipos de associação o ajudam a refi nar mais seus modelos.
Agregação Uma agregação é um tipo es pecial de associação. Uma agregação modela um relacionamento
tem 1//1/ (oupa,,'ede, no jargão da UML) cnlre pares. Esse relacionamento signi fi ca que um ohjcto contém outro. Pares significa que um objeto não é mais importante do que o outro. Novo TERMO
Novo TERMO
Um re/acioIlOm€IIIO IOdo/parte descreve o relacionamento entre objclos onde um
ohjclo contém outro.
Uma agregaç(io é um tipo especial de associação que modela o relacionamento 'tem um ' de relacionamentos todo/parte entre pares.
Importância. no contexto de uma agregação, significa que os objclos podem existir independen-
temente uns dos outros. Nenh um objetoé mais importante do que o outro no relacionamento. Considere a agregação ilustrada pela Figura 8. 12. FIGURA 8 .12 AgregllÇli o eml'e 1/111
banco e seus cliellles.
I
Banco
1..·0 • Cliente
Aqui, você vê que um Banco pode conter qualquer número de obj etos Cl iente. O losango aberto ajuda seu modelo a indicar qual objeto é o todo e qual é a parte. Aqui, o losango diz que Ba nco é o todo. Banco é o objelo que ' tem um ' no re lacionamento. Banco contém objetos Cl i ente. Em termos de programação, isso poderia significar que Banco contém um array de objetos Cl i ente.
NOTA
Um losango aberto simboliza agregação. O losango toca o objeto que é considerado o todo do relacionamento: a classe que se refere à outra classe. O todo é constituído de partes. No exemplo anterior, BanCO é o todo e os objetos CI i ente são as partes. Outro exemplo de agregação é um carro e seu motor. Um carro 'tem um' motor. Nessa agregaçâo, o carro é o todo e a parte é o motor.
Como Banco e Cl iente são independentes, eles são pares. Você pode dizer que o objeto Banco e o objeto Cl ien te são pares, porque os objelos Cl i ente podem ex istir independentemente do objeto Banco. Isso sign ifica que, se o banco encerrar suas operações, os cI ientes não desaparecerão
Introdução à UM L
187
com o banco. Em vez disso, os clientes podem se tornar clientes de outro banco. Do mesmo modo, um cliente pode sacar seus fundos e o banco conti nuará. A agregação entre objetos funci ona como esses exemplos reais. Um objeto pode conter outro olr jeto independente. Queue ou Vector é um exemplo de objeto que pode conter outros objetos, através da agregação.
D ICA
Quando você deve m odelar a agregaçáo? Você deve modelar uma agregação quando o objetivo de seu modelo for descrever a estrutura de um relacionament o de pares. Uma agregação mostra explici tamente o relacionamento estrutural todo/parte. Entret ant o, se você estiver mais interessado em modelar quem faz o que em um relacionam ento, é melhor usa r uma associação si mples: sem o losango.
Composição A composição é um pouco mais rigorosa do que a agregação. A composição não é um relacionamento entre pares. Os objetos não são independentes uns dos outros. A Figura 8. 13 ilustra um relacionamento de composição. FIGURA 8 .13 COlllposiç(10 elllre .nta.f filiai.f .
1/1/1 b (/IICQ
e
I
Banco
-----.{I-_-_-_~F~;'_;:·_'~_-_ll
1...-,
Aqui, você vê que Banco pode conter muitos objetos fi 1ia l . O losango fechado diz que esse é um relacionamento de composição. O losango também diz quem ' tem um ' . Neste caso, Banco ' tem um ', Otl contém , objetos filial.
Ne TA
Um losango fechado sim boliza a composição. O losango toca o objeto que é considerado o todo do relacionament o. O todo é cons tituído de partes. No exemplo anteri or, Banco é o todo e os objetos Filial são as part es.
Como esse é um relacionamento de composição, os objetos Fil ; a1 não podem ex istir independentemente do objeto Banco. A composição diz que, se o banco encerrar suas atividades, as fil iais também fecharão. Entretanto, o inverso não é necessariamente verdade. Se uma filia l fec har, o banco poderá permanecer funcionando. Um objeto pode part icipar de uma agregação e de um relacionamento de composição ao mesmo tempo. A Figura 8.14 modela tal relacionamento.
I
188
FIGURA
Dia 8
8 .14
,.~
Banco em 1/111 relacioname"to
de agregaçtia e de
composiçtio. silllulwneamellle.
~,
.1
Filiei
1
1.. •
• Clie nte
D ICA
Quando você deve modelar uma composição? Assim com o a agregação, você deve modelar uma composição quando o objet ivo de seu modelo fo r descrever a estrutura de um relacionamento . Uma composição mostra explicitamente o relacionamento estrut ural todo/parte.
Ao contrário da agregaçã o, a composição não modela rela cionamentos
todo/parte de pares. Em vez disso, a parte é dependente do todo. Voltando ao exemplo Banco, isso significa que quando o banco encerrar suas atividades, as fili ais t ambém fecharão. Em l erm os de programação, isso significa que quando o o bjeto Banco fo r destruido, os o bjetos F1l1al também serão destruídos. Novamen te, se o o bjetivo de seu modelo f or capturar os papéis dos objetos na associação, você deve usar uma associação simples.
NOTA
l embre-se d e que agregação e com posição são simplesmente refinamentos o u subtipos da associação. Isso significa que você pode modelar agregação e composição como uma associação simples. Tudo depende do que você est iver t entando modelar em seu diagrama.
Generalização Um relacionamento de genera lização é um relacionamento entre o gera l e o especí fi co. É a herança.
°
°
Novo TERMO Um re/acionamenfO de generalização indica um re lacionamento entre geral c especifico. Se você tem um relacionamento de generalização, então sabe que pode substit uir uma classe filha pela classe progenitora. A generali zação incorpora o relacionamento 'é um ' sobre o qual você aprendeu no Dia 4. Confonne foi aprendido no Dia 4, os relacionamentos 'é um ' pennitem que você defi na rel acionamentos com capaci dade de substituição. Através de relacionamentos com capacidade de substituição, você pode usar descendentes em vez de seus ancestrais, ou fi lhos em vez de seus progenitores. A UML fornece uma notação para modelar generalização. A Figura 8.15 ilustra como você mo-delaria a hierarquia de herança BankAccount.
Introdução à UML
189
FIGURA 8 .15
BIonkAccount
A hierarquia de herança BankAccount.
"fi SavingsAccoun1
OYerd,aftAccoun1
Ch~kingAccoun1
1\ TimaMa1uri1yAccoun1
Rewa,dsAccoun1
Uma linha chcia com um a seta fechada e vazada indica um relac ionamento de ge nera lização.
Reunindo tudo Agora que você já viu a modelagem básica de classe eos relacionamentos, pode começar a monlar modelos bastante expressivos. A Figura 8.8 apresentou um exem plo de dependência simpl es. Usando o que aprendeu durante lodoo dia, você pode tomar esse mode lo um pouco mais expressivo. A Figura 8. 16 expande o relacionamento modelado na Figura 8.8. Figura página 198 em ba ixo FIGURA 8 .16
Psychia1ris1Object
Vm modelo de dependência mais e.'(pressivo.
+ examine ( I
---------
SadObject + queryMood
HappyOblect ~
)
+ queryMood ( )
A Figura 8. J 6 acrescenta uma generalização para que você possa ver quai s objetos pode substi· tuir por MoodyObject nesse relacionamenlo. Do mesmo modo, a Figura 8. 17 expande a hierarquia de herança apresentada na Figura 8. 15.
Dia 8
' M \'
FIGURA 8 .17
,...,
V II/a hierarquia de herança
,
BankAccount
mais dela/hada.
,
O...../L"
,
..
a
;$.. . .
,
Row .. _
", ,,,"
Examinando esse modelo, você pode ver exatamente o que cada classe acrescenta na hierarquia. Tal modelo poderia ajudar outros desenvolvedores a ver o que cada classe o ferece, aci ma e além de suas descendentes. Todos esses modelos têm um elemento comum . Cada modelo contém apenas infonnações sufi cientes, apenas notação suficiente, para transmitir a idéia. O objeti vo desses modelos não é usar cada notação d isponível. Todos esses modelos também com binam diferentes elementos da UML. Como uma linguagem de programação, a UML pcnnite que você combine suas várias pan es de mane iras excl usi vas. Através da combinação de vários elementos, você pode cria r modelos muito expressivos.
Resumo Hoje, você aprendeu os fundament os da modelagem de classe e relacionamentos. Após prat icar os exercícios de hoje, você deverá consegui r começar a desenhar model os de classe simples, usando a UML. A UML fornece notações para modelar classes, assim como os relacionamentos entre objetos, A UML fornece notações para descrever três tipos de re lacionamentos: •
Dcpendência
•
Associação
•
Genera lização
Introdução à UM L
A UML também reconhece dois subtipos de associação: agregação e composição. Combinando todos esses elemeruos, você pode gerar diagramas de classe expressivos. Seu domínio da UML é importante para documentar e transmitir seus projetos para outros.
Perguntas e respostas P. Você I)ode misturar os três lipos de relacionamenlOS dentro do mesmo modelo? R. Sim. Seu modelo pode ilustrar qualquer combinação dos relacionamentos delineados neste dia. O modelo ex iste para descrever os re lacionamentos entre suas classes. Você deve modelar os relac ionamentos entre suas classes. P. Como você
" S:I
a UML? Existem ferramentas específicas?
R. Você pode usar a UML como quiser. Você pode dese nhar seus diagramas em uma ferramenta de mode lagem, em um quadro negro ou em um guardanapo de papel. Depende da si tuação. Se você estiver em uma discussão interativa sobre o projeto, provavelmente desejará usar um quadro negro, pois atualizar um computador pode ser complicado. As ferrame ntas de model agem por computador são melhor lIsadas quando você quer documentar forma lmente um projeto.
Workshop As perguntas e respostas do leste são forneci das para seu melhor entendi mento. Veja as respostas no Apêndice A, "Respostas".
Teste I. o que é UML? 2. Qua l é a diferença entre uma metodolog ia e uma linguagem de modelagem? 3. Que tipo de relacionamento ex iste entre Employee e Payroll , no Laboratório I do Dia 7? 4. Examine cuidadosam ente o modelo da Figura 8.15. Usando apenas o modelo, o que você pode dizer a respeito de MoodyObject? 5. Exam ine os laboratórios do Dia 7. Encontre um exemplo de dependência. 6. Na UML, o que os sinai s a seguir sim bolizam: +, #, -? 7. O Dia 2 apresentou a segui nte interface: publ ic interface Queue ( publ i c void enqueue( Object obj ); publlC Object dequeue() ;
Dia 8
pub l i C boo1ean lSEmpty(); pu b1 i c Object peek() ; }
Q ue tipo de relacionamento Queue tem com os elementos que contém? 8. No Dia 3, Laboratório 3, a classe Deck criava várias cartas. Que tipo de relacionamento ' tem um ' isso representa? 9. Como você ilustra que uma classe
OLl
método é abstraio?
lO. Qual é o objeti vo fi nal da modelagem? Qua is conseqUências esse obj etivo tem? I I. Explique associação, agregação e composição. 12. Expl ique quando você deve usar associação, agregação e composição.
Exercícios I. Modele a classe Queue defin ida na questão 7.
2. Modele um relacionamento de com posição abelhalcolméia. 3. Mode le o relac ionamento entre Bank e BankAccount do Laboratório 2, Dia 7. 4. Modele a associação entre um com prador e um comerci ante. Especifique os papéi s, a mu ltiplicidade e o nome da dependência. 5. Modele a hierarquia de funcionári os do Laboratório 2 do Dia 5. Através de seu mode lo, transmita o que cada classe adiciona acima e a lém de suas descendentes. 6. Veja o Dia 6. Modele a hierarquia de herança Persona 11tyObject.
SEMANA
2
DIA Introdução à AOO (Análise Orientada a Objetos) Ontem, você aprendeu ti vis ualizar seus projetas de classe atraves de modelos de classe. Você viu como os modelos de classe podem aj udaroulros desenvo lvedores a entender melhor seu projeto, destacando os diferentes tipos de objetos e relacionamentos que eles encontrarão em seu software. As linguagens de modelagem, como a UM L, fomecem a você e ti seus colegas desenvo lvedores lima linguagem comum para falar a respeito de projeto. Entretanto, a questão ainda permanece; como você projeta software orientado a objetos? Os modelos simplesmente captu ram um instantâneo de seu projeto. Eles não o ajudam a entender seus problemas ou a fo rmu lar lima sol ução. Em vez disso, os modelos são simplesmente o resultado
final do projeto de software. Como você chega lá? Nos próximos dois dias, você vai aprender a respeito da AOO (Aná li se Orientada a Objelos) e POO (Projeto Orientado a Objetos). AOO é uma estratégia orientada a objelos para entender um problema. Você usa AOO para ajudar a entender o nllcleo do problema que deseja resolver. , Após entender seu problema, você pode começar a projetar uma solução. E aí que o Projeto Orientado a Objetos (POO) entra em ação. No restante da lição de hoje, você vai aprender a respeito de AOO. Hoje você aprenderá:
• Sobre o processo de desenvolv imento de software • Como a AOO o aj uda a entender seus problemas de software
Dia9
1 194
• Como chegar a um entendimento de seu problema usando casos de uso • Como usar a UML para visualizar sua anál ise • Como construir seu modelo de domínio • O quc fazer com lUdo que você cria durante a AOO
o processo de desenvolvimento de software Existem tantas maneiras de desenvolver software quanto existem desenvolvedores. Entretanto, uma equipe de desenvolvimento de software precisa de uma estratégia uni ficada para desenvolver software. Nada será feito, se cada desenvolvedor fizer sua própria atividade. As metodolog ias de software definem uma maneira comum de encarar ° desenvolvimento de software. Uma metodologia rreqUentemente conterá um a linguagem de modelagem (como a UML) e um processo. Novo
TERMO
Um processo de software mostra os vários estágios do desenvolvimento de software.
Um exemplo familiar de processo de software é o processo de cascata. FIGURA 9 .1 O proce.fSo de
casemo.
Análise de requisitos
" Projeto
L Irnplementaçio
" Teste
Conforme a Figura 9. 1 ilustra, o processo de cascata é seqUencial e un idireciona l. O processo é const ituído de quatro estágios di st intos: I. Análi se de requi sitos
2. Projeto 3. Implementação 4. Teste
°
Quando segue o processo de cascata, você vai de um estágio para próximo. Entretanto, uma vez que você complete um estágio, não há volta - exatamente como descer uma cascata ou um penhasco escarp."ldo! O processo de cascata tenta evitar alteração, proibi ndo mudar quando um estágio está concluído. Tal estratégia protege os desenvolvedores de requisitos que mudam
Introd ução à AOO (Análise Orientada a Objetosl
195
constantemente. Entretanto, ta l processo rigido freqüentement e resulta em software que não é o que você ou seu cl iente quer. Quando você analisa um problema, projeta uma solução e começa a implementar, seu entendi· menta do problema é continuamente aprofundado. O mel hor entendimento de seu problema pode muito bem invalidar uma análise ou projeto anterior. Os requi sitos podem até mudar enquanto você desenvolve (ta lvez um concorrente tenha acrescentado um novo recurso em seu produto). Infelizmente. o processo de cascata não pode enfrentar a realidadc do moderno desenvolvim ento de softwa re - requisitos que mudam constantemente. Embora este li vro ni'lo tente impor nenhuma metodologia específi ca, há um processo que tem se mostrado muito efi ciente para desenvolvimento orientado a objetos: o processo iterativo. Este li· vro impõe esse processo!
o processo iterativo O processo iterati vo é o oposto do processo de cascata. O processo iterativo permite alterações em qualquer ponto do processo de desenvolvimento. O processo iterativo permite al teração ada. tando uma estratégia iterativa e incremental para o desenvolvimento de software. Um processo ileralivo é uma estratégia iterativa e incrememal para desenvolvimen· to de softw are. Outro modo de pensar a respeito do processo é como uma estratégia 'evolut iva'. Cada ileraçi'l o aperfeiçoa e elabora gradualmente um produto básico em um produto amadurecido.
Novo TERMO
Uma estratégia iterativa Ao contrário do processo de cascata, o processo iterativo pennite que você continuamente volte e refine cada estágio do desenvolvimento. Por exemplo, se você descobrir que o projeto simplesmente ni'lo funciona ao executar a implementação, pode voltar e faze r um projeto adici onal e uma nova anã lisc oÉ esse refin amento contínuo que torna o processo iterati voo A Figura 9.2 ilustra a estratégia.
Uma estratégia incremental Ao segui r um processo iterativo, você não conclui sim plesmente lim a iteração grande que constrói o programa inteiro. Em vez disso, o processo iterativo divide o traba lho de desenvolvi mento em vári as iterações pequenas. A Figura 9.3 ilustra essa estratégia incremental.
Dia 9
FIGURA 9 .2 UII/a ileraç(10.
Implementação
Análise
Projeto
Implementaç60
Teste
Fim da Iteraç60
FIGURA 9 .3
Iterllçlo 1
O processo iterO/h·o.
Iteraç!o 2
•••
Iteraç60 N
Ent rega
Cada iteração do processo inl roduz uma pequena melhoria incremental no programa. Essa melhoria pode ser um novo reçurso ou um refinamento de um recurso já existente.
Introd ução à AOO (Análise Orientada a Objetos)
197
De qualquer modo, a iteração tem um objetivo específico e, no fina l da iteração, você tem uma melhoria notável na funcionalidade . Imagine que você esteja criando um MP3 player. Durante uma iteração do projeto, você pode terminar o componente que reproduz um arquivo MP3. Para determi nar se o componente funciona, você pode codificá-lo de modo que abra e reproduza um arquivo de música específi co. Na próx ima iteração, você pode adicionar a capacidade de escolher qual arquivo vai ser reproduzido. Em cada iteração, você tem um progresso mensurável. No final da pri meira iteração, você pode ouvir o componente reproduzir uma música. No fina l da iteração seguinte, você tem um mecanismo que perm ite escol her dinamicamente uma música para tocar. Seguindo uma estratégia iterativa, você vê o progresso constantemente. Por outro lado, se você tentar faze r tudo sim ultaneamente, poderá ser dificil ver qualquer forma mens unível de progresso. Em vez disso, o projeto parecerá constantemente atolado em um ún ico lugar - nunca há qualquer resultado. Se um projeto nunca for adiante, o mora l vai ba ixar e se tornará diflcil determinar o que precisa ser fe ito em seguida. Moral baixa e confusão sobre o que fazer em seguida fragme ntará e matará um projeto.
A LER T A
Os processos iterativos precisam ser cuidadosament e m onitorados para se garantir que eles não sejam simplesment e reduzidos a 'cavar' uma solução. A AOO e o POO fornecem ta l verificação de sanidade.
o progresso constante fornece a você relorno constante. Você pode usar esse retorno como um modo de garant ir se está no caminho certo. Se você tentar completar o proj eto inteiro de uma vez, não saberá se criou a solução correta até tenninar. Voltar e corrig ir algo que niio foi fei to corretamente será muito mai s dispendioso se você precisar voltar e reescrever o programa inteiro! A iteração, por outro lado, torna muito mais barato vo ltar e corrigi r algo. Como você recebe retomo constante, é mais prováve l que ident ifique um problema mais cedo. Se você identificar seus problemas mais cedo, será mais fác il refazer uma iteraçiio ou duas para corrigi-lo. É sempre mais desejável reescrever urna iteração do que reescrever um programa inteiro! Se você mantiver s uas iterações peq uenas, não perderá muito tempo, caso tenha de se des faze r de alguma delas. Se um problema chegar à base da iteração original, uma estratégia iterativa não poderá salvá-lo. Tal problema fundamental pode ser dispendioso demais para corrigir e pode danificar a qualidade do produto.
Uma metodologia de alto nível Este livro apresenta uma metodologia de desenvolvimento orientada a objelos infonnal. A metodologia sclcciona e escol he as técn icas que se mostraram eficazes a partir de outras metodolog ias. A metodologia consiste em um processo iterativo, no q ual uma iteração tem quatro estágios:
Dia9
1 198
• Anál ise • Projeto • Im plementaçilo • Teste
NOTA
Após o estágio de teste. você também pode ter estágios de la nçamento e manutenção. Esses são estágios importantes no ciclo de vida de um projeto de sohware. Entretanto, para os propósitos da tição de hoje, esses estágios serão omitidos. HOje, você vai fo cal izar análise, projeto. implementação e teste.
As metodolog ias ' reais' freqUentemente enumeram estágios ad icionais. Entretanto, quando você está aprendendo pela pri mei ra vez, esses quatro estágios silo aqueles que mais im portam . Por isso, este li vro se concentra nesses quatro estágios. O restante deste dia abordará a análise orientada a objetos.
AOO (Análise Orientada a Objetosl AOO (Análise Orientada a Objetos) é o processo usado para entender o problema que você está tentando resolver. Após completar a análise, você deverá entender os requisitos do problema, assim como o vocabulário do domín io do problema. Novo
TERMO
Analise orientada a ohje/os é um processo que usa uma estratégia orientada a objetos
para ajudá- lo a enlender o problema que está tentando resolver. No final da análise, você deverá entender o domínio do problema e seus requisitos em tennos de classes e interações de objetos.
Para projetar uma solução para um problema, você precisa entender como os usuários uti lizari'io o sistema . A resposta dessa pergunta são os requisitos do sistema. Os requ isitos in formam a você o que os usuários querem faze r com o sistema e quais tipos de respostas eles esperam receber. Novo
TERMO
Sislema é o termo da AOO para um conjunto de objetos que interagem. Você pode
dizer que esses objetos constituem um sistema ou modelo do probl ema.
Esses objetos são instâncias de classes derivadas de objetos concretos ou abstratos no domín io do problema que está sob est udo. A análise também o ajuda a se familia rizar com o domínio do problema. Estudando o domínio, você começa a identificar os objetos de que precisa para modelar corretamente o sistema. A AOO, confonne o nOme sugere, é uma estratégia orientada a objetos para anál ise de requisitos. A AOO util iza uma estratégia baseada em 00, modelando o problema através de objctos e suas interaçõcs. Existem dois modelos principais. O modelo de caso de uso descreve como um usuário interage com o sistema . O modelo de domínio captura o vocabu lário princi pa l do siste-
Intro dução à AOO (Aná lise Orientada a Objet os )
199
ma. Usando o modelo de domín io, você começa a identi fi car os objetos que pertencem ao seu sistema. Um modelo de domínio corrctamente construído pode resolver muitos problemas no mesmo domínio.
Usando casos de estudo para descobrir o uso do sistema Ao começar a analisar um problema, você primei ro precisa entender como seus usuários utilizarão ou interagirão com o sistema. Esses usos com preendem os requisitos do sistema e prescrevem o sistema que você cria. Atendendo os req ui sitos de seus usuários, você produz um sistema útil. Novo
TERMO
Os requisitos são os recursos ou caractedsticas que o sistema deve ter para resolver determinado problema.
Um modo de descobrir esses usos é através de análise de casos de uso. Através da análi se você definirá vários casos de uso. Um caso de uso descreve como um usuário vai interagir com o sistema.
Novo
TERMO
Novo
TERMO
Novo
TERMO
Amílise de ClI:,-oS de /(so é o processo de descobena de casos de liSO através da criação
de cenários e histórias com usuários cm potencial ou ex istentes de um sistema. Um caso de liSO descreve a intcração entre o usuário do sistema e o sistema - como O usuário ut ilizará o sistema do seu próprio ponto de vista.
A criação de casos de uso é um processo iterativo. Existem vários passos que você deve dar durante cada iteração, para fonnalizar seus casos de uso. Para defin ir seus casos de uso, você deve: i. identificar os atares. 2. Criar uma lista preliminar de casos de uso. 3. Refi nar e nomear os casos de uso. 4. Definir a seqilêncii\ de eventos de cada caso de uso.
5. Modelar seus casos de uso.
NO TA
Você não cri a casos de uso no vácuo ! Enquanto deriva seus casos de uso, você deve consultar aqueles que utilizarão o sistema - seus clientes. A participação do client e é absolutamente fundam ental para se descobrir os casos de uso (a não ser que você esteja escrevendo o software para si m esm o). Seus clien t es são os especialistas do dom ínio. Eles conhecem bem seu espaço de atuação e sabem do que precisam em seu software. Sempre se certi fique de contar com o conhecimento deles e usá-lo para ori entar os requisitos de seu software. Fazer os usuários comporem histó ria s sobre seu d ia ideal de intera ção com o sistema pode ser uma boa maneira de quebrar o gelo nessa atividade.
Dia 9
Antes de co ntinuar com o dia, é importa nte dizer que os exemplos não ten tam real izar u ma análise completa de um sit e da Web on-line. Em vez disso, o s exemplos ensinam os passos que você dará enquanto realizar uma análise real. Assi m, muitos casos de uso serão omitidos. Na próxima semana, você trabalhará com uma análise ori entada a objetos completa.
Identifique os atores O primeiro passo na definição de seus casos de uso é definir os atores que usarão o sistema.
Novo
TERMO
Um (1/01' é tudo que interage com o sistema. Pode ser um usuário humano, outro sistema de com putador ou um chimpanzé.
Você preci sa pedir aos seus clientes para que descrevam os usuários do sistema. As perguntas podem incluir as seguintes: • Quem principalmente usará o sistema? • Existem outros sistemas que usarão o sistema? Por exemplo, ex istem quaisquer usuários que não são seres humanos? • O sistema se comun icará com qualquer outro sistema? Por exemplo, há um banco de dados já existente que você precise integrar? • O sistema responde ao estimulo gerado por alguém que não seja usuário? Por exemplo, o sistema precisa fazer algo em certo dia de cada mês? Um estimu lo pode ser proveniente de fontes nonnal mente não consideradas ao se pensar do ponto de vista puramente do usuário. Considere uma loja da \Veb on-line. Uma loja on-line pennite que usuários convidados naveguem pelo catálogo de produtos, verifique o preço dos itens e solicite mais infonnações. A loja também permite que usuários registrados com prem itens, assi m como cont rola seus pedidos e mantém informações dos usuários. A partir dessa breve descrição, você pode idenli ficar dois ata res: usuários convidados e usuári os registrados. Cada um desses doi s atares interage com o sistema. A Figura 9.4 ilustra a notação UML para um ata r: um desenho de pessoa com um nome. Você deve dar a cada um de se us atares um nome não ambíguo. FIGURA 9. 4 Q.ç al ores lia UML.
Usutirio Regist rado
Introd ução à AOO (Análise Orientada a Objetosl
201
É importante evitar confu são
ao nomear seus atores. Dê a cada ator um nome que identifique exclusivamente o ator. Uma boa atribuição de nomes é fundamental. Os nomes devem ser simples e fáceis de lembrar. •
E importante notar que detemlinado usuário do sistema pode assumir o papel de muitos alares di ferentes. Um ator é um papel. Por exemplo, um usuário poderia entrar no site como convidado, mas posteriormente se conectar como registrado para poder fazer uma compra.
N O TA
AL ERTA
Um usuário pode assumir muitos papêisdiferentes enquanto interage com um sistema. Um ato r descreve o pape/que o usuário pode assumi r enquanto interage com o sistema.
Quando você começar a definir seus casos de uso, crie uma lista preliminar de atares. Não se atrapalhe ao identificar os atares. Será diflcil descobrir todos os atares na primei ra vez. Em vez disso, encontre atares suficientes para começar e adicione os outros à medida que os descobrir.
Os atares são os instigadores de casos de uso. Agora que você já identificou alguns atorcs, pode começar a definir os casos de uso que eles executam.
Crie uma lista preliminar de casos de uso Para defin ir seus casos de uso, você precisa fazer algumas perguntas. Comece com sua lista de atores conhecidos. Você precisa perguntar o que cada ator faz com o sistcma. No caso da loja da Wcb on- line, você tem usuários regi strados c usuários convidados. O que cada um desses atares faz? Os usuários conv idados podem fazer o seguinte: I. Navegar pelo catá logo de produtos. 2. Pesqu isar o catálogo de produtos. 3. Procurar um item específico. 4. Pesqui sar o site. 5. Adicionar itens em um carri nho de compras e especificar a quantidade. 6. Ver o preço dos itens selecionados. 7. Mudar a quantidade de itens em seu carrinho. 8. Ver a lista de produtos popular e nova. 9. Navegar pela lista de itens desejados de outros usuários. 10. Solicitar mais inrormações sobre produto.
Dia 9
Os usuários regi strados podem fazer o seguinte: 1. Tudo que o usuário convidado pode fazer. 2. Fazer uma compra. 3. Adicionar itens em sua lista de itens desejados. 4. Ver uma lista personalizada recomendada. 5. Manter sua conta. 6. Assinar notificações. 7. Tirar proveito de ofertas especiais personalizadas. 8. Contro lar seus pedidos . 9. Ass inar várias li stas de di stribu ição. 10. Cance lar um pedido.
NOTA
Provavelmente existem muit o mais casos de uso. Entretanto, para nossos propósitos aqui e por brevidade, isso ê suficiente para começar.
Quando tentar identi fi car casos de uso, você tambêm deverá fazer a pergunta, "como um atar muda seu papel?" No caso da loja on-l ine, um usuário convidado pode se tomar um usuário registrado, das seguintes maneiras:
• O usuário convidado pode se conectar com o site. •
O usuário convidado pode se registrar no si te.
Um usuário registrado se torna um us uári o convidado, como segue: •
Um usuári o regi strado pode se desconectar do site.
Até aqu i, essas perguntas são orientadas pela interação. Você també m pode adotar uma estratégia orientada por resultados para a descobelta. Por exemplo, você pode d izer que um usuário registrado recebe uma notificação. Um segundo ponto de vista pode ajudá-l o a descobrir casos de uso que você poderia ter ignorado, se si mplesmente fica sse com o primeiro ponto de vista. Finalmente, considere as várias entidades que os usuários manipulam . Aqui, você vê produtos, informações sobre a conta e várias listas de produto e descontos. Como todas essas entidades entram no sistema? Quem adiciona novos produtos e edita ou excl ui produtos antigos? Esse sistema precisará de um terceiro atar, o administrador. Passando pelo processo anteriormente del ineado, você pode verificar que os administradores podem fazer o seguinte :
Introd ução à AOO (Aná lise Orientada a Objetosl
203
I. Ad icionar, edi tar e exclu ir produtos. 2. Ad icionar, edi tar c excl ui r incentivos.
3. Atualizar infonnaçõcs de conta.
As perguntas podem levar a outras perguntas. Por exemplo, quem atua\iza a lista de produtos p0pu lares? Quem envia noti fi cações e correspondências para as listas de dist ribuição? Um quarto ator, o próprio sistema, executa todas essas ações.
Refine e nomeie os casos de uso Agora que você tem uma lista pre liminar de casos de uso, precisa refinar a lista. Em particular, você desejará procurar oport unidades de dividir ou combinar os casos de uso. Dividindo casos de uso Cada caso de uso deve executar um objetivo principal. Quando você encontrar um caso de uso que estiver fa zendo muita coisa, desejará dividi·\o em dois ou mai s casos de uso. Considere o caso de uso a seguir: Os usuários convidados podem adicionar itens em um carrinho de compras e especi ficar a quan· tidade. Você deve dividir esse caso de uso em dois: • Os usuários convidados podem adic ionar itens em um carrinho de compras. • Os usuários convidados podem especificar a quantidade de um item. Você pode fazer a div isão de casos de uso, dev idoà maneira como eles se relacionam entre si. Os casos de uso são muito parecidos com as classes. Um caso de uso pode conter outro. Assim, se uma instância de caso de uso exige que outra faça seu trabalho, ela pode usá-Ia. Um caso de uso também pode estender o com portamento de outro caso de uso. Como resultado, você pode colocar comportamento comum em um caso de uso e, então, desenvolver outros casos de li SO que sejam especia li zações do original. Pegue o exemplo "os usuários registrados podem faze r uma compra". Um caso de uso pode espec ializar o ped ido, criando um caso de uso pedido para presente. Um pedido para presente poderia ser entregue sem recibo. Combinando casos de uso Você nãoquercasos de uso redundantes. Um modo de evitar a redundância é ficar atento às vari· antes do caso de uso. Quando você as encontrar, deverá combinar as variantes em um (mico caso de uso. Novo TERMO Uma varianle de caso de uso é uma versão especializada de outro caso de uso mais geral.
Dia 9
Considere os dois casos de uso a seguir: •
Os usuários convidados podem pesqu isar o catá logo de produtos .
•
Os usuários convidados podem procurar um item específico .
Aqui , o segundo é si mplesmente uma variante do primeiro caso de uso mais geral. Neste caso, o caso de uso difere apenas nos parâmetros de pesquisa. É melhor ter si mplesmente um caso de uso e documentar a variante nos modelos de caso de uso que você construirá posteriormente. Uma variante é muito parecida com uma instância de uma classe. Lembre do exemplo BankAccount. Um objcto BankAccount com um saldo de US$ l 0.000 pode ter mais dinheiro que um BankAccount com US$ l 00. Entretanto, ambos ainda são objetos BankAccount. Tudo que d iferencia um objeto BankAccount de outro é Ova lor de seus atributos. Os casos de uso funcionam da mesma maneira.
Os casos de uso resultantes Após concluir o refinamento de seus casos de uso, você deve nomear cada caso de uso. Assim como na atribuição de nomes de atores, você deve se esforçar por nomear seus casos de uso de maneira que evite confusão. Aqui estão os casos de uso resultantes para us uários convidados e usuários registrados, após a divisão e a combinação: I. Navegar pe lo catálogo de produtos.
2. Pesquisar o catá logo de produtos. 3. Pesquisar o site. 4. Adicionar item no carri nho de compras.
5. Ver o preço dos itens. 6. Mudar a quantidade de item. 7. Ver a lista de produtos destacada.
8. Navegar em uma lista de itens desejados. 9. Solicitar informações sobre produto. 10. Pedir. I I. Manter o pedido.
12. Ad icionar itens na li sta de itens desejados.
13. Atualizar a conta. 14. Assinar a correspondência. 15. Aplicar incent ivos.
Introdução à AOO (Análise Orientada a Objetos l
205
16. Conectar. I 7. Desconectar. 18. Registrar. Neste ponto, você tem uma lista de casos de uso bem desenvolvida. Agora, basta especi fi car totalmente cada caso de uso.
Defina a seqüência de eventos de cada caso de uso A breve lista de casos de uso s6 infonna parte da história. Internamente, muito mai s poderia estar ocorrendo dentro de um caso de uso. Pegue um pedido como exemplo. Um usuário não pode fa· zer um pedido em um passo. Em vez disso, ele deve usar uma seqüência de passos para conclu ir um ped ido com êxito (como fornecer um método de pagamento). A seqüência de passos que um usuário usa para completar um caso de uso é conhecida como cenário. Um caso de uso é const ituído de vários cenários. Novo
TERMO
Um cenário é uma seqUência ou fluxo de eventos entre o usuário e o sistema.
Como parte de sua análise de casos de uso, você deve especificar os cenários de cada caso de uso. Vamos desenvolver o caso de uso Pedido. Primeiro, comece descrevendo o caso de uso em um parágrafo: O usuário registrado prossegue com a totalização e pagamento, para adquirir os itens de seu carrinho de compras. Uma vez na página de totalização e pagamento, o usuário fornece informações de entrega. Uma vez fornecidas, o sistema totaliza e apresenta o ped ido. Se tudo estiver correto, o cliente poderá optar por cont inuar com o pedido. Quando o usuário continua com o pedido, o sistema consulta suas informações de pagamento. Uma vez fornec idas, o sistema autoriza o p
TERMO
NOTA
Condições previas são aquelas condições que devem ser satisfeitas para que um caso de uso comece. Condições posteriores são os resultados de um caso de uso. Um dos problemas desse tipo de sistema é que você provavelmente não está reun indo casos de uso dos usuários do sistema, mas das pessoas que querem que você os escreva. Lembre-se de que os modernos aplicativos da Web e outros aplicativos que se d eparam com o cliente, como quiosques, podem exigir que você trabalhe com grupos convergentes.
Dia 9
Aqui, a condição prévia é que o usuário já colocou itens no carrinho. O caso de uso Ped ido pede os itens do carrinho. A condição posterior é um pedido. Após completar a caso de uso, o sistema conterá um ped ido para o usuário. Nesse ponto, ajuda considerar todos os caminhos alternativos no caso de uso de ped ido. Talvez a autorização de pagamento fa lhe ou o usuário decida cancelar o pedido, antes do término. Você precisa capturar esses cami nhos alternativos. Após se sentir à vontade com o caso de uso, você deve escrevê-lo formalmente. Um modo de escrever o caso de uso é listar os passos seqüencialmente. Após os passos, você deve listar as condições prévias, as condi ções posteriores e os camin hos alternat ivos. Considere novamente o caso de uso Pedido: o
Pedido I. O usuário registrado passa para a totalização e pagamento. 2. O usuário registrado fornece infonnações de entrega. 3. O sistema ex ibe o total do pedido. 4. O usuário registrado fornece informações de pagamento . 5. O sistema autoriza o pagamento. 6. O sistema conli rma o pedido. 7. O sistema envia um e-mail de continnação.
o
Condições prévias o
o
Condições posteriores o
o
Um carrinho de compras não vazio . Um pedido no sistema.
Alternat iva: cance la ped ido Durante os passos I a 4, o usuário opta por cancelar o pedido. O us uário volta para a homc page.
o
Alternativa: a autorização falhou No passo 5, o sistema fa lha em autorizar as info rmações de paga mento. O usuário pode reintrod uzir as infonnações ou cancelar o pedi do.
Você precisará completar o mesmo processo para cada caso de uso. Defi nir formalmente os cenári os o ajuda a ver o fluxo de eventos no sistema, assim como a solidificar seu entendi mento do sistema.
Introdução à AOO (Análise Orientada a Objet osl
207
Ao escrever seus casos de uso, inclua apenas as informa ções que fizerem sentido. A ssim com o na m odelagem de classe, seu objeti vo é transmitir algum tipo de informaçã o. Inclua apenas as informaçóes para transmitir o que você está tentando fazer.
DICA
Inclua apenas as condições prêvias necessárias para Que o caso de uso comece. Nilo inclua informações extras e desnecessárias. Certifique-se de consultar outros textos sobre casos de uso. Existem muitas maneiras de escrever um caso de uso (não há um padrão). Ao escrever seus casos de uso pela primeira vez, considere o uso de uma ficha de arquivo com um lápis. Desse modo, você não terá de estar diante de um computador, enquanto gera seus casos de uso iniciais. Dependendo de quem forem seus clientes, pode ser dificil trabalhar com eles diante de um computador.
Diagramas de caso de uso Assim como a UML fo rnece uma maneira de documentar e transmiti r projeto de classe, também existem maneiras formais de capturar seus casos de uso. De especial interesse são os diagramas de caso de uso, diagramas de interação e diagramas de atividade. Cada um ajuda a visual izar os vários casos de uso. Os diagramas de caso de uso modelam os relacionamentos entre casos de uso e os re lacionamentos entre casos de usoe atares. Embora a descrição textual de um caso de uso possa aj udá-lo a entender um caso de uso isolado, um diagrama o ajuda a ver como os casos de uso se relacionam uns com os outros. A Figura 9.4 mostra como modelar atares. A Figura 9.5 ilustra a notação UML para um caso de uso: uma elipse rOlUlada. FIGURA 9 .5
O caso dI!
liSO
lia UMC
(
pedido)
Coloque um atar e um caso de LISO juntos no mesmo diagrama e você terá um diagrama de caso de uso. A Figura 9.6 é o diagrama de caso de uso Pedido.
FIGURA 9 .6
O caso de
I/S0
Pedido.
---,,>.. (
pedidO )
Usuá ri o Registrado
Esse diagrama é mu ito simples; entretanto, examinando-o, você pode ver que o usuário registrado executa o caso de uso Pedido.
Dia 9 Os diagramas podem ser um pouco mais complicados. O diagrama também pode mostrar os relacionamentos existentes entre os próprios casos de uso. Conforme você já leu, um caso de uso pode conter e usar outro. A Figura 9.7 ilustra lal relacionamento. FIGURA 9 .7
Um relaciOllamento lisa.
Pedido
«uA»
(
AssInatura da corrupond6ncl l
Aqui , você vê que o caso de uso Registro usa o caso de uso Assinatura da correspondência. Como parte do processo de regi stro, o usuário pode optar por receber e-mail s e notificações. A Figura 9.8 ilust ra o segundo tipo de relacionamento, o relacionamento estende. FIGURA 9 .8
Um relaciollamelllO eSfellde. U."'rio Registr.OO
Consult•• 1"" de prod uto. dsstacad.
Vê recomendações de produto estende a genérica Consulta a /is/a de produ/os destacada, apre-
sentando ao usuário registrado uma li sta de produtos personalizados para suas preferências de compras. A normal Vê recomel1dações de produto, confonne vista por um usuári o convidado, pode sim plesmente mostrar os itens mais vendidos ou mais solicitados. Essa extensão apresenta ao usuário produtos nos qua is seu perfil sugere que ele poderi a estar interessado. Assim como nas classes, é possivel ter um caso de uso abstraio. Um caso de uso abSlrato é um caso de uso que outros casos de uso utilizam ou estendem, mas que nunca é usado diretamente por um atar em si. As abstraçôes nonnal mente são descobertas após você ter fe ito sua análise de caso de uso inicial. Enquanto você estuda seus casos de uso, pode encontrar meios de extrair características comuns e colocá- Ias em casos de uso abstratos.
Introd ução à AOO (Aná lise Orientada a Objetosl
209
Diagramas de interação Os diagramas de caso de uso aj udam a modelar os relacionamentos entre casos de uso. Os diagra· mas de interação aj udam a capturar as interações entre os vários atares paT1icipantes do sistema. Vamos expandiras casos de uso que vi mos anterionnente. Vamos adicionar um novo ata r, o representante de serv iço ao cliente. Freqüentemente, um usuário registrado pode se esquecer de s ua sen ha. O representante de serv iço ao cliente está lá para ajudar o usuário a reaver o acesso à sua conta. Vamos criar um novo caso de uso, Senha Esquecida: Um usuário registrado liga para o representante de serviço ao cliente e informa ao represen· lante que perdeu sua senha. O representante de serv iço ao c li ente pega o nome completo do usuário e extrai as informações de conta do usuário. O representante de serviço ao cliente fa z então várias perguntas ao us uário registrado, para estabelecer sua ident idade. Após passar por várias interpelações, o representante de serviço ao cliente exc lui a senha antiga c cria uma nova. Então, o us uário recebe a nova senlla por e·mail. Esse caso de uso também pode ser descrito como segue: o
Senha Esquecida I. O usuário registrado liga para o representante de serviço ao cliente. 2. O usuário regi strado fornece o nome completo. 3. O representante de serviço ao cliente recupera as infonnações do cliente. 4. O usuário registrado responde a várias perguntas de idemificação. 5. O representante de serviço ao cliente cria uma nova senha. 6. O usuário recebe a nova senha por e·ma il.
•
Condições prévias •
o
Condições posteriores o
o
O us uári o esqueceu s ua senha. Uma nova senha é enviada por e·mail ao usuário.
Alternativa: a identificação falhou O usuário pode fa lhar cm responder corretamente as perguntas de identificação no passo 4. Se ass im for, a chamada term inará.
o
Alternati va: usuário não encontrado No passo 2, o nome fornec ido pode não ser o de um usuário conhecido. Se assim for, o re· presentante de serviço ao cliente se oferecerá para registrar o usuário chamador.
Existem dois tipos de diagramas de interação: diagramas de seqUência e diagramas de colabora· ção. Vamos explorar cada um deles.
Dia 9
Diag ram as de seqüência
Um diagrama de seqüência modela as interaçôes entre o usuário registrado, o representante de serviço ao cliente e o site Web, com o passar do tempo. Você deve usar diagramas de seqUência quando quiser chamar a atenção para a seqUência de eventos de um caso de uso, com o passar do tempo. A Figura 9.9 apresenta um diagrama de seqUência para o caso de uso Senha Esquecida. Conforme voeê pode ver na ilustração, um diagrama de seqUênci a representa os eventos entre cada atare o sistema (o site Web). Cada part icipante do caso de LI SO é representado no início do diagrama como uma caixa ou como um desenho de pessoa (mas você pode chamar ambos de caixa). Uma li nha tracejada, conhecida como linha da vida, sai de cada caixa. A linha da vida representa o tempo de vida da ca ixa durante o caso de uso. Assim, se um dos atares fosse embora durante o caso de uso, a li nha termi nari a na úl tim a seta que termi na ou se origina no atar. Quando um atar deixa um caso de uso, você pode dizer que seu tempo de vida terminou. FIGURA 9 .9
A diagrama de
seqiiêllôa Se"ha Esquecida.
SileWeb Registrado
Representante de ServIço ao Cliente
1: Relata senha esquecida 2: SoIic:lte nome completo
3: Fornece nOrntl oompleto
.: Recupera inlormaç6es do u.....io 5: Devolve .egi"ro 6: Fel pargunln de Identificaçâo
7: Responde as perguntas
8: Gera nova IIn ll. 9: Envia senlla por e·mail
I
Novo
TERMO
Uma linha da vida é uma linha tracejada que sai de uma caixa em um diagrama de seqUência. A linha da vida representa o tempo de vida do objelo representado pela caixa.
As selas se originam na linha da vida para indicar que o ata r enviou uma mensagem para outro atar ou para o sistema. Quando você desce na linha da vida, pode ver as mensagens conforme
Introd ução à AOO (Aná lise Orientada a Objetos)
2 11
elas se originam seqüencialmente, com o passar do tempo. O tempo corre de cima para baixo em um diagrama de seqUênc ia . Assim , subi ndo na linha da vida, você pode reprod uzi r a seqüência de eventos de trás para frente.
Diagramas de colaboração Você deve usar diagramas de seqüência se tiver a intenção de chamar a atenção para a seqüênc ia de eventos com o passar do tcmpo. Se você quiser modelar os relacionamentos entrc os atares e o sistema, então deve criar um diagrama de colaboração. A Figura 9. 10 modela o caso de uso Senha
E.~qllecida
como um d iagrama de colaboração.
Em um diagrama de colaboração, você modela uma interação conectando os part ici pantes com uma lin ha. Ac ima da linha, você rotula cada evento que as entidades geram , junto com a direção do evento (para quem e le é dirigido). Ele também aj uda a numerar os eventos, para que você saiba em qual ordem eles aparecem.
FIGURA 9 .10
O diag/'ama de
colaborartio Se"ha & q/lecidll.
Sil. W.b
Use diagramas de seqüência para modelar a seqüência de event os em um cenário, com o passar do tempo. Use diagramas de col abora ção para m odelar os relacionamentos entre os atares em um cenário.
Dia 9
Diagramas de atividade Os diagramas de interação modelam bem as açôes seqüenc iais. Entretanto, eles não podem modelar processos que podem ser executados em paralelo. Os diagramas de ativ idade o aj udam a modelar processos que podem ser executados em paralelo. Considere outro caso de uso Pesquisa. Esse caso de uso pesqui sa o site Web c o catálogo de produtos simultaneamente, usando o caso de uso Pesquisa o catálogo de produtos e o caso de uso Pesq/lisaosite. Não há motivo pelo qual essas duas pesquisas não possam ser executadas simultaneamente. O usuário ficari a impaciente se tivesse de esperar que todas as pesqui sas termi nassem seqUencialmente. A Figura 9. 1 I modela esses processos através de um diagrama de ativi dade. Uma elipse representa cada estado do processo. A barra preta grossa representa um ponto onde os processos devem ser sincronizados - ou reunidos-, antes que o fluxo de execução possa ser retomado. Aqui , você vê que as duas pesquisas são executadas em paralelo e depois reunidas, antes que o site possa ex ibir os resultados.
FIGURA 9.11
O diagrama de
alil'idade Pesquisa.
Vamos ver de perto os diagramas de interação e os diagramas de atividade nos próx imos dias. Entretanto, ambos se mostram üteis ao se analisar um sistema.
Introd ução à AOO (Aná lise Orientada a Objetos)
213
Construindo o modelo de domínio Através da análise de caso de uso, você captura as interaçôes do sistema. Entretanto, os casos de uso também o aj udam a capturar o vocabulário do sistema. Esse vocabulário constitui o domínio do problema. O vocabulário do domínio identi fi ca os pri ncipa is obj etos do sistema, O modelo de domínio lista os objetos que você precisa para modelarcorretamente o sistema. Pe· gue a loja on·l inc. Através dos casos de uso, você pode identificar muitos objctos. A Figura 9. 12 visua liza alguns desses objelos. Neste ponto, você pode mode lar os relacionamentos entre os objetos do domínio. A Figura 9 .1 3 resume alguns desses relacionamentos. FIGURA 9 .12
Os objelos do
*'
ReprfteII""'" do s .....
00 CIiont.
domínio. lhW
u...-",>'«t '
FIGURA 9 .13
Sil.. d. W.b
CII"ogO
0,
,
relaciOl/alllelltos
dos objetos do domínio.
O
• Usu6tiO
Item
7* lIepreHm.nl. de 50rviço ao Client..
Usujtio Comridado
UI",",- II-;irltado l ~
..
In'ormaç~ I I
Pedido
•
'
Dia 9
o modelo de domínio é importante por vârios motivos. Primeiro, o modelo de domínio modela seu problema independentemente de quaisquer preocupações com implementação. Em vez disso, ele modela o sistema em um nível conceituaI. Essa independência proporciona a flexibilidade para usar o modelo de domínio que você const rói para resolver muitos problemas diferentes dentro do domín io. Segundo, o modelo de domínio constrói a base do mode lo de objeto que, fina lmente, se tornarâ seu sistema. A implementação final pode adicionar novas classes e remover outras. Entretanto, o domíni o forneee algo para que você comece e construa seu projeto - um esqueleto. Finalm ente, um mode lo de domínio bem definido estabelece claramente um vocabu lúriocOlllum para seu problema. Encontrando-se um vocabu lârio comum, todos os envolvidos no projeto poderão encará-lo a parti r de uma posição e um entendimento iguai s.
E agora? Você reuniu casos de uso. Você criou diagramas de interação. Você até iniciou um modelo de domínio. O que fazer em seguida? Os casos de uso têm três uti lizações pri ncipais. O primeiro uso trata da func ional idade. Os casos de uso infonnam a você como o sistema funcionarâ. Os casos de uso informam quem usarâ o sistema. o que esses usuârios farão com ele e o que esperam receber do sistema. A análise de caso de uso o aj uda a aprender a respei to do sistema que você pretende construir. Segundo, os casos de uso fornecem uma lista de tarefas 'a faze r', ã medida que você desenvolve o sistema . Você pode começar priorizando cada caso de uso e fornecendo uma estimativa do tempo que cada um demorarâ para tenninar. Em seguida, você pode ptanejar o tempo de seu desenvolvimento em torno dos casos de uso. A conclusão de um caso de uso pode se tornar um marco. Os casos de uso também se tomam itens de barganha. Freqüentemente, as restrições de tempo o obrigarão a sacrificar um caso de uso por outro. Finalmente, os casos de uso o ajudam a construi r seu mode lo de domíni o. O modelo de domínio servirá como o esqueleto de seu novo sistema. (E se você o tiver fe ito corretamente, poderá reut ilizar esse mode lo em qualquer lugar!) Quando você perceber que o model o de domín io e a análise de caso de liSO estilo quase terminados, pode começar a fazer o protótipo das diferentes partes do sistema. Entretanto, não faça o protótipo de tudo. Você só deve fazer o protótipo dos aspectos do sistema que parecem confusos ou arriscados. Fazer o protótipo pode aprofundar o conhecimento, assim como descobri r se uma idéia é possível, identi fi cando e reduzindo os riscos.
Intro dução à AOO (Análise Orientada a Objet os )
215
Dicas para a AOO eficaz • Evite a paralisia da análise. A paralisia da análise ocorre quando você tenta realizar a análise perfeita. Você n~nca vai ad iant e, pois fica tentando entender perfeitamente o p roblema. As vezes, o ent endimento total não é possivel sem algum projet o e implementação. • Faça iteração. Faça iteração de tudo. Quando você com eçar a análise, gere uma lista preliminar de casos de uso. Priorize os casos de uso e, em seguida, apresente-os através de iterações. Cada iteração deve abranger certa q uantidade de análise, projeto e implement ação. A q uantidade de implementação aumentará à m edida que o projeto prossegui r: muito pouco no inicio, muito mais durante os estágios posteriores. • Incl ua os especialist as no dom inio em sua análise - mesmo q ue o especialista seja um cliente. A não ser que você seja especia lista no d omi nio, precisa rá da entra da para modelar o sistem a corretamente. • Não introduza impl em entação em sua análise. Não deixe a impl ementação ent ra r furt ivamente em sua análise.
Resumo A análise orientada a objetos apl ica objetos ao processo de análise do problema. A AOO o ajuda a descobrir os requisitos do sistema que você pretende construir. Os casos de uso o ajudam a identi fi car como os us uários vão interagi r com o sistema. Os casos de uso descrevem a interação, assim como o que os usuários es peram receber do sistema. Modelos como os d iagramas de interação e os diagramas de atividade ajudam a visualizar essas interaçõcs. Cada tipo de modelo vê o sistema de um ponto de vista ligeiramente diferente. Assi m, você precisará lembrar das di ferenças e usar cada tipo quando for apropriado. Os casos de uso oajudam a definir seu modelo de domínio. O modelo de domínio serve como es· queleto do sistema que você fina lmente construirá. O modelo de domínio tem a vantagem dc ser livre de qua lquer imp lementação ou uso específico. Como resultado, você pode aplicar seu mode lo de domínio em mui tos problemas d iferentes.
É im portante perceber q ue a AOO é real mente um modo orientado a objetos de ver um proble· ma. Os casos de uso nada mais são do que obj etos. Um caso de uso pode se relacionar com o utros casos de uso, através do uso ou da ge nerali zação. As vari antes dos casos de uso não são diferen· tes da di ferença entre instâncias de classe. Os atares são objetos também. A AOO decompõe um problema em vários casos de uso e objetos de domínio. Uma vez divi di· do, você pode fazer iteraçôes até obter a solução fina l.
Dia 9
Perguntas e respostas P
o q ue acontece se você esquece um caso de uso?
R Se você verificar que se esq ueceu de um caso de uso, volte e acrescente-o. Se você precisar do caso de uso imed iatamente, então deve acrescentá- lo imediatamente. Se ele puder esperar, tome nota c explore-o durante a próxima iteração. P Ao elaborar um caso de uso, você sempre precisa fazer diagramas de seqüê ncia, colaboração e a tividade?
R Não. Nem sempre você precisa faze r todos os três. Faça o que fo r necessário pa ra ajudar seu entend imento do caso de uso. Entretanto, você provavelmente deve pelo menos esboçar um dos diagramas. Você nunca sabe quais problemas ou incógn itas poderia descobrir. Normalmente, sempre fazemos pelo menos o diagrama de seqüência, a não ser que faça mais sentido começar com um dos outros. P Como você sabe quando tcm casos de uso sufic ientes ?
R Na verdade, você nunca sabe se encontrou todos os casos de uso. Saber quando parar vem através da experiência. Entretanto, você provavelmente tem casos de uso suficientes quando percebe que possui um entend imento adeq uado do problema eque se sente confiante de que pode prosseguir. Se você omiti r um caso de uso, sempre pode voltar e acrescentá-lo em sua análise. Entretanto, você deve tomar cuidado com a análise em demasia de um problema. Não sucumba à paral isia da análise.
Workshop As perguntas e respostas do teste são fornecid as para seu mel hor entendimento. Veja as respostas no Apênd ice A, "Respostas".
Teste I. o que é um processo de software? 2. O que é um processo iterativo? 3. No fi na l da AOO, o que você deve ter fe ito? 4. O que os requisitos do sistema informam a você? 5. O que é caso de liSO? 6. Qua is passos você deve dar para defin ir seus casos de LISO?
Intro dução à AOO (Análise Orientada a Obje tos )
217
7. O que é um atar? 8. Quais são algumas perguntas que você pode fazer para descobri r os atares? 9. COnl O os casos de uso podem se relacionar entre si? 10. O que é uma variante de caso de uso? I I.
O que é um cenário?
12.
Quai s são algumas mane iras pelas quais você pode mode lar seus casos de uso?
13.
Descreva as diferenças entre os vários modelos usados para visua lizar casos de uso.
14.
Para que serve um mode lo de domínio?
15.
I)ara que servem os casos de uso?
Exercícios I. Quais outros casos de uso você poderia acrescentar na lista de casos de uso da loja on-li ne? 2. Pegue um dos casos de uso da questão I e desenvolva-o. 3. Uma variante de caso de uso é um caso específico de um caso de uso mais geral. Quais variames você consegue identificar nos casos de uso de usuário convidado e de usuário registrado? 4. Qua is outros objetos de dom inio você consegue encontrar?
SEMANA
2
DIA Introdução ao POO (Projeto Orientado a Objetos) Ontem, você viu como a AOO (Análi se Orientada a O bjetos) o ajuda a entender um problema c seus requisitos. Através da análi se de casos de uso e da construção de um modelo de domín io, você pode capturar o ' mundo real ' ou detalhes em nível de domín io deseu problema. Entretanto, a ADO é apenas parte da história geral do desenvolvim ento. O roo (Projeto Orientado a Objetos) o ajuda a pegar a domínio que você encontrou na AOO e a projetar urna so lução. Enquanto o processo da AOO o aj udou a descobri r muitos dos objelos de domín io do problema, o o ajuda a descobrire projetar as objetos que aparecerão na solução específi ca do probl ema.
roo
Hoje você aprenderá: •
Como transformar sua aná lise cm uma solução
•
Como identi fi car e projetar os objelos que aparecerão em sua solução
• Como os cartões CRC (Classe Responsabilidade Co laboração) podem aj udá-lo a descobrir responsabilidades e relacionamentos de objetos • Como você pode usar a UML para capturar seu projeto
Dia 10
POO (Projeto Orientado a Objetosl Novo
TERMO
Novo
TERMO
POO é o processo de construir o modelo de objeto de uma soluçâo. DilO de outra ma· neira, roo é o processo de dividir uma solução em vários objetos const itu intes.
o modelo de objeto é o projeto dos objetos que aparecem na sol ução de um proble·
ma. O modelo final de objeto pode conter muitos objetos não encontrados no domí· nio. O modelo de objeto descreverá as várias responsabi lidades, relacionamentos e estrutu ra do objeto.
o processo de POO o ajuda a descobrir corno você vai implementar a análise que com pl etou du· rante a AOO. Principalmente, o modelo de objeto que conterá as classes pri ncipais do projeto, suas responsabil idades e urna definição de como elas vão interagir e obter suas informações. Pense no roo em termos da construção de uma casa para uma família. Antes de construi r a casa de seus sonhos, você decide quai s tipos de ambientes deseja em sua casa. Através de sua análise, você pode achar que deseja uma casa que tenha uma cozinha, dois quartos, doi s banheiros e um lavabo, uma sala de estare uma sala de jantar. Você também pode precisar de um gabinete de lei· tura, uma garagem para dois carros e urna piscina. Todos os ambientes e a piscina compreendem a idéia e o projeto de sua casa. O que você faz em seguida? Um construtor simplesmente começa a construir? Não. Primeiro, um arqui teto descobre como os ambientes mel hor se encaixam, comoa fiação deve ser passada e qua is vigas são necessárias para manter a casa de pé. Então, o arquiteto prepara um conjunto de cópias heliográficas detalhadas, que capturam o projeto. O construtor usa essas cópias hei iográ· ficas como guia, enq uanto constrói a casa. Usando os termos do mundo da construção, você usa o roo para criar as cópias heliográficas de seu programa. Um processo de projeto formal o ajuda a determinar quais objetos aparecerão em seu programa e como eles vão interagir ou se encaixar. O projeto indicará a estrutura de se us objetos e um pro· cesso de projeto o ajudará a descobrir muitos dos prob lemas de projeto que você encontrará ao cod ificar. Ao se trabalhar como parte de uma equipe, é importante identificar e resolver o máximo de pro· blemas de projeto possível, antes de iniciar a construção. Resolvendo-se os problemas antecipadamente, todo mundo trabalhará sob o mesmo conjunto de suposições. Uma estratégia consistente para o projeto tornará mais fácil reunir todos os componentes posteriormente. Sem um projeto claro, cada desenvolvedor fará seu próprio conjunto de suposições, freq Uentemente incompativel com outros conjuntos de suposições. O trabalho será duplicado e a divisão de res· ponsabilidades entre os objetos será desfei ta. De modo geral, o projeto que emerge poderia se tornar fac ilmente uma confusão, se não estiver todo mundo na mesma pági na.
Introdução ao POO (Projeto Orientado a Objetos)
Além disso, erros são exponencia lmente mais dispendiosos para corrigir, quanto mais tarde na seqüência de desenvolvi mento eles forem descobertos. Os erros no projeto têm um custo muito baixo para corrigir. Enquanto projetar sua solução, você verá que freqi.ienlemen le ex iste mais de uma solução para o problema. O POO pennite que você explore cada sol ução interessante e decida antecipadamente qua l caminho deve seguir. Através do projeto, você pode tomardeci5Ões acertadas, e através da documentação, você pode documentar o motivo pelo qual optou pelas escolhas que fez. O modelo de objeto identifica os objetos significativos que aparecem na solução; entretalllo, o modelo de objeto é um superconjunto do domínio. Embora mu itos dos objetos que apa recem no modelo de domínio encontrem seu lugar no projeto, muitos objetos não encontrados no modelo de domínio também aparecerão. Do mesmo modo, objetos não encontrados na análi se podem achar seu lugar no projeto, cxatamente como a fiação não aparece na análise inicia l de uma nova casa. Uma vez tendo seu projeto, você pode começar a codi fic ar. Dito isso, não leve o projeto ao extremo. Exatamente como a AOO pode sofrer de paralisia da análise, o POO pode so frer de paralisia do projeto. Você deve evitar o projeto demasiado de sua solução. Simplesmente não é possível prever cada decisão de projeto que você precisará tomar, antes de tomá-la, e alguma parte do projeto pode ser deixada para o momento da construção. Você não quer ser pego tentando criar o projeto perfe ito; você precisa começar a cod ificar em algum momento. O que você precisa fazer é projetar os aspectos arq uitetonicamente significativos do sistema. Como você sabe quais aspectos de seu sistema são arqllitetonicGmente significativos? As partes significativas são os aspectos do sistema onde uma decisão diferente alteraria completamente a estrutura ou o comportamento do sistema. Lembre da loja on- line e do carrinho de compras discutidos ontem. Sabendo que tem um objeto carrinho de compras em sua análise, você precisa projetar quais abstraçôes especificas serão usadas para representar o carrinho de compras - como um mostrador de preço, uma loja persistente e um monitor de expiração. Mas você não precisa projetar uma tabela hashing ou vetor que será usado para representar o conteúdo do carrinho - esse é um assunto apropriado para lima fase de projeto detalhado (implementação), mas é detalhado demais para esta fase.
Como você aplica POO (Projeto Orientado a Objetol? o poo é um processo iterativo que identifica os objetos e suas responsabilidades em seu sistema, e como esses objetos se relacionam . Você refina continuamente o modelo de objetos, quando faz a iteração pe lo processo de projeto. Cada iteração deve dar uma idéia mais aprofundada do projeto e ta lvez até do próprio dominio.
Dia 10
NO TA
Quando você aprender mais sobre o problema que está tentando resolver durante o projeto, tal vez precise aplicar m ais análise. l embre-se de que não há vergonha em volta r e refinar sua análise ! A única vergonha está em cri ar soft ware inútil.
Existem vários passos pouco defin idos que você pode seguir para construi r seu modelo de objeIas. Nonnal mente, você: I. Gerará uma lista inicial de objetos. 2. Refi nará as responsabi lidades de seus objetos. 3. Desenvo lverá os pontos de interação. 4. Detalhará os relacionamentos entre objetos. 5. Construi rá seu mode lo. Seu entendimento do projeto aumentará quando você completar esses passos e repetir o processo.
NO TA
Existem muitas maneiras de completar o poa. Os passos delineados anteriorm ente constituem um processo informal que combina aspectos de muitas m etodologias diferentes. A metodologia que você vai seguir dependerá da exper iência, do dominio, da postura da empresa e do bom gosto. No final do paa, você deve ter decom posto a solução em vá rios objetos. Como você chega a esses objetos fi ca por sua conta e da sua equipe de projeto.
Passo 1: gere uma lista inicial de objetos Quando começa a projetar seu sistema, você precisa começar com o domínio que de fi ni u durante a análise. Cada objeto do domín io e cada ator deve se tomar uma classc cm seu novo mode lo de objetos. Você verá que alguns dos objetos de domínio não terão lugar em seu modelo de objetos final ; entretanto, neste ponto, você não pode ter certeza de qual terá; portanto, você precisa incl uir todos eles. Lembre da loja on-line do Dia 9, "Introd ução à AOO (Aná li se Orientada a Objelos) ". A Figura 10.1 iluslra as classes básicas que aparecerão em seu mode lo de objetos ini cial. Ao procurar a lista de classes inici al, você também desejará considerar todos os eventos q ue possam afelar seu sistema. Cada um desses eventos deve aparecer inicialmente como uma classe. O mesmo pode ser dito para todos os relatórios, leias e disposit ivos. Todos esses elementos devem ser transformados em uma classe.
223
Introdução ao POO (Projeto Orientado a Objetos)
FIGURA
10.1
As classes ba.çicas da
''''"
"'"''
l oja oll· lille.
' ,m
CII!JIomerServiceAep ShopplngClrt
Aecollml nlo,m.tion
Glle.tUM'
Aqui estão algumas dicas para animar essa lista de classes inicial: • Transforme cada ator em uma classe. • Transfo rme cada objeto de dom ínio em uma classe. • Transforme cada evento em uma classe. • Considere como o sistema apresentará informações e transform e cada tela em um objeto. • Represen te todos os outros sistemas ou di spositivos com os quais o sistema interage com o classes.
Neste ponto, você não pode di zer muito a respeito das c lasses que listou. Você pode ter uma idéia geral das responsabilidades e relacionamentos dos objetos; entretanto, você precisa se aprofundar um pouco mai s, antes de poder finalizar seu entendimen to das novas classes.
Passo 2: refine as responsabilidades de seus objetos Uma li sta de objetos é um bom ponto de partida, mas é apenas uma pequena parte de se u projeto g lobal. Um proj eto com pleto capturará as responsabilidades de cad a obj eto, assim como a estrut ura e os re lac iona men tos do objeto. Um proj eto mostrará como ludo se encaixa. Para ter esse entendimento, você precisa identificar o que cada objeto faz. Existem dois aspectos q ue você precisa explorar para que possa responder à pergunta, ·'0 que o objeto faz?" Primei ro, você precisa explorar a responsabi lidade. Através do encaps ulamento, você sabe q ue cada objeto deve ter um número peq ueno de responsabilidades. Dura nte o projeto, você prec isa identi ficar as responsabilidades de cada objeto e d ividira objelo. quando e le começar a
Dia 10
fazer coisas demais. Você também precisa ce rtificar~se de que cada responsabil idade apareça apenas uma vez e esse conhecimento é espalhado igualmente entre todos os objetos. Em seguida, você precisa explorar o modo como cada objeto faz seu trabalho. Os objctos fre~ qüentemente delegarão traba lho para outros objelos. Através de seu projeto, você precisa ident i~ fi car essas colaborações. Novo
TERMO
Um objero delega trabalho para colaboradores.
Novo
TERMO
Colaboraçcio é o relacionamento onde os objetos interagem para realizar o mesmo propósito.
Um entendimento pro fun do dos relacionamentos e responsabili dades de um objeto é im portante. Em um nível prático, as responsabilidades serão transform adas em métodos. Os relacionamentos serão transformados em estruturas; entretanto, um entendimento global da responsabilidade o ajudará a dividir a responsabilidade eficientemente entre os objetos. Você prec isa evitar ter um pequeno conjunto de objetos grandes. Através do projeto, você terá a certeza de dividir as responsabi lidades.
o que são cartões CRC? Uma maneira dc distribuir responsabilidades e colaborações é através cio lISO de cartões CRC (Classe Responsabilidade Colaboração). Confonne o nome sugere, um cartão CRC nada mais é do que uma ficha de arquivo 4x6 com linhas. Quando você começa a projetar pela primeira vez, é di ficil simplesmente começar a listar métodos e atributos. Em vez disso, você precisa começar a identifi car o propósito de cada objeto. Os cartões CRC ajudam a defin ir o propósi to de um objeto, chamando a atenção para as responsabilidades do objeto. Quando usa cartões CRC, você simplesmente cria um cartão para cada classe. Você esc reve o nome da classe no início do cartão e, em seguida, divide o cartão em duas seções. Li ste as responsabilidades no lado esquerdo e, no lado direito, liste todos os outros objetos que o cartão precisa para executar suas responsabilidades. A Figura 10.2 ilustra um modelo de cartão CRC. Os cartões CRC são intencionalmente de baixa tecnologia. Você é intencionalmente limitado pelo tamanho do cartão. Se você achar que o cartão não é grande o su fi ciente, silo boas as chances de que é preciso dividir a classe. Uma grande vantagem dos cartões e RC é que você não fi ca preso a um computador. Acredite se qui ser, ter de projetar diante de um computador nem sem pre é desejável. O projeto não é um exercício solitário e pode exigir conversas e di scussões ent re os projet istas. Os cartões CRC liberam você e seus colegas projet istas para projetar quando e onde quiserem. Projete no almoço; vá até uma sala de conferê ncia ou para um banco de parque. Desde que possa usar seus cartões, você pode projetar.
Introdução ao POO (Pro je to Orientado a Objetos)
FIGURA 10.2
Nome da dasse
Um modelo tle caruio CRC.
"'I'i:
-
CoI.bor.çoos
--
Responsabilidade
Os cartões CRC também o liberam de ter de manter um modelo eJelrônica atualizado. Em breve seu modelo mudará freqUenteme nte e ter de manter o modelo atualizado pode ser uma grande inconveniência. Em vez disso, você simplesmente pega seus cartões e apaga, rabisca, dobra, escreve e os rasga, quando necessário. O custo de mudar um cartão é muito mais barato (e mais imediato) do que ter de atua lizar algo em seu computador. Ter de parar a sessão de CRC e atualizar um computador pode interromper o fluxo da sessão. Finalmente, os cartõcs C RC têm apelo em nossos instintos mais primiti vos. Eles apenas aj udam a ler algo concreto que você possa pegar. Em vez de ter de modelar vários projetas alternativos, você pode apenas movimentar seus cartões. Para modelar interaçôes, você pode sentar-se com outros prajetistas, dividir os cartões e percorrer as interações. Tal processo interat ivo est imula a d iscussão.
Como você aplica cartões CRC? A criação de cartões CRC não é um exercicio solitário. Em vez disso, você deve manter sessões CRC com outros projetistasldesenvolvedores, para que o processo de projeto seja o mai s interativo possível. Você in icia uma sessão escolhendo vários casos de uso. A quantidade que você escol he depende do tempo de que dispõe e da dificuldade dos casos de uso. Se você tiver alguns casos de uso muito grandes, pode fazer sentido ter uma sessão por caso de uso. Se você tiver muitos casos pequenos, vai querer atacar vários casos de uso durante a sessão. O importante é que você manipu le casos de uso relacionados e que torne notável o progresso em seu projeto. Não pernlilaqueo processo sofra empecil hos. Quando você escolher os casos de uso, identifique as classes principais e crie um cartão para cada uma. Quando você tiver os cartões, divida-os entre os projetistas e, em seguida, inicie a ses-
Dia 10
são. Durante a sessão, você investigará o cenário de cada caso de uso. À medida que você percorrer o cenário, cada projetista deverá se concentrar nas responsabilidades e colaborações de sua classe. Quando sua classe for necessária no cenário, notará seu liSO e di rá a todos os outros projetistas se preci sa delegar para outro objeto. A estratég ia que você adota para a sessão de projeto é questão de preferência pessoal. Algumas metodologias ex igem o desempenho do papel, onde cada projetista recebe um cartão e representa o papel da classe. Out ras metodologias são um pouco mais formais. A estratégia escolh ida fi ca por sua conta e de s ua equipe de projeto. O importante é que todos fiquem engajados no processo e que ninguém fique excl uido. Edurante essas sessões que você di scut irá a lternati vas de projeto, descobrirá novos objetos e constru irá a camaradagem na equipe.
Um exemplo de cartão CRC Vamos considerara caso de uso Pedido do Dia 9 e ver como você poderia usarcartõcs CRC para atribuir responsabilidades para ele. •
Pedido
• O usuário registrado passa para a totalização e pagamento. • O usuário reg istrado fornece informações de ent rega.
•
•
O sistema mostra o total do pedido.
•
O us uário registrado fornece infonnações de pagamento.
•
O sistema autoriza o pagamento.
•
O sistema confirma o pedido.
•
O sistema envia um e-mail de confinnação.
Condições prévias •
•
Condições posteriores •
•
Um pedido no sistema.
Alternativa: cancelar pedido •
•
Um carrinho de compras não vazio.
Durante os passos I a 4, o usuário opta por cancelar o ped ido. O us uário volta para home page.
fi
Alternativa: a autorização fal hou •
No passo 5, o sistema falha em autorizar as informações de pagamento. O usuário pode introd uzir novamente as infonnações ou cancelar o pedido.
Comece ident ificando as classes. Imed iatamente, você verá: Usuár i o Reg i s trado, Ped i do, Pagamento, Confi nnação de Pedido, Carri nho de Comp ras, Infonnações de Entrega . Talvez você também q ueira incluir Si s tema ; entretanto, há um problema . Nos casos de uso, tudo que não fo i feito por um ator foi feito pelo sistema ' tudo incluído'. O objetivo da análise era entender o problema. Então, em vez de tentar dividir o sistema em suas partes, a análi se tratou como uma grande caixa preta. Através do projeto, você vai decompor o sistema em suas panes.
°
Intro dução ao POO (Projeto Orientado a Objetos)
227
Se um cartão de sistema vai ser incluido ou não vai depender da complexidade do sistema. l)ode ajudar listar todas as responsabilidades do sistema em um canão e, em seguida, dividir esse cartão em várias classes, posteriormente. Neste caso, entretanto, você pode tentar decom por o sistema, antes de começar. Sc você se esquecer de algo, sem pre pode acrescentar posteriormente. Comece lendo os passos do cenário. Aqui , o sistema autoriza o pagamento, ex ibe e con fi rma o ped ido. O sistema também cria e introduz o pedido. Você pode começar divid indo o sistema em Funcionário, Mostra Pedido e Terminal de Pagamento. Quando você se sentir confortável com s ua lista de classes, pode criar os cartões C RC, como ilustrado na Figura 10.3.
FIGURA 10.3 Algl/l/:" dos cOr/ue:.' CRC
-
ç.,w,., ... c
'
...
u_ ......,_
=l
para Pedido.
o próximo passo é percorrer cada passo do cenário e identi fi car responsabilidades. O passo I, "o usuário regist rado passa para a totalização e pagamento", é simplesmente um cl ique em um link na interface. Por simplic idade, vamos ignorar a interface. O passo 2, "o usuário registrado fornece informações de entrega", é um pouco mais interessante. Aqu i, você vê que o Usu!rio Reg l st rado é responsável por fornecer suas informações de entrega para o Funcion!rio. A Figura 10.4 il ustra o cartão resultante.
FIGURA 10.4
O carl(io e RC de Usu6rio Regi s trado.
Usu6rio Registredo fomoce informaçOes de
enlreg~
In lormaç6e. de Entrega
Dia 10
NO TA
Quando você ver que um objeto 'providencie' ou 'forneça' algo, precisará certifica r-se de que o objeto nâo esteja atuando como uma simples estrutura de dados. Se o objeto não fizer nada alêm de forn ecer acesso às informações, você desejará combinar esse objeto com o q ue manipula os dados.
o passo 3. "o sistema exibe o total do ped ido''. é um pouco mais complicado. Antes que o sistema possa exibi r algo, o Funci onári o deve introduzir o pedido, ver o preço dele e totalizar o pedido. O Funcionário usará o Carri nho de Compras para recuperar os itens e o Mos tra Pedido para exi~ bir o pedido resu ltante; entretanto, o Fune i onári o provavelmente não deve ser responsável tam~ bém por ver o preço ou por totalizar o pedido. Essas tarefas são melhores delegadas para outro objeto. Lembre~ se de que um objeto só deve ler um peque no núm ero de responsabilidades. O objeto Ped i do deve cuidar de ver o preço e talaiizar o pedido. A Figura 10.5 resume as respo n~ sabilidades do Funcionlirio até aqu i. O objeto Pedido também deve conter as itens que estão sendo adquiridos. O objeto Item fo i ig~ norado durante a criação da lista de classes; portamo, você deve adicioná- lo agora. O objelo I tem contém informações de preço e produto, assi m como uma quant idade. O objeto Item também conterá todos os incentivos que o usuário possa ter apl icado. I ncent i vo é outra classe que você deve adicionar na lista. Os passos restantes são muito parecidos com os outros. O Usuário Registrado fornece infonnaçôes de Pagamento, o Funcionlirio autoriza o pagamento e fina liza o pedido. As fi guras 10.6e 10.7 resumem as responsabi lidades do Funcionário edo Usuário Registrado. FIGURA 10.5 O carl{io CRC
recuperllnformaç6es de In'rllla. pallamento
Inlo.m~.
pafO Functom1 r io.
Introdu! o pedido
C'fflnho d. Comp," Pedido
uibe o pedido
Mo",. Ptilldo
funeioné rio d. Entr80'
~
229
Introdução ao POO (Pro jeto Orientado a Objetos)
FIGURA 10.6
Fu nclon6 rlo
O Cl/rlaO CRC para
llK:upera informaçóes de entrega e pag,mento
InfOfmaç6e* d. Enu~.
Funcionl'irio cOlI/plelo.
introduz o pedido
ea" inho de Compras ~;do
exibe O pedido
Mostre Pedido
autori .. o pedido confirma o pedido
Terminal de P'g.mento ~ido
Quantas responsabilidades por classe? Ao desenvolver seus cartões CRe, você precisa garantir que cada classe tenha apenas duas ou três responsabilidades principais. Se você tiver mais responsabil idades, deverá dividi r a classe em duas ou mais classes separadas. AL.ERTA
Você deve considerar a d ivisão da classe, mas em sistemas reais, isso não é necessariamente realista.
FIGURA 10 .7
Usu6rio Registrado
O C(lrf(io CRC IXlra
101'11_ inlormllÇ6l1 de emr"'ll'
InformJÇ~" cIt
Usul'irlo Registrado.
fom«e inlormllÇ6.s da PIIIamenlO
Pag.mento
Entteg.
--I
Pegue a classe Funcionário, por exemplo. O Func ionário é responsável por: • •
Recuperar infonnações de pagamento e entrega . Introduzir o pedido.
I 230
Dia 10
•
Exibir o pedido.
•
A utorizar o pagamento.
•
Confi rmar o pedido.
Confonne a Figura 10.8 ilustra, e ntretanto, todas essas responsabilidades caem sob a responsa bi lidade de ' Processa Ped ido'.
FIGURA 10.8 Processa Pedido.
Funcio nário r.,;upe .. informaç6e. de en!
~.menta
Uw'rio Roogimado
introdu. a pedido
C.urinha d, Compr •• Padldo
exibe a pedido
Moerr. Pedido Terminal de P.Ol me nta
autoriza o pedido conllrma a pedido
Proce_ Pedido
Pedido
É importante li sta r subtarefas; entretanto, você não quer fazer isso em demas ia. O importa nte é que todas as sub-responsabil idades trabal hem para um objeti vo com um. Aqu i, todas as tarefas trabalham para o processamento de um pedido. Ao trabalhar com cartões C RC. você também precisa lembrar que eles ate ndem um propós itoespecífico: defi ni r responsabi lidades e relac ionamelllos de colaboração simples. Não use cartões C RC para desc rever relacioname ntos complexos. Você também deve manter os cartões CRC com baixa tecno log ia cesso de cartão C RC.
não tente automati zar o pro-
Limitações do cartão CRC Assim como toda boa fe rramenta, os cartões C RC têm seus usos, bem como suas lim itações. Os cartões C RC foram dese nvolvi dos originalmente como uma ferramen ta de e nsino. Como res ultado, eles são uma manei ra excelente de encarar o projeto inicial de um siste ma - especia lmente se você for in icia nte e m 00. Entreta nto, os cartões CRC são di ficcis de usar quando o projeto se toma mais complicado. Pode se r di ficil controla r inte raçôes complexas entre os objetos, si mplesme nte através do uso de cartões C RC. Um grande número de classes também toma o uso de cartões desajei tado. Também
Introdução ao POO (Projeto Orientado a Objetos)
pode se tornar dificil continuar a usar cartões CRC, quando você começar a modelar. Manter os dois em sincronismo pode ser um desafio. O melhor conselho é usar o que runciona. Use cartões CRC até que eles não mais auxiliem o processo de projeto. Nesse momento, simplesmente pare de usá- los.
DICA
auando você deve usar cartões CRC? • Durante os est ágios iniciais do projeto. • auando você ainda é inicia nte em 00. • Para descobrir responsabilid ades e colaborações. • Para percorrer um cená rio. Não apenas os cartões encontrarão responsa bilidades e colaborações, como pOdem ajudá-lo a ent ender melhor o cenário. • Em projetas menores ou para se concentrar em uma seção menor de um projeto grande. Não tente atacar um projeto inteiro com cart ões CRC.
Passo 3: desenvolva os pontos de interação Quando tiver completado seus cartões CRC para um conjunto de casos de uso, você preci sará desenvolver os pontos de interação. Um ponto de interação é qualquer lugar onde um objeto use Outro. Novo
TERMO
Um pomo de imeraç(;o é qualquer lugar onde um objeto use outro.
Existem vários problemas a considerar sobre o ponto de interação.
Interfaces Você precisa de uma interrace bem definida, onde um objeto usa Outro. Você qucr ter certeza de que uma alteração em uma implementação não vá danificar o outro objeto.
Agentes Você também precisa ver as interações de forma crít ica. Pegue uma biblioteca, por exemplo. Uma bibli oteca tem prateleiras e livros. Quando é hora de tirar um livro da prateleira, o livro chama um método removeBook() na prateleira ou é a prateleira que chama um método removeBook () no livro? Na verdade, a responsabi lidade não pertence ao livro ali à prateleira. Nenhuma das escolhas modela adequadamente o mundo real. No mundo real, uma bi bliotecária ou um cliente retirará o li vro. As bibliotecárias e os clientes também podem aparecer no mundo 00; entretanto, o mundo 00 chama esses atares de agentes ou intermediários. Novo
TERMO
Um agellle faz a mediação elllre dois ou mais objelos para atingir algum objetivo.
Ao desenvolver pontos de interação, você desejará prestar atenção em lugares para usar agentes.
Dia 10
Considerações futuras Ao considerar as interaçõcs, você desejará procurar lugares onde o objcto que está sendo usado possa mudar. Se você puder local izar tais lugares com precisão, desejará projetar a interação de ta l maneira, que ela não será desfeita caso novos objetos sejam introduzidos. É para planejamento de mudança que serve sofiware ã prova do futu ro. Em tais lugares, você desejará estabelecer relacionamentos com capacidade de conexão e usar polimorfismo, para que possa introduzir novos objetos a qualquer momento. Assim como em tudo, entretanto, não faça isso em demasia. Planeje a alteração apenas em lugares onde você saiba com certeza absoluta que ocorrerá uma mudança. Existem duas maneiras gerais de prever o futuro: • •
A alteração é um requisito. Se os requisitos solicitarem alterações futura s, projete a alteração. Se você est iver usando uma biblioteca de terceiros e quiser migrar para uma nova versão de forma transparente no futuro, planeje a alteração.
O primeiro caso é melhor resolvido pelo estabe lec imento de algum tipo de relacionamento com capacidade de substituição, através de herança. O segundo req uer um pouco mais de trabalho. Você desejará em pacotar a biblioteca em classes que criou. Você deve tomar o cuidado de projetar esses em pacotamentos para tirar proveito da capacidade de conexão e do pol imorfismo.
Tra nsformação de dados Durante o projeto, você pode encontrar lugares onde precisa transformar dados, antes de passá-los para outro objeto. Normalmente, você delegaria tal transformação de dados para outro objeto; se precisar alterar a transformação, você só precisará atualizar a classe de transformação. Essa prática também ajuda a divid ir responsabi lidades. Ao considerar esses pontos de interação e adicionar novas classes, talvez você precise rever e atualizarseus cartões CRC ou modelo de obj etos (se você tiver iniciado a modelagem formal).
Passo 4: detalhe os relacionamentos entre os objetos Quando você tiver estabe leci do os relacionamentos de responsabilidade e colaboração básicos, precisará detalhar os relacionamentos complexos entre as classes. Eaí que você define as dependências, associações e generalizações. Detalhar esses relacionamentos é Ulll passo importante, pois isso define como os objetos se encaixam. Isso também define a estrutura interna dos vários objetos. Comece com os cartõcs CRC. Embora eles não capturem cada relacionamento, eles capturam a colaboração. Eles também vão sugerir alguns outros relacionamcntos. Procure classes que com partilhem responsabilidades semelhantes. Se duas ou mais classes compartilham o mesmo conjunto de responsabilidades, são boas as chances de que você possa contar
Intro dução ao POO (Projeto Orientado a Objetos)
com as característi cas comuns em uma classe base. Você também desejará rever o passo 3 e considerar todos os relacionamentos com capacidade de substituição que poderia preci sar.
Passo 5: construa seu modelo Q uando chegar ao passo 5, você já terá modelado o sistema fonnal mente. Um mode lo completo consistirá em diagramas de classe e diagramas de interação. Esses diagramas descreverão a estrutura e o relacionamento das váríasclasses do sistema. A UML também define modelos para mooelar interaçôes, transição de estado e atividades. Para os propósitos deste livro, entretanto, você vai se concentrar nos d iagramas de classe e interação. Você viu os dois ti pos em dias anteri ores. A Figura 10.9 modela a estrutura Pedido. O modelo Pedido ilustra todos os relacionamentos importantes entre Pr:dido e as classes que ex ibem o pedido. Concebivelmente, você também terá modelos que ilustram os relacionamentos entre Func ion.! r io, Pedi dos e Usuâr i o Registrado. Novamente, você quer apenas modelar o que faz senti do~ os componentes arqu itetônicos interessantes. Lembre-se de que você está tentando transmitir informações específicas através de seus modelos e não simplesmente apresentar modelos por questões de documentação. A Figura 10. 10 ilustra o diagrama de seqüência atua li zado para o caso de uso Pedido.
FIGURA
10.9
MOI/elo Pedido.
,, •
-
Você também pode criar d iagramas de seqüência e co laboração para modelar as intcrações importantes do sistema. Quando tiver terminado de criar os modelos, você deverá ter descrições de todas as principais estruturas e intcrações encontradas no sistema. Esses mode los dizem como os vários objetos estão estruturados, como eles se relacionam e como eles se encaixam para modelar a solução do problema elaborado durante a aná lise,
Dia 10
FIGURA 10.10
Diagrama de seqiiéllôa de Pedido.
~~.'" .. , ,..! , ", I..,),
Resumo o
POO continua o trabalho da AOO, pegando o domínio e transformando-o em uma solução para seu problema. Através do processo de POO, você pega seu modelo de domínio e constrói o modelo de objetos de sua solução. O modelo de objetos descreve os aspectos arquitetonicamente significativos de seu sistema, como a estrutura e os relacionamentos dos objetos - como os objetos se encaixam. No fi nal do POO, você deverá ter uma boa idéia do que implementará no cód igo. A grosso modo, existem cinco passos iterativos que você pode segui r, enquanto rea li7..a o POO: Passo I: gerar a lista de objetos inicial. Passo 2: refinar as responsabilidades de seus objetos através de cartões CRC. Passo 3: desenvolver os pontos de inleração. Passo 4: detalhar os relac ionamentos entre os objetos. Passo 5: construir seu modelo. Cada passo refina mais seu modelo de objetos e o deixa mais próximo da cópia heliográfica de seu sistema.
Perguntas e respostas P Por que é importante projetar o sistema antes de começar a cod ificar? R Ex istem varios fa tores que tornam o projeto importante. O, projeto o aj uda a prever problemas de implementação, antes de começar a cod ifi car. E muito mais fá cil corrigi r um problema em um modelo do que percorrer todo seu código para corrigí-Io.
Introdução ao POO (Projeto Orientado a Objetos)
O projeto também garante que todos os desenvolvedores de uma equipe de desenvolvimento estejam na mesma página. O projeto esclarecerá muitas das suposições arquitetônicas importantes que, de outro modo, teriam sido decididas durante o desenvolv imento. Deixando as decisões arqu itetônicas para o momento da imp lementação, você corre o risco de ter cada desenvolvedor tomando suas próprias decisões incompatíve is. fi t)or que é importante m odela r seu projeto antes de começar a codificar?
R Modelaré importante pelo mesmo motivo que a maioria das pessoas redige lima palestra antes de proferi -la. Modelando o projeto, você pode ver corno as partes se encaixam. Tendo urna idéia visual do projeto, você pode decidir se está confortável com ele. Você pode verificar se tudo se encaixa de fonna tranqüila e sensata. ,
As vezes, a so lução de um problema parecerá simp les, quando você pensar a respeito dela. Pegar a solução e escrevê-Ia formalmente o obri ga a se r intelectualmente honesto quanto a so lução. Ter de transformar fonnalmente um problema o obriga a pensar de forma crítica e exata sobre a solução. O que parece simples em sua mente pode fazê-l o arrancar os cabe los, antes de você tenninar! P Como você sabe quando seu projeto está concluído? R Não existem regras rígidas e rápidas que indiquem quando um projeto está tenn inado. Se você se preocuparem ter um projeto perfeito, poderá cai r facilmente na paralisia do projeto. Tudo se resume ao que você está tentando transm itir através de seus modelos, seu níve l de experiência c o nível de experiência de sua equipe. Se você tiver uma equipe experiente, provave lmente poderá apenas modelar a arquitetura de alto nivel do sistema. Se você Oll os membros de s ua eq uipe são iniciantes nos objetos, provavelmente desejarão passar mai s tempo projetando. De qualquer modo, você term ina quando se sente confiante o s uficien te de que pode pegar o projeto e implementar a solução. O processo de desenvolvimento 00 é iterativo. Você deve usar isso para seu prove ito e fazer as iterações para obter sua arquitetura final.
Workshop As perguntas e respostas do teste são fornec idas para seu melhor entendimento. Veja as respostas no Apêndice A, ·'Respostas" .
Teste I. Q uais são as três vantagens de um projeto fonnal ? 2. O que é Projeto Orientado a Objetos 3. O que
POO?
c modelo de objetos?
4. Qua is são os inconvenientes do projeto em demasia?
Dia 10
5. Como você sabe quais aspectos de seu sistema são arqllitetollicamente sigllificativos? 6. Quais são os cinco passos básicos do POO (Projeto Orientado a Objctos)? 7. Como você gera a lista de objetos inicial? 8. O que um proj eto completo captura? 9. O que os cartões C RC o aj udam a identificar? 10. O que é uma colaboração? I I. Por que um entend imento profundo dos relacionamentos e responsabi lidades de um objeto é importante? 12. O que é um cartão C RC? 13. Descreva um moti vo pelo qual os cm1ôcs CRC são intencionalmente de baixa tecnologia. 14. Quando você deve usar cartões CRC? 15. Qual é um problema importante dos cartões CRC? 16. O que é ponto de interação? 17. Quais são as quatro considerações que você pode fazer em relação a um ponto dc interação? 18. O que é um agente? 19. O que você fará quando detalhar os relacionamentos complexos entre os objetos e por que isso é importante? 20. Qual tipo de modelos você poderia criar?
Exercícios I. No Dia 9, Exercicio 2, você desenvolveu o seguinte caso de uso: •
•
Remove item •
O us uário convidado seleciona um item do carrinho de compras.
•
O us uário convidado soli cita ao carrinho para que remova o item.
Condições prévias •
•
Condições posteriores •
•
O carrinho contém um item para remover. O item não aparece mais no carrinho.
Al ternativa: Operação cancelada •
O usuário pode optar por cancelar a transação, após o Passo I.
Para melhorar suas habilidades, use cartões C RC para descobrir as responsabilidades. Quais responsabilidades o Carrinho de Compra s terá?
SEMANA
2
DIA Reutilizando projetos através de padrões de projeto No capítu lo de ontem , você viu como o projeto orientado a objclos o ajuda a projetar uma solução para um problema. Através do POO ( Projeto Orientado a Objclos), você constrói uma cópia hel iográfica que diagrama os objclos que compreendem seu sistema. Uma vez que tenha esse projeto, você pode começar a imp lementar sua solução. Entretanto, você provavelmente tcm algumas perguntas.
• Como você sabe que o seu projeto é um bom projeto? • Seu projeto lerá conseqüências imprev isíveis no futuro? •
Como outros projel islas resolveram esse problema ou um problema semelhante no passado?
•
E quanto à reutili zação? A POO (Programação Orientada a Objclos) fornece reutili zação de cód igo, mas o POO ( Projeto O rientado a Objetos) permite reutilização?'
Este capítu lo o ajudará a responder essas perguntas e mui to mais. quando você explorar o ass unto 'padrões de projeto' . Hoje você aprenderá:
I.
•
Corno usar padrões de projeto
•
Corno aplicar quatro padrões comuns N.R.T.: Usaremos o POO p..1ra Projcto Orientado a Objetos e a 1'00 para Programaçllo Oricntada a Objclos
238
Dia 11
• Como um projeto pode ti rar proveito do uso de padrões • Como evitar uma annadi lha de padrão comum
Reutilização de projeto Um objetivo importante da POO é a reutilização de código. Quando reuti liza código, você ganha tranqUi lidade, sabendo que seu software está construído em uma base de código confiáve l e testada. Além disso, você sabe que o código reutilizado reso lverá seu problema. Taltranqüilidade enquanto você programa é ótima, mas e quanto à tranqUi li dade enquanto projeta? Como você sabe se seu projeto é bom? Fe lizmente, os ,padrões de projeto podem ajudara dirim ir muitas das dúvidas que você encontrará ao projetar. A medida que o tempo passa, muitos projetistas e programadores têm notado que os mesmos elementos de projeto aparecem repetidamente em todos os se us projetas. A comunidade de 00 resolveu identificar, nomear e descrever esses conceitos de projeto recorrentes. O resultado é uma lista sempre crescente de padrões de projeto. Novo
TERMO
Um padrfla de projeto é um conceito de projeto reut ilizável.
Veri fi ca-se que você pode reutil izar padrões de projeto em todo o seu projeto, exatamente como se você reutilizasse uma classe dentro de seu programa. Essa reutilização traz as vantagens da reut ilização da POO (Programação Orientada a Objetos) para o POO (Projeto Orientado a Objctos). Quando usa padrões de projeto, você sabe que baseou seu projeto em projetos confiáveis e comprovados pelo uso. Tal reutil ização permite que você saiba se está na trilha certa para uma sol ução con fi ável. Quando você reuti liza um padrão de projeto, está usando um projeto que outros usaram com êxito, muitas vezes anteriormente.
Padrões de projeto O li vro Desigll Pallems - Elell/ents of Rem·ab/e Objecl-Oriellled Software, de Gamma, Helm, Johnson e Vli ssides, apresentou pela primeira vez o conceito de padrões de projeto para muitos na com unidade de 00. Esse trabalho de base não apenas definiu um conjunto de padrões de projeto reutilizáveis, mas também defin iu formalmente o padrão de projeto. De acordo com esse trabalho, um padrão de proj eto consiste em quatro elementos: • • • •
O nome do padrilo O problema A solução As conseqUências
Reutilizando projetas através de padrões de proj et o
239
o nome do padrão Um nome identi fica exclusivamente cada padrão de projeto. Assim como a UML fornece uma linguagem de projeto comu m, os nomes dos pad rões fornecem um vocabu lário com um para descrever os elementos de seu projeto para outros. Outros desenvolvedores podem entender seu projeto rápida e faci lmente, quando você usa um vocabulário comum. Um simples nome reduz um problema inteiro, a solução e as conseqüências a um único termo. Assim como os objetos oaj udam a programarem um nível mais alto e abstrato, esses termos permitem que você projete a partir de um nível mai s alto e abstrato e não fiq ue preso aos detalhes que se repetem de um projeto para outro.
o problema Cada padrão de projeto existe para reso lver algum conjunto distinto de prob lemas de projeto e cada padr.l0 de projeto descreve o conjunto de problemas para o qual foi fei to para resolver. Desse modo, você pode usar a descrição do problema para determinar se o padrão se aplica ao problema especi fico que está enfrentando.
A solução A sol ução descreve como o padrão de projeto resolve o problema e identi fica os objetos arquitetonicamente significativos na solução, assim como as responsabilidades e relacionamentos que esses objetos compartilham.
NOTA
É importante notar Que você pode aplicar um padrão de p rojeto a uma classe inteira de problemas. A solução é uma solução geral e não apresenta uma resposta para um problema específico ou concreto. Su pon ha Que você Quisesse encontrar a melh or maneira de percorrer os itens do ca rrinh o de compras d o capítulo 10, " Introdução ao POO (Projeto Orientado a Objetos )". O padrão de projeto Iterator propõe um modo de fazer justamente isso; entretanto, a solução apresentada pelo padrão Iterator não é dada em termos de ca rrinhos de compras e itens. Em vez disso, a sol ução descreve o processo de varredura de qualquer lista de elementos. Quando você começar a usar um padrão de projeto, deve m apea r a sol ução geral para seu problema especifico. Às vezes, pode ser difrcil fazer esse mapeamento; entretanto, o próprio padrão de projeto precisa permanecer genérico para que permaneça aplicável a muitos problemas especificos diferentes.
As conseqüências Não existe um projeto perfeito. Todo bom projeto terá bons com prom issos e todo com promisso assumido terá seu próprio conjuntoespeçial de conseqüências. Um padrão de projeto enumerará as principai s conseqUências inerentes ao projeto.
Dia 11
As conseqüências não são novidades. Você assume compromissos sempre que opta entre duas alternat ivas ao programar. Considere a diferença entre usar um array e lIsar uma lista encadeada . Um array proporciona pesqu isa rápida pelo índice, mas funciona muito lentamente, quando você precisa reduzir ou expandir o array para inserir ou excl uir elementos. Por outro lado, a lista encadeada proporc iona rápidas adições e excl usões, mas tem uma sobrecarga de memória maior e pesquisas de índ ice mais lentas. Aqui , as conseq üências de uma lista encadeada são a sobrecarga de memória e as pesquisas mais lentas. A conseqüência de usar um array é O fat o de que o redi mens ionamento do array é dispend ioso, mas as pesqu isas indexadas são muito rápidas. O que você esco lhe depende da importância da memória e da velocidade em seu projeto, se você va i fazer muitas adições e exclusões de elementos e se vai fazer muitas pesqui sas. É sempre importante documentar suas deci sões de projeto, assim corno as conseqüências resu l-
tantes. Ter essas decisões documentadas ajuda os outros a entender as escolhas que você fez e a determ inar se o projeto pode ajudar a resolver seus próprios problemas. Do mesmo modo, as conseqUências do padrão de projeto pesarão bastante em sua deci são de usar o padrão. Se um padrão de projeto tem conseqüências que não correspondem aos objetivos de se u projeto, você não deve usá-lo - mesmo que ele possa resolver seu problema.
Realidades do padrão Quando você começa a aprender sobre padrões, é importante saber o que eles podem e o que não podem fazer. As listas a seguir podem ajudara manter corretas as intenções por trás dos padrões. Os padrões são: Projetos reutilizáve is que provaram funcionar no passado. • So luções abstratas para um problema de projeto geral. • So luções para problemas recorrentes. • Um modo de construi r um vocabulário de projeto. • Um regiSlro público da experiência do projeto. • Uma solução para um problema. •
Os padrões não são: Uma solução para um problema especí fi co. • A resposta mágica para todos os seus problemas. • Uma muleta, você mesmo ainda precisa fazer seu projeto funci onar. • Classes concretas, bibliotecas, soluções prontas. •
Padrões por exemplo Existem muitos livros que catalogam os padrões de projeto. Não há moti vo para repetir fonnal mente esses catálogos aqui; entretanto, ex iste um conjunto de padrões de projeto que você en-
Reutilizand o pro jetas através de padrões de proj et o
241
contrará quase que diariamente. Nenhum texto introdutório sobre 00 será completo sem apresentar esses padrões para você. Em vez de apresentar csses pad rões ronnalmente, vamos adotar uma est ratégia mais informal, através de exemplo. Hoje, este capítulo apresentará três padrões importantes: • Adaplcr • Proxy • lterator
o padrão Adapter o Capítulo 4, "Herança: obtendo algo para nada", apresentou o conce ito de relacionamentos com capacidade de subst itu ição. O Capítulo 6, "Polimorfismo: aprendendo a prever o futuro", mostrou como você pode usar esses relacionamentos para adicionar novos objelos em seu siste· ma a qualquer momento. Quando você usa herança para definir capac idade de substituição, entretanto, seus objetos podem se tornar restritos pelas hi erarquias resultantes. Para poder se conectar com seu programa, um objeto deve fazer parte da hierarquia com capacidade de substituição. Então, o que você faria se quisesse conectar um objeto em seu programa, mas ele não pertencesse à hierarquia correta? Uma solução seria pegar a classe que você quisesse usar e, em seguida, editá-Ia para que ela herde da classe correta. Essa solução não é 6ti ma por alguns motivos. Nem sempre você terá o código-fonte das classes que deseja usar. Além disso, sc essa classe já usar herança, você terá problemas se sua linguagem não suportar herança múltipla. Mesmo que você tenha o código-fo nte, simplesmente não e prático ou razoável reescrever um objeto cada vez que você quisesse que ele fi zesse parte da hierarq uia 'correta'. A defin ição da hierarqu ia 'correta' mudará para cada programa. Se você tiver uma classe razoave lmente abstraída, não desejará ficar ed itando sempre que qui ser reut il izá-Ia. Em vez disso, você deve si mplesmente uti Iizá-la ina lterada. Reesc revê-Ia a cada vez anula os propósitos da reutil ização e da abstração. Se você ficar criando edições especiais de uma classe, poderão restar muitas classes redundantes para ma nter. O padrão Adapter apresenta uma solução alternativa que resolve o problema da incom pat ibilidade, transformando a interface incom patível naquela que você precisa. O padrão Adapter funciona empacotando o objeto incompativel dentro de um objeto adaptador compativel. O objeto adaptador contem lima instância do objeto e expõe o objeto através da interface que se encaixa em seu programa. Como a classe adaptadora empacota um objeto, às vezes esse padrão é referido como padrão Empacotador. Novo TERMO Um adaptador é um objelo que lransfonna a interface de outro objeto.
Dia 11
Implementando um adaptador A Figura 11 . 1 resume a hierarq uia MoodyObject apresentada no Capítulo 7, "I>olimorfismo: hora de escrever código".
FIGURA '1 .1 ii hicl'(lI'(fllitl
r.yd'liootn.tOb;.c:t
M~I
MoodyObject.
..... mln. (ob) , M
.. qu.ryModd ( I I f1fIlMood I J; StrlllQ
* Happ'j'ObjlCt
C.,ef,lHObJICt
S~1Ct
• getMQOd (): Suing
'getMood (): Str ing
'gtlMood II: Sning
Psychi atri stObject 56 pode exami nar um objeto se for um MoodyObject. O método exami nc() de Psych i atri stObject depende do método queryMood() de MoodyObject. Qualquer outro tipo de objelo precisará encontrar um psi quiatra diferente. A Figura 11.2 apresenta a nova hierarquia Pet. ~
FIGURA " .2
ii hierarquia Pet. ..1/1Uk ( 1; SlrinQ
00,
'"
BI,d
.. apeek ( ) : Sning
.. apeek II : String
• lpe.k I ) : Snlng
I
Cada Pet fala de sua própria maneira espec ializada e fornece sua própria implementação de speak (). Então, qual é o problema? Hoje em dia, até animais de esti mação vão ao psiquiatra. mas o objeto Psychi at ri stObject não pode examinar um Pet porque um Pet não é um objelo MoodyObject. Para que o objelo Psychia tristObject possa examinar um Pet ,você precisará criar um adaptador Pet. A Li stagem 11. 1 apresenta um possível adaptador Pet.
Reutilizando projetos através de padrões de projeto
243
LISTAGEM 11 .1 PetAdapter.java
public cla ss PetAdapt er extends MoodyObject ( private Pet pet; publlc PetAdapter( Pet pe t ) { this.pet • pet: }
protected String getMoodO { II i mplementando apenas porque é exigido por II MoodyObject . como também sObrepõe queryMood. II nAo prec i samos di sso return pet.speak( ): } publ ic yold queryMoodO { System . out . println( getMood{) }: } }
o PetAdapter
empacota uma instância de Pet. Em vez de expor a interface Pet, o adaptador oculta essa interface atrás da interface MoodyObject, basicameme transformando a interface de Pel. Quando chega um pedido no PetAdapter, o adaptador delega para a instância de Pet, conforme for necessário. A Listagem 11 .2 mostra o adaptador cm ação. LISTAGEM 11 .2 Usando o adaptador
PetAdapter dog • new PetAdapter( PetAdapter cat .. new PetAdapter( PetAdapter bird • new PetAdapter( PsychiatristObject psychiatrist ..
new Dog () ) ; new CatO ); new Bird() ) : new PsychiatristObject{):
psychiatrist . examlne( dog }i psychiatrist.examine( cat }; psychiatrist.examine( bird );
Uma vez empacotada, a instância de Pet se parece com qualquer outro MoodyObject para o objeto Psychi a tri s tObject. Essa sol ução é mais eficiente do que as al ternativas, pois ela exige que você escreva apenas uma classe adaptadora. Essa classe adaptadora pode manipular qualquer objeto Pet que apareça. A parte complicada é garantir que o PetAdapter empacote todas as instânc ias de Pet, antes que você passe o objeto Pet para o objeto PsychiatristObject.
Dia 11
A implementação fornecida anteriormente é cham ada de adapt ador de objeto, pois o adaptador usa composição para transfor mar a interface d e u ma instãncia. Você também pode implementar um adaptador através de herança. Tal adaptador é conhecido com o adaptador de classe, pois ele adapta a própria definição da classe. Se sua linguagem suporta heranças múltiplas, ent ão você sempre pode herdar da classe que deseja usar, assim como de uma classe da hierarquia. Se sua linguagem não tem herança múltipla, como Java, suas escolhas podem ser limitadas, dependendo de como a hierarquia foi construrda. Na verdade, talvez você tenha de se privar da heran ça totalmente e usar composição, no ponto em que também pOderia cr iar um adaptador de objeto_ Embora a heran ça múltipl a funcione, essa solução é limitada . Criar uma nova subclasse para cada classe que você quiser usar pode levar a uma proliferação inaceitável em classes empacotadora s. Cada estratégia tem sua própria limitação. Um adaptador de classe só funcionará para a classe que herda. Você precisará de um adaptador separado para cada subclasse. Do mesmo modo, embora um adaptador de objeto possa fun cionar com cada subclasse, você precisa rá de subclasses do adaptador, se quiser mudar a m aneira como ele empacota várias subclasses.
Quando usar o padrão Adapter
o padrão Adapter é Ílt il quando você quer usar um objeto que tem uma interface incompatível. O pad rão Adapter permite que você reutili ze diretamentc objetos que, de outro modo, precisaria alterar ou jogar fora. Os adaptadores também são Ílteis no sentido de uma ação preventiva. De tempos em tempos, você precisará empregar bibliotecas de terceiros em seus programas. Infelizmente, as APls das ferramentas de terce iros podem variar substancia lmente entre as versões, especialmente para novos produtos ou para tecnologias que estão amadurecendo. As APl s tam bém podem variar bastante em relação às bibliotecas de um prod uto concorrente. O padrão Adaptcr pode ajudar a evitar que seu programa tenha que trocar de APl s e que fi que preso ao fornecedor. Criando uma interface adaptadora que você controla, é possível trocar para novas versões de urna bibli oteca a qualquer momento. Basta criar uma subclasse do adaptador para cada biblioteca que você queira usar. Também é importante notar que um adaptador pode ser simples ou complicado e o nível de complexidade depende do objeto que está sendo empacotado. Alguns adaptadores se resumem si mplesmente a mapear um pedido para o método correto. Outros precisar.10 realiz,:'lr mais processamento. Use o padrão Adapter, quando: •
você quiser usar objetos incompatíveis em seu programa;
•
você quiser que seu programa permaneça independente de bibliotecas de terceiros .
A Tabela 11.1 destaca o usuário do padrão Adapter.
Reutilizando projetos através de padrões de proj eto
245
TABELA 11 .1 O padrão Adapter Nome do podrfí()
Adapter, Wrapper
Problema
Como reut ilizar objetos incompatíveis
Soluçllo
Fornecer um objeto que converta a interface incompatível em uma compatível
ConseqUlnc ias
Tomar incompatíveis objetos compatíveis; resulta em classes extras talvez muitas - , se você usar herança ou precisar manipular cada subclasse de uma fonna diferente
o padrão Proxy Normalmente, quando um objeto quer interagir com outro, ele faz isso atuando diretamente sobre o outro objeto. Na maioria dos casos, essa estratégia di reta é a melhor estratégia, mas existem ocasiões em que você desejará controlar o acesso entre seus objetos de forma transparente. O padrão Proxy trata desses casos.
Publicação/assinatura e o padrão Proxy Considere o problema de um serviço de eventos publicação/assinatura onde um objeto vai registrar seu interesse em um evento. Quando o evento ocorrer, o publ icador noti l1cará os objetos assinantes do evento. A Figura 11 .3 ilust ra um possíve l relacionamento publicação/assinatura. FIGURA 11 .3
Um re/lIdol/lImemo pllblicaç(io/assilwlllm.
h..,te ....... tot • ,egl.C" UI.um • • : lIscene. l:
publistle.
•
. "boe.lboo.
•
I.ht..,. • , eceiV
Na Figura 1 1.3, muitos Li s teners reg istram se u interesse nos eventos gerados por um EventGenerator. Quando o EventGenerator gerar um evento, ele colocará o evento em cada um de seus objetos Usteners. Embora essa solução fu ncione, ela coloca uma grande carga sobre o EventGenerator. O Event Generator não tem apenas a responsabil idade de gerar eventos, como também é responsável por controlar todos os l i s teners e colocar os eventos neles.
o padrão Proxy apresenta uma solução elegante para esse problema. Considere a Figura 11 .4. Em vez de conter seus Listene r s diretamente, o EventGenerator pode conter um ListenerProxy. Quando o gerador precisa disparar um evento, ele o dispara uma vez para o proxy. Então, fica por conta do proxy controlar e atualizar todos os l ; stener s.
Dia 11 AtNJ,,_u.r_
FIGURA 11 .4
UlI/lI soluçtio proxy. • ~""'" / ....111 : E..."f / :
u.....-
I
I
• .--1...,E...,nr ( _nr : ev"nt ): Subse.jl)e.
Publi~h",
•
Ev.ntG. n... ror • ,egllr.. , (ilor.. n.. , : USl8ne. I :
,
,
Publ illhlr
•
Subserlbe.
Utt.., ..
• , _iveEv.nr I.venl: Evem I:
o padrão Proxy geral o cenário publicação/assi nat ura descreve um possível uso para um proxy; entretanto, você pode usar prox ies em muitos lugares. Primeiro, o que é um proxy? Um proxy é um substituto ou lugar reservado que intermed ia o acesso ao objeto de interesse real. Você pode usar um proxy em qualquer lugar onde preci se de um substilUto ou lugar reservado para outro objeto. Em vez de usar um objeto diretamentc, você usa o proxy. O proxy cuida de todos os detal hes da comun icação com o objeto (ou objetos) real. Novo TERMO
Um proxy é um substituto ou lugar reservado que interrnedia o acesso ao objeto de interesse real. Para todos os efeitos, o .mbstitullI é indistinguivel do objeto real que
intennedia. Um substituto intermed ia o acesso a um objeto (ou objetos) subjacente de forma transparente. Você pode considerar um subst ituto como um modo de enganar seus objetos. Por exemplo, enquanto o EventGenerator pensa que está se comunicando apenas com um Li s tener, o Li stenerProxy pode, na verdade, di vul gar a mensagem para muitos objctos Listeners diferentes. Poder enganar seus objetos é uma capaci dade importante, pois ela permite que você realize todos os tipos de mágica nos bastidores. Um substituto pennite que você coloque responsabilidades nele, sem ter de incorporar essas responsabilidades no usuário do substituto. Mais importante, seu substituto pode executar todas essas responsabilidades sem que os outros objetos sai bam o que você está fazendo.
Reutilizando projetas através de padrões de proj et o
247
As responsabilidades que você pode colocar no substituto são infi nitas; entretanto, o uso comum incl ui ad icionar otimi 7.açõcs, realizar tarefas de limpeza, fa zer recursos remotos parecerem locais e adiar operações dispendiosas.
Ouando usar o padrão Proxy Use subst itutos quando: •
Você quiser ad iar lima operação dispendiosa. Considere um objeto que extrai informações de um banco de dados. Embora seu programa talvez precise saber ri respeito dos 01>jetos que pode extra ir, ele não precisa extrair todas as informações até que rea lm ente acesse o objeto. Um substituto pode representar o objeto rea l e carregar as informações extras quando elas forem necessárias.
Outro exemplo é um substituto de arquivo, que permite gravar em um arquivo, mas que apenas realiza a operação de Entrada e Saída no arquivo real, quando você já tiver term inado. Em vez de fazer várias gravações lentas, o substituto fará uma única gravação grande. • Você quer proteger de fomm transparente o modo como um objeto é usado. A maioria dos objetos é mutante, como as classes da linguagem Java. Um substituto protetor poderia tornar uma coleção de classes imutável, interceptando pedidos que de outro modo al terariam uma coleção de classes. Filtrando todas as chamadas de método através de um substituto, você pode governar de fomla transparente as operações permitidas sobre um objeto. • O objeto real ex iste remotamente, através de uma rede Oll processo. Os substitutos são importantes para a computação distribuída. Um substituto pode fazer com que um recurso distribuído pareça como se fosse um recurso local , encaminhando os pedidos através de uma rede ou através de um espaço de processo. • Quando você quer executar ações ad ic ionais de forma transparente, ao usar um objeto. Por exemplo, talvez você queira contar o numero de vezes que um método é chamado sem que o chamador saiba. Um substituto de contagem poderia controlar a acesso a um objelo. A Tabela 11 .2 destaca o usuário do padrão Proxy. TABELA 11.2
O padrão Proxy
Nome do padrão
Proxy, Surrogale
Problema
Precisa controlar o acesso a um objcto
Solução
Fornecer um objeto que intennedie o acesso a outro objeto de fomla transparente
Conseqüências
Introduz um nível de procedimento indireto no uso do objeto
o padrão Iterator Os padrões de projeto freqUentemente descrevem soluções para problemas comuns. Quando você programa, são boas as chances de que vá gastar muito tempo escrevendo códigos que fa -
Dia 11
zem laços (Ioops) sobre objetos de uma hierarquia. Conforme você aprendeu no Capítulo 8 " Introdução à UM L", o relacionamento entreobjetos é uma agregação. O todo contém partes. A Listagem 11.3 apresenta um método conhecido que faz um laço sobre o conteúdo de um maço de cartas (uma coleção) e constrói lima representação de caracteres do maço.
LISTAGEM 11 .3 Fazendo um laço sobre o conteúdo de um monte de cartas public String deckToString( Deck deck ) I String cards .. "" ; for( i nt i '" O: i < deck.sizeO ; i ++ ) Card ca rd .. deck.get( i }; cards .. cards + card.display( );
I
I return cards:
I Suponha que você quisesse fazc r um laço sobre um array de cartas. A Listagem I 1.4 mostra o método que pode manipular justamente esse caso.
LISTAGEM 11 .4
Laço sobre o conteúdo de um ar ray
publiC String deckToString( Card [] deck ) I String cards .. ""; for( int i .. O: i < deck.length; i ++ ) ( cards .. ca rd s + deck (i) .displ ay();
I return cards ;
I Finalmente, e se você quiser faze r um laço sobre o maço de cartas na ordem inversa? Você precisarâ adicionar um OUl ro método. A Listagem 1 1.5 mostra um método que faz um laço sobre o maço na ordem inversa.
LISTAGEM 11 .5
Laço sob r e o conteúdo de um maço de cartas na ordem inversa
public String reverseDeckToString( Deck deck ) ( String cards .. "": for( in t i .. deck.sizeO - 1: i > -1; i - ) I Ca rd card = deck.get( i ) ; ca rds .. cards + card.display();
I return cards ;
I
Reutilizando projetas at ravés de padrões de projeto
249
Se você lembrar das Iições sabre acoplamento e responsabil idades, deverá ouvir uma voz em sua mente, gritando, "Cód igo ruim!" Sempre que você quiser fazer um laço sobre urna coleção de cartas, precisará adicionar um método que sai ba como faze r um laço sobre esse tipo de coleção específico. Na verdade, a lógica não é tão diferente assim de um caso para outro. Os únicos elementos que mudam são os métodos e atributos que você acessa na coleção. Agora você vê um alerta: cód igo repet ido. O problema é que cada um dos métodos listados anteriormente é dependente da implementação da coleção, como um Oeck ou um Array. Quando programa, você sempre quer garant ir que não fique acoplado a lima im plementação específica. O acoplamento torna seu programa resistente à mudança. Pense a respeito. O que acontecerá se você quiser mudar a coleção que contém suas cartas? Você precisará atualizar ou adicionar um novo método que faça o laço. Se você não tiver sorte, simplesmente mudar a implementação da colcção poderia necessitar de alterações por todo o seu programa.
NOTA
Embora um único objet o possa conter apenas um laço, seu programa inteir o provavelmente vai repetir os laços muitas vezes, em muitos lugares diferentes.
Outro problema com esses exemplos provém do fato de que os mecanismos de navegação estão , cod ificados no método. E por isso que você precisa de um método para o laço para frente e outro para o laço inverso. Se você quiser fazer um laço aleatoriamente pelas cartas, então precisará de um terceiro método (e um para cada tipo de coleção). Você não preci sará apenas de vários métodos, como também prec isará implementar novamente a lógica de navegação, sem pre que definir um laço. Infelizmente, tal duplicação de lógica é um sintoma de responsabilidade confusa. A lógica de navegação deve aparecer em um e apenas um lugar. Fe lizmente, o padrão Itera to r resolve muitos dos problemas de forte acoplamento e confusão de responsabi lidade, colocando a lógica do laço ou iteração em seu próprio objeto. A figura 11.5 ilustra a interface Iterator. FIGURA 11 .5 II illfeljace Iterator.
/ter.for
I
+ fim fi ." voId +nufll."WJId + ;,Don" ( J : boalH" + ~um",!1rem ( J .. ObjtlCf
A interface 1te rator fornece uma interface genérica para iteragir sobre uma coleção. Em vez de escrever seus laços e métodos para usar uma coleção específica, você pode si mplesmente programar a interface genérica do I terator. A interface Iterator ocu lta completamente a implementação da coleção subjacente.
Dia 11
NOTA
A l inguagem Java defin e uma interface iteradora ligeiram ente diferente: Java .utll .1 terator. o I terator J ava define apenas três métodos: pub li c boo-
lean hasNeJl.t(). publlc vold remove() e public Obj ect neJl.t{). A interface Java Iterator per mite que você faça uma iteração apenas em um a direção. Ao chegar ao fim. você não pode vol tar para o inicio. Fora essa defi· ciência, a interface Java Iterator é semel hante àquela apresentada anteriorm ente. Ao escrever programas Java, você deve usar Java. ut i 1 .1 terator para que outros programas Java possam usar suas implem ent ações do Iterador. Para os propósitos deste livro. entretanto, esta lição continuará sendo verdade para a definição do Iterado r clássica forn ecida na Figu ra 11.5.
A Listagem 11.6 apresenta um método alternat ivo deckToString (), q ue faz laço sobre urna instânc ia de Iterator. LISTAGEM
11 .6
l aço sob r e o conteúdo de uma i nstância de Iterato r
pub li c Stri ng deckToString( Iterator i ) { String cards " ""; for ( i. f i r st() ; !i.isOoneO ; i.nextO ) Card card • (Card) i.current l tem() ; ca rds • cards + card.displ ay() ; } return cards ;
I
}
Apenas porque um objeto passa de volta um iterador, não significa que o objelo rea lmente annazena seus itens dentro do iterador. Em vez disso, um iterador dará acesso ao conteúdo do objeto. Existem três vantagens de usar um iterador para percorrer uma coleção. Primeiro, um iterador não Ovinculará a uma coleção específica. Todos os métodos origi nais faria m laço sobre implementações específicas da co leção. Corno resultado, cada método di fe riria apenas nos métodos que chama na coleção. Se esses métodos fi zessem um laço sobre um iterador, como na Listagem 11 .4, você s6 precisaria escrever um método deckToStr i ngO. Segundo, o iterador pode retornar seus elementos em qualquer ordem que achar conveni ente. Isso sign ifica que uma implementação do iterador poderia retornar os elementos em ordem. Outro iterador poderia retornar os elementos na ordem inversa. Usando um iterador, você pode escrever a lógica de navegação uma vez e razer com que ela apareça em apenas um lugar: no próprio iterador. Finalmente, um iterador torna simples mudar a coleção s ubjacente, quando isso fo r necessário. Como você não programou para uma implementação específica , pode trocar para uma nova coleção a qualquer momento, desde que a coleçãosaiba como retornar uma instânc ia de I terator .
Reutilizando projetos através de padrões de projeto
251
Implementando um Iterado r A maioria das coleções Java jâ dâ acesso a um iterador. Por exemplo, a li okedl 1s t usada internamente pelo objeto Deck já tem um método iterat or() que re· t orna um Java. ut i 1 . I t erator; ent retanto, isso ajuda a ver uma implementação de iterador real e esse padrão não é específico da linguagem Java. Assim, ig. no re o método 1terator() de linkedli st da linguagem Java por uns instant es.
A Listagem 11.7 mostra uma implementação de Ilerator que permi te ao usuário percorrer os elementos de uma coleção LinkedList. LISTAGEM 11 .7 Uma implementação de Iterator pub l i C class Forwardlterator implements Iterator { pr;vate Object [] items; private int index : pub l iC ForwardIterato r( ja va.util.Linkedl1st items ) ( this.items • items . t oArray(): } pub l iC bool ean isDone() ( if( index •• items .length ) return true ; } return fal s e : } publi c Object currentItem() if( !isDoneO ) I return items [index } return null: } public vo i d next() I if( !isDoneO ) ( i ndex++: } }
pub li c vo i d firstO index ,. O: }
}
I
I l:
I
Dia 11
Outra implementação poderia fornecer uma iteração inversa (veja o código-fonte completo, para um exemplo disso). A Listagem I J.8 ilustra as alterações que você precisará fazer em Deck para que possa retomar um hernIaL LISTAGEM 11 .8 Uma cla ss e Oeck atualizada public class Deck ( private java.util . UnkedUst deck ; publiC Deck() { buildCa rdsO;
I public Iterator itera torO ( return new ForwardIterator{ deck );
I
II reduzido por brevidade I
Alternativamente, é to talmente válido, do ponto de vista da 00, considerar um it erador como uma exten são da coleção a que ele dá acesso. Como resultado, você tem algumas outras opções de implementação. A linguagem Java possibilita uma construção conhecida como classe interna. Uma classe interna é uma classe definida dentro de outra classe. Como a classe interna é definida dentro de outra classe, ela tem total acesso a todos os métodos públicos dessa classe, assim como aos métodos protegidos e privados, e às variáveis internas. A analogia mais próxima na linguagem C++ é uma classe fr iend. friend fornece acesso especial a outro objeto confiável.
É fácil abusar d a classe friend e da classe interna, pois elas podem destruir o encapsulament o facilm ente; entretanto, um iterador realmente faz parte da coleção. A listagem 11 .9 mostra uma implementação de classe interna do Iterator da classe Deck.
LISTAGEM '1.9 Uma imp l ementação de Iterator de classe interna public class Oeck { private java.util.LinkedList deck; publ ie DedO { buildCardSO; I
publ ic Iterator HeratorO {
Reutilizando projetos através de padrões de projeto
return new ForwardIterator();
I l/reduzido por brevidade private class Forwardlterator imp lements Iterator { int index; publiC boolean isDoneO { II note que a classe interna tem acesso II liberado à variâvel interna deck de Deck if(index •• deck.sizeO ) ( return true;
I return false:
I publiC Object currentItem() { if( !i sDoneO ) ( return deck .get( index ) ;
I return null;
I publiC void next() { if( !isDoneO) ( i ndex++ ;
I I public void first() { index a O'•
I I I
253
Dia 11
Quando usar o padrão Iterator Existcm várias razõcs para se usar o padrão llerator:
• Você podc usar um ilerador quando quiser ocultar a implementação de uma coleção. • Você pode usar um iterador quando quiser fornecer diferentes tipos de laços sobre uma coleção (como laço para frente, laço inverso, laço filtrado etc.). • Você pode usar um iterador para manter a interface de lima coleção si mples. Você não preci sará adicionar métodos para ajudar a fazer o laço sobre o conteúdo. Basta deixar os usuários do objeto utilizarem um iterador. • Você pode defi nir uma classe de coleção base que retome um itcrador. Se todas as suas coleções herdarem dessa base, o iterador pennitirá que você trate todas as suas coleções genericamente. Na verdade, ja va.ut i l.Collecti on faz j ustamente isso. Essa utilização também é a forma geral do padrão Iterator. O exemplo Deck é uma versão abreviada do padrão I t erat or. A classe Oeck não herda de uma classe de coleção base abstrata; assim, você não pode tratá-Ia genericamentc. • Os iteradores também são lIteis para fornecer acesso otimizado às coleções. Algumas estruturas de dados, como a tabela hashing, não fornecem um modo otimi zado de fazer a iteração sobre os elementos. Um iterador pode fornecer lal ordenação, ao custo de um pouco de memória exlra. Entretanto, dependendo de seu aplicativo, a economia de tempo pode mais do que compensar a sobrecarga de memória. A Tabela 11 .3 destaca o usuário do padrão lterator. T ABELA 11 .3
O padrão Iterator
Nome do padrão
Iterator, Cursor
Problema
Fazer laço sobre uma coleção sem se tornar dependente da implementação da coleção
SOlução
Fornecer um objeto que manipule os detalhes da iteração, ocultando assim os detalhes do usuário
Conseqü6ncias
Navegação desacop lada, interface da coleção mais simples, lógica de laço encapsulada
Apossando-se de um padrão Alguns padrõcs são mais dificeis de entender do que outros; entretanto, a comp lex idade pode ser enganosa. Antes de poder aplicar ou mesmo pensar sobre a alteração de um padrão, você preci sa entender o padrão: em outras palavras "apossar-se dele". Existem alguns passos que você deve dar para poder dominar um padrão: I. Leia o padrão. 2. Leia o padrão novamente, prestando bastante atenção nos principais participantes e no exemplo de cód igo.
Reutilizando projetos através de padrões de projeto
255
3. Pratiq ue a implementação do padrão.
4. Aplique o padrão cm um problema real. Q uando você tiver conclu ído esses quatro passos, provavelmente terá um entendimento total do pad rão. Neste ponto, você pode começar a alterar o padrão de acordo com as necessidades específicas de seu problema; entretanto, você não deve tentar aumentar um padrão com o qual ainda não esteja familiarizado.
Resumo Os padrões de projeto são uma ajuda útil ao projetar suas soluções. De sua própria maneira, os padrões são a consciência coleti va da comunidade de 00, que tem anos de experiência de projeto. Os padrões de projeto podem fornecer diretrizes valiosas durante o proj eto. Você precisa ter cm mente os limites dos padrões de projeto, quando os utili zar. Um padrão de projeto lrata de um problema abstraiO e de apenas um problema. Um padrão de projeto não fo rnece a solução para um problema específico. Em vez disso, o padrão fornece uma solução abstrata para um problema geral. Fica por sua conta fornecer o mapeamento entre o problema abstrato e seu problema específico. Mapear um padrão de proj eto provavel mente é o maior desa fi o que você enfrentará enquanto • usar padrões. E uma habi lidade que vem apenas com o tempo, estudo e prática.
Perguntas e respostas P A linguagem Java usa padrões? R Sim, muitas das AP ls Java empregam padrões. Na verdade, cada um dos padrões que você leu a respeito hoje cstá representado na linguagem Java. Aqui está uma li sta brcvc dos padrões que voeê viu : Iteralo)": as classes Java Collection Proxy:
RMI da linguagem Java
Adapte)": usado am plamente por receptores de eve nto
P Todos os padrões são traduzidos para todas as linguagens? R Não. Cada linguagem é diferente. Alguns pad rões são impossíveis de implementar em certas linguagens, enq uanto outros são desnecessários, dev ido aos recursos internos da linguagem.
Dia 11
Workshop As perguntas e respostas do teste são fornecidas para seu melhor entendimento. Veja as respostas no Apêndice A, "Respostas".
Teste I.
o que é uma classe adaptadora?
2. Qual problema o padrão Iterator resolve? 3. Por que você usaria o padrão lterator? 4. Qua l prob lema O padrão Adapter reso lve? 5. Por que você usaria o padrão Adapter? 6. Qua l probl ema o padrão Proxy resolve? 7. Por que você usaria o padrão Proxy?
8. Considere a sit uação a seguir; qual padrão você usaria e por que?
A Sun Microsystems, IBM e Apache fornecem bibl iotecas para analisar documentos XML. Você optou por usar a biblioteca Apache em seu aplicativo XML. No futuro, contudo, você poderia optar por usar um fornecedor diferente. 9. Considere a situação a seguir; qual padrão você usaria e por que?
Você deve escrever um aplicati vo que recupere dados de um armazém de dados baseado , em arq ui vo. As vezes, seu aplicativo será executado de forma loca l e poderá acessar o armazém de dados at ravés de meios diretos. Outras vezes, o cliente será executado de forma remota e precisará se comunicar com um servidor para ler o armazém de dados. 10. O padrão Adaptertransfonn a lima interface. O padrão Proxy altera a interface de um objeto?
Exercícios I. As 1istagens I I. \ o c I \. I \ apresentam uma classe de ca rrinho de compras e uma classe de item. A Listagem 11.12 apresenta uma interface iterator. Use essas defi ni ções para criar um ilerator que permita fa zer a iteração pelo conteudo do carrin ho de compras. LISTAGEM 11.10
ltem . j ava
publl c cla ss I tem { prlvate i nt i d; pr; vate i nt quantity ; private fl oa t unit_pr icc ;
Reutilizando projetos através de padrões de projeto
LISTAGEM 11 .10 Item.java (continuação) private String description; private float discount;
r·* Cria um novo item com a quantidade. • • * * *
*
preço . descrição e desconto por unidade dados. @param id a id do produto @param quantity o número de itens selecionados @param unit prlce o preço com desconto anterior @param description a descrição do produto @param discount o valor em dõlar a ser subtraído por item
./ public Item( int id . in t quantity. float unit_price. float discount . String desc) { this.id • id; this.quantity • quantity; this.unit_price ~ unit_price; this.discount • discount; this.description • desci }
r·
• @return int a quantidade do item
./
pubhc int getQuantityO { return quantity; }
r·
* @param quantity a nova quantidade
./
public void setQuantity( int quantity ) { this.quantity ~ quantity; }
r·* @return o preço ./
unit~rio
do item
public float getUnitPriceO { return unit_price; }
r·
* @return float o preço total do item menos todos os descontos
./
257
Dia 11
LISTAGEM 11 .10 Item.java (continuação) publiC float getTotalPrice() I return ( unit_price * quantity ) - ( discount * quantity ) ; J
,u * @return String a descrição do produto
"
public String getOescription() I return description; J
,u * @return int a id do produto
"
public int getID() I return 1d ; J
J
LISTAGEM 11 .11 ShoppingCart.java public class Shopp i ngCart { java.uti l. linkedList items
'U
=
new java.util.linkedlist();
* adiciona um item no carrinho * @param item o item a adicionar
"
public void addItem( Item item) { items.add( item ); J
,u * remove o i tem dado do ca rrinho * @param item o item a remover
"
public void remove Item ( Item item) { items.remove( item ); J
,u * @return int o número de itens no ca rrinho
" public int getNumber Items() I return items.size(); J
Reutilizando projetos através de padrões de proj eto
LISTAGEM 11 .11
259
ShoPP lngCart . java (continuação)
1« * recupera o item indexado
* @param index o f nd ice do item * @retun Item o item no indice
LISTAGEM 11.12
lterator . java
pub li c interface Iterator { publ ic vo i d first() ; publ ic vo i d next(); publ i c boolean isDone() : pub l ic Dbject currentltem(); }
2. O Pe tAdapter, apresentado anteriormente no capítulo, está limitado a apenas empacotar uma instância de Pe t . Altere o adaptador de modo que você possa mudar o objeto que ele em pacota, a qualquer momento. Por que você desejaria um adaptador mutante?
Respostas do teste I. Uma c lasse adaptadora transforma a interface de um objeto em uma interface esperada por seu programa. Um adaptador contém um objeto e delega men sagens da nova interface para a interface do objeto cont ido. 2. O padrão Iterator descreve um mecanismo para fazer laços pelos elementos de uma coleção. 3. Você usaria o padrão Iterator para conter lógica de navegação cm um lugar, para fo rnecer um modo padron izado para percorrer coleções e para ocu ltar do usuário a implementação da coleção. 4. O padrão Adapterdescreve um mecanismo que permilea você transformar uma interface de objetos. 5. Você usaria o padrão Adapter quando precisasse uti lizar um objeto que tivesse uma interface incompatível. Você também pode usar empacotadores preventivamente, para isolar seu código das alterações de AP ls.
Dia 11
6. O padrão Proxy intennedia de fonna transparente o acesso a um objeto. Os proxies adicionam um procedi mento indireto para uso de objetos. 7. Você usari a o padrão Proxy sempre que quisesse intennediar o acesso a um objeto de uma maneira que uma referência simples não permitisse. Exemplos comuns incl uem recursos remotos, oti mizações e limpeza de objeto geral, como contagem de referência ou reunião de estatísticas de utilização. 8. Nessa situação, você pode usar o padrão Adapter para criar uma interface independente daquela fornecida pela Sun, IBM ou Apache. Criando sua própria interr.1ce, você pode pennanecer independente da APl ligeiramente diferente de cada fornecedor. Empacotando a bibl ioteca, você está livre para trocar de bibl ioteca a qualquer momento, seja para migrar para uma nova versão ou para trocar de fornecedor, pois você controla a interfàce do
Respostas dos exercícios I. LISTAGEM
11 .13 ShoppingCart.java
publiC class ShoppingCart { java.util.LinkedList items· new java .util .Linked Lis t O ;
r·
* adiciona um item no carr inho * @param item o item a adicionar
./ pub l ic void addItem( Item item ) { items. add ( item) ;
I
r·
* remove o item dado do ca r rinho *
~param
i tem o item a remover
./ public void removeltem( I tem item) ( items .remove ( item );
Reutilizando projetos através de padrões de projeto
LISTAGEM 11 .13 ShoppingCart .java (continuação) }
..
/
*
~return
int o número de itens no carri nho
./ publi c int getNumber Items() I return ltems . size(); }
/
..
* recupera o item indexado * @param index o lndice do item * @retun Item o item no í ndice
./ public Item getItem( int index) { return (Item) items.get( index ); }
publ ic Iterator iteratorO { II ArrayL i st tem um mé todo i terator() que retorna um iterador /1 entretanto, para propÓS itos de demonstração , ajuda ver um ite rador simples return new Cart I terator ( items ); }
}
LISTAGEM 11 .14 Cartlterator.java public class Cartlterator implements Iterator { private Object [] items; private int index; publiC CartIterator( java.util .LinkedList items ) ( this.ltems· items.toArray{); }
publ iC boolean isDoneO ( if(index >- items.length ) { retu rn true ; }
return fal se ; }
public Object currentItemO ( i f( !isDoneO ) {
261
Dia 11 LISTAGEM 11 .14
Ca rtIterator. java (continuação)
return items [index] : }
return null; }
publ i c vo1d nex tO I i ndex++; }
publ iC void first() I index· O; } }
2. Tornando o adaptador mutante, você pode usar o mesmo empacotador para empacotar muitos objetos difcrentes e não precisa instanciar um empacotador para cada obj eto que precise ser empacotado. A reutilização de empacotadores faz melhor uso da memória e Iibera seu programa de tcr de pagar o preço da instanciação de muitos empacotadores. LISTAGEM 11 .15
Mu tableAdapter. j ava
pu bl ic cla ss MutableAdapter extends MoodyObject I private Pet pet : publl c Mutabl eAdapter ( Pet pet ) { setPet ( pe t ): }
protected St ri ng getMoodO ( II implementando apen as porque é ex i gido por II MoodyObject . como também SObrepõe queryMood II não prec i samos dele return pet . spea k(): }
public yo id queryMood() { System . out.p r intln( getMood() ) ; }
public void setPet ( Pet pet ) { t hi s.pet • pet : } }
SEMANA
2
DIA Padrões avançados de projeto No capítulo de ontem, você viu como os padrões de projeto pennitem reutili zar projetas testa· dos. Hoje, você continuará seu estudo de padrões de proj eto, exami nando mais três padrões. l-laje você aprenderá: •
Sobre três im portantes padrões de projeto
•
Co rno garantir que seus objelos permaneçam únicos
• Como aprimorar um exemplo anterior • A respeito de algumas annadilhas comuns de padrão que você deve evitar
Mais padrões por exemplo Vamos continuar nosso estudo sobre padrões de projeto, considerando três padrões importantes: •
Abstract Factol')'
• Singlelon • Typesafe Enum Cada um desses padrõcs aparece cm quase todos os projetas. Na verdade, você pode usar opa· drão Typesafe Enurn para corrigir um exemplo do Capítulo 3, "Encapsulamento: hora de escrever algum código! "
264
Dia 12
o padrão Abstract Factory o Capítulo 7, " Polimorfis mo: hora de escrever algum cód igo", mostrou como você pode combinar herança e pol imorfi smo para escrever software ' à prova de futuro'. Os relacionamentos com capacidade de conexão da herança, combinados com o polimorfismo, permitem que você comx:-
te novos objelos em seu programa, a qualquer momento; entretanto, há um inconveniente. Para que seu programa possa instanciar esses novos objetos, você deve entrar no cód igo e aliem-lo para que ele instancie os novos objetos, em vez dos antigos. (E você precisará fa zer isso em todos os lugares onde os obj elos antigos são instanciados !). Não seria ótimo se houvesse um modo ma is fácil de coneclar seus novos objetos? O padrão Abstract Factory reso lve esse problema através da de legação. Em vez de instanc iar ohjetos através de seu programa, você pode delegar essa responsabilidade para um objeto chamado jaclory. Quando um objeto precisar criar outro, ele solicita rá que o factory faça isso. Usando um factol)', você pode isolar toda a criação de objeto em um (mico local. Quando você preci sar introduzir novos objetos em seu sistema, só precisará atualizar o factory. para que ele crie uma instância de Sllas novas classes. Os objetos que usam o factory nunca saberão a d ife rença. A Fig ura 12. 1 ilustra o projeto geral do padrão Abstracl FaCIOI)'. FIGURA 12 .1
4 bsll..,lfeaory
O padnio Abslracl FaclOIy. • factoryMethodA 4J : A • factortMethod8 { J : 8
eo.....-Feetorv1
c onc,.,.Fect0
• fectoryMethodA 41: A • feetoryMe1hod8 {): B
.. foctoryMethodA { ) : A • foctoryMethodB rI: B
~
, "
«cnoe,..
\
/
>:>
~
~« r
"
A
.. 1/
~
..',,,,:>
Padrões avançados de projeto
265
o padrão Abslracl Faclory usa herança e capacidade de conexão. A classe base de
faCI OI)' define todos os métodos de criação de objcto e cada subclasse fa ctol)' defi ne quais objetos cria, s0brepondo os métodos.
Implementando um Abstract Factory Existem ocasiões em que você precisará usar bibliotecas de tercei ros dentro de seu programa. Infelizmente, quando é hora de migrar para uma nova versão, você pode veriricar que as AP ls públicas das bib liotecas mudaram ligeiramente. Felizmente, o padrão Abstract Facto l)' ofe rece uma solução que torna lranqüi la a migração da bibli oteca. Imagine que você esteja traba lhando em um projeto que usa um ana lisador XML. Um ana lisador XML receberá um documento XM L como String e retornará uma representação de objeto do documento, conhecida como Oocument. Se você sabe que va i atualizar a bibl ioteca no futuro e que a AP l vai mudar, existem alguns passos que pode dar para se proteger.
XM L XML, ou Extensible Markup Language, é uma linguagem para escrever tag s que descrevem dados. Assim como a HTML oferece tags para formatar e exibir seus dados, a XML perm ite que você def ina suas própria s tags personalizadas para d escrever o significado conceituaI de seus dados. A HTMl é uma maneira excelente de marcar dados para exibição . Entretanto, a HTMl não tem a capacidade de transmitir o significado dos dados. A HTML pode dizer para que você coloque negrito em uma palavra, mas não pode dizer que a palavra negritada é o título do documento. Em vez disso, você precisa aplicar esse significado externa· mente ao documento. Po r outro lado, a XML fornece um mecanismo que permite dizer que alguma palavra em um documento é um título. Se você usar essas tags para marcar seus dados, dife· rentes programas poderão ler seus documentos e saber o que significa cada dado. A ssim, por exemplo, você pode escrever um programa que sabe formatar titulos com negrito. Ou ent ão, você pode escrever outro programa que lê uma lista de documentos e formula uma lista de títulos pa ra sua seleção. Tentar escrever os mesmos programas para ler um documento HTML seria muito mais d ifícil. Nos últimos anos, a XM l se tornou um padrão importante para a troca de dados. Duas entidades não relacionadas podem se comunicar, desde que ambas entendam as tags XMl. Como resultado, a XM l tem aparecida como o padrão para comunicação entre empresas. Para comprar e vender, duas empresas podem concordar com um conjunto de tag s comuns. Uma vez que tenham essas l ags, elas podem troca r documentos XML l ivremente. Considere o seg uinte documento XML de uma receita:
Chlc ken Tacos Chlc ken
Dia 12
I Esse documento XML tem tags que descrevem os dados de uma recei ta. Se você entende a marcação de tag, sabe que os dados que estão entre as tags representam o nome da receita. Todos os dados que estão entre as tags con têm informações de ingredientes. Você pOde escrever progra mas que reconhecem a marcação Rec i pe. Um programa poderia permitir que você selecionasse receitas que gostaria de fazer durante uma semana. Esse programa poderia usar os documentos Recipe que você escolhesse pa ra formular e imprimir uma lista de compras. Outro prog rama poderia imprimir o menu da semana. Para que um programa leia e entenda um documento XML, ele deve analisar o documento. Um analisador de XMl pegará um documento e o converterá em uma árvore de objetos. Cada objeto conterá parte do documento. Seu programa pode sim plesmente percorrer esses objetos para extrair informações do d ocumento. Assim, se você escrever um programa que leia receitas, ele poderá percorrer todos os objetos Ingredi ent para construir uma lista de ingredientes. Atualmente, muitos analisadores de XML estão d ispon íveis para escolha. Cada analisador tem uma APl ligeiramente diferente, de modo que, uma vez que você escre va um programa para usar um analisador especifi co, poderá ser dificil trocar de analisador posteriormente. O padrão Abstract Factory apresenta uma maneira de evitar que você fique preso ao uso de um analisador em particular.
Novo TeRMO Um (II/(Ifisador de X/odL pega um documento XM L e o transfom18 em uma representação de objelo. In icialmente, você precisa empacotar o analisador em um objeto que controle.
NO TA
Lembre-se de que um empacotador é um adaptor. Um empacotador converte a interface de um objeto em uma interface alternativa. Norma lmente, você usa um empacotador para converter a interface em uma interfa ce esperada por seu programa.
Como você controla a APl do empacotador, garante uma APl estável que seu programa pode usar. A Li stagem J 2. J ilustra um possível empacotador analisador.
LISTAGEM 12.1 Parser . java public interfa ce Parser ( publ ic org.w3c. dom. Oocument pa rse( String document );
I
Padrões ava nçados de projeto
NOTA
267
org.w3t.dom.Dotument é uma interface definida pelo W3C para representar um documento XMl como uma estru tura de objetos. Você pode encontrar mais informações sobre a interface Docum ent no endereço http: //'rMW . w3 .org/ TRI 2000/C R-DOH-level - 2- 200D0510/ java-binding.html.
As listagens 12.2 e 12.3 mostram duas possíve is implementações: Vers ionOneParser, que usa a versão 1.0 da biblioteca, e Vers ionTwoParser, que usa a versão 2.0 da bi blioteca. LISTAGEM
12.2 Vers i onOneParser .java
public class VersionOneParser implements Parser { publi C org.w3c.dom.Oocument pa r se( Str i ng document ) ( II instancia a versão 1 do ana l isador II XMlParser p • new XM l Parser{) ; II passa o documento para o anal i sador e retorna o resultado II return p.parseXML( document ) ;
I I
LISTAGEM
12.3 VersionTwoParser.java
public cla ss Vers i onTwoParser impl ements Pa r ser I public org .w3c.dom.Oocument parse( String document } I II instancia a versão 2 do ana l isador II OOMParser parser · new DOMParser(} ; II passa o documento para o ana l isador e retorna o resultado II return parse r . pa r se( document };
I I Seu programa pode usar o padnlo Abslract Factory para criar a versão correta do anal isador, sempre que ele precisar analisar um documento. A Listagem 12.4 apresenta a interface base de faclol)'. LISTAGEM
12.4 ParserFactory.java
public interfa ce ParserFactory I pub l ic Parser createPar ser() ;
I Você precisará de duas implementações concretas de factory, pois existem duas implementações do analisador. As li stagens 12.5 e 12.6 apresentam essas implemcnlaçõcs.
Dia 12
LISTAGEM 12.5 VerslonOneParserFactory.java publl C class VerslonOneParserFactory implements ParserFactory I public Parser create Parser () I return new VersionOneParser(); }
}
LISTAGEM 12.6 Vers i onTwoPa rserFactory .java public class VersionTwoParserFactory implements ParserFactory ( publi c Parser createParser() { return new VersionTwoParser(); } }
Agora, em vez de instanciar anali sadores diretamente, seu programa pode usar um a das classes factory para recuperar objetos analisadores, quando precisar anal isar um documento. Você precisará simplcsmente instanciar a factory correta no início do programa e torná-Ia disponível para os objetos de seu programa. o padrão Factorv Method est á intimamente relacionado ao padrão Abstracl Fact ory. Na verdad e, um Abstract Factory pode usar Factory Method para criar os objetos que retorna. Um método Fact ory nada mais é do que um método que cria objet os. createParser() é um exemplo de método Factory. Você também pode encontrar exemplos de métodos Factory em toda a APl Java. Class.newlnstance() é um exemplo de método Factory. Conforme você pode ver, um método Factory pode aparecer em uma classe normal o u em uma Factory abstrata. Em qualq uer caso, ele cri a objetos, ocultando assim a classe rea l do objeto criado.
Quando usar o padrão Abstract Factory Use o padrão Abstract Factory, quando: •
Você quiser ocultar o modo como um objeto é criado.
•
Você quiser ocultar a classe atual do objeto criado.
•
Você quiser um conj unto de objetos usados jUlllos. Isso evita que você use objctos incompatíveis juntos.
•
Você quiser usar d iferentes versões de uma implementação de classe. Um Abstract Factory permite que você troque essas diferentes versões em seu sistema.
A Tabela 12.1 destaca o usuário do padrão Abstracl Factory.
Padrões avançados de projeto
T ABELA 12 .1 O pad rão Abstract Fact ory
Nome do padráo
Ahstract Factory
Problema
Precisa de uma maneira de trocarobjetos plugáveis de ronna transparente
Solução
Fornecer urna interface abstrata que providencie métodos para instanciar os ohjetos
Conseqüências
Pernl ite que você troque facilmeOle novos tipos de classe em seu sistema; entretanto, é dispendioso adicionar tipos não re lacionados
o padrão Singleton Quando projetar seus sistemas, você verá que algumas classes deveriam ter logicamente ape nas urna instânc ia, como um factory ou um objeto que acesse algum recurso não com parti lhado (conexão de banco de dados, região de memória etc.). Nada, entretanto, imped irá que um objeto instancie outro. Como você impõe se u projeto? O padrão Singleton fornece a resposta dessa pergunta. O padrão Singleton impõe se u projeto colocando a responsabilidade da criação e da intennediação do acesso à instância no próprio objela. Fazer isso garante que apenas uma instância seja criada, além de fornece r um unico ponto de acesso para essa instância. A Figura 12.2 i lustra a assinatura de uma classe singlclon (carta unica de detenninado naipe). FIGURA 12.2 O padrtio Sillglelofl
A Listagem 12.7 ilustra LIma possível classe singleton. LISTAGEM 12.7
Uma implementação do padrão Singleton
pub l ic class Singleton (
II uma referência de classe para a instância sing l eton private static Singleton instanc ej
II o constru t or deve ser oc ulto para que os objetos não possam instanciar II protected permite que outras classes herdem de Sing leton protected Sing letonO ()
II um método de classe usado para recupera r a i nstância singleton public static Sing leton getlnstance() ( if( instance •• nul l ) ( instance • new Singleton()j }
Dia 12
LISTAGEM
12.7 Uma implementa ção do padrão Sing l eton (continuação) return i nstance;
} }
A classe Si ngl eton contém uma instância estática de Si ngl eton e dá acesso à instância de 51ngl eton através do método da classe gctlnslance().
Implementando um Singleton
o Capítulo 7, Laboratóri o I, apresentou uma classe Payroll. Uma classe de folha de pagamento ' real' provavelmente acessaria um banco de dados de funcionários. Poderia ser lima boa idéia ter apenas urna instância de Payroll , para ev itar conflitos dc recurso. A classe Pay ro11 é um a boa candidata para o padrão Si ngleton. A Listagem 12.8 apresenta um singleton Payroll. llSTAGEM
12.8 Payro 11 Si n9 I eton
publi C cla ss Payroll {
II uma referência de cl asse para a i nstãncia singleton única private static Payroll instance ; total_hours; pri vate I nt private Int total_sales; private doub l e total_pay;
II oculta o construtor para que outros objetos não possam instanciar protected Payroll O II
II II
observe o uso de stat i c: você não tem uma instância. quando recupera uma in stâ ncia; portanto , o mêtodo deve ser um mêtodo de cl asse ; daf.
sta ti c public static Payroll getInstanceO { if( instance •• null ) { in stance = new Payro ] l() ; }
return instance ; }
public void payEmployees( Employee [] emps ) { for ( int i = O: i < emps. length ; i ++ ) ( Employee emp z emps [i]; tota l_pay .- emp.ca l culatePay(); emp.printPaycheck();
II
Padrões avançados de projeto
LISTAGEM 12 .8
271
Payroll Si ngleton (continuação)
} }
public void calculateBonus( Employee [] emps ) ( for( i nt i - O: i < emps.length; i ++ ) I Employee emp • emps [i]: System .out.println{"Pay bonus to " + emp.getLastName{) + ", " + emp.getFirstName{) + " $" + emp.calculateBonusO ): } }
public void recordEmployeelnfo( CommissionedEmployee emp ) { total _sales +- emp.getSales(): }
public yoid recordEmpl oyeelnfo( HourlyEmployee emp ) { total _hours +- emp.getHours(): }
pub l ic void printReport() I System.out.println{ "Pay roll Report:" System.out.println{ "Total Hours: + System.out.println( "Total Sales: " + System.out.println( "Total Paid: $" + II
); total_hours ): total _sales ); total_pay ):
} }
o singleton Payrol l adiciona um método getlnstanceO. Esse método é responsável por criar e dar acesso à instância singleton. Preste bem atenção ao construtor; aqu i, o construtor é protegido para que outros objctos não possam inadvertidamente instanciar mais objctos Payro 11. Como ele é protegido, outros objelos podem herdar de Payro 11 . A Listagem 12.9 dá um exemplo de como você pode usar o singleton. LISTAGEM 12 .9
Usando o singleton Payro ll
II recupera o singleton de folha de pagamento pay roll payroll - Payroll.getInstanceO;
II cri a e atualiza al guns fun cion6rios Comm i ssionedEmployee emp! • new Commiss i onedEmployee( MMr.", ·Sales" , 25000 .00, l OOO.OO} ;
Commi ssionedEmployee emp2 • new CommissionedEmployee( "Ms.", "Sa l es", 25000.00 , l OOO.OO} ;
empl.addSales{ 7 );
Dia 12
LISTAGEM
12.9 Usando o si ngleton Payroll (conti nuação )
emp2.addSales( 5 }: Hour 1yEmp1oyee Hourl yEmployee emp3.addHours{ emp4.addHou r s(
emp3 • new Hour1yEmp 1oyee ( NMr. N. "Mi nimum Wage-, 6.50 ) : emp4 .. new HourlyEmployee{ NMs . N, "Min imum Wage-, 6.50 ) ; 40 }: 46 );
II
usa os métodos sobrecarregados payro 11 . recordEmp 1oyee I nfo ( emp2 ): payrol1.recordEmployeelnfo( empl ) ; payrol1 . recordEmployee lnfo( emp3 ); payro 11 . r ecord Emp 1oyee I nfo ( emp4 ) :
Uma vez que você tenha uma instância singleton, ela funci onará como qualquer o utra instância. O quê é interessante a respeito da Listagem 12.9 e:
Payroll pay roll • Payro ll .getlnstance() : Note que você não escreve mais:
Pay rol l payroll - new Payrol l O:
Herança e o padrão Singleton O padrão Singleton apresenta algumas dificuldades de herança. Especificamente, quem gerencia a instância sing leton, a progenitora ou a filha? Você tem algumas escolhas. A primeira escol ha é simplesmente criar a subclasse singletoll e atualizar a progenitora para instanciar a filha. As listagens 12. 10 e 12.11 destacam essa estratégia. LISTAGEM
12.10 Chi l dSing l eton
pub li c class ChildSi ng le ton extends Singleton { protected Chi ldSing letonO {} publi c String toString() { return "I am the child singl eton " ;
I I
LISTAGEM
12.11 Updated Si ngleton
public cl ass Si ngl eton {
II
uma referência de classe para a instância s ingleton
Pad rões avançados de projeto
LISTAGEM 12. 11
273
Upda ted Singleto n (continuação)
private static Sing leton i nstance:
II II
o constructor deve ser oculto para que os objetos não possam i nstanc i ar protected penmite que out ras classes herdem de Sing1eton protected ParentS1ngletonO I}
II
um mé todo de classe usado para recuperar a instância sing leton public s t atic Si ngleton getInstanee() { if ( ins tance •• nul 1 ) I ins tance • new ChildSi ngleton(): } re tu rn instance; } publi C Stri ng toString() { return "I am the si ng1eton" : }
}
Essa solução tcm o inconven iente de exigir alterações na classe progenitora. Uma solução alternat iva inclui Fazer com que o sing leton leia uma variável de confi guração na primeira vez que getInstanceO for chamado. O singleton pode instanciar qualquer objeto que seja especificad o no valor da configuração. Você também pode pennitir que cada fi lha forneça sua própria implementação de getInstan eeO. Essa estratégia não exigi rá alterações na progen itora.
Este quadro conta com um truque da linguagem Java, embora exista um análogo na linguagem C++. Trata-se de um a SOlução interessante que tira proveito do fato de que os blocos estáticos são executados quando uma classe é carregada na linguagem Java. Através de outra estratégia, você pode adicionar um método protegido regi stere Si ngletons ) na progenitora. Você pode colocar um bloco estático na fil ha, que a instancie. Dentro do construtor, a filha pode se registrar na si ngleton progenitora. Aqui est á o código da classe Singleton atualizada:
public class Slngleton (
II uma referênCia de classe para a instância de singleton private static Singleton ins tance;
II o construtor deve ser oculto para que os objetos nao possam instanciar II protected permite que out ras classes herdem de 51ngleton protected 5ingleton () I)
Dia 12
II um método de classe usado para recuperar a instAncia de singleton pub l ic static Singleton getInstance() I If( i nstance •• null 1 ( Ilvalor pad r
return instance ; }
protected static void register( Singleton s } ( if( i nstance •• nu l l ) I Instance • Sj } }
} E a classe ChildSi ng leton atual izada :
pub l lc class ChlldSingleton extends Singleton I statlc { new ChildSingleton() j }
protected ChildSingleton() { Singleton.register( this ); } }
Para fazer t udo isso funcionar, você precisará chamar CI asso forName(
~ChlldSi ngl eton ~
)
Ant es de cha mar o método get lnstanceO do singleto n. Aqu i est á um exemp lo:
Class.forName( "ChildSingleton" }; Singleton 5 • Singleton.getlnstance() ; System.out . prlntln( s. toString() l j
Quando usar o padrão Singleton Use o padrão Si ngleton quando você quiser restringir uma classe a ter apenas urna instância. A Tabela 12.2 destaca o usuário do padrão Si ngleton. TABELA 12 .2 O padrão Si nglet o n
Nome do padrão
Singlclon
Pro blema
Deve existirapenas uma instância de um objeto no s istema em detenninado momento.
Solução
Pemlit ir que o objeto gerencie sua própria criação e acesso através de um método de classe.
Padrões ava nçad os de proj et o
TABELA 12.2 O padrão Singlet on (continuação)
Acesso controlado à instânc ia do objcto. Também pode dar acesso a um número definido de instâncias (como apenas seis instâncias), com uma ligeira alteração no padrão. É um pouco mais dificil herdar um singleton.
Conseqü6ncias
o padrão Typesafe Enum A Listagem 12. 121i sta uma seleção da classe Card, apresentada pela primeira vez no Capítulo 3, Laboratôrio 3.
de Card.java
LISTAGEM 12.12 Uma seleção
pub li c class Card { private int rank : private int suit ; private boo l ean fa ce_up ;
II constantes usadas para instanci ar II naipes pub l i c static public static public static public static II valores public static public st atic publl c static public static public static publi c static public static public static public static public static public static public statlc publ ic static
f ina l fi na 1 final fi na 1
i nt i nt i nt i nt
OIAMONOS HEARTS SPAOES CLUBS
fi na 1 fi nal fi na 1 fina 1 final final fi nal fi nal final fi nal final final final
i nt i "t i nt i "t int i nt i nt i nt i "t int i nt int fnt
I NO
THREE FOUR FIVE 5IX SEVEN EIGHT NINE
- 4; • 3; • 6; • 5;
• 2·•
• 3; • 4; • 5;
• 6;
• • • m • JACK • QUEEN • KING • ACE •
7; 8; 9 ·• 10 ; 74 ;
81 ; 75 ;
65 ;
II cria uma nova ca rta - usa as constantes apenas para in i cia l izar pub l ic Card( in t s ult . lnt rank ) { II Em um programa real . você precisa ri a fazer a validação nos argumentos. th i s. suit • suft; this.rank • rank ; J
J
Dia 12
Ao instanciar objetos Card, você precisa passar uma constante de número e de naipe vá lida. A utilização de constantes dessa maneira pode levar a mu itos problemas. Nada o impede de passar qualquer lnl que você queira. E, embora você deva referenciar apenas o nome da constante, isso abre a representação interna de Cardo Por exemplo, para con hecer o naipe da carta (Card), você deve recuperar o va lor int e depois com pará-lo com as constantes. Embora isso funcione, essa não é uma solução limpa. O problema reside no fato de que naipe e número são objetos propriamente ditos. i nt não resolve isso, pois você precisa aplicar um significado a i nl. Novamente, isso confunde a responsabi li dade, pois você precisará reapl icar esse signi fi cado sempre que encontrar um in l que represente um número ou um naipe.
Li nguagens corno C++ têm uma construção, conhecida como enllmeração; entretanto, as enumerações se red uzem sim plesmente a um atal ho para declarar urna lisla de constantes inteiras. Essas constantes são limitadas. Por exemplo, elas não podem fornecer comportamento. Também é difici l adicionar mais constantes. Em vez disso, o padrão Typesafe Enum fornece lima maneira 00 de declarar suas constantes. Em vez de declarar simples constantes inteiras, você cria classes para cada tipo de constante. Para o exemplo Card, você criaria uma classe Rank (número) e uma classe SUl l (naipe). Então, você criaria uma instância para cada valor de constante que quisesse representar e a tomaria publ icamcnte disponivel a partir da classe (através de public fina l, cxatamentc como as outras constantes).
Implementando o padrão Typesafe Enum Vamos vcr a implementação das classes Rank e SUll, nas listagens 12. 13 c 12.14. LISTAGEM 12.13 SuiLjava puhl i c f i na l class Suit { Ilde f ine estaticamente todos os va lo res vãl idos de Sui t public static fi nal Suit OIAMONOS • '"w Suit ( (cha r )4 ); public static fi nal Suit HEARTS • '"w Sui t e (char)3 ); publ ic static fi nal Suit SPADES • n,w Sui te (char)6 ) ; publ ic static final Suit CLUBS • n,w Suite (char)5 ) ;
II ajuda a fazer a iteração pelos valores da enumeração pub l ic static final Sui t [] SU IT
= (
OIAMONDS . HEARTS . SPADES . Cl UBS ) ;
II variável de i nstancia pa ra conter o valor de exibição private final char disp l ay;
II não permite i nstanciação por ob j etos externos private Suite char displ ay ) {
Padrões ava nçados de proj et o
LISTAGEM 12 .13
277
Suit.java (continuação)
thiS.display • disp l ay; }
II
retorna o valor de Suit publlc String toStringO { return String.valueOf( d;spl ay }; }
}
SUl t é simples. O construtor recebe um char que representa o SUl t. Corno Su i t é um objeto compl eto, ele também pode ter métodos. Aqui, o sua fornece um método toStri ngO. Uma enu meração de typesafe que pode adicionar quaisquer métodos que se mostrem úteis. Você também notará que a constante é privada . Isso impede que os objetos instancicm objetos SUl t di retmnente. Em vez di sso, você está restrito a usar apenas as instâncias de constante dec laradas pela classe. A classe também é declarada como final para que outras classes não possam ser subclasses de la. Ex istem ocasiões em que você permiti rá a herança. Nessas ocasiões, torne o construtor protegido e remova a declaração final. Devido ao m odo como a linguagem Java funcio na, certi f ique·se de fornecer versões f inais de equal sO e hashCode() q ue chamem super, caso você abra sua enumeração para a herança. Se não, você estará abert o a problemas estranhos, caso suas subclasses redefinam esses m ét odos incorretam ente.
NOTA
Você notará que a classe SUl t define várias instâncias de constante, uma para cada um dos na ipes válidos. Quando você preci sa de um valor constante de sua, pode escrever Su;t.DIAMONDS. A classe Rank, na Listagem 12.14, fu nciona de modo seme lhante a SUl t ; entretanto, ela acrescenta mais alguns métodos. O método getRank() retoma o valor de Rank. Esse valor pode ser im portante para calc ular o valor de uma jogada. Ao contrário das constantes originais, você não precisa mais apl icar signifi cado às constantes Ra nk ou Su; t. Em vez disso, elas contêm seus pr6prios sign ificados, pois são objelos. LISTAGEM 12 .14
Rank . java
publ;c final class Rank { pub l ;c pub l ic public public public publi c
static static static s tati c static stat ic
final fi nal final fi nal fi nal fi na 1
Rank Rank Rank Rank Rank Rank
new Rank( 2, 'THREE .O •• oe. Rank( 3, FOUR FIVE
• • SI' • SEVEN •
ne. ne. ne. ne.
Rank( Rank( Rank( Rank(
"2 M } ; "3 M } ;
4, "4 M } ; 5 , "5 M };
6, "6~ }; 7, "7" };
Dia 12 LISTAGEM 12.14 Rank.java (continuoção) public public publ ic publlc publ ic pu bl ic publ i c
stati c s tati c s tati c s tatic s ta tic s ta tic static
f inal fi nal fi na 1 fi na 1 fi na 1 f ina l f inal
Rank Rank Rank Rank Rank Rank Rank
EIGHT • ne. Rank( NINE • ne. Rank( TE' • ne. Rank( JACK • ne. Rank( OUE EN • ne. Rank( KING • ne. Rank( ACE • ne. Rank(
8. "8- ); 9. "9" ); 10 . "10" I ; 11. "J " ); 12. "O - ); 13. " 1(" I ; 14. "A" I ;
public sta ti c fi na l Rank [] RANK " { TWO , THREE, FOUR, FIVE, SIX , SEVEN, EIGHT, NIN E, TEN, JACK, OU EEN, KING, ACE I ; private f ina l i nt rank : pri vate f ina l St r ing disp lay ; private Rank( int rank, Stri ng display ) { this.rank • r ank: th i s. di splay • di sp lay;
I publiC int getRank() { return rank; }
pubhc St ring toStringO { return displ ay; }
I Por exemplo, você nào precisa mais apl icar sign ificado a i nt 4 e sabe que 4 significa OIAMONOS. Quando você prec isar determinar o val or da constante, pode fazer comparações de objeto usando equa 1s (). A Listagem 12.15 mostra as alterações que você precisaria fazer em Ca rd para poder usa r as navas constantes. (As classes Oec k e Oea 1er atualizadas estão disponíveis no código-fonte deste capítu lo). LISTAGEM 12.15 A classe Ca rd. java atu allzada publ i c cla ss Card { private Rank rank ; private Suit suit: priva te boolean face_up;
Padrões avançados de projeto LISTAGEM 12.15 A classe Ca r d.java a tua lizada (continuação)
II
cria uma nova ca rt a - usa as cons t antes apena s para inicia l iza r public Card( Suit s uit , Rank rank ) ( II Em um programa rea l , você preci saria fazer a validação nos argumentos. this.suit = s uit; th i s . rank • rank ; J
publ ic Sult getSuit () { re tu rn sui t; J publi C Rank getRank{) { return rank; J publ ic void f aceUp() ( face_up • true ; J pub l ic vo i d f aceDown() ( f ace_up • false;
J publiC boolean i sFaceUp() ( return f ace_up; J publi C String di splay() ( return rank.toS tr ing() + suit .toStri ng(); J J
Você também pode ter notado que Ra nk e Sui t declaram arrays de constantes. Isso torna fá ci l fazer um laço pelos valores das constantes di sponíveis. A Li stagem 12. 16 mostra como esse fato s implifi ca bastanle o método bui I dCards O de Ded. lISTAGEM12.16 O mé t odo buil dCards{ ) atua lizad o private void buildCards() ( deck • new java . uti l . LinkedList() ; f or( lnt 1 • O; i < Suit.SUIT.length; i ++ ) I for ( i nt j • O; j < Rank.RANK. length; j ++ )
I
Dia 12 LISTAGEM 12.16 O mé todo buildCardsO atualizado (continuação)
deck.add( new Card( Sui t.SUIT
( i ], Rank . RANK
(j]));
} } }
Quando usar o padrão Typesafe Enum Use o padrJo Typesa fe Enum, quando: • •
Você se achar esc revendo numerosas primitivas públicas ou constantes de String. Você se achar impondo identidade em um va lor, em vez de derivar a identidade do próprio valor. A Tabe la 12.3 destaca o usuário do padrão Typesafe Enum.
T ABELA 12.3 O padrão Typesafe
Enum
Nome do padrão
Typesafe Enum.
Problema
As constantes inteiras são limitadas.
Solução
Criar uma classe para cada tipo de constante e depois fornecer instâncias de constante para cada valor de constante.
Conseqüências
Constantes 00 extensíveis. Constantes úteis que têm comportamento. Você ainda precisa atualizar código para usar as novas constan tes, quando elas forem adic ionadas. Ex ige mais memória do que uma constante simples.
Armadilhas do padrão Você pode abusar dos padrões de projeto, assi m como acontece com qualquer outra ferramenta, usando-os incorretamente. Os padrões de projeto não garantem um bom projeto; na verdade, incluir um padrão em um lugar onde ele realmente não pertence arruinará seu projeto. Você precisa ser criterioso em sua deci são de inc luir um padrão em seu projeto. Recentemente, os padrões de projeto vêm sendo cada vez mai s criticados. Infelizmente, existe uma tendência, especialmente entre os iniciantes, de ser pego na tentati va de aplicar o máximo de padrões possivel a um projeto. O entusiasmo de usar padrões tem feito muitos desenvolvedores se esquecerem do objelivo dos padrões e ate do próprio processo de projeto. Não caia nessa annadilha! Não fi que cego com o prazer de aplicar padrões e deixe o projeto se reduzir ao máximo de padrões possível. Você não ganhará pontos de seus colegas por usara máxi mo de padrões, mas ganhará pontos por produzir um projeto limpo e coerente. Tal projeto poderia nem mesmo usar padrões!
Padrões ava nçad os de proj et o
DIC A
28 1
Existem algum as diretrizes q ue o ajudarão a evitar a armadilha do padrão: Dica 1: colocando parafusos redondos em buracos quadrados. Se você se achar pensando, ~como posso usar em m eu projetor, estará com problem as. Em vez disso, você deve pensar, ~ já vi esse projeto antes; acho que existe um padrão que o reso l ve~. Agora, vá e veja um livro sobre padrões. Sempre com ece do ponto de vista do problema e não da solução (o padrão). Dica 2: ataques de amnésia. Você estará com problemas, caso não possa explicar, em duas frases ou m enos, porq uê escol heu um padrão e quais as van ta· gens que ele oferece. Você deve poder explicar facilmente porque incluiu um padrão e para que ele con tribui em um projeto. A situação é sem esperança, caso você não consiga pensa r em uma explicação.
Existe uma segunda armadi lha, mais sut il, nos padrões: tagarelice do padrão. Não use padrões para tentar parecer inteligente e não tente sugerir o uso de um padrão que você não tenha estuda· do. Você pode ria mencionar o pad rão, mas sej a c laro, caso não esteja fami li arizado com ele. Você não deve apenas usar padrões apropriadamente em seu projeto, mas também em suas con· versas. Não é uma boa idéia contri buir para os fatores que prejudicam a prática de usar padrões.
Resumo Os padrõcs de projeto são uma aj uda útil ao se projetar suas sol uçõcs. De sua própria maneim, os padrões são a consciência colet iva da comunidade de 00, que tem anos de experiência em projeto. Lembrc· se dos limites dos padrões de projeto, quando utili zá· los. Um padrão de proj eto tmta de um e apenas um problema abstmto. Um padrão de proj eto não fornece a solução para um problema especifi co. Em vez disso, o padrão fornece uma sol ução abstmta para um problema geral. Fica por s ua conta fornecer o mapeamento entre o problema abstrato e seu problema específico. Mapear um padrão de projeto provavelmente é o maior desafi o que você enfrentará ao usar padrões. Trata-se de uma habi lidade que só vem com o tempo, estudo e prática.
Perguntas e respostas P C omo você escolhe um padrão de projeto? R Cada padrão de projeto tem um problema e padrões relacionados. Estude o padrão, se a descrição do problema parece corresponder ao seu caso. Também ajudará, se você exa· minar todos os padrões relacionados . Se, após estudar o padrão, ele parecer resolver seu problema, tente aplicar o padrão ao seu caso. Certifique·se de examinar as conseqüências. Se qualquer lima das conseqüências entrar em conflito com seus requ isitos, você prova· velmenle deverá ignorar O padrão. P C omo você sabe qua ndo d eve usar um pad rão de projeto? R Não hã uma resposta fáci I para essa pergunta.
Dia 12
Você não pode usar um padrão, caso não o conheça, e ninguém pode conhecer todos os padrões de projeto disponíveis. Quando você projetar, tente obter o máximo de entradas possível. Pergunte às pessoas se elas conhecem padrões que possam ajudá·lo a tomar suas decisões de projeto. Estude os padrões. Quanto mai s padrões você conhecer, mais oport un idades verá para usá· los. P A linguagem Java usa quaisquer dos padrões abordados hoje? R Sim. A linguagem Java usa muitos dos padrões abordados hoje. Padrão Factory Method: muitas classes Java têm métodos factory (um padrão intima· mente relac ionado ao padrão Abstract Facto!)'). Padrão Sing leton: java.lang.System é um exemplo dc singlcton na linguagcm Java. Padrão Typesa fe Enum: o padrão Typesafe Enum ainda não eSlava definido quando mui· tas das APls Java foram criadas. As ad ições futuras na A Pl Java usarão o padrão Typesa· fe Enum.
Workshop As perguntas e respostas do teste são fornecidas para seu melhor entendimento. Veja as respos· tas no Apêndice A, "Respostas".
Teste I.
o que é uma classe
empacotadora?
2. Qual problema o padrão Abstract Factory resolve? 3. Por que você usaria o padrão Abstracl Factory? 4. Qual problema o padrão Singleton resolve? 5. Por que você usaria o padrão Singleton? 6. Qual prob lema o padrão Typesafe Enum resolve? 7. Por que você usaria o padrão Typesafe Enum? 8. Os padrões garantem um projeto perfeito? Por que sim
Oll
por que não?
Exercícios A Li stagem 12. 17 apresenta a classe Bank do Capitulo 7. Transforme Bank em um singleton.
Padrões avançados de projeto
LISTAGEM 12.17
Bank.java
publie elass Bank { private java.util.Hashtable aeeounts
=
new java . uti l. Hashtable():
publi e void addAecount( St ring name , BankAceount aeeount ) ( accounts . put( name , account ): }
public double totalHoldings() { double total = 0.0: java.util.Enumeration enum - accounts.elements{): while( enurn . hasMoreElements() ) { BankAccount account = (BankAccount) enum.nextElement(): total + ~ account.getBalance( ): }
return tota 1; }
pub l ic lnt totalAceounts() ( return accounts.size(): }
publiC void depos ite St r ing name, double ammount ) ( BankAccount account - retr ieveAccount( name ); if( account l- nu11 ) ( account.depositFunds( ammount ) : } }
public double balance( String name ) { BankAccount account - ret r ieveAeeount( name ): if( aeeount ! - null ) { return account.getBalanee() : }
return 0.0; }
private BankAeeount retrieveAeeount ( Stri ng name ) { return (BankAceount) aceounts . get( name ) : } }
2. Considere a classe Error apresentada na Listagem 12.18. Essa classe define várias constantes. Aplique o padrão Typesafe Enum no projeto dessa classe.
I 284
Dia 12
LISTAGEM 12.18
Errar.java
public class Errar (
II nheis de erro publ ic final s tati c publi c fi na I static public fi na I statlc publ1c final statlc private in t leveI ;
i nt i nt i "t i "t
NOISE INFO WARNING ERROR
• O·• • 1.• • 2; • 3;
publiC Errar( int level ) { this. l evel ·level;
I public int getlevelO { return leveI;
I publ iC String toString() { switch (level) { case O: return "NOISE "; case I: return "INFO " .• case 2: return "WARNING "; default: return "ERROR ";
I I
I 3. Projete e crie um Abstract Factol)' para a hierarquia BankAccount apresentada como uma solução no Capitulo 7, Laboratório 3.
Respostas do teste I. Uma classe em pacotadora transforma a interface de um objelo naquela esperada por seu programa. Uma classe empacotadora contém um objeto e de lega mensagens da nova interrace para a interface do objeto contido. 2. O padrão Abstract Factol)' fornece um mecanismo que instancia instâncias de classe descendentes especificas, sem revelar qual descenderue é realmente criado. Isso permite que você conecte de forma transparente diferentes descendentes em seu sistema. 3. Você usa o padrão Abstract Factol)' para ocultar os detalhes da instanciação, para ocultar qua l classe de objeto é inslanciada e quando quer que um conj unto de objetos sejam usados j untos. 4. O padrão Sing leton garante que um objeto seja instanciado apenas uma vez.
Padrões avançados de projeto
5. Você usa o padrão Singleton quando quer que um objeto seja instanciadoapenas uma vez. 6. Usar constantes primitivas não é uma estratégia de 00 para programação, pois você precisa aplicar um sign ificado externo â constante. Você viu quantos problemas a decomposição da responsabilidade poderia causar! O padrão Typesafe Enum resolve esse problema, transformando a constante em um objeto de nível mais alto. Usando um objeto de nível mais alto, você pode encapsular melhor a responsabilidade dentro do objeto constante. 7. Você deve usar o padrão Typcsafe Enum quando se achar declarando constantes públicas que devem ser objetos propriamente ditos. 8. Não, os padrões não garantem um projeto perfeito, pois você poderia acabar usando um padrão incorretamente. Além disso, usar corretamente um padrão não signi fi ca que o restante de seu projeto seja válido. Muitos projetos v~ílid os nem mesmo contêm um padrão.
Respostas dos exercícios I. LISTAGEM 12 .19
Bank .java
publie elass 8ank { private java.uti l. Hashtable aeeounts
=
new java .u til.Hashtable()i
private statie 8ank instanee; proteeted Bank
o {}
publie stat i e Bank getlnstanee() { if( i nstanee •• nul1 li instanee • new Bank()i }
return in stanee : }
publie void addAeeoun t( Stri ng name . BankAeeount aeeount 1 I aeeounts.put ( name , aeeount )i }
pub l ie double total Hold ings() { double total· 0.0; java.util.Enumeration enum: aeeounts.el ements() ; while( enum.hasMoreElemen t s() ) { BankAeeount aeeoun t • (BankAeeount )enum. nextEl eme nt() i total +- aeeoun t.getBalanee () ;
Dia 12
LISTAGEM 12.19 Bank.java (continuoção) }
return total: }
publiC int totalAccounts() I retur n accounts.size(): }
public void deposite String name , double ammount ) I Ba nkAccoun t account = retri eveAccoun t( name ): if( account ! - null ) I account.depositFunds( ammount ): } }
publi c double balance( Stri ng name ) { BankAccount account " retrieveAccount( name ) : if( account ! - nu11 ) { return account.getBa l ance{): }
return 0.0 ; }
private BankAccount retrieveAccount( String name ) ( return (BankAccount) account s . get( name ); } }
2.
LISTAGEM 12.20 Level . java pub li c fina l cla ss leve1 { pub1i C fi na1 publ ic f i nal publ ic fi nal pub l ic fi na l
st at ic stat ic static stat ic
level level level level
NOISE INFO WARNING ERROR
",w n.w n.w " n.w " " "
level ( level ( level ( leve 1 (
pr'iYate i nt level ; priva te String name; private leve1 ( int leve l, String name ) ( th i s . level " level : th i s . name " name;
O, "NOISE" ); 1 , "INFa " ) ; 2, "WARNING" ) ; 3, "ERROR" ) ;
Padrões avançados de projeto
287
LISTAGEM 12.20 level. j ava (continuação) }
public lnt getleve l() { return l evel; }
public String getName() ( return name ; } }
LISTAGEM 12.21 Errar.java pub li C class Error { pr;vate level level ; pub l iC Errar( Level level ) { th1s.level - level; }
public level getlevel() { return level; }
publl C String taString() ( return l eve l .getName(); } }
3. A so lução cons iste em uma abstrata Factory de conta bancária (escri ta como uma interface; entretanto, ela também pode ser uma classe abstraIa) e lima Concreta Factory de conta bancária. A Factory tem um método para criar cada tipo de con ta bancária. Essa Factory oculta os detalhes da instanciação e não necessariamente o subti po do objeto.
LISTAGEM 12.22 AbstractAccountFactory .java public interface AbstractAccountFactory { public CheckingAccount crea teCheckingAccount( doubl e initOepos it . int tran s , dauble fee )i
Dia 12
LISTAGEM 12.22
AbstractAccountFactory .java (continuação)
publiC OverdraftAccount createOverdraftAccount( double initOeposit. double rate ); publiC RewardsAccount createRewardsAccount( double initOeposit. double interest . doubl e min }; public SavingsAccount createSavingsAccount( double init6alance. double interestRate ); public TimedMaturityAccount createTimedMaturi tyAccount{ double init6alance . double interestRate. double feeRate ); }
LISTAGEM 12.23
ConcreteAccountFactory .java
public cl ass ConcreteAccountFactory implements AbstractAccountFactory I pub l ic CheckingAccount createCheckingAccount( double initOeposit . int trans. double fee ) I return new CheckingAccount{ i nitOepos i t . trans. fee ); }
public OverdraftAccount createOverdraftAccount( double initOeposit. double rate) ( return new OverdraftAccount{ initOeposi t . rate ); }
public RewardsAccount createRewardsAccount{ doub l e initOeposit. double interest. double min ) I retu r n new RewardsAccount{ initOeposit . i nterest . mi n ) ; }
publiC SavingsAccount createSavingsAccount{ double initBa l ance. double interestRate ) I return new SavingsAccount( initBa l ance. interestRate ) ; }
pub l iC TimedMaturityAccount createTimedMaturityAccount( double initBa l ance. doub l e interestRate. double feeRate ){ return new TimedMaturityAccount( initBalance. interestRate. feeRate ); } }
SEMANA
2
DIA 00 e programação da interface , . com o usuano A interface com o usuário (U I) forn ece a interface entre o usuário e seu sistema. Quase todo sistema moderno lerá alguma fonna de UI , seja grá fi ca, dirigida pela linha de comando ou mesmo baseada em telefone ou fala. (A lguns sistemas podem combinar todos os quatro tipos!). Em
qua lquer caso, você preci sa tomar um cuidado especial no projeto e implementação de suas interfaces com o usuário. Fe lizmente, a POO pode trazer as mesmas vantagens para sua UI que
apresenta para Outros aspectos do sistema. Hoje você aprenderá: •
Como a POO e a construção da UI se relacionam
•
Sobre a importância de uma UI desacoplada
•
Quais padrões o ajudam a desacoplar a UI
POO e a interface com o usuário Fundamental mente, o processo de projetar e programar interfaces com o usuário não é diferente do processo de projetar e programar qualquer outro aspecto de seu sistema. Talvez você precise aprender algumas novas APls para que possa construir suas Uls, mas no fi nal, você precisa aplicar na UI os mesmos princípios orientados a objetos que aplicaria nas outras partes do seu sistema.
Dia 13
NOTA
Você aprenderá com o encarar o desenvolvimento de UI do ponto de vista de um desenvolvedor. Como desenvolvedor, você projetará e implem entará as classes que constituem e suport am a interface com O usuário. A lição de hoje não abordará o assunto geral d o projeto de UI. O projeto de UI abrange todos os aspectos de como os recursos de um programa se tornam disponíveis para um usuári o. O assunto geral do projeto de UI está completam ente removido da programa ção e está m ais enraizado nas artes gráfi cas e na psicologia. O ACM Special Interest Group on Computer-Hum an Interaction (SIGCHI) é um recurso excelente de informações sobre projeto e usabilidade da UI.
A questão merece ênfase: ao projetar e programar s uas interfaces com o usuário, você deve aplicar em suas Uls os mesmos princípios de 00 que aplicaria no restante do seu sistema! Freqüentemente, as interfaces com O us uário são sim plesmente reunidas e jogadas no sistema como uma cogitação posterior. Em vez disso. o código de sua UI precisa ser tão orientado a obj etos quanto o códi go do restante do sistema. A implementação da UI precisa usar encapsulamento, herança e polimorfismo corretamente. Você também precisa cons iderar a UI enquanto realiza AOO e POO (Projeto O rientado a Objetos). Sem uma análisee projeto corretos, você pode perder certos requisi tos e verificar que escreveu uma UI que não é suficientemente fl exível para fornecer o nível desejado de funci onal idade ou uma UI que não pode se adaplar às alterações fut uras.
A importância das Uls desacopladas Você verá que o mesmo sistema freqUentemente exige diversas interfaces di ferentes, muitas vezes não relacionadas. Por exem plo, um sistema de abastec imento pode permitir que as pessoas façam pedidos pela Web, pelo telefone, através de PDA ou de um apl icativo local personalizado. Cada uma dessas interfaces se ligará ao mesmo sistema; entretanto, cada estratég ia se ligará ao sistema e apresentará as informações de sua própria mane ira. Você também verá que os requis itos da interface com o usuário podem se tornar um alvo móve l. Os sistemas am adurecem com O tempo, quando novos rec ursos são adi cionados e quando os usuários expõem áreas de deb iIidade. Em resposta, você precisará alua iizar continuamente a interface com o usuário para poder expor cada novo recurso e corrigir todos os defeitos. Essa real idade pede uma interface com o usuário cujo projeto seja fl exível e possa aceitar alterações prontamente. A melhor maneira de obter nexi bi lidade é projetando um sistema que seja completamente desacopiado de sua UI. Um projeto desacoplado pennite que você adicione qualquer UI no sistema e faça alterações nas Uls existentes, sem ter de fazer alterações correspondentes no sistema em si. Um projeto desacoplado tambêm permite testar os recursos do sistema, mesmo que você não te-
00 e prog ramação da interface co m o usuá ri o
291
nha lenn inado de desenvolver a UI. Além disso, um projetodesacoplado permite que você aponte precisamente erros que são da UI ou que são do sistema. Felizmente, a POO é a solução perfei ta para esses problemas. Isolando as responsabilidades corretamente, você pode di minuir o impacto das alterações em partes não relacionadas do sistema. Isolando funcionalidade, você deve consegui r adicionar qua lquer interface em seu sistema, a qua lquer momento, sem fazer alterações no sistema subjacente. O segredo é não incorporar o código da UI dentro do próprio sistema. Os dois devem ser separados. Vamos ver um exemplo que desacopla incorretamentc a UI. A Listagem 13. 1 mostra como lIào se deve escrever urna UI.
LISTAGEM 13.1 Vis ualBankAccou nt.ja va i mport import import i mport i mport i mport i mpo rt
javax . swing.J Pane1i ja vax . swing.J l abel; java.awt.BorderLayout ; java.awt.event.Actionl istener ; java.awt.event.Act ionEven t; javax.swing. JText Field ; javax.swing.JButton i
publ i c cl ass V1sualBankAccount extends JPane l implernents Ac t ionlis t ener {
II dados privados private doub l e balance ;
II el ementos da UI private private private private
Jlabel JTextfield Jbutton Jbutton
balancelabel = new JLabelO i amountField z new JTextField( 10 }; deposHButton = new Jbu tton( "OeposH" ) i wHh drawButton = new Jbutton( "Withdraw" ) ;
public VisualBa nkAccoun t( double initOepos i t ) ( setBala nce( i nitOeposit )i buil dUIO j
I
1/ manipula os eventos dos botões pub l ic voi d actionPerfonmed( ActionEvent e ) ( if( e.getSource() aa depositButton ) { doubl e amoun t • Ooubl e. pa rseOoub le ( amountFi eld.getText(} l j depositFunds( amou nt }; }else if ( e.ge t Source() .~ wi t hdrawBut t on } ( double amount = Ooub l e.parseOouble( amountFiel d.getText() )i if( amoun t > getBa l ance() ) { amount • getBala nce() ;
Dia 13
LISTAGEM
13.1 VisualBankAccount.java (continuação )
I withdrawFunds( amount ):
I I pr ivate void bUildUI() I se tLayout( new 80rderLayout() ):
II constró i a tela JPa ne l buttons .. new Jpanel ( new BorderLayout() ): JPanel balance .. new Jpanel ( new BorderLayout() ): buttons.add( deposit8utton , BorderLayout .WEST ): buttons.add( withdrawButton, BorderLayout.EAST l: balance.add( balanceLabe l , 80rderLayout.NORTH ); balance.add( amountField, Borderlayout.SOUTH l: add( balance, BorderLayout.NORTH l : add( buttons , BorderLayout.SOUTH );
II configura os ca llbacks para que os botões fa çam algo II o botão de depósito deve chamar depositFunds() depositButton.addAc t ionli stener( this ): II o botão de saque deve chamar withdrawFunds withdrawButton.addActionListener( this ):
I public void depositFunds( double amount ) I set8alance( get8alance() + amoun t );
I publiC double getBalance() { return balance :
I protected vo i d setBalance( double newBalance ) { balance · newBalance; balanceLabel.setText( "Balance: " + balance):
I public double withdrawFunds( double amount } I setBala nce( getBalance() - amount }; return amount;
I
I
00 e prog ra m ação da inte rface com o usuário
293
VisualBankAccount usa a biblioteca Swing d a linguagem Java para se apresent ar. Toda linguagem 00 tem bibl iotecas para cri ar e exibir i nterfaces gráficas com o usuári o (GUI). Não se preocupe se você não ent ender t udo q ue há neste exemplo. ~ importante apenas que você entenda o significado geral do exemplo. Um pouco de experiência pOde ajudar. Swing fo rn ece uma classe para cada elemento importante da GUI, como botões (J Buttonl, rótulos (JLabel) e campos de ent rada (JTextfield). Você coloca esses elementos juntos, d entro de painéis (JPanel), para construir sua UI. Cada elemento da GU I t em um m étodo addAct ionLi stener(). Esse método permite que você registre um objeto que implementa a interface Act i onL is tener como u m callback. Quando o elemento da GU I gera r um event o (norm almente como resu ltado de um clique de mouse), ele informará cada um de seus receptores de ação. Aqui, VisualBankAccount at ua como receptor. Quando recebe u m event o de um dos botões, ele executa a ação corr et a e, em seguida, deposita o u saca di nheiro.
Vi sua 1BankAccount fomece Ioda a funcionalidade da classe BankAccount, apresentada em lições anteriores. Vi sual BankAccount também sabe como se apresentar, como se vê na Figura 13. 1. FIGURA 13.1 VisualBankAccount dentro
de um freme.
Quando você digitar um va ia r e clicar no botão Deposit ou Withdraw, a conta bancária exlrairá o va lor do campo de entrada e chamará seu método wi thdrawFunds () ou depas i tFund s () , com o valor. Como VisualBankAccount é um Jpanel ,você pode incorporá-lo em qualquer OU I Java. Infelizmente, a UI não está desacoplada da classe de conta bancária. Tal acoplamento forte toma impossível usar a conta bancária em outras fonnas de interfaces com o usuário ou para fornecer uma UI diferente, scm ler de allerar a própria classe Vi sua 1BankAeçount, Na verdade, você precisará criar uma versão separada da classe para cada tipo de UI dentro da qual que ira que ela participe.
Como desacoplar a UI usando o padrão Model View Controller o padrão de projeto MVC (Modcl Vicw Comroller) fornece uma estratégia para o projeto de interfaces com o usuário que desacoplam com pletamente o sistema subjacente da interrace com o usuário.
Dia 13
NOTA
MVC é apenas uma estratégia para o projeto de int erfaces com o usuário orientadas a objetos. Existem outras estratégias válidas para o projeto de interface com o usuário; entretanto, a MVC é uma estratégia testada que é popular no setor do software. Se você acabar realizando o trabalho da interface com o usuário, especialm ente em relação ã Web e ao J2EE da Sun, encontrará o MVC. O DocumentNiew Model, popularizado pelas Microsoft Foundation Classes e o padrão de projeto PAC (PresentationAbstraction Controf), forn ecem alterna· tivas à MVC. Veja o livro Pattern-Oriented Sohware Architecture A Sysrem of Parterns, de Frank Buschmann et ai, para uma apresentação comple ta dessas alternati vas.
o padrão MVC desacopla a UI do sistema, dividindo o projeto da UI em três partes separadas: • o modelo, que represe nta o sistema •
O modo de v isua lização, que exibe o modelo
•
O controlador, que processa as entradas do usuário
Cada parte da tríade MVC tem seu conjunto próprio de responsabilidades exclusivas.
o modelo o modelo é responsável por fornecer: •
Acesso à funcionalidade básica do sistema
•
Acesso às informações de estado do sistema
•
Um sistema de noti ficação de mudança de estado
O modelo é a camada da tríade MVC que gerencia o comportamento básico e o estado do sistema. O modelo responde às consultas sobre seu estado a part ir do modo de visualização e do controlador e aos pedidos de mudança de estado do controlador. Um sistema poda ter muitos modelos diferentes. Por exemplo, um sistema de banco pOde ser constituído de um modelo de con ta e um modelo de cai xa. Vários modelos pequenos repartem melhor a responsabi lidade do que um único modelo grande. Não deixe o t ermo modelo confundi-lo. Um modelo é apenas um objeto que representa o sistema.
o controlador é a camada da tríade MVC q ue interpreta a entrada do usuário. Em resposta à entrada do usuário, o controlador pode comandar o modelo ou o modo de visualização para que mude ou execute alguma ação. O modo de visualização é a camada da tríade MVC que exibe a representação gráfica a li text ual do modelo. O modo de visual ização recupera todas as infonnações de estado a respeito do modelo, a partir do modelo.
00 e p rog ra m ação da inte rface com o u s u ário
295
Em qua lquer caso, o modelo não sabe absolutamente que um modo de visualização ou controlador está fazendo uma chamada de método. O modelo só sabe que um objeto está chamando um de seus métodos. A única conexão que um modelo mantém com a UI é através do sistema de noti ficação de mudança de estado. Se um modo de vis ualização o u controladorestiver interessado na noti ficação de mudança de estado, ele se registrará no modelo. Q uando o modelo mudar de estado, percorrerá s ua lista de objetos registrados (freqUentemente chamados de receptores ou observadores) e informará cada objeto da mudança de estado. Para construir esse sistema de notificação, os modelos normalmente empregarão o padrão Observer. o padrão Observer O padrão Observer f orn ece um projet o para um mecanismo de publicação/assi natu ra entre objetos. O padrão Observe r perm ite que um objeto (o observador) registre seu interesse em outro objeto (o observável). Quando o o bservável q uiser notifica r os seus observadores de uma alteração, ele chama rá um m étodo update() em cada observador. A Listagem 13.2 define a interface Observer. Todos os observadores que quiserem se registrar com O objeto observável devem im plementar a interface Observer. LISTAGEM 13.2
Observer . jovo
publlc interface Observer I publ ic vold update(); } Um observável fornecerá um m ét odo, através d o q ual os observadores podem regis· trar e anular o registro de seu interesse em atualizações. A Listagem 13.3 apresenta um a classe que im plementa o padrão Observer.
Implementando o modelo Aplicar o padnl0 MVC no Vi suai BankAccount pode torná-l o mu ito mai s nexíve l. Vamos começar reti rando a funcio nalidade básica do 'sistema' do código de exibição, para podermos criar o modelo. A Listagem 13.3 apresenta a funcionalidade bás ica da conta bancária LISTAGEM 13.3
BankAccountModel . java
i mpo r t java.util.ArrayList õ import java.util.lterator õ public class BankAccountModel {
II
dados privados private double balance;
o modelo.
Dia 13
LISTAGE M
13.3 BankAccountModel .java
prlvate ArrayList l isteners
=
new Arraylist();
public BankAccountHodel( doub le initOeposit } { setBala nce( initOeposlt }; J
public void depositFunds( double amount ) { setBalance( get8alance() + amount ): J
publi C double getBalance() { return balance: J
protected vo id setBalance( double newBalance ) { balance· newBalance: updateObservers(): J
public double withdrawFunds( double amou nt } { if( amount > getBalanceO ) { amoun t • get8a lance();
J set8alance(get8alanceO - amount ); return amount; J
publiC void register( Observe r o ) { receptores.add( o ); o.updateO; J
publi c void deregister( Observer o ) í receptores.remove( o ); J
private void updateObservers() { lterator i • receptores.iterator(); while( i .hasNextO ) í Observer o " ( Observer) i .next(); o.update() ;
J J
J
0 0 e prog ramação da interface co m o usu á ri o
297
A BankAccount Mode 1 é semel hante à classe Ba nkAccount original, apresentada em lições anteriores; enlretanlO, o mode lo também usa o padrão ObselVer para adicionar suporte ao registro e atual ização de objetos que estejam interessados na not ifi cação de mudança de estado. Você também notará que essa classe contém toda a lógica do sistema. Agora, wi thdrawFunds() , veri fi ca o valor do saq ue para garanti r que ele não seja maior do que o saldo. E fu ndamental manter tais regras de domfn io dentro do modelo. Se essas regras fossem obedecidas no modo de vi sualização ou no controlador. cada modo de visualização e controlador dcsse modelo precisaria manter a regra. Confonne você já viu, ter cada modo de visualização ou controlador impondo essa regra é uma mistura de responsabi lidades e é algo propenso a erros. A mistura de responsabil idades também torna di fic il mudar a regra, pois você precisa mudá-Ia em cada lugar. Os relacionamentos com capacidade de substituição também tornam a colocação de regras no modo de visuali zação uma prática perigosa. Devido aos re lacionamentos com capacidade de substituição, um modo de visualização funci onará para qualquer subclasse; entretanto, se você colocar as regras de saque no modo de visua lização, este não funcionará mais para um objcto OverdraftAccount. Isso porque os objetos OverOraftAccount penn item que você saque valores maiores do que o saldo corrente.
o modo de visualização O modo de visualização é responsável por: • • •
Apresentar o mode lo para o usuário Registrar no mode lo noti fi cação de mudança de estado Recuperar informaçõe s de estado do modelo
O modo de visualização é a camada da tríade MVC que ex ibe in formações para o usuário. O modo de visuali zação obtém info rmações de exibição do modelo, usando a interface pub lica deste, e também se registrará no modelo para que ele possa ser infonnado da mudança de estado e se atualize de acordo.
NDTA
Um un ico modelo pode ter mu itos modos de visualização diferentes.
Implementando o modo de visualização Agora que existe um modelo, é hora de implementar o modo de visualização da conta bancária. A Listagem 13.4 apresenta o modo de visualização de BankAccountModel. LISTAG EM
13.4 BankAccountView.java
impo rt javax.swing.JPanel; impo rt javax. swing .J l abe l;
Dia 13
LISTAGEM
13.4 BankAccountView. java (continuação)
import java.awt.Borderlayout; import javax.swing.JTextField; import javax.swing .JButton; public class BankAccountView extends JPanel implements Observer I public final static String DEPOSIT "-Deposit"; public final static String WITHDRAW " ·Withdraw"; private BankAccountModel model; private BankAccountController cont rol ler;
II
El ementos da GUI . aloca tudo prev i amente para evitar valores nulos private JButton depositButton " new Jbu t ton( DEPOSI T ) ; private JButton withdrawButton • new Jbutton( WITHDRAW }; private JTextField amountField • new JTextFie l d() ; private Jlabel balancelabel "new JlabelO; pub l ic BankAccountView( BankAccountModel mode l ) I th i s.model z model o th i s.model.register( this l i attachController( makeCon t rol l er() ) ; buildUI O; J
II
chamado pelo mode l o quando este muda publ ic void updateO I balancelabe l .setText( "Balance: " + model.getBalanceO ); J
II
dá acesso ao valor introduzido no campo public double getAmount() 1 II supõe que o usuári o in t roduziu um núme ro válido ret urn Oouble.parseDouble( amoun t Fie l d. getText() ) ; J
II
insere o controlador dado no modo de vi sua l ização . permite que um objeto externo configure o controlador pub l ic void attachController( BankAccountCont roller controller ) I II cada modo de visualização sõ pode ter um cont rolador; portanto. remove o antigo primeiro if( th i s.control ler Jw nu l l ) ( Il remove o contro l ador antigo depositButton.removeActionlistener( cont roller ); withdrawButton . removeActionlistener( controller ); J
00 e prog ra m ação da inte rface com o usuário
LISTAGEM 13 .4
299
BankAccountView. java (continuação)
thiS.contro11er control l er; deposi tButton.addActi onListener( contro1ler ); withdrawButton . addActi onListener( contro1 l er lj I
}
protected BankAccountContro11er makeContro11er() { return new BankAccountController( this , mode1 )j }
private void buildUIO { setLayout( new BorderLayout() ) i
II as socia cada botão a uma st r i ng encomendada II o controlador usar! essa stri ng para i nterpretar eventos depositButton.setActionCommand( DEPOSIT )i wit hdrawButton.setAct ionCommand( WITHORAW l;
II constrói a tel a J Panel buttons • new Jpanel ( new BorderlayoutO ) i J Panel balan ce· new Jpanel( new Borde rlayout() ) j buttons .add( depositButton, BorderLayout.WES T lj buttons. add( withdrawButton , BorderLayout.EAST }; balance.add( balanceLabel, BorderLayout.HORTH ); ba lance.add( amountField, Borderlayout.SOUTH ); add( balance , Borderlayout . NORTH ); add( buttons , Borderlayout.SOUTH ) ; } }
o construtor dc BankAccountV iewaceita uma refe rência para um BankAccount Model. Na criação, BankAccountView se regi stra no modelo, cria e se anexa ao seu controlador e constrói sua UI. O modo de visua li zação usa o modelo para recuperar todas as informações de que necessita para a exibição. Quando saldo mudar, o mode lo chamará o método updateO do modo de visual ização. Quando esse método for chamado, o modo de visualização atualizarn sua tela de sa ldo.
°
Normalmente, um modo de visualização criará seu próprio controlador, como BankACcountView faz dentro do método factory makeControll erO. As subclasses podem sobrepor esse método factory para criar um controlador diferente. Dentro do método attachControll er(), o modo de visualização registra o controlador nos botões de depósito e saque, para que o controlador possa receber eventos do usuário.
Você notará, entretanto, quc o modo de visualização primeiro remove qualquer control ador previamente existente. Um modo de visual ização normalmente lerá apenas um controlador.
Dia 13
Você também notará que attachControll er() é um método publico. Usando esse método, você pode trocar de cOluroladorsem ter que faze r uma subclasse do modo de visualização. Esse método pennite que você crie diferentes cont roladores e passe-os para o modo de visualização. O controlador que você verá na próxima seção interpretará os eventos do usuário exatamente como Vi sua 1BankAccount os interprelava (apenas com uma ligeira mod ificação). Nada o impede de escrever controladores que bloqueiem o usuário ou limitem o que ele pode fazer. Ao contrário de um modo de visuali zação, que só pode ter um controlador por vez, um modelo pode ter muitos modos de visualização diferentes. A Listagem 13.5 apresenta um segundo modo de visualização para a conta bancária. LISTAGEM
13.5 BankAccount CLV . java
pub l i C class BankAccountCLV implements Observe r { private BankAccountModel model : publ ic BankAccountCLV( BankAccountModel mode l ) { this.model • mode l : th i s.mode l. register( this ) ; }
pub l ic voi d updateO { System.out.println(
~Current
Balance: $" + model.getBalanceO ) :
} }
BankAccountCLV si mplesmente imprime o saldo na li nha de comando. Embora esse comportamento seja si mples, BankAccountCLV é um modo de visualização alternativo para BankAccountModel. Você notará que esse modo de visualização não ex ige um controlador, pois ele não acei la eventos do usuário. Nem sempre você precisa fornecer um contro lador. DICA
Um m odo de visualização pode nem sempre aparecer na tela. Tome como exemplo um processador de textos. O modelo do processador de textos controlará o texto introduzido, a formatação, notas de pé d e página etc. Um m odo de visualização exi birá o texto no editor principal; entretanto, outro modo de visual ização poderá converter os dados do modelo para o formato POF, HTMl ou Post script e, em seguida, gravá-lo em um arquivo. O modo de visualização que gra va em um arquivo não aparece na tela; em vez disso, o m odo de visualização exibe em um arquivo. Outros programas podem ent ão abrir, ler e exibir os dados do arqu ivo.
00 e prog ramação da interface com o usuário
301
o controlador o controlador é responsável por: Interceptar os eventos do usuário do modo de visualização Interpretar o evento e chamar os métodos corretos do modelo ou modo de visualização Regist rar-se no modelo para notificação de mudança de estado, se estiver interessado
• • •
O controlador atua como a cola entre o modo de visualização c o mode lo. O controlador intercepta eventos do modo de visualização e depois os transrorma em pedidos do modelo ou do modo de visualização.
NOTA
Um m odo de visualizaçào t em apenas um controlador e um controlad o tem apenas um modo de visualizaçào. Alguns modos de visualizaçào permitem que você config ure seu controlador diretamente.
Cada modo de vi sualização tem um controlador e toda a interação com o usuário passa por esse controlador. Se o controlador ror dependente das inronnaçôes de estado, ele também será registrado no modelo para notificação de mudança de estado.
Implementando o controlador Com um modelo e um modo de visualização já criados, resta apenas construir o controlador de BankAccountView. A Listagem 13.6 apresenta o controlador do modo de visualização. LISTAGEM 13,6 BankAccountControll er . java import java.awt.evento.Actionlistener; import java . awt.evento.Act ionEvent; public class BankAccountCon troller imp l ements Actionlistener { private BankAc countView view ; private BankAccountModel model o publiC BankAccountController( BankAccountView view, BankAccountModel mode l )
I this.view ~ view; this. mode l • model; }
publiC void actionPerfonmed( ActionEven t e ) { Str i ng command z e.getActionCommand() ; double amount • view , getAmount(); if( command .equals( view.W ITHDRAW ) ) {
Dia 13
LISTAGEM
13.6 BankAccountController.java (continuação) mode l. withdrawFunds( amount }; }else 1f( command.equals( view.OEPOSIT ) } { model .depositFunds( amount }; J
J
J Na construção, o controlador aceita uma referência para o modo de visualização e para o modelo. O controlador usará o modo de visuali zação para obter os valores introduzidos no campo de entrada. O contro lador usará o modelo para realmente sacar e depositar dinheiro na conta. BankAccountControlle r em si é muito simples. O controlador implementa a interface Actionli stener de modo que possa receber eventos do modo de visualização. O modo de visuali zação cuida do regi stro do cont rolador para eventos, de modo que o controlador não precisa faze r nada a não ser interpretar eventos, quando os receber. Quando o controlador recebe um evento, ele verifica o comando do evento para determinar se o evento é um saque ou um depósito. Em qualquer caso, ele faza chamada correspondente no modelo. Ao contrário do VisualBankAccount original, o controlador só precisa chamar depositFunds() ou wi thdrawFunds () . Ele não precisa mais se certificar de que o valor do saq ue não seja maior que o saldo, pois agora o modelo cuida desse detal he do domin io.
Reunindo o modo de visualização e o controlador A Li stagem 13.7 apresenta um pequeno metodo main que reÍlne o modelo e dois modos de vi suaIização. O método ma i n não precisa fazer nada no controlador, poi s o modo de visualização cuida desse detal he. LISTAGEM
import import import import
13.7 Reuni ndo o modelo , modos de visualização e o controlador
java.awt.eve nto.Windowlistener; java.awt.eve nto.WindowAdapter i java.awt.evento.WindowEvent; javax.swing.J Frame i
public class MVCOriver ( pub l i c static void main( St ring [] args ) { BankAccountModel model • new BankAccountModel( 10000.00 }; BankAccountView view " new BankAccountView( model ); BankAccountCLV clv ~ new BankAccountC l V( mode l )i J Fr ame frame
z
new JFrame();
00 e prog ramação da interface com o usuário
LISTAGEM 13 .7
303
Reunindo o modelo, modos de visualização e o controlador (cont . )
WindowAdapter wa • new WindowAdapter() { publ1c void windowClosing( Wi ndowEvent e ) I Sys tem.e xit( O l i
I frame.addWind owlistener( wa )i frame .getContentPane().add( view li frame . pack() ; frame. showO i
I I
o pri meiro método ma i n cria uma instância do modelo. Quando o método mai n tem o modelo, pode então criar vários modos de visualização. No caso de BankAccountView, o método mai n tam· bém preci sa incorporar o modo de visualização em um quadro, para que o modo de visualização possa ser exibido. A Figura 13.2 ilustra a saída resultante. FIGURA 13 .2
Um ",odeio de COI/Ia bol/cória com ,·órias modos de I"isllali:aç(;Q.
Se você executar MVCOri . . er, verá dois modos de visua lização separados no mesmo modelo. Usando o padrão MVC, você pode criar quantos modos de visua lização de seus modelos subja· cernes preci sar.
Problemas com o MVC Assim como acontece com qualquer projeto, o MVC tem suas deficiências e também seus pon· tos críticos. Os problemas incluem: • • •
Uma ênrase nos dados Um rorte acoplamento entre o modo de visualização/controlador e o modelo Uma oportun idade de ineficiência
Dia 13
A gravidade dessas de fi ciências depende do problema que se está resolvendo e seus requisi tos.
Uma ênfase nos dados Em uma escala 00 de pureza, o padrão MVC não se classifica próximo ao topo, devido a sua ênfase nos dados. Em vez de pedir a um objeto para que faça algo com seus dados, o modo de visualização pede seus dados ao modelo e depois os ex ibe. Você pode di minuira gravidade desse problema, exibindo apenas os dados que retirado modelo. Não reali ze processamento adicional nos dados. Se você se achar realizando processamento extra nos dados, após recuperá-l os ou antes de chamar um método no modelo, si'lo boas as cha nces de que o modelo deve faze r esse trabalho para você. Existe uma linha tênue entre faze r muito e fazer o que é necessári o nos dados. Com o passar do tempo, você aprende rá a diferenciar entre fazer muito com os dados e fazer ape nas o que é necessário.
DICA
Se você verificar que repete o mesmo CÓd igo em cada modo de visualização, considere a colocação dessa lóg ica no modelo.
Evitar o padrão MVC unicamente por motivos de pureza pode insultar algumas realidades da programação. Tome como exem plo um site Web. Algumas empresas impõem uma scparaçi'lo clara entre apresentaçi'lo (o modo de visualização) e a lógica corporati va (o mode lo). A imposição de tal separação tem uma base corporativa vá lida: os programadores podem programar e o pessoal ligado ao conteúdo pode escrever conteúdo. Retirar o conteúdo da camada de programação signi fi ca que quem não for programador pode criar conteúdo. Impor o conteúdo na camada de programação significa que a pessoa que está escrevendo conteúdo deve ser um programador ou que um programador prec isa pegar o conteúdo e incorporá- lo dent ro do código. É muito mais dificil atualizar um si te, se você incorporar conteúdo no cód igo. A realidade também di z que os requisitos não são defin iti vos, muito menos conhecidos. Novamente, o site Web se constitui em um exemplo excelente. Um site Web prec isa gerar código HTM L para ex ibir em um navegador da Web. E quanto aos POA s, telefones celulares e outros di spositivos de exibição? Nenhum deles usa I-ITML básica. E quanto daqui a se is meses? São boas as chances de que vão existir outras formas de exibição. Para satisfazer requisitos desconhecidos, você precisa de um projeto que seja fl exível. Se você tiver um sistema muito estático, com requisitos bem definidos, poderá usar uma allemativa, como um PAC. Se você não tiver sorte sufici ente para ter tais requisitos claros, precisará considerar o MVC.
Acoplamento forte
o modo de visualização e o cont rolador são forteme nteacoplados à interfa ce pública do modelo. As alteraçõcs na interfa ce do modelo exigirão alterações no modo dc visualização e no controlador. Quando lisa o padrão MVC. você supõe implicitamente que o modelo é estàvel, e é provável
00 e prog ramação da interface com o usuário
305
que o modo de visualização mude. Se esse não for o caso, você precisará escol her um projeto diferente ou estar preparado para fazer alterações no modo de visualização e no controlador. O modo de visualização e o controlador também são intimamente relacionados entre si. Um controlador é quase sempre usado exclusivamente com um modo de visualização especifico. Você pode tentar encontrar reut il ização através de um projeto cuidadoso; mas mcsmo que não encontre, o padrão MVC ainda fornece uma boa divisão de responsabil idades entre os objetos. A 00 não é simplesmente um meio de reuti lização.
Ineficiência Você deve tomara cuidado de evitar ineficiências ao projetar e implementar uma UI baseada em MVC. As ine liciências podem aparecer no sistema em qualquer parte da tdade MV C. O modelo deve evi tar a propaguçi'ío de notificações de mudança de estado desnecessárias para seus observadores. Um modelo pode enfi leirar notificações de mudança relacionadas para que uma notificação possa sign ilicar muitas mudanças de estado. O model o de evento A WT (Abstract Window Toolkit) da linguagem Java usa essa estratégia para redesenhar a tela. Em vez de redesenhar após cada evento, A WT enfi leira os eventos e realiza uma única operação para redesenhar.
Ao projetar o controlador e o modo de visualização, talvez você que ira considerar a colocação dos dados na memória cache, caso a recuperação de dados do modelo seja lenta. Após uma notificação de mudança de estado, recupere apenas o estado que mudou. Você pode aumentar o padrão do observador para que o modelo passe um identificador para o método update(). O modo de visualização pode usar esse identificador para decidi r se precisa ou não se atualizar.
Resumo A interface com O usuário é uma parte importante de qualquer sistema. Para alguns, ela pode ser a única parte do sistema com a qual eles interagem; para esses, a UI ri o sistema. Você sempre deve encarar a análise, o projeto e a implementação da UI exatamente como encara qua lquer outra parte do sistema . Uma UI nunca deve ser uma cogitação posterior ou algo co locado no sistema no últim o momento. Embora existam muitas estratégias para o projeto da UI, o padrão MVC fornece um projeto que oferece Oex ibilidade, desacoplando a UI do sistema subjace nte. Mas, assim como acontece com qualquer outra decisi'lo de projeto, você ai nda precisa ponderar os prós e contras do MVC, antes de decidir utilizá- lo. O MVC não o exime das realidades de seu sistema.
Perguntas e respostas P Sua classe BankAccountModel contém toda a lógica do sistema. O modelo sempre precisa conter a lógica ou ele pode atuar como um gateway para o sistema real?
Dia 13 ,
R Depende. As vezes, o modelo agirá como um gateway para o sistema; outras vezes, o modelo será incorporado dentro do sistema real. Tudo se resume a uma decisão de projeto. Em qualq uer caso, a UI não tem meios de saber se o modelo alua como um gateway ou não. P Na Listagem 13.6, você escreveu: i f( command.equals( vlew.WITHORAW ) ) I model.withdrawFunds( amount ) ; I else if( command.equals( view.DEPOSIT ) ) I model.depositFunds( amount ); J
Isso não é lógica com estruturas condicionais? Achei que você tinha dito que lógica com estruturas condicionais é considerada 'má' 00.
R Si m. Esse é um exemplo de lógica com estruturas condicionais. Para manter esse exemplo simples, decid imos tornar o controlador um Act i onL is tener que manipulasse os dois eventos. Em uma implementação rea l, você pode evi tar a lógica com estruturas condicionais capturando o evento original dentro do próprio modo de visualização e fazendo com que o modo de vi suali zação gere seus próprios eventos persona lizados. Por exemplo, o modo de visualização poderia gerar eventos de depós ito e saq ue. O cont rolador poderia receber cada um desses evelllOS separadamente. Talvez o controlador implementasse os métodos depos i tPerformed () e wi thdrawPerformed (). O modo de visualização chamaria o método correto no control ador, dependendo do evento; assim, não haveria mais nenhuma estrutura condicional, mas um exemplo muito mais di ficil de eruender. P Certo. Sua resposta anterior me fez sentir um pouco melhor. Mas se você implementar o controlador conforme explicado anteriormente, o modo de visualização não terá de executar lógica com estrutura condicional para descobrir qual botão chamou o evento?
R Não. O modo de visuali zação pode ev itar a lógica com estruturas cond icionais fornece ndo um receptor separado para cada botão. Quando existe uma correspondência de um para um entre um elemento e seu receptor, você não precisa usar lógica com estrut uras condi cionais para descobrir onde o evento se origina. (Veja no Exercicio 2 a im plementação alternativa.)
Workshop As perguntas e respostas do teste são fornecidas para seu melhor entendimento. Veja as respostas no Apêndice A, "Respostas".
00 e prog ramação da interface com o usuário
307
Teste I. Como a análise, o projeto e a implementação da UI são diferentes do restante do sistema? 2. Por que você deve desacoplar a UI do sistema s ubjacente? 3. Qua is são os tres componentes da tríade MVC? 4. Quais são as duas alternativas para o padrão MVC? 5. Descreva as responsabi lidades do modelo. 6. Descreva as responsabi lidades do modo de visua li zação. 7. Descreva as responsabi lidades do controlador. 8. Quantos modelos um sistema pode ter? Quantos modos de visual ização um modelo pode ter? Quantos controladores um modo de visualização pode ler? 9. Quais ine fi c iências você deve evitar ao usar o padrão MVC? 10. Quai s suposiçõcs o padrão MVC faz ? I I. Qua l é a hi stória do padrão MVC? (Note que esta pergunta exige que você reali ze uma rápida pesquisa na Web.)
Exercícios I. A Listagem 13.8 apresenta uma classe Employee. Altere a classe Empl oyee de modo que ela possa registrar e reti rar o registro de receptores, assi m como informá-los de alterações de estado. A Li slagem 13.2 apresenta uma interface Obse r ver que você pode usar para este exercic io. LISTAGEM
13.8 Emp l oyeeMode l. java
publiC abs tract class Employee ( private Stri ng first_name : private Stri ng la st_name : pr;vate double wage: public Employee(String first_name , String last_name.double wage) ( this.first_name = first_name; th i s.last name · l ast_name : th i s.wage • wage : } publ ic double getWage() ( return wage: }
Dia 13
LISTAGEM 13.8 Empl oyeeMode l. java (continuação)
publiC void setWage( double wage ) ( this . wage • wage: J publiC String getFi r stName() ( return first_name : J public String getlastName(){ return la st_name ; J public abstract double calcu latePay{): publ ic abstract double calculateBonus() : publiC void pri ntPaycheck() ( String full _name • last_name + ", " + first_name; System . ouLprintln( MPay: " + full name + " $" + calculatePayO ) :
J J
2. Usando as listagens 13.9 e 13. IOcomo ponto de partida, escreva um novo BankAccountControl 1er q ue implemente a nova interface BankActiv i tyl i stener e man ipule os eventos sem lógica com estruturas condicionais. A Li stagem 13.9 apresenta um BankAc t i v i tyEvent e seu BankAc t i v i tyl is tener correspondente. LISTAGEM 13.9 BankActivitylistener.java e BankActivityEvent.java
pub l ic inter fa ce Ba nkActivitylis tener { public vold withdrawPerformed( BankActivityEvent e ): pUbli c vo i d depositPerformed ( BankActivityEvent e ): J
pub l ic c l ass BankActivityEvent ( private double amount: pUblic BankActivityEvent( doubl e amount ) { thls.amount • amount;
00 e prog ramação da interface com o usuário
LISTAGEM 13 .9
309
BankAc tivitylistene r.ja va e BankAct iv ityEve nt. java (continuação)
}
public double getAmount() { return amount: } }
Li stagem 13. IOapresenta lima BankAccount Vi ew atual izada. Esse BankAccountVi ew intercepta os eventos ActionEvent do botão e encarnin ha o novo evento BankActivityEvent para o controlador. A
LISTAGEM 13 .10
import i mport i mport i mport i mport i mpo rt i mpo rt import
BankAccountView.java
javax .swlng .JPanel : javax. swing.J l abel : java.awt .Bo rderlayout : javax .swing.J Text Field : javax.swing.JButton : java.util.Arraylist: java.awt.event.Actionlistener; java.awt.event .Ac t ionEvent;
publiC class BankAccountView extends JPane l implemen t s Obse rver I public final static String DEPOSIT "·Deposit" : public final static String WITHDRAW " ·Withdraw"; private BankAccountModel mode l : private BankAccountController cont rol leri
II Elementos da GUI , aloca tudo previament e para ev i tar valores nulos private private private private
J8utton depositButton - new Jbutton( DEPOSI T ): J8utton wit hdrawBu tton - new Jbu t ton( WITHDRAW )i JTextFleld amountField ~ new JTextFie l d() : ~ new Jlabel (): Jlabel balancelabel
pub l ic 8ankAcceuntView( BankAccountModel mede l ) ( this.model • mode l: th i s. model.register( this ) : attachController( makeControl l er() ) : bu ildUI O; }
II chamado pelo mode lo, quando este muda
Dia 13
LISTAGEM 13.10 BankAccountView .java (continuação) publiC vo id update() { balancelabel.setText( "Ba lance: •
+
model.getBalanceO );
J
II
codifica o cont rol ador dados no modo de visualização. permite que objet o externo con fi gure o controlador public voi d attachController ( BankAccount Controll er controller ) { this . control ler • cont roller: J
pro tected Ba nkAccou ntController makeCont rolle r () { return new BankAccountController( this. model ); J
II
dã acesso ao valor i ntrod uzido no campo private double getAmoun t() { II pres supõe que o usuário i ntroduz i u um número válido return Double.parseDouble( amount Field .getText() ) ;
J private void fireDeposHEventO { BankActivityEvent e • new BankActivityEvent ( getAmount() ); control l er.depositPerformed ( e ) : J
pr ivate voi d fireWHhdrawEventO { BankActivityEvent e " new BankActiv i tyEvent ( getAmount() ) ; control l er.withdrawPerformed( e )i J
private void build UI () { setLayout( new BorderLayoutO }i
II
assoc ia cada botáo a uma string de encomenda depositButton . setAc ti onCommand( DEPDS IT li withdrawButton.setActionCommand( WI THORAW l i II const rói a tela JPa ne l buttons • new Jpa nel ( new BorderLayoutO ) i JPa nel balance" new Jpanel ( new BorderlayoutO l; buttons.add( depos itButton . Borde rlayout.WEST l: but t ons . add( wit hdrawBu t ton. Borderlayout.EAST ); bal ance .add( ba l ancelabel, BorderLayout.NORTH )i bala nce.add( amountF ield . BorderLayout. SOUTH }; add( balance . Borderlayout.NORTH ) ;
00 e prog ramação da interface com o usuário
LISTAGEM 13.10 BankAccountView .java (continuaçõo) add( buttons. BorderLayout.SOUTH ); depositButton.addAct ion Li stener( new ActionListenerO I publi c voi d actionPer formed( Action[vent e ) { f ireOeposit[vent(); J J
J; withdrawButton.addAction Listener( new ActionListenerO I public void actionPerformed( Act i onEvent e ) { fireWithdraw[vent() ; J J
J; J J
3 11
,
PAGINA E
B
NCO
SEMANA
2
DIA Construindo software confiável através de testes Quando você usa programação orientada a objetos, se esforça para escrever software natural, confiável, reutilizável, manutenível, extensível e oportuno. Para atingir esses objetivos, você deve entender que a 'boa' 00 não acontece por acidente. Você deve atacar seus problemas atra-
vés de uma análise c um projeto cuidadosos, e ao mesmo tempo nunca perder de vista os princípios básicos da POO. Somente enlãoa 00 pooe começar a cumpri r suas promessas. Mesmocom uma análise e um projeto cuidadosos, en tretanto, a POO não é uma fónnuta mágica. Ela não o
protegerá de seus próprios erros ou dos erros dos outros. E erros acontecerão! Para produzir software confiáve l, você prec isa testá-lo. Hoje você aprenderá: •
Onde os testes entram no processo iterativo
•
Sobre os diferentes tipos de testes
•
Como testar suas classes
•
Como testar software incompleto
•
O que você pode fazcr para escrever cód igo mai s confiável
•
Como tornar seus testes mais eficazes
Dia 14
Testando software 00 A 00 não evitará que erros aconteçam em seu software. Mesmo os melhores programadores cometem erros. Os erros são nonnalmente considerados como um defeito de software que surge de um erro de digitação, de um erro na lógica ou apenas por um engano bobo comet ido d urante a codi ficação. Embora a implementação de um objeto seja uma fon te de erros comum, eles aparecem em outras formas. Erros também podem resultar quando um objeto usa outro incorrctamente. Os erros podem até ser provenientes de fa lhas básicas na aná! ise ou no próprio projeto. Por sua própria natureza, um sistema 00 é repleto de objetos interagi ndo. Essas interações podem ser a fonte de todos os tipos de erros. Fe lizmente. você pode proteger se u software de erros, através de testes de software , onde é possível validar a aná lise, o projeto e a imp lementação de se u software. Assim com o a 00, o t est e não é uma solução mágica; é extremamente difícil testar seu software completamente . O número total de caminhos possiveis através de um programa não trivial torn a difíci l e demorado obt er cobertura total do código. Assim, mesmo um código testado pode abri gar erros ocultos. O melhor que você pode fazer é realizar uma quantidade de testes que garantam a qualidade de seu código, enquant o também permitam cumprir seus pralOS finais e permanecer dentro do orçamen to. A 'quantidade' real de t estes que você realizará dependerá da abrangência do projeto e de seus próprios níveis de bem-estar.
Testes e o processo de desenvolvimento de software iterativo A Figura 14. I ilustra a iteração apresentada pela primeira vez no Capítulo 9, " I ntrodução à anál ise orientada a objetos". O teste é a últi ma etapa de uma iteração. Antes de você sair de lima iteração, o teste é uma etapa im portante. O estágio de testes verifica se todas as alterações fe itas por você durante essa iteração não dan ificaram qualquer func iona lidade existente. O estágio de testes lambém verifica se toda nova funci onalidade adicionada agora funci ona corretamente. Por esses motivos, os testes realizados antes de se sair de uma iteração são freqüent emente referidos como testesfimcionais Oll de aceitação.
Construind o softwa re confiâvel através de testes
FIGURA 14.1 Uma ileraçtio.
Inicio da iteraç60
3 15
'\ An6li..
/
~
..-..j
l
Projlll1O
Implementaçlo
Testa
... NOTA
Fim di itlraç60
o test e no fi nal de uma iteração é um marco importante. Para sair da iteração, seu sistema deve passar pelos testes; entretanto, t ambém devem ocorrer testes durante outros estágios de uma it eração. As lições de hoje mostrarão a você como usar testes eficientemente, durante a implementação e estágios de t estes da iteração. Torn e os testes um objetivo e algo que você faz por todo o desenvolvimento. Se você não pensar nos testes at é o final do d esenvolvimento, poderá descobrir que não é possivel testar seu software. Em vez disso, você precisa desenvolver pensando nos testes. Você deve tornar os testes uma parte integrante do processo de desenvolvimento.
Se erros forem encontrados, você deverá voltar e corrigi-l os. Norma lmen te, você voltará para a • implementação e tentará corrigi r o problema no código. As vezes, isso é tudo que você preci sará faze r: bastará corrigir a implementação, testar tudo novamente e prossegu ir. Entretanto, os erros podem ser provenientes de uma fal ha de projeto ou mesmo de um requi si to ignorado ou mal -entendido. Talvez você precise voltar ao projeto ou à análi se, antes de poder corrigir um erro na imp lementação. ApÓS corrigir um erro, não é suficiente apenas testar o erro corrigido. Em vez disso, você precisa realizar todos os testes. Ao corrigir um erro, você pode introduzir facil m ente um ou mais erros novos!
Um mal-entendido na análise signi fi ca que o sistema não funcionará confonne o cl iente espera. Um sistema deve funcionar confomle o esperado e o cliente que conduz o sistema deve concordar com o que é esperado do comportamento. Não apenas você precisa testar o código quanto a
Dia 14
fa lhas de implementação, como também precisa testar o código para ver se ele func iona conforme o esperado. Para testar um sistema, você precisa escrever e executar casos de teste. Cada caso de teste testará um aspecto especifico do sistema. Um caso de teste é o bloco de construção básico do processo de teste. O processo de teste executa vários casos de teste para poder validar completamente um sistema . Cada caso de teste consiste em um conj unto de entradas e saídas esperadas. O teste executará um caminho especifico através do sistema (caixa branca) ou testará algum comportamento de fi nido (ca ixa preta).
Novo
TERMO
Um caso de teste exerci ta uma fu ncionalidade específi ca para ver se o sistema se comporta como deveria. Se o sistema se comportar conforme o esperado, o caso de teste passa. Se o sistema não se comportar conforme o esperado, o caso de teste falha. Um caso de teste falho indica que existe um erro no sistema. Você sempre quer que todos os seus casos de teste passem 100% das vezes. Não tente ignorar um caso de teste fa lho, se cem outros casos passarem. Todo teste deve passar ou você não poderá cont inuar seu trabalho! Ex istem duas maneiras de basear seus casos de teste: teste de caixa preta e de caixa branca. Uma estratégia de teste efi caz terá uma mi stura de casos de teste baseados em ca ixa preta e em caixa branca. O teste de caixa preta testa se o sistema fun ciona confonne o esperado. Dada uma entrada especílica, o teste de caixa preta testa se a saída ou comportamento correto, vis ível externamente, resulta confonne definido pe la especi fi cação da classe ou do sistema.
Novo
TeRMO
Novo
TERMO
No lesle de caixa branca. os testes são baseados unicamente na implementação de um método. Os testes de caixa branca ten tam atingir 100010 de cobertura do código.
Ao testar classes indi viduais, o teste de ca ixa preta é baseado nos requisitos funcionais da classe. Ao testara sistema inteiro, o teste de caixa preta é baseado nos casos de uso. Em qualquer caso, o teste de ca ixa preta verifica se um objeto ou sistema se comporta conforme o esperado. Por exemplo, se um método deve somar dois núm eros, um teste de caixa preta enviará dois n(J meros para o método e, em seguida, verificará se a saída é ou não a soma correta dos dois números. Se um sistema deve permi tir que você adicione e remova itens de um carrinho de compras, um teste de caixa preta tentará adicionar e remover itens do carri nho. O teste de caixa branca, por outro lado, é baseado na implementação de um método. Seu objetivo é garanti r que cada desvio do código seja executado. O teste de caixa preta é avaliado para cobrir apenas de um terço à metade do código real. Com o teste de caixa branca, você projeta seus testes de modo a exercitar cada desvio do cód igo e na esperança de el im inar todos os erros latentes.
Construind o softwa re confiâvel através de testes
DICA
3 17
Os testes de caixa branca, exceto quanto aos programas mais simples, raram ente podem alcançar uma cobertura razoável da combinação de caminhos através do programa. Existem dois passos que você pOde dar para melhorar a eficiência de seus testes: • Escreva seus programas de m odo que eles tenham um numero mínimo de caminhos . • Identifique caminhos críticos e certifique-se de testá-los.
Por exemplo, se um método divide dois números e existe um desvio de erro que é executado quando você tenta dividir por 0, será preciso garantir que exista um caso de teste que exerci te essa condição de erro. A nào ser que seja especificado na documentação da interface, você saberia a respeito desse desvio examinando o própri o código. Assi m, os testes de caixa branca devem ser baseados no próprio código. Em qualquer caso, os testes de caixa preta e de caixa branca governam o modo como você cria seus casos de teste. Cada um desempenha um papel importante na forma de teste que você pode executar.
Formas de teste No todo. ex istem quatro fOnllaS importantes de teste. Esses testes variam de testes de nível mais baixo, que examinam osobjetos individuais, até os testes de nível mais alto, que exami nam o sislema inteiro. A execução de cada um ajudará a garanti r a qualidade global de seu soft ware.
Teste de unidade
o teste de unidade é a unidade de nível mai s baixo dos testes. Um leste de unidade examina apenas um recurso por vez. Um leslede unidade é o dispositivo de teste de níve l mais baixo. Um leste de unidade envia uma mensagem para um objeto e depoi s veri fica se ele recebe o resultado esperado do objeto. Um teste de unidade verifica apenas um recurso por vez.
Novo
TERMO
Em termos de 00, ulllteste de unidade exam ina uma ún ica classe de objeto. Um teste de unidade verifica um objeto enviando uma mensagem e verifica se ele retorna o resultado esperado. Você pode basear os testes de unidade no teste de caixa preta e no de caixa branca. Na verdade, você deve realizar ambos, para garantir que seus objetos funcionem corretamente. Embora cada classe escrita deva ter um teste de unidade correspondente, você provavelmente deve escrcvcrocaso de teste antes de escrever a classe. Você vai ler mais sobre esse ponto posteriomlcnte . Hoje, focalizaremos o teste de unidade, pois ele é fundam ental para a escrita de software 00 confiável. Na verdade, você deve realizar os testes de unidade por todo O desenvolvimento.
Dia 14
Teste de integração Os sistemas 00 são constituidos de objetos que interagem. Enquanto os testes de unidade examinam cada classe de objeto isoladamente, os testes de integração verificam se os objetos que compõem seu sistema interagem corretamente. O que poderia funcionar isoladamente pode não funci onar quando com binado com outros objetos! As fontes comuns de erros de integração são provenientes de erros ou mal-entendidos a respei to dos fomlatos de entrada/saída, confl itos de recurso e seqíiênc ia incorreta de chamadas de método. Novo
TERMO
Um leSle de inlegraçiio verifica se dois ou mais objetos funcionam em conjunto cor-
relam ente.
Assim como os testes de un idade, os testes real izados durante os testes de integração podem ser baseados nos conceitos de caixa branca e de caixa preta. Você deve ter um teste de integração para cada iteração importante no sistema.
Teste de sistema Os testes de sistema veri fi cam se o sistema inteiro fun ciona conforme descri to pe los casos de uso. Enquanto executa testes de sistema, você também deve testar o sistema de maneiras não descritas pelos casos de uso. Fazendo isso, você pode veri fi car se o sistema manipula e se recupera nonnalmente de condições imprevistas.
DICA
Tente fazer estes testes em seu sistema: Testes de ação aleat óri a Os testes de ação aleatória consistem em tentar executar operaçOes em ordem aleatória. Testes de banco de dados vazio Os testes de banco de dados vazio garantem que o sistema pode falhar norm almen te, caso exista um problema maior no banco de dados. Casos de uso mutantes Um caso de uso mutante tran sforma um caso de uso válido em um caso de uso inválido e ga rante que o sistema possa se recuperar corret am ente da interação.
Você fi caria surpreso com o que um usuário pode tentar fazer com seu sistema . Ele pode não cair sob um dos casos de uso ' normais'; portanto, é melhor estar preparado para o pi or. Um leste de sistema examina o sistema inteiro. Um teste de sistema verifica se o sistema fu nciona confonne mencionado nos casos de uso e se ete pode man ipular normalmente situaçõcs incomuns e inesperadas.
Novo
TERMO
Os testes de sistema tam bém incluem testes de esforço e de desem pen ho. Esses testes garantem que o sistema satisfaça quaisquer requisitos de desempenho e possa funcionar sob as cargas es-
Construind o softwa re confiâvel através de testes
3 19
peradas. Se possivel, é melhor executar esses testes em um ambiente que corresponda o máxi mo possível ao ambiente de produção. Os testes de sistema são um aspecto importante dos testes de aceitação. Os testes de sistema veri · ficam unidades funcionais inteiras si multaneamente, de modo que um unico teste pode mexer com muitos objetos e subsistemas diferentes. Para sai r de uma iteração, o sistema deve passar nesses testes com êxi to.
Teste de regressão Um teste é válido apenas enquanto o que for testado não mudar. Quando um aspecto do sistema mudar, essa parte - assim como todas as partes dependentes - deverá ser novamente testada. Teste de regressão é o processo de repetição dos testes de unidade, integração e de sistema após as alterações serem feitas. Os te.l·tes de regressão examinam as alterações nas partes do sistema que já foram validadas. Quando uma alteração é feita, a pane que foi alterada - ass im corno to· das as partes dependentes - deve ser novamente testada.
Novo
TERMO
É absol utamente fundamental testar novamente, mesmo depois de uma pequena alteração. Uma pequena alteração pode introduzir um erro que poderia danificar o sistema inteiro. Fel izmente, para executar o teste de regressão basta executar novamente seus testes de unidade, integração e sistema.
Um guia para escrever código confiável Embora cada forma de teste seja importante para a qualidade global de seu software, hoje você vai foca lizar o que pode fazer de melhor em seu trabal ho diário para garantir a qualidade dos sis· temas que escreve. Para escrever código confiável, você precisa fazer o teste de unidade, apren· der a diferenciar entre condições de erro e erros, e escrever documentação útil .
Combinando desenvolvimento e teste Um fato esq uecido na Figura 14.1 é que os testes devem ser um processo dinâmico. O teste não deve ser algo a ser evitado, inserido somente no final , feit o por outra pessoa ou completamente ignorado. Na verdade, podc scr impossível começar a testar de repente, quando o sistema esti ver pronto. Em vez disso, você precisa aprender a começar a testar enq uanto desenvolve. Para testar enq uanto desenvolve, você preci sa escrever testes de unidade para cada classe que criar.
Um exemplo de teste de unidade Vamos ver um teste de unidade para a classe SavingsAccount, apresentada pela primcira vcz no Capítulo 5, "Herança: hora de escrever algum código". A Listagem 14. 1 apresenta a classe Sa vi ng sAccountTes t.
Dia 14
LISTAGEM 14.1 SavingsAccountTest.java public class Sav ingsAccou ntTest ( public s tati c voi d main( St r ing [] args ) I Savi ngsAccountTest sat : new SavingsAccountTest(); sat.test_applyinglnterest(); J public vo1d test_applyinglnterest() ( SavingsAccount acct • new SavingsAccount( 10000.00 , 0.05 ); acct.addlnterestO ; print_getBalanceResult( acct . getBalan ce() , 10500 .00 , 1 ); J
private void print_getBa lanceResult( double actua l, double expected, int - test ) ( if( actual
==
expected ) (
II
passou
System.out.pr i ntln ( "PASS : test I" + test +. interes t applied - properly· ) ; System .out . println(); ) else ( II f alhou Sys tem.out.println( "FAIl: test '" + test + " interest applied - incorrectly " ) : System.out.println( "Val ue returned: " + actual ); System.out.pr in tln ( "Expected value: " + expected ,; System.out. pri ntl nO; J
J J
SavingsAccountTest é um teste de unidade que exami na a classe SavingsAccount. Um teste de unidade pode ser constit uido de vários casos de teste. Aqui, a classe Sav ingsAccoun tTest s6 tem um caso de teste: tes t _app l yi ng Interest. O caso de teste test_app l yi nglnteresl verifica se uma instância de Sav ingsAccount aplica corretamente os juros em seu saldo. Um teste de unidade pode ter mui tos casos de teste, mas cada caso de teste deve verifica r apenas um recurso do objeto.
Construind o softwa re confiável através de testes
321
Sav; ngsAccountTest é referido como teste de unidade porque exami na o bloco de construção ou unidade de nível mais baixo no mundo 00: o objeto. Um teste de unidade deve exami nar apenas um objeto por vez. Isso significa que cada objeto deve ser testado, assim como uma ent idade independente. Se não for assim, você não poderá testar o objeto. Mesmo que isso seja possível. você poderá acabar testando inadvertidamente muitos objetos ao mesmo tempo. Ao esc rever testes de unidade, você deve evitar o máximo possível a va lidação manual da saída. Confonne você pode ver cm test app lyinglnterest, o caso de teste rea liza toda a validação necessária automaticamente. FreqUentemente, a validação manual é demorada e propensa a erros. Ao executar casos de teste, você quer resultados precisos o mais rápido possível e com o menor esforço. Caso contrário, você poderá começar a neg li genciar os testes.
Por que você deve escrever testes de unidade Os testes de unidade o ajudam a detectar erros. Se você danificar algo em sua classe, saberá imedi atamente, pois o teste de unidade lhe in formará. O teste de unidade é sua primeira linha de defe sa contra erros. A captura de um erro em nível de un idade é muito mais fácil de manipular do que tentar rastrear um erro d urante o teste de integração ou de sistema. Os testes de unidade também pennitem que você saiba quando tenn inou de escrever uma classe: uma classe está pronta quando todos os seus testes de unidade passam! Como desenvolvedor, pode ser extremamente útil ter ta l ponto fina l bem defin ido. Caso contrário, poderá ser di ficil saber quando uma classe está ' pronta '. Saber que você terminou e pode prosseguir evita que fiq ue tentado a acrescentar mais funcionalidade do que precisa em uma classe. A escrita de testes de unidade pode ajudá-lo a pensar a respe ito do projeto de suas classes, especi al mente se você escrever seu caso de teste antes de escrever a classe; entretanto, para escrever o caso de teste, você prec isa usar sua imaginação e fing ir que a classe já existe. Fazer isso proporciona a você muita liberdade para experimentar a interface de suas classes. Quando terminar de escrever O teste, você poderá escrever a classe e, em segu ida, tudo será compilado. ,
Os testes de unidade podem aj udá-lo a refazer seu código. E mais fácil fa zer alterações em seu código, se você ti ve r um teste de unidade, pois assim tem um retorno instantâneo em relação às s uas alterações. Você não precisa se preocupar muito quanto à introdução de erros; você pode simplesmente executar seu teste novamente, para ver se a lgo foi danificado. Usando testes de unidade, você não precisa ficar assombrado com a aborrecida pergunta, ;'será que eu danifiquei algo?" Você pode fazer alterações com mais confiança. Finalmentc, você ncm sempre pode estar por peno para testar o sistema. Você pode mudar para outros projetas ou outros membros de sua equipe podem precisar reali zar os testes. Os lestes de unidade penn item que outra pessoa, que não seja o autor, teste um objeto.
Dia 14
Escrevendo testes de unidade Sav; ngsAccountT es t e tes t _app 1yi n9 Interes t () sào exem plos muito simpl es de teste de unida· de e de caso de teste; entretanto, escrever testes de un idade desde o início para cada classe, pode se tornar demorado. Imagi ne um sistema onde você precise escrever centenas de casos de teste. Se escrever cada teste de unidade desde o in ício, você acabará fazendo muito trabalho redundan· te. Você precisa criar ou reut ili zar uma estrutura de teste. Novo TERMO Uma /wr lllllr(l é um modelo de domínio reutilizável. A estrutura contém todas as classes com uns a um dom ínio inteiro de problemas e serve como a base para um apti· cati vo especifico no domínio. As classes de uma estrutura de li nem o projeto geral de um ap li cativo. Como desenvolvedor, você sim plesmente estende essas classes e, em seguida, fornece suas próprias classes especifica s para o prob lema, para cri ar um apl icativo. No caso de uma estrutura de teste, a estrutura define um esqueleto que você pode reut il izar para escrever e executar testes de unidade. Uma estrutura de teste permite que você escreva testes de unidade rápi da e convenientemente, eliminando trabalho redundante e propenso a erros. Lem· bre·se de que ludo que você programa pode conter erros, até seu código de leste. Ter uma estru· tura bem testada pode resolver muitos erros de teste. Sem uma estrutura, a sobrecarga extra dos testes poderia ser suficiente para imped i·lo de fazê· los. Uma estrutura de teste completa conterá classes base para escrever testes de unidade, suporte in· terno para automação do teste e ut il itários para ajudar a interpretar c relatar a saída. Hoje, você aprenderá a usar JU nit, uma estrutura de teste gratuita, lançada sob a " IBM Public License", para testar classes Java. Você pode fazer download da J Uni! a parti r do endereço http://www.jun it.orgl.
NOTA
o download de JUnit inclui o códig o-fonte. JU nit tem um projeto excelente e você faria muito bem em estudar o código e a documentação incluída. Em par· ticu lar, a documentação realiza um excelent e trabalho de documentar os pa· drões de projeto usados para escrever JUnit.
JUnit A JUnit fornece classes para escrever testes de unidade, para vali dar a saída e para executar os casos de teste em urna GU i ou em um ambiente de linha de comando. j uni t. framework.TeslCase é a classe base para defi nir testes de unidade. Para escrever lestes de unidade, você simplesmente escreve lima classe que herda de TestCase, sobrepõe alguns métodos e fornece seus próprios mé· todos de caso de teste.
Construind o softwa re co nfi âvel através de testes
NO TA
323
Ao escrever casos de teste JUn it, você deve iniciar o nome de qualquer método de caso de t est e com testo A JUnit fornece um m ecanismo que carregará e executará autom aticamente qualquer método que comece com testo
A Listagem 14.2 apresenta uma versão JUnit de Sav i ngsAeeountTest. LISTAGEM 14 ,2
SavingsAecountTest.java
import junit.framework.TestCase ; import junit.framework,Assert; publie elass SavingsAeeountTest extends TestCase { publie void test_applyinglnterest () { SavingsAceount aeet • new SavingsAeeount(10000 .00 , 0,05 ); aect.addlnte rest() ; Assert.assertTrue( "interest app l ied ineorreetly", aeet.getBalaneeO •• - 10500 .00 };
} public SavingsAecountTest( St r ing name } I super( name );
} } A versão J Un it de Sav i ngsAecountTes t é mu ito mais simples do que a origi nal. Ao se usar J Un ir, não há mot ivo para reproduzir cód igo de exibição ou testes lógicos. Você pode simplesmente usar a classe Assert fornec ida pela JUn it. A classe Assert fornece vários métodos que recebem um valor booleano como argumento. Se o valor boo leano for fa lso, um erro será gravado. Ass im, aqu i, o teste passa para Assert o va lor booleano retornado pela comparação aeet . getBa 1ance () z= 10500 .00. Se a comparação for avali ada como falsa, JUnit sinalizará um erro. Então, como você execula esses testes? A JUnit oferece a você várias opções para a execução de seus casos de leste. Essas opções caem em duas categorias: es/a/icas e dinãmicas. Se você optar por usar o mélodo estático, precisará sobrepor o método ru nTest() para chamar o teste que deseja executar. Na linguagem Java, o modo mais conveniente é escrever lima classe anôn ima para que você não precise criar uma classe separada para cada leste que queira executar. A Listagem 14.3 mostra a deç laração anônima:
I 324
Dia 14
LISTAGEM 14.3 Uma
Anonymous SavingsAccountTest
SavingsAccountTest test • new Sav ingsAccoun tTest( "tes t _applyi nglnterest" ) ( public void runTest() I test_applyinglnterest(); J J;
test. run() j As classes anôn irnas são convenientes porque elas permitem que você sobreponha um método ao instanciar um objeto, tudo sem ter de criar uma classe nomeada em um arqu ivo separado. Aqui, o método principal instancia um Savi ngsAccountT es t , mas sobrepõe o método run Tes t O para executar o caso de teste tes t _app1yi ng Interes t (). Uma c/asse anõnima é uma classe que não tem um nome. As classes anôni mas não têm nome porq ue elas são simplesmente definidas ao serem instanciadas. Elas não são declaradas em um arquivo separado ou como uma classe interna.
Novo
TERMO
As classes anôni mas são uma excelente escolha para classes usadas uma lmica vez (se a classe for pequena). Usando uma classe anôn ima, você evita a necessidade de criar uma classe nomeada separada. Apesar de serem tão convenientes, as classes anônimas têm defi ciências. Você precisará criar uma para cada método de caso de teste que queira chamar. Se você tiver muitos casos de teste, o uso de classes anôn imas pode exigir muito código redundante. Para superar essa defic iência, a JUnit também fornece um mecanismo dinâmico, que procurará e executará qualquer método que comece com testo Para os propósitos da lição de hoje, contaremos com esse mecanismo dinâmico. A JUnit também forn ece um mecanismo, conhecido como conjunto de teste, para executar vários casos de teste. A JUnit forn ece um meca nismo para definir estaticamente a bateria de testes a serem executados como um con junto; entretanto, o mecan ismo dinâmico pesq uisará e encontra rá cada método de teste. Hoje, contaremos com esse mecanismo automático para encontrar e executar os testes. A JUnit também fornece alguns outros recursos convenientes. Considere a versão atualizada de Sav ingsAccountTes t , na Listagem 14.4, que também testa o método wHhdrawFundsO. LISTAGEM 14.4 SavingsAccountTest.java import junit.framework.TestCasej import junit.framework .Assertj
Construindo softwa re confiâvel através de testes
LISTAGEM 14.4
325
SavingsAeeountTest.java (continuação)
publiC class SavingsAecountTest extends TestCase ( private SavingsAccount aeet; publi e void tes t_applyinglnterest() ( aeet.addlnterest(); Assert . assertTrue( "interest applied - 10500.00 );
ineorrectly~.
acet . getBalance() ••
I publi c void test_withdrawFunds() t aect.withdrawFunds( 500.00 )i Assert . assertTrue ( "i neorreet amount wi thdrawn", acct . getBa 1ance () •• - 9500.00 );
I proteeted void setUpO ( ace t • new Sav ingsAeeount( 10000.00. 0.05 );
I publie SavingsAeeountTest(String name) I supere name ):
} } Nessa versão, você notará que o teste contém dois métodos novos: test_withdrawFundsO e setup(). setup() sobrepõe um método na classe base TestCase. Sempre que a JUni t chamar um método de teste, primeiro ela fará urna chamada a setup(} para estabelecer o acessório de teste. Um acessório de teste define o conjunto de objetos sobre os quais um teste operará. Estabelecer um acessório de leste pode consumira maior parte do tempo que leva para escrever casos de teste. Novo TERMO
o acessório de teste prepara o conjunto de objetos sobre os quais um caso de teste
aluará. Os acessórios também são convenientes, pois eles permitem que você compartilhe o mesmo acessório dentre um conjunto inteiro de casos de tesle, sem ter de duplicar código.
Dia 14
A JUnit garante que os objetos acessórios estarão em um estado conhecido, chamando setup() antes de executar cada teste. A JUnitlambém fornece um método tearOown{) correspondente, para realizar toda limpeza do acessório, depois que o teste for executado. Para executar seus casos de teste, a J Unit fornece executores de teste para exerci tar e reunir os resultados de um teste. A JUnit fornece uma versão gráfi ca e urna versão baseada em linha de comando desse utilitário. Para executar SavlngsAccountTest graficamente, basta digitar: java ju ni t.swingu l .TestRunner A Figura 14.2 ilustra ajanela principal de lUnit. FtGURA 14.2 A UllJrincipal de JUII;I.
.
.
u
Usando a UI, você pode navegar até a classe SavingsAccoun tTest. Uma vez carregada, você pode simplesmente executar o teste, pressionando o botão Run. Conforme a Figura 14.3 mostra, a UI da lUnit exibe o número de testes executados, assim como o número de testes fa lhos. A UI tam bém fo rnece uma barra gráfica que mostra se os testes falharam ou não. A J Unit é uma fermmenta excelente, pois ela penn ite que você receba retorno claro e instantâneo de seus casos de teste. Assim, se aque la voz em sua mente importuná-l o com, "e se eu dan ifiq uei algoT', você pode descobrir rapidamente, executando novamente seus testes de unidade.
Escrevendo testes de unidade avançados Vamos considerar um exemplo ligeiramente mais com plicado. O Exercicio I do Capitulo I I, "Reutili zando projetas através de padrões de projeto", apresentou uma possível implementação de um I tem. Atualmente, para apresentar um I tem,você deve chamar vários métodos de obtenção
Construind o softwa re confiâvel através de testes
327
e processar os dados para exibição. Infe lizmente, solici tar os dados para um objeto não é a melhor estratégia de 00. Você deve ped ir ao objeto para que faça algo com seus dados.
FIGURA 14.3 A UI,)riIICipal de JUllil. o/JÓs
e.T.ecular com êxito os casos de leSfe.
u
IHt.'PI"''''''''''' -.-...".""
Considere a Listagem 14.5, que apresenta uma implementação a lternat iva de Item.
LISTAGEM 14.5 Item.java publlC class Item { private private private private private
in t int float Stri ng float
i d;
quantity; un1tPrl ce ; descri pti on ; di scount ;
publiC Item( int id . int quantity. float unitPrice . fl oat disco unt. String - desc) { this.ld • i d; th i s.quantity quantity; this.unitPrice • unitPrice; th i s.d i scount • discount; th i s.descript i on • desc; 2
}
publ ic void displ ay( ItemOisplayFonmatter format ) { fonmat .quantity( quantity );
I 328
Dia 14
L ISTAGEM
14.5
Item.java (continuação)
format.id( i d ); format.unitPrice( un i tPrice ); format.di scount( discount ) ; format.de sc ription( desc ripti on ); format.adju s tedPri ce( getTotaIPrice() ) ; }
publi c float getTotalPrice() { return ( unitPrice * quantity ) - ( discount * quantity ): } }
Aqui, você pode pedir a Itempara que se apresente, usando um formatador de exibição. O formatador cuidará da formatação dos dados para ex ibição. Ter um objeto de formatação separado é uma estratégia mel hor do que fazer com que outro objeto chame métodos de obtenção o u incorporar a lógica de exibição no próprio objeto Item. Quando os requi sitos de exibição mudarem, você poderá sim plesmente criar novas implementações de ItemDispl ayFonnatte r. A Listagem 14.6 define a interface Itení)1splayFonna tter e a Listagem 14.7 apresenta uma possivel implementação da interface. LISTAGEM
14.6 I temDispl ayFormatter . java
publi c interface ItemDisp layForma tter { public void quant i ty( int quantity ) : public void ide in t ld ): publ ic void unitPri ce( float unitPrice }: publi c vo i d disco unt( float discount ): publi c void descri ption( String desc r iption ): publi c void adj us tedPri ce( float total }: publi c Stri ng formatO : }
LISTAGEM
14.7 ItemTableRow. ja va
publi C cla ss I temTabl eRow impl ements ItemDisplayfonnatter private private private pri vate
i "t
quantity:
i nt i d ,' float un1tPr ice ; fl oat di scoun t ;
I
Construindo softwa re confiâvel através de testes
329
LISTAGEM 14.7 ItemTableRow.java (continuação) private String description; private float adjPrice: publlC void quantity( lnt quantity ) ( this .quantity z quantity; J
publ ic vo1d id(intid){ this.1d • 1d: J
publiC void unitPrice( float unitPrice ) ( this.unitPrice • un i tPrice : J
publ ic void discount( float discount ) { this.discount • discount; J
public void descri pt ion{ String descripti on ) { this.description • description:
J publiC void adjustedPrice( float total ) ( this . adjPrice · total: J
publiC String format() { String row ·""; row • row + "" + cow • row + " | " + cow • row + " | " + cow • row + " | $" + cow o row + " | $" + cow • row + " |
" ; return row: J
id + "": quantity + "": description + ""; unitPrice + "" ; adjPri ce + "" ;
J
o formatador I temTabl eRow cria uma representação de linha de tabela HTML para o Item, usando símbolos da moeda norte-americana. Outros formatadores poderiam formatar as dados de outras maneiras,
Dia 14
Esse exemplo apresenta alguns desafios de teste interessantes, assim como algumas oportunidades. Para essas classes, si mplesmente chamar um método e verifi car uma saída não resolverá. O teste do método di splay() de Item é de especial interesse, pois um teste de unidade só deve verificar uma classe isoladamente; entretanto, para testar di sp 1ay () , você também deve passar para ele um ItemOispl ayFonnatter. Fe li zmente, os objetos fa lsificados oferecem uma alternativa que ainda pennitirá a você testar I tem isoladamente.
Um objelOfa/sificado é um substituto simplista de um objeto real. Ele é chamado de objeto ralsificado porque o objeto roi ralsificado para propósitos de teste. Embora o objeto falsificado possa ter uma implementação simplista, ele pode conter funciona lidade extra para ajudar nos testes.
Novo
TERMO
Os objetos falsificados são int imamente relacionados aos objelos s tubs. Objetos s tubs real izam apenas trocas de mensagens enquanto que os objelos fa lsificados se diferem no sentido de que eles realmente executam alguma função, em vez de si mplesmente aceitarem uma chamada e retomarem algum valor prévio. Às vezes, os objctos fa lsificad os são chamados de simuladores. Um objeto fals ificado fornece um substituto si mplista para um objelo real. Esse subst ituto não aparecerá no sistema real, apenas no código de teste. O objetivo de um objeto fa lsificado não é fornecera funcionalidade real do objelo que imita. Em vez disso. o objeto fa lsificado deve fornecer uma implementação simplista que pode ter mais suporte para leste. Mantenha os objetos fatsifi cados o mais simples possivel. Normalmente, um objeto falsificado deve ser um objeto independente que não con ta com quaisquer outros objetos falsificados. Se seu objeto falsificado tiver dependências demais, provavelmente ele é muito complicado.
Por exemplo, considere um acessar de banco de dados que retoma objetos Item. Você codificaria o acessor fa lsificado para retomar Omesmo objelo I tem repetidamente; entretanto, se você fizer testes de unidade em um objeto que recupera objetos Itemusando o acessor, ele não saberá a diferença. Tal estratégia isola o objcto que está sendo testado dos defeitos nos objetos que utiliza. Você pode usar um objelo fa lsificado para testar se o método di spl ay() de Item usa objetos ItemOi sp 1ayFonna tter corretamente. A Listagem 14.8 apresenta um I tenOi sp1ayForma tter fa lsificado. LISTA GEM
14.8 MockOi spl ayFonnatter ,java
impo rt j un1t.framewor k.Assert; public cl ass MockOisplayFormatter impl ements It emO i splayFormatter I
Construind o softwa re confiâvel através de testes
LISTAGEM 14.8
331
MockDlsplayFormatter.java (continuação )
private priva te private private pri vate private
test_quantity; test_id ; float test_unitPrice ; float test_discount; String test_description; float test_adjPrlce;
private private private private private private
fnt fnt float float Stri ng float
; "t ; "t
quantity; id-o
unitPri ce ; di scount; de scription ; adjPri ce ;
publ ic void verify() { Assert. assertTrue ( "quanti ty set i ncorrectly" , test quanl i ty "" quant i ty );
Assert.assertTrue( Assert.assertTrue( _ un i tPri ce ); Assert.assertTrue( Assert.assertTrue( - description ); Assert.assertTrue(
"id se t i nc orrectly ", test_id • • id ); "unitPrice se t incorrectly", test_unitPrice •• "d i scount set incorrectly". test_discount •• discount); "desc ription set incorrect l y", test_description •• "adjPrice set i ncorrectl y· . test_adjPrlce .... adjPrice) ;
} pubhc void test_quantity( int quantity ) { test_quantity • quantity; )
publi C void test_id ( int id ) { test id .. ido
} publiC void test_unitPrice( f loat unitPrice ) { test unitPrice • unitPrice;
} publiC void test_discount( float discount ) { test_discount • di scount; }
publiC void tes t _description( String description ) { test_description • descr ipt ion ; }
Dia 14
LISTAGEM 14.8
MockDlspl ayFonnatter.java (continuação)
public void test_adjustedPrice( float total ) I test_adjPrice • total; }
public void quant1ty( int quantity ) { this.quantity • quantity ; }
public vo i d i de int id ) { this. id = id; }
public void unitPrice( float unitPrice ) { this.unitPrice • unitPrice; }
pub l iC void discount( float discount ) ( th i s.d l scount • discount; }
public void descrip t lon ( String description ) { this.description • description; }
publi c void adjustedPrice( float total) { this.adjPrice = total; }
public String fonnat() ( II não estamos testando funcionalidade de fonnatador return "NOT IMPLEMENTED"; } }
De certa forma, MockOi spl ayFormatter é semelhante à implementação real; entretanto, você notará que ele não implementa um verdadeiro método formatO. Você também notará que ele acrescenta vários métodos para confi gurar os valores esperados, assim como um método para verificar as entradas de Item em relação a esses valores. A Listagem 14.9 ilustra como você poderia usar a ex ibição fals ificada para fazer o teste de un idade da classe I tem.
Construind o softwa re confiâvel através de testes
LISTAGEM 14 .9
ItemTest.java
import junit.framework.TestCase; import junit.framework.Assert; publiC class ItemTest extends Tes t Case { private Item item;
II constantes para valores do construtor private private private priva te private
f1 na 1 fi na 1 final final fina 1
statlc static static static static
1nt lO int QUANTITY float UNIT- PRICE float DISCOUNT St ring OESCRIPTION
• 1; • 10;
100 . 00f; • 5. 00f; • "ITEM_TEST"; -
protected void setUp O { item· new Item( lO , QUANTITY, UNIT_PRICE, OISCOUNT , OESCRIPTION li
I publiC void test_displayValues() { MockDisplayFonmatter formatter : new MockOispl ayFormatter(); formatter.test_id( 10); fonmatter. test_quantlty( QUANTITY ); fonmatter.test_unitPri ce( UNIT_PRICE ,; fonmatter.test_discount( OISCOUNT }i fonmatter.test_description( OESCRIPTION ); fl oat adj_total = ( UNIT_PRICE * QUANTITY ) - ( OISCOUN T * QUANTITY ); formatter.test_adjustedPrice( adj_total l; item.display( formatter l; formatter . verify();
I publiC ItemTest( String name ){ supere name }i I
I
333
Dia 14
o método
test_di spl ayYa l uesO de ItemTest cria um MockOi spl ayFormatter, configura as entradas es peradas, passa isso para o objeto Iteme usa o fomlatador para validar a entrada. Internamente, o método veri fy() do formatador usaa classe Assert de JUn it para va lidar a entrada.
Os objetos falsificados são um conceito poderoso, pois você pode programá-los para qualquer coisa. Você pode ter obj etos fal sificados que contém o número de vezes que um método é chamado ou um que controle o volume de dados que um objeto envia por urna rede. Tudo depende de seu aplicativo e do que você precisa monitorar. Os objetos fa lsificados pennitem que você realize todos os tipos de monitoramento e teste que não são possíveis, caso um objeto simplesmente cri e todos os objetos de que precisa. Isso levanta a questão, "e se meus obj etos criarem os objetos de que precisam ?" A maior parte dos exemp los deste livro cai nessa categoria (espera-se, contudo, que os exemplos tenham permanecido mais inteligíveis !). Uma solução é editar as classes para que os obj etos instanciem um objeto fal sifi cado, em vez do objeto real. Tal estratég ia, entretanto, não é limpa, pois o obriga a alterar o cód igo que você está testando. Se você mudar a implementação do código antes de testar, não estará realmente testando a classe que entrará em se u sistema. A melhor solução é escrever código quc seja fácil de testar. Tal conselho pode parecer retrógrado. A sabedoria convenc ional diz que normal mente você escreve testes para testar cód igo que já escreveu . Você não escreve cód igo para que possa testar! O exem plo Item ilustra uma lição importante. Projetar suas classes de modo que elas sejam fáce is de testar pode res ultarem um código ma is OO! Nesse caso, passaras objetos dependentes tornou oobj eto menos dependente de uma classe espec ífica. tornando-o. assim. mai s afeito à conexão.
DICA
DICA
Projete suas classes de modo que você possa t est á-Ias facilmente. Projete lembrando dos objetos falsificados. Seu código pode se t ornar mais orientado a objetosl
Escreva suas classes de modo Que os objetos dependentes seja m passados e não i nstanciados dentro do próprio objeto. Essa prática leva a ob jetos independentes.
Embora seja verdade que o método d"isp layO de Item seja dependenle da interface ItemOispl ayFonnatter, ele não é dependente de uma implementação específica da interface, como TahleRowFonnatter ou mesmo MockOi splay Formatte r . Em vez disso, Item está livre para usar q ualquer imp lementação de I t eníl i sp layFormat ter, pois ele não se obriga a usar qualquer uma específica, criando a instância por si mesmo.
Construind o softwa re confiâvel através de testes
DICA
335
Dicas para testes efi cazes: • Você deve otimizar a velocidade de seus testes. Testes rápidos fo rnecem retorno instantã neo; portanto, você poderia esta r mais apto a usá-los. • Compile seus casos de teste junto com suas classes normai s. Essa prática o obrigará a manter seus casos de teste atualizados com o código. • Evite val idação visuaVmanual de saída de t este, pois ela é propensa a erros. Em vez disso, use algum m ecanismo automático. Se você precisa manter uma base de código q ue não possui testes de unidade, escreva os test es à med ida que precisar deles.
Escrevendo código excepcional Tesla ré uma maneira importante de garantir a qualidade do código que você escreve; entretanto, esse não é o único passo que você pode dar. Você também precisa aprender a identificar a diferença entre uma cond ição de erro e um erro. Uma condição de erro e um erro não são a Illesma coisa! Você sabe o que é um erro; um erro é um defeito. Uma condição de erro é li geiramente diferente. Uma condição de erro ou exceção é um a falha previsível que acontece sob ccrtas circunstâncias no domínio. Pegue como exemplo a loja on-line. Períodos de inatividade da rede acontecem o tempo todo. Um periodo de inatividade da rede não é um erro (a não ser que seu código tenha causado isso!). Em vez de tratar condições de crro como erros, você precisa codificar em tomo delas. Por exemplo, se sua conexão com o banco de dados falha, você precis.1. tentar reconectar. Se ela ai nda estiver inativa. você prec isará tratar da cond ição nonnalmente e infonnar o usuário do erro. Toda li nguagem tem sua própria maneira de relatar cond ições de erro. As linguagens Java e C++ empregam um mecanismo conhec ido como exceções para sinal izar condições de erro. Linguagens como C contam com códigos de retorno. Qualquer que seja a linguagem usada, você deve escrever seu cód igo para detectar e se recuperar nonnal mente de condições de erro. As exceções Java e C++ funcionam de modo seme lhante. As exceções são apenas outro tipo de objeto; entretanto, o compilador Java o obriga a tratar delas, se dctcnninar que uma exceção pode ocorrer e você não tratar dela. Aqui está um dos métodos da classe URL da linguagem Java: publi c URLConnection openCon nection( ) throws IOExceptlon Você vê que o método tem algumas infonnaçõcs extras. O método indica que pode lançar IOException, significando que, sob condições nonnais, o método retomará um objeto URlConnect i on. Se houver um erro na abertura da conexão. entretanto. o método lançará lima exceção rOException. Na linguagem Java, você trata de exceçôes em blocos t ry/ catch. A Listagem 14. 10 mostra como você poderia manipular uma chamada de openConnection.
Dia 14
LISTAGEM 14.10 Tratando de uma exceção
java . net.URL url .. new java.net.URL( .. http: //www.samspubl ish i ng.com/ .. ) ; java.net. URLConnection conn; try I conn = url.openConnection(); I catch ( j ava. lo .IOExcept ion e ) { II um erro ocorreu II registra um erro , escreve al go na te l a II faz algo para trata r do erro
I Quando você faz uma chamada para openConnect i on, faz isso normalmente; erllretanto, você deve faze r a chamada dentro de blocos try/catch ou d izer explici tamente que o método no qua l a chamada é fei ta também lança uma exceção IOException. Se a chamada para openConnection () resultar no lançamento de uma exceção, conn não será con* figurado. Em vez di sso, a execução conti nuarâ dentro do bloco ca t ch, onde você pode tentar re* cuperar, registrar um erro, impri mir uma mensagem na tela o u lançar outra exceção. Se você não capturar a cxceção expl icitamente ou lançar urna nova exceção, a exceção subirá como bolha na pil ha de chamadas, até chegar ao topo ou que alguém fina lmente a capture.
O importante é que você programe para condições de erro, usando o mecanismo incorporado à sua linguagem. Isso signi lica que, quando você projetar suas classes, também deverá considerar as d iversas cond ições de erro, modelá*las através de objetos exceção e faze r com que seus métodos as lancem.
Escrevendo documentação eficaz Há mais um passo que você pode dar para melhorar a qualidade de seu trabalho: documentá* lo. Existem mui tas formas de documentação, cada uma com seu próprio níve l de eficácia.
Código-fonte como documentação O código*fonte, até seus testes de unidade, é urna fo nna de documentação. Qua ndooutras pessoas precisam pegar e manter se u cód igo, é importante que ele seja legíve l e bem organizado. Caso contrârio, ninguém poderá ter idéia do que ele faz .
O código-fonte é a forma mais importante de documentação, pois é a única documentação que você tem de manter.
Conve nções d e codific ação O primei ro passo que você pode dar para transformar seu cód igo em boa documentação é escolher uma convenção de codi fi cação e fi car com ela. As convenções de codificação podem cobri r tudo, desde corno você endenta suas chaves até corno nomeia suas variáveis. A convenção espe-
Construind o softwa re confiâvel através de testes
337
cífica não é tão importante assim. O importante é que sua equipe de projeto, e preferi velmente sua empresa, escolha uma convenção e fique com ela. Desse modo, qualquer um pode pegar um trecho de código e acompanhá·lo - bem, pelo menos não se atrapalhar com a fonnatação. A Li stagem 14.1 1 apresenta uma maneira de declarar classes Java.
LISTAGEM 14.11
Um exempl o de cla sse
public class extends implements (
I
II II II II
vari ávei s públicas variáveis protegidas varilivei s privadas constantes
II II II
métodos públicos métodos protegidos métodos privados
DI CA
Os nomes de classe sempre devem começar com uma letra maiúscula. Os nom es de método sempre devem começar com uma letra minúscula. Os nomes de variável sempre devem com eçar com uma letra minúscula. Qualquer nome contendo várias palavras deve ter as palavras reunidas e cada palavra começar com letra maiúscula. Po r exemplo o método someMethod() e a classe HappyObj ect. As constantes sempre devem aparecer em MAIÚSCULAS. As variáveis normais devem aparecer em minúsculas. (Not e que essas convenções sáo voltadas à li nguagem Java . A s convenções para Smalltalk e C++ podem ser diferentes.)
Aqui está uma maneira de declarar métodos e instruções i f /e 1se aninhadas:
public void method() { if ( co ndicional) { I else {
I I
Dia 14
Constantes As constantes lambém podem serv ir corno uma forma de documentação. Use constantes quando você se achar ut ilizando um va lor cod ificado. Uma constante bem nomeada pode dar uma idéia do objetivo de seu código.
Comentários Assim como uma constante bem colocada, nada aj uda a tomar o código mais intel igivel do que um comentário bem colocado; entretanto, você precisa encontrar um equilíbrio em seus comen~ tários. Co loq ue comentários demais e eles perderão o sign ifi cado. Aqui está um erro de comentário inútil, mas comum:
pub l i c voi d i d( int id ) { thi s . 1d • 1d ; II configu ra id }
Comentári os inúteis informam o que o cód igo está fazendo. Se você precisa explicar cód igo, e n~ tão ele pode eSlareompli cado demais. Os comentários devem dizer para q ue serve o código. Eles devem descrever impl ementações não intuiti vas.
NOTA
Note que um comentári o sob uma assinatura de m étodo não substi tui um bom e ctaro nome de m étodo.
Nomes Os nomes de variável, método e classe devem ser significativos. S ol elre~os e seja consistente em s ua gra fi a. I)or exemplo, sem pre co loque inicia l maiúscula na segunda palavra de um nome com várias pa lavras, como em testCase. Como alternativa, você pode dividi r as palavras com um hi~ fen (-), corn o cm test -case. Novamente, o aspecto mais importante é a consistênc ia. Torne sua regra de atribuição de nomes parte de sua convenção e então a uti lize consistentemente.
Cabeçalhos de método e classe Quando você escrever uma classe ou um método, sempre se cert ifique de incluir um cabeçalho. Um cabeçalho de método incluirá uma descrição, uma lista de argumentos, uma descrição do re~ torno, assim como condições de lima exceção e efeitos colaterai s. Um cabeçalho pode alé incluir condições prévias. Um cabeça lho de classe nonnalmente incluirá uma descrição, o número da versão, a lista de aUlores e o histórico de revisão. Quando você programar em Java, cert ifi que~se de tirar proveito do Javadoc. (Veja o Apêndice B para mai s informações.) O Javadoc fornece várias tags para escrever cabeçal hos. Se você usar Javadoc, poderá si mplesmente executar suas classes através de um processador, que fará automaticamente documentação da AP l baseado na Web, conven iente para suas classes.
Construind o softwa re confiâvel através de testes
DICA
339
Quando você ti ver documentado algo, terá se comprometido a manter essa documentação atualizada, seja com o Javadoc, um documento de projeto ou um com entár io. Uma documentação obsoleta é inútil. Mantenha -a atualizada!
Resumo Hoje, você aprendeu sobre os testes e o que pode fazer como desenvolvedor para garantir a qualidade de seu trabalho. Ao todo, existem quatro fonnas gerais de teste: •
Teste de unidade
•
Teste de integração
•
Teste de sistema
•
Teste de regressão
Em se u trabalho diário, o teste de unidade é sua primeira li nha de defesa cont ra erros. O teste de unidade também tem as vantagens de obrigá-lo a considerar seu projeto do ponto de vista do teste e de fornecer a você um mecanismo que torna mais fácil refazer as coisas. Você também viu a importância do tratamento correto das condiçõcs de erro e da manutenção da documentação. Todas essas práticas aumentam a qualidade de seu código.
Perguntas e respostas P Por que os desenvolvedores detestam testar? R Parece haver uma cultura do 'ev itar teste' entre os programadores. Achamos que esse é um problema cu ltural. Na maioria das empresas, o pessoal de controle de qualidade é um grupo separado dos desenvolvedores. Eles vêm, testam o sistema e redigem relatórios de erro. Essa estratégia co loca o programador na defensiva, especialmente se o gerente de projeto pressionar o desenvolvedor a refazer o trabalho. De certa forma, os testes se tornam uma punição e uma fonte de trabal ho extra. Ver os testes como trabalho extra também faz parte do problema. Muitas equipes de projeto deixam os testes para o tinal do desenvolvimento; assi m, ele se torna algo que você faz quando já terminou de fazer o trabalho 'real'. Infel izmente, quanto mai s você retarda os testes, mais difíceis eles serão de fazer. Quando você começar a testar, provavelmente encontrará um grande número de erros, pois não tinha testado até esse ponto. Isso o leva de volta à fase de punição, especial mente porq ue você, provave lmente, está perto de seu prazo final. E se você estiver próximo ao seu prazo final , a pressão do gerente de projeto aumentará.
o evitar teste é UIll problema que se auto-alimenta.
Dia 14
P 110r que os testes são feitos freqücntcmente por um grupo de controle de qualidade separado? R Testes independentes ajudam a garamir que o pessoal de teste não evite, subconsciente· mente, áreas onde podem ex istir problemas - uma fal ha na qual os desenvolvedores p0dem estar propensos.
P Em todas as lições de hoje, você usou JUnit. Por que você escolheu J Unit? R JUnit é uma fe rmmenta de teste gmtuila que faz bem seu tmbalho. A J Un it é bem projeta· da e é suficientemente pequena para que você possa por suas mãos no proj eto faci lmente. Ela ta mbém não possui os detal hes que muitos outros prod utos têm. Em nossa op inião, ev itar os detal hes durante os testes de unidade é uma vantagem. A JUnit o co loca mais perto do código, pois você preci sa escrever se us próprios lestes, confi g umr os dados e vali dar a saída. Ela o obriga a considerar seu projeto e até a aumen· tá- lo, para que seja fácil testar. Com algumas das ferra mentas de teste mai s automatizadas, você pode perder essas vantagens; entretanto, a J U nit é uma estrutura de teste de unidade. Você prec isará encontrar outras ferramentas para alguns dos testes de integração c de sistema. P O teste de unidade parece um fardo. Não tenho tempo para fazer o teste de unidade. O que devo fazer? R Os testes de unidade podem parecer um fardo na pri me ira vez que você escreve um deles. Com todo honest idade, às vezes, os testes de unidade são dispend iosos para escrever. Eles se pagam com o passar do tempo. Quando você escreve um teste, pode reutilizá-lo repet idamente. Sem pre que você mudar a implementoção de sua classe, bastorá executar novamente seu teste de un idade. Os testes de unidade tornam mui to ma is fác il fazer alterações em seu código. Não ter tempo é um argum ento fraco. Imagine quanto será mais demorado encontrar, rastrear e corrigir erros, se você de ixar os testes para o fi m - um momento em que você normalmente está sob ainda mais pressão. P Como você sabe se já testou o suficiente? R Você já testo u em um nível mínimo, quando ti ver um teste de unidade para cada classe, um teste de integração para cada interação importante no sistema e um leste de sistema para cada caso de uso. Se você vai o u não fazer testes ad icionais depende de seu pmzo finai, assim como de seu nível de bem·estar.
Construind o softwa re confiâvel através de testes
341
Workshop As perguntas e respostas do teste são fornecidas para seu melhor entendimento. Veja as respostas no Apêndice A, " Respostas".
Teste I. Como erros podem entrar em seu software? (Ou talvez a pergunta deveria ser, ;;como você causa erros em se u software?") 2. O que é um caso de teste? 3. Quai s são as duas maneiras nas quais você pode basear seus testes? 4. Defi na teste de caixa branca e de caixa preta. 5. Quai s são as quatro forma s de teste? 6. Defina teste de l/IIidade. 7. Qual é o objeti vo por trás dos testes de integração e dos testes de sistema? 8. Por que você deve ev itar ler de deixar os testes para o fina l de um projeto? 9. Por que você deve ev itar validação manual ou visual ao testar? Qual é a alternativa? 10. O que é uma estrutura? I I. O que é um objeto fal sificado? 12. Por que você deve usar objetos fa lsificados? 13. Qual é a diferença entre um erro e uma condição de erro? 14. Ao escrever seu código, como você pode garantir sua qua lidade?
Exercícíos I. Faça down load da JUn it c leia cooks t our, que é encontrado no d iretóri o doc. 2. Escreva um teste de unidade para HourlyEmployee do Capítulo 7, " Po limorfi smo: hora de escrever algum cód igo", que testa o método cal culatcPay().
SEMANA
2
Em revisão Nesta semana, você aprendeu a respeito da estratégia iterativa para o processo de desenvolvimento de software. O processo de desenvol vimento de software incl ui os estágios de análise, projeto, implementação e teste. Você aprendeu que a estratégia iterai i va para Odesenvol vimento de software perm ite voltar e re finar qualquer estágio do processo. Esse refinamento contínuo leva a lima solução mais completa e correta para seu prob lema. O Dia 8 apresentou a Unified Modeling Language. No Dia 9, você aprendeu sobre AOO (Análise Orientada a Objctos), o primeiro passo do processo de desenvo lvimento. A AOO pcnni te que você entenda o problema que está tentando resolver. Um método de ADO é através do uso de casos de li SO para descrever como os usuários utilizarão o sistema. Uma vez que você tenha seus casos de uso mapeados, pode usar linguagens de modelagem, como a UML, para visua lizar graficamente o domínio de seu problema. O Dia 10 descreveu o POO ( Projeto O rientado a Objetos), o processo de pegar o modelo de domínio e criar o modelo de objetos que você usará durante a implementação. A lista a seguir apresenta os passos básicos necessários para completar o roo. I. Gere uma lista de objetos inicial. 2. Refine as responsabilidades de seus objetos. 3. Desenvolva os pontos de interação. 4 . Detalhe os relacionamentos entre objetos. 5. Construa seu modelo. Os cartões CRC o ajudam no mapeamento das responsabilidades e das interações entre cada obj eto. Os dois dias seguintes abordaram os padrões de projeto, conceitos de projeto reutil izável que rornecem atal hos para o POO. Você aprendeu sobre padrões, como Adapter, Proxy, Iterator, Abstract Factory, Singleton e Typesare Enum, e quando é apropriado usá-los. O Dia 13 explicou o projeto de UI com o padrão MVC. O pad rão MVC rornece nexibilidade, desacoplandoa UI do sistema subjacente. Ele também enratiza a necessidade de executar o processo de projeto de software para a UI como para qualquer outra pane do sistema.
Construind o softwa re confiâvel através de testes
343
Finalmente, o Dia 14 apresentou os testes nos estágios de implementação e lestes de cada iteração. Você aprendeu a respeito das duas mane iras de testar seu código: teste de caixa branca e teste de caixa preta. Você também aprendeu as quatro formas de teste: teste de unidade, de integração, de sistema c de regressão. O teste é a maneira fi nal de garantir a qualidade de seu código. Se ele não for levado em consideração durante todo o processo de desenvolvimento, você poderá se deparar com sérias conseqüências. Agora que você tenni noll as lições desta semana, deve entendera processo de desenvolvimento de software.
SEMANA
3
Reunindo tudo: um projeto 00 completo 15 Aprendendo a combinar teoria e processo 16 Iteração 2 do j ogo vinte-c-um: adicionando regras 17 Iteração 3 do jogo vinte-c-um: adicionando aposta
18 Iteração 4 do jogo vinte-c-um: adicionando uma GUJ 19 Aplicando uma alternativa ao MVC 20 Divertindo-se com o j ogo vi nte-c-um
21 O último quilômetro
Panorama Na pri meira semana, você aprendeu a teoria por trás da POO. A segunda semana forneceu um processo para segu ir ao aplicar essa teoria. Nesta semana, você reuni rá as lições das duas primeiras semanas, em um projeto de POO completo. Esse projeto fa rá você percorrer todas as fases do desenvolvi mento - do início ao fim do projeto. Seu projeto fin al será o desenvolvimento dojogo de cartas vinte-c-um. O Dia 15, o primei ro dia desta semana, o conduzi rá na pri meira iteração do j ogo e adicionará as funci onalidades básicas da ADO, através de im plernentaçãoe leste. Nos dias 16 e 17, você vai adicionar mais funcionalidades no jogo, através de mai s duas iterações. O Dia 18 mostrará como se acrescenta uma GUI ao sistema.
o Dia 19 forn ecerá uma outra maneira de imp lementar uma GUI, através do padrão de projeto PA C. No Dia 20, você vai aprender a adicionar vários jogadores não-humanos e verá como transformar seu jogo em um simulador. Finalmente, o Dia 21 reunirá tudo e retirará todas as aparas.
SEMANA
2
DIA Aprendendo a combinar teoria e processo A Semana I o ajudou a entender a teoria por trás da POO. A Semana 2 forneceu um processo
para segui r ao aplicar essa teoria. A Semana 3 mostrará a você como reunir as lições das semanas I e 2, apresentando um projeto de POO completo. Esse projeto o conduzirá por Iodas as rases do desenvolvimento, do início ao fim do projeto. A lição de hoje apresentará o jogo vinte-c-um, um jogo de cartas popu lar. No fin al da lição de hoje, você completará a análise, projeto e implementação funcional iniciais do jogo. Você não
criará um jogo virllc-c-um inteiro em uma única etapa grande. Em vez disso, você vai aplicar o processo iterat ivo durante a semana, à medida que completa o jogo vi nte-c-um. Hoje você aprenderá como:
•
Aplicar análise e projeto 00 em um jogo de carias real
•
Usar o processo iterativo para obter e ver resultados rápidos
• Evitar muitas tentações comuns do desenvolvimento • Evitar características procedurais que podem peneirar em seu programa
Jogo Vinte-e-um o vi llle-e-um é um jogo de cartas popular, onde o objetivo é obter a contagem mais alta de cartas do que a banca, sem ultrapassar 21. O objetivo desta semana é criar um jogo vi nte-e-um bastante
Dia 15
com pleto, escrito em Java, usando os conceitos de POO que você aprendeu por todo o livro. Embora alguns recursos sejam omitidos por mot ivos de ctareza e brev idade, você deverá ter um j ogo tão viciante e demorado quanto os jogos de paciência ou FreeCell que acompanham muitos sistemas operacionais populares.
ALERTA
o autor não se responsabiliza pelo t empo perdido enquanto se joga este joga i
Por que vinte-e-um7 A pergunta pode vir à mente, "por que vinte-e-um ?" O objet ivo de programar o vi nte-e- um não é transformá- lo em um jogador profi ssional. Ex istem dois mot ivos pam se usar o vinte-e-um como um projeto de roo introdutório: • Quase todo mundo está familiarizado com pelo menos um jogo de cartas. • Acontece que o jogo vi nte-e-um se encaixa bem no parad igma da roo. Em geral, a maioria das pessoas está fami liarizada com o domínio do jogo de cartas. Uma parte importante da AOO e do POO é ter um especialista no domín io presente, enquanto se identifica os casos de uso e o modelo de domínio. Si mplesmente não é possível conseguir um especialista no domín io como parte da conct usão destas lições. Um jogo de cartas não exige um especialista no domínio. Em vez disso, sua própria experiência e um bom li vro de regras ou site Web fornece tudo de que você precisa para completar a anál ise e o projeto por conta própria. Se ficar confuso, você pode até pegar um baralho e simular um cenário. Como resultado, um jogo de cartas é muito mais acessíve l do quc tentar aprender um domínio completamente desconhecido. Um dom ínio conhecido não o desviará do obj eti vo rea l destas lições - aprender a aplicar os princípios da 00. O vinte-c-um - na verdade, como a maioria dos jogos de cartas cm gera l - também tende a se encaixar bem no paradigma da roo. As interações entre bancas, jogadores, suas mãos e as cartas podem ajudá-lo a ver que um sistema 00 é constituído das interações entre os vários objetos. Os jogos de cartas também tendem a manter características procedurais suficientes para que seja fácil ver como uma estratégia de roo pode ser muito diferente de uma procedural, especialmente ao se aplicar regras de jogo.
Declaração da visão Ao se iniciar um projeto, freqUentemente aj uda começar com uma dcctaração da visão ou objetivoo O objetivo da declaração é formar o propósi to gera l do sistema que você va i criar. O objetivo
Apre nde ndo a combina r teoria e processo
349
da declaração não é capturar cada aspecto do problema que você está tentando resolver. Em vez disso, a declaração simplesmente infonna o intento e fornece um ponto de partida para a análise. Novo
TERMO
A declaraçlio da visão forma o propósito geral do sistema que você vai criar e do problema que está tentando resolver.
Aqui está a declaração da visão do sistema do j ogo vinte-c-um: O jogo vinte-e-um penni te que um jogador partic ipe dele, de acordo com regras de cassino comuns. Embora seja aparentemente evidente, essa declaração serve como um catalisador excelente para a análise.
Requisitos de sobreposição Antes de iniciar a análise, ajuda enumerar todos os requisitos de sobreposição. Exi stem apenas algumas restrições no jogo vinte-e-um. A ún ica restrição im portante agora é como o us uário vai interagi r com O sistema. O j ogo vi nte-e- um permite que o usuário interaja com o sistema através de uma linha de comando ou UI gráfi ca. É claro que a iterações iniciais penn itirão apenas uma interação através de uma interface de li nha de comando. O jogo vinte-e-um também deve ser implementado na linguagem de programação Java.
É importante listar tais restrições antecipadamente, para que você não apareça com um projeto que tome a obediência a essas restrições impossível.
Análise inicial do jogo vinte-e-um O Capítu lo 9, " Introdução à (AOO) Análise O rientada a Objetos", apresentou a AOO, assim cama o processo de desenvolvimento iterativo. De acordo cam as inrormações do Cap ítulo 9, este projeto segu irá um processo de desenvol vi mento iterativo e iniciará cada iteração desse processo com a análise. Ao se iniciar a aná lise, freqüentemente é util começar com a declaração da visão mencionada anteriormente: O jogo vinte-e-um permitc que um jogador participe dele, de acordo com as regras dc cassino comuns. Você pode usar a declaração para gerar uma lista in icial de perguntas. São essas perguntas que dirigirão sua análise.
350
Dia 15
As regras do jogo vinte-e-um A partir da declaração da visão do jogo vinte-c-um, você pode naturalmente perguntar, "q uais são as regras de cassino comuns dojogo vi nte-c-um?" Você vai usar essas regras para mode lar o jogo vi nte-c-um. O objclivo pri ncipal do j ogo vi nte-c-um é reunir uma mão de cartas cujo va lor seja maior que a mão da banca, sem ultrapassar 2 1. Cada carta recebe um valor numérico de 1 a II , onde oás va le I Oll 11 (dependendo do que orereça a você uma mão mel hor), as cartas numéricas valem os seus respectivos números e todas as cartas de fi g ura valem 10. O naipe não tem relação com o va lor da
carta. A Figura 15. 1 mostra alguns exemplos de mãos.
É clara que o jogo real é um pouco mais comp licado. Vamos ver cada componente importante do •
Jogo.
Aposta Antes quea banca distribua as cartas, cada jogador deve fazer uma aposta. Nonnalmente, a aposta está sujeita a algum li mite, como USS25 Oll USS50 por jogo.
Distribuindo as carta s APÓS cada jogador ter fe ilo uma aposta, a banca pode distribuir as cartas. Começando com o pri-
meiro jogador, a banca distribui urna carta aberta, para cada jogador, terminando consigo mesmo. Então, a banca repete esse processo, mas distribui sua própria carta fechada. A carta fechada da banca é conhec ida como carta ocu lta.
A distribuição de cartas termina quando a banca servir a cada jogador, incluindo ela mesma, duas cartas. O jogo começa quando a di stribuição terminar.
Jogando
o jogo pode diferir dependendo da carta aberta da banca . Se a carta aberta da banca valer
10 (chamado de valor 10) ou for um ás (valor I I), a banca deverá verificar sua carlaoculla. Se a carta oculta resul tar em um total de 2 I (chamado de 21 ou vi nte-e- um natural), o jogo tenninará automaticamente e se passará para o pagamento. Se a carta oculta não resultar em um total de 21 , o jogo começará nonnalmente. Se a carta aberta da banca não for de valor 10 ou um ás, o jogo passará automaticamente para o primeiro j ogador. Se o jogador tiver um 21 natural, ele tcrá bat ido e o jogo passa para o jogador seguinte. Se o j ogador não tiver 21, terá d uas escolhas: receber mai s cartas ou parar:
Aprendendo a combinar teoria e processo
FIGURA 15.1
ExemlJ/o de II/(ios do jogo rime-e-11m.
10 de Copas
10
Às de Paus
A
...
•10
= 21
5 de Ouros
2 de Copas
5
2
K
Damas de Copas
Q
=17
Às de Espadas
A
•
9 de Espadas
6 de Paus
•
6
•9
•10
2
• 9
10 de Espadas
10
•5 • Rei de Ouros
35 1
= 21
Valete de Espadas
J
...
=25
6
J
receber mais cartas - Se Ojogador não estiver satisfe ito com sua mão, ele pode optar por tirar outra carta, o que é chamado de receber mais cartas. O jogador pode receber ma is cartas até ultrapassar 21 (estouro) ou parar (não precisa de mai s cartas). parar - Se o jogador estiver satisfeito com sua mão, ele pode optar por parar e não receber mais cartas. Após o jogador estourar ou parar, o jogo passa para o jogador seguinte. Esse processo se repete até que cada jogador tenha jogado. Após cada jogador participar, a banca então joga suas cartas. Quando a banca completa sua vez, o jogo passa para o pagamento, onde ocorre a contagem de pontos e o pagamento.
Dia 15
Pagamento Após a banca tcnninar sua vez (ou saber que tem vinte-e-um pontos), o jogo passa para o pagamento. Durante o pagamento, cada jogador que estourou perde sua aposta; cada jogador com uma mão menor que a da banca perde s ua aposta; cada j ogador com uma mão melhor que a da banca ganha s ua aposta; e cada j ogador com uma contagem igual â da b..1nca empata e nenhum pagamento é feito. As apostas são pagas unifonnemente. Se um j ogador ti ver um vinte-e-um e a banca não, ojogador receberá uma vez e meia o que apostou. Por exemplo, se o j ogador tivesse apostado US$ IOO, receberia US$ 150 ([ I 00 • 3]/2).
Miscelânea Precisamos menc ionar mais alguns detalhes importantes sobre o jogo virlle-e-um:
O bara lho - O jogo vinte-c-um é jogado com quatro baral hos padrão de 52 cartas. Esses quatro baralhos são combinados em uma unica pilha de cartas grande. Nú mero de jogadores -
De um a sete jogadores podem j ogar vi nte-e-um .
Dobrando - Após receber suas duas cartas, o jogador pode opiar por dobrar. Quando o jogador dobra, ele duplica sua aposta, recebe mais uma carta e termina sua vez. Seguro - Quando a carta aberta da banca é um ás, o j ogador pode optar por fazer uma aposta segura. A aposta segura é igual â metade da aposta original. Se a carta ocu lta da banca fornecer â banca um 21 natural, o jogador não ganha nem perde. Se a carta oculta da banca não fornecer a ela o 21 , o jogador perderá sua aposta segura. Dividindo pares - Diz-se que um jogador tem um par, se as duas cartas iniciai s distribuídas tiverem o mesmo valor. Se o j ogador recebeu um par, ele pode optar por desdobrar as cartas em duas novas mãos. Se o jogador dividir o par, a banca di stri buirá a cada nova mão mais uma carta e, em seguida, o jogador deverá colocar uma aposta igual na nova mão. Um j ogador pode dividir qualquer par que resulte de urna divi são subseqllente (exceto quanto a um par de ases). Digno de nota também é o fato de que qualquer contagem 21 resultante de uma di visão não é tratada como um 2 1 natural. Uma vez fei ta a div isão, o j ogador aposta normalmente ou pára, para cada mão.
Identificando os atores Existem dois mot ivos para se fazer a análise . Através da análise, você cria o modelo de caso de uso: a descrição de como o usuário vai usar o sistema. Através da análise, você também cria o modelo de domínio: uma descrição do vocabulário princi pal do sistema.
O pri meiro passo na defini ção de seus casos de uso é defi nir os atores que usarâo o sistema. A part ir da descrição da última seção, é fácil ver que existem dois atores principai s no jogo vinte-e-um - 0(5) j ogador(es) e a banca.
Apre nde ndo a combinar teoria e processo
353
Esses dois atares respondem ã pergunta, "quem usará principalmente o sistema?" Através dos casos de uso, você pode defi nir como esses atores usarão o sistema.
Criando uma lista preliminar de casos de uso Uma mane ira efi caz de gerar os casos de uso in ic iais é perguntar o que cada um desses atores pode fazer. Os jogadores podem fazer o segu inte: I. Fazer apostas.
2. Pedir mai s cartas. J. Parar.
4. Estourar. 5. Consegu ir um vinte-e-um.
6. Fazer um seguro.
7. Divid ir pares.
8. Dobrar. 9. Decid ir jogar novamente. 10. Sa ir. A banca pode fazer o segui nte: I. Di stribu ir cartas.
,-.
Efetuar o jogo.
J. Receber ma is cartas.
4. Parar.
5. Esto urar. 6. Consegu ir um vinte-e-um.
Podem existi r mais casos de uso, mas essa lista fornece muito material inicial.
Planejando as iterações o Capítulo 9 apresentou o processo de desenvolvimento iterativo. Segu ir um processo de desenvolvimento iterat ivo permi tirá que você construa rapidamcntc uma implementaçl'lo de vinte-e-um simples. Cada iteração continuará a acrescentar mais fu ncionalidades no jogo.
Dia 15
Tal estratégia possi bilita resultados rápidos e quamificáveis. Seguir tal estratégia permite a você tratar dos problemas confonne eles apareçerem - não tudo de uma vez, no fina l do desenvolvimento. Uma estratégia iterativa impede que você seja atropelado por uma avalanche de problemas no final do desenvolvi mento. Fundamental para um desenvolvimento iterativo é o planejamento das iterações o melhor que você puder, desde o início. Quando novos fatos se apresentarem, é totalmente aceitável rever o plano; entretanto, um esboço das iterações desde o início, proporciona uma orientação para o projeto, objetivos e, mais importante, dá uma idéia do que se vai obter quando os objet ivos forem at ingidos. Normalmente, você planeja suas iterações classificando os casos de uso pela importância. Ao trabalhar com um cli ente, é melhor deixá-lo classificar a importància de cada caso de uso. No caso do jogo vinte-e-um, você deve classificar os casos de uso com base em sua importância para o jogo. Escolha os casos de uso absolutamente necessários para jogar e faça esses casos de uso primeiro. Outros casos de uso podem esperar iterações posteriores. Tal estratégia permite que você crie um jogo runcional o mais rápido possível. Para os propósitos do projeto do jogo vi nte-e-um, existirão quatro iterações principais.
Iteração 1: jogo básico A Iteração I criará o jogo básico. Ela refinará e implementará os casos de uso preliminares a segui r. Casos de uso do jogador: I. Receber mai s cartas. 2. Parar. 3. Estourar. Casos de uso da banca: I. Di stribuir cartas. 2. Receber mais erutas. 3. Parar. 4. Estourar. No final da Iteração I, você deverá ter um jogo que é jogado na linha de comando. O jogo terá dois participantes: a banca e um jogador. A banca distribuirá as cartas e pcnn itirá que cada jogador receba mais cartas, até decidir parar ou estourar. Após cada jogador jogar, o jogo terminará.
Aprendendo a combinar teoria e processo
355
Iteração 2: regras A Iteração 2 adicionará regras ao jogo. A Iteração 2 refinará e implementará os casos de uso preliminares a seguir: Casos de uso do jogador: 1. Consegui r um vinte-e-um .
A banca pode: 1. Efetuar o jogo (detectar ganhadores, perdedores e empates).
2. Conseguir um vinte-e-unt. No final da Iteração 2, tudo da iteração I ainda func ionará. Além d isso, o jogo detectará e ind icará quando um jogador ti ver um vinte-e-um, estourar, parar, ganhar, perder e empntar.
Iteração 3: aposta A Iteração 3 acrescentará aposta básica e em dobro ao jogo. A Iteração 3 refinará e implementará os casos de uso preliminares a seguir: Casos de uso do jogador: 1. Fazer apostas.
2. Dobrar. Casos de uso da banca: 1. Efetuar o jogo (para aposta).
No final da lteração 3, tudo dns Iterações 2 e 1 ainda funcionará. Além di sso, o jogo permiti rá aposta básica e em dobro.
Iteração 4: interface com o usuário Alteração 4 dará nlg uns toques fi nais na UI de linha de comando e construi rá uma UI gráfica. A lteração 4 refinará e implementará os casos de uso preli minares a seguir: Casos de uso do jogador: 1. Decidir jogar novamente.
2. Sair.
Dia 15
NO TA
Para os propÓSitos d este projeto, a aposta segura e a divisão de pares fo ram om itidas. Esses recursos são deixados com o exercício para o leitor . Vinte-e-um é u m jogo com muitas v ariantes. Freqüentem ente, o seguro não é permitido e a divisão complica demasiadamente o sistema. Considere essa vari ante como um novo livro d este, dedicado ao jogo vin te-e-uml l embre-se apenas de que você o uviu fa la r sobre isso primeiro aqui.
Iteração 1: jogo básico A lição de hoje o conduzirá através da prim eira iteração dojogo de cartas vinte-e-um . No fina l da lição de hoje, você terá o esque leto bás ico de um j ogo vi nte-c-um.
NO TA
Antes de ca da seção. talvez você queira deixar o livro d e lado e l entar faz er a análise, projeto o u impl ementação. Se você fizer su as próprias experiências, certifique·se de vo ltar, ler cad a seção e comparar seu trabalho com o material apresentad o, antes de continuar. Tente avaliar criteri osamente to das as discrepãncias entre sua sO lução e a sol ução do livro. Embora você possa ter uma solução superior (existem mu itas maneiras de enca rar esse jogo). Certifiq ue-se de estar correto e de que sua solução seg ue os princípios da poa.
Análise do jogo vinte-e-um A iteração de hoje refinará e implementará os casos de uso a seguir:
Casos de uso do jogador:
1. Receber mais cartas. 2. Parar. 3. Estourar. Casos de uso da banca: I . Distribuir ca rtas. 2. Receber ma is cartas.
3. Parar.
4. Estourar. Apôs ter um conj unto de casos de li SO refinados, você pode criar um modelo in icial do domínio e iniciar o projeto.
Apre nde ndo a combinar teoria e processo
357
Refinando os casos de uso Vamos começar com o caso de uso de distri buição de cartas da banca, pois essa ação inicia o Jogo. Primeiro, você precisa descrever o caso de uso em um parágrafo: Começando com o pri meiro jogador, a banca distri bui uma carta aberta para cada j ogador, term inando com ela mesma. Então, a banca repete esse processo, mas d istri bui sua própria carta fechada. A distribuição de cartas tennina e então o j ogo começa, quando a banca tiver distribuído para cada jogador, incluindo ela mesma, duas cartas. •
Distribuição de cartas: I. A banca di stribui uma carta aberta para cada jogador, inclu indo ela mesma. 2. A banca distribui uma segunda carta aberta para lodos os jogadores, menos para e la mesma. 3. A banca di stribui uma carta fechada para ela mesma .
•
Condições prévias: •
•
Novo jogo.
Condições posleriores: •
Todos os j ogadores e a banca têm uma mão com duas cartas.
Os casos de uso Jogador recebe mais carIas e Jogador pára seguem natural mente. Vamos começar com o caso de uso Jogador recebe mais carlOs: O jogador decide que não está satisfeito com sua mão. O jogador ai nda não esto urou; portanto, ele decide receber mais cartas. Se o j ogador não estourar, ele pode optar por receber mais cartas novamente ou parar. Se o jogador estourar, o jogo passará para o j ogador seguinte. •
Jogador recebe mais cartas: 1. O jogador decide que não está satisfeito com sua mão. 2. O jogador solicita outra carta da banca. 3. O jogador pode dec idir receber mais cartas novamente ou parar, se o lotai em sua mão for menor ou igual a 21.
•
Condições prévias: •
•
Condições posteriores: •
•
O jogador tem uma mão cuj o valor total é menor ou igual a 21. Uma nova carta é acrescentada na mào do jogador.
Alternativa: o j ogador estoura Uma nova carta faz a mão do jogador ultrapassar 21 . O jogador estoura (perde). Em seguida, começa a vez do próx imo jogadorlbanca.
Dia 15
Jogador pára é um caso de uso simples: O jogador decide que está satisfeito com sua mão e pára.
• Jogador pára: 1. O jogador decide que está contente com sua mão e pára. • Condições prévias: • O jogador tem uma mão cujo valor é menor ali igual a 2 1. • Condições posteriores: • A vez do jogador termina. Neste ponto, está se tornando claro que Jogador/Banca estoura não é um caso de uso, pois isso é um subproduto de outras ações. A banca e o jogador nunca executarão uma ação de estouro; en~ tretanto, os jogadores optarão por receber mais cartas ou parar. Com os casos de uso E:Nouro removidos, apenas os casos de uso Banca recebe mais carias e Bal1ca pára permanecem. Vamos começar com o caso de uso Banca recebe mais carIas: A banca deve receber mais cartas se o total em sua mão for menor que 17. Se a banca não estou~ rar após receber mai s cartas e o total em sua mão ainda for menor que 17, ela deve receber mais cartas novamente. A banca deve parar em qualquer total maior ou igual a 17. Quandoa banca es~ toura ou pára, o jogo termina. •
Banca recebe mais cartas: I. A banca recebe mais cartas se sua mão é menor que 17. 2. Nova carta acrescentada na mão da banca. 3. Se o talaI for menor que 17, a banca deve receber mais cartas novamente.
• Condições prévias: • A banca tem uma mão cujo total é menor que 17. • Condições posteriores: • Nova carta na mão da banca. • O jogo termina. • Alternativa: banca estoura •
A nova carta faz a mão da banca ser maior que 2 1. A banca estOura. Alternativa: banca pára A nova carta faz a mão da banca ser maior ou igual a 17. A banca pára.
Assi m como Jogador pára, Banca pára é relativamente si mples: A banca tem na mão um total maior ou igual a 17 e pára. •
A banca pára:
Aprendendo a combinar teoria e processo
359
4. A mão da banca tem um total maior ou igual a 17 e pára. • Condições prévias: • Mão da banca maior Oll igua l a 17 . • Condições posleriores: • Jogo tennina.
Modelando os casos de uso Para a Iteração 1, os casos de LI SO são mui to simples. Os modelos de caso de uso se riam mais pesados do que interessantes; portanto, eles serão om itidos. As interações entre a banca e os jogadores são um pouco mais in teressantes. A Figura 15.2 mostra a seq Uência de eventos seguida dentro do caso de uso Distribuir carias. A Figura 15.3 mostra a seqüência de eventos seguida dentro do caso de uso Jogador recebe mais carias. FIGURA 15.2 M ICO de Cartas
O diagrama da .çeqllé/lcia do caso de liSO Di stribui
cartas.
,,
J ogador
rl-~ __R~:~f: ~~a \-
___
. DistribuI carta aberta Recupera carta
-1- ____________ -O
..:. _I .
, uma carta
I
:
D'
~
~-----------_._------ --- --_~
,: Oistribui carta abarta , I
uma carta
f-
~
, ,I
:, - - - - - - - - - - - - '- - - Distribui carta aberta
-u;;:';c~;a
- - -
-1], ,, ,, ,,, ,,
Dia 15
FIGURA 15.3 O diagra/J/a da seqiil!lIcia do caso de liSO Jogador recebe mais cartas.
*-,,
Jogador
,,,
..*-,,,
MI'; "I '11l1::!
,, ,,,
~
,,
Solicita uma nova ca rta Recupera uma Ctlrta
------- -----<,J uma ca rta
Adic iona carta na mão
,, ,, ,, ,
,, ,, , Modelando o dominio Usando os casos de uso como base para o modelo de domínio, você pode isolar sete objetos do domín io diferentes: BlackjackGame, Oealer, Player , Card, Oeck, OeckPile e Hand (Jogo vinte-e-um, Banca, Jogador, Carta, Baral ho, Maço de cartas e Mão). A Figura 15.4 mostra o diagrama do modelo de domínio resultante.
Projeto do jogo vinte-e-um Aplicando projeto orientado a objetos na análise anteri or, você chegará a um modelo das classes principa is do projeto, suas responsabi Iidades e uma definição de como elas vão interag ir e obter suas in formaçõe s. Você pode então pegar o projeto e trabalhar na implementação.
Cartões CRC
o modelo de domínio fornece um bom ponto de partida para urna li sta inicial de objetos. Com essa lista de objetos, você pode usar cartões CRC para mostrar as várias responsabilidades e colaborações dos objetos. Antes de conti nuar, pode ser um bom exercicio para você, tent ar gerar uma lista de cartões CAC por conta prÔpria.
36 ,
Apre nde ndo a combinar teoria e processo
FIGURA 15 .4
Vinte-e-um
O /1/odelo de domínjo do jogo
7' ~
villle-e-/l/1/.
1... 7 Jogador
, ,
,
MOo
Banca
,
,
Maço de cartas
,<
,
0 ... 19
4
Carta
I
,
52
Baralho
As fi guras 15.5 a 15. 11 ilustram a possível saída de uma sessão de ca rtão CRC. FIGURA 15 .5
Vinte-e-um
O C(m(io CRC do
jogo
l,jllle-e-lIl11.
~ ~decartas
Oõiiii
~
,
~ ~ ~ ~ ~decertBS I
~. Banca
Dia 15
FIGURA 15.6
O caru10 CRC Baralho.
FIGURA
15.7
O camio CRC Corra.
Banca
Carta
Aprendendo a combinar teoria e processo
FIGURA 15.8
O caru10 CRC
Jogador
Jogador.
FIGURA 15.9
O cllrll10 CRC Banca.
Banca herda de jogador
363
Dia 15
FIGURA 15.10 O caru10 CRC Mt1o.
FIGURA 15.11 O cllrll10 Maço de cOrlas.
Mão
Maço de cartas
A UI de linha de comando
o Capítulo 13, ';00 e programação de interface com o usuário", apresentou o padrão de projeto MVC. A UI do jogo vinte-c-um utili zará o padrão de projeto MVC. Como resu ltado dessa decisão de projeto, você precisa adici onar um mecanismo observador no Jogador, assim como um objeto Console para apresentar os jogadores e recuperar entrada do usuário. Como existe apenas um Conso l e, o objeto Consol e é um candidato para o pad rão Singleton.
o modelo do jogo vinte-e-um Ao todo, nove classes e d uas interfa ces constituem o modelo de classe Blackj ack (vinte-e-um) completo. A Figura 15. 12 ilustra o modelo.
Aprendendo a combinar teoria e processo
365
FIGURA 15.12
O modelo de classe
alackjack.
MO"'"
'MI ):booI" • • _PiOre< 1";1'1• .,.,,, )
• newG.omel )
A próxima seção detalhará a implementação desse modelo.
A implementação As seções a seguir fornecem a implementação das partes principais do modelo ilustrado na Figura 15. 12.
NeTA
Todo o código-fonte está disponível para download na página da Web deste livro. Visite o endere ço www.samspubl i shi ng. com e na guia BookSt ore digite o numero ISBN 0672321092; em seguida, na página do livro Sam s Teach Yourself Object Oriented Programm ing in 21 Days, dê um clique no link Downloads e depois em Source Code.
A classe Card A classe Card (Carta) é implementada de fo nna muito parecida cam a classe Card apresentada no Capítulo 12, ·· Pad rões avançados de projeto" . A classe Rank (Número) mudou um pouco. A Listagem 15.1 apresenta a nova implementação da classe Rank (Número).
Dia 15
LISTAGEM 15.1 Rank.java i mpo rt java.utll.Col l ections; impo rt java.utll.list; impo rt java.ut il.Arrays ; public final cl ass Rank ( publ i c public public public pu bl ic publi c pub 1i c pu bli c publ ic publ ic publ ic pub l ic publ ic
static static sta ti c static static static static static static static static static stat i c
final fina l fi na l final fi na l fi na1 fi na l fi nal fi na1 final fi nal fina l final
Rank Rank Rank Rank Rank Rank Rank Rank Rank Rank Rank Rank Rank
TWO
THREE FOUR FIVE SI X SEVEN EIGHT NINE TEN
JACK QUEEN KING ACE
• oew • new • new • new • new • new • new • new • new • new • new • new • new
Rank( Rank( Rank( Rank( Rank( Rank( Rank( Rank( Rank( Rank( Rank( Rank( Rank(
2, "2" ) ; 3, " 3" ) ;
); ); ); 7 , "7" ) ; 8, "8" ) ; 4, "4" 5, " 5" 6, "6"
9, "9" ); 10, " 10" ) ; 10, "J" ) ;
lO, "Q" ) ; l O, " K" ); 11,
"A"
);
private stati c fina l Rank [] VAl UES = I TWO. THREE , FOUR, FI VE , SIX, SEVEN, EI GHT. NINE, TEN. JACK, QUE EN, KING, AC E I ;
II fornece uma lista nao modif icá ve l para fa zer laço publ ic sta tic final llst RANKS ,. Collections .unmodif iable Li st( Arrays . asList( VAl UES ) ) ; rank; private fi nal int private fi nal St ring di splay ; private Rank ( lnt rank,String display ) { this.rank a rank; this.display = di spl ay; )
public i nt getRank() { return rank;
I publi C Stri ng toSt ring() { return di spl ay ; I )
Aprendendo a combinar teoria e processo
367
Você notará que as constantes da classe Rank (Número) foram atualiL1das para refletiras valores numéricos do jogo vinte-e-um. A classe também foi alterada para comer um objeto público l ist não modificáve l, em vez do array modificáve l apresentado no Capitulo 12. Usar um objeto l is t nâo mod ificável impede mod ificações inadvert idas da enumeração li sL
As classes Deck e Deckpil e A classe Deck (Baralho) mudou consideravel mente em relação ao apresentado no Capitulo 12. A Listagem 15.2 apresenta a nova im plementação. LISTAGEM 15.2 Deck.java import java.uti l .lterator: import ja va.util . Ra ndom: pub li c class Deck ( private Card [) deck: private int index: pub l ic DedO { buildCardsO: }
public void addToStack( Deckpile stack) { stack.addCards( deck ): }
private voi d bui ldCardS() ( deck • new Card [52): Iterator suits
E
Su it. SUITS.iterator() :
in t counter • O: while( s uit s .hasNext() ) ( Sui t suit • (Suit) suits .next(); Iterator ranks • Rank.RANKS .itera tor() ; whi l e( rank s . ha sNext() ) ( Rank rank • (Ra nk)ranks . next(): deck[cou nter ] • new Ca rd( su'it, rank ) : counter++: } } } }
Dia 15
A classe Deck (Baralho) si mplesmente sabe como construir seus objelos Card (Cartas) e depois inseri r-se em Deckpi l e (Maço de cartas). A Listagem 15.3 apresenta Deckpi 1e (Maço de cartas). LISTAG EM 15.3 Deckp il e . java
impo rt java.ut i l.Arraylist ; import java.ut i l . l t erator; import java . util.Random ; publiC class Deckpile ( private Arraylis t stack - new Arraylist () ; private int index ; private Random rand = new Random() ; public void addCards{ Card [] cards ) { for( lnt i • O; i < cards.lengt h; i ++ ) { stack.add{ cards [i] ) i } }
pub l ic void shuf f le() reset(); randomizeO; randomizeO; randomi ze O ; randomizeO; }
I
pub l ic Card dealUp() ( Card ca r d a deal() ; if( card la nul1 ) { card.se tFaceUp{ true } re turn card ; }
l;
public Card deal0ownO ( Card card a deal(); i f( card ! - null )( card . setFaceUp( false ); } return cardi } publi C void resetO { index - O;
Aprendendo a combinar teoria e processo
LISTAGEM 15.3
369
Oeckpile.java (continuação)
lterator i .. stack.iterator(); while( 1.hasNext() ) I Ca rd card - (Card) i.next() ; card.setFaceUp(false); J J
private Card deal () { if( index ,- stack.size() ) I Card card - (Card) stack.get( index , ; i ndex++; return card ; J return null; J private void randomize() { int num_cards .. stack.size(); for( int 1 - O; i < num_cards; i ++ ) ( int index ~ rand.nextlnt( num_cards ) ; Card card_i .. (Card) stack.get( i }; Ca rd card_lndex .. (Card) stack.get( index ); stack.set( I, card_index }i stack.set( index, card_i };
J J J
A classe Deckpi le (Maço de cartas) sabe como em baralhar seus objetos Card (Cartas), di stribuí-los e acrescelllar objetos Card (Cartas) para si mesmo. Ao contrário da classe Deck (Baralho) origina l, Deckpile (Maço de carias) mantém uma referência para todos os objetos Card (Carta) que retorna. Desse modo, e le pode recuperar faci lmente todos os objetos Card (Carta) c se reconfigurar. Embora isso não modele completamente o mundo real, simplifica muito o gerenciamento de cartas. Entender o raciocínio existente por trás dessas alterações é importante. Oec k (Baralho) e Deckpi1e (Maço de cartas) implementam apenas os comportamentos de que o j ogo precisa. Essas classes não fornecem funcionalidade adicional ' apenas para o caso de precisarmos algum dia'. É impossível prever o futuro e você não tem quaisq uer requi sitos de funciona lidade extra; portanto, você não deve acrescentá-Ia até saber que precisa dela. Se você tentar implementar toda possi bi lidade de ' e se' ou realizar uma abslração prematura, nunca acabará de implementar suas classes. Se você conseguir temliná- Ias, são boas as chances
Dia 15
de que a funcionalidade ou abstração acrescentada não esteja correta. Além disso, você só terá mai s trabalho a fa7.er, pois preci sará manter uma funcionalidade que ninguém mais precisa ou usa. Tentar programar toda hipótese 'e se ' é um problema comum, encontrado por programadores in iciantes em 00. Você deve evi tara tentação de adicionar mais funcional idade do que a absolutamente exigida para seus objetos! Entretanto, você deve isolar as partes do s istema que sabe que mudarão.
As classes Player e HumanPlayer A classe PI aye r (Jogador) contém um objeto Hand (Mão). A Listagem 15.4 aprese nta a classe Hand. LISTAGEM
15.4 Hand.j ava
import java.util . ArrayList: i mport java.ut i l.lterator: publi c cla ss Hand { private ArrayList ca rtas · new ArrayList(); private sta t ic fina l i nt BLACKJACK z 21: publiC void addCard( Ca rd card )1 cards.add( card ): J publi c boolean bust() { i f( total() > BlACKJACK ) { return true: J return fa lse : J public vo 1d re se t() { cards . clear() ; J publi c void turnOver(){ lterator i z cards. ite rator(); while( i.hasNext() ) { Card card • (Card)i.next{) : card. se tFaceUp ( true } :
J J
Apre nde ndo a combinar teoria e processo
LISTAGEM 15 .4
37 1
Hand.java (continuação)
publiC String toString() { Iterator i • cards.iterator()j String string - ."j whi l e( i.hasNext() ) { Card card • (Card) i .next()j str ing · string + " " + card.toStringOj }
return stringj }
publi C int total () { int total · Oj Iterato r i • cards .i ter ator() j wh i le( i .hasNext() ) { Card card - (Card) i.next{)j total +- ca rd . getRank() .getRank() j } return t ot a I j } }
A classe Hand (Mão) sabe como adicionar objetos Card (Can as) em si mesma, reconfigurar-se, virar suas canas, calcular seu total e representar-se como uma String. Você pode notar que a classe Hand (Mão) conta ases apenas como II. A próxima iteração acrescentará supone para contagem de ases como I ou I I . As listagens 15.5 e 15.6 apresentam as classes PI ayer (Jogador) c HumanPI ayer (Jogador Humano), respectivamente. LISTAGEM 15 .5
Playe r .java
impor t java.uti l . ArrayList j import java.util.rterator j pub l ic abst ract class Pl ayer { private Hand hand j private String name j private ArrayList l isteners • new ArrayList()j public Pl ayer( St ring name , Hand hand ) { this . name • name j this . hand • hand j }
Dia 15
LISTAGEM 15.5 P1ayer.java (continuação) publi C vo id addCard{ Ca rd card ) I hand. addCa rd( card ) ; notifyLis teners(); }
public void play( Dealer dealer ) { II como antes, joga até que o jogador estoure ou pare while( !isBusted() &&hit() ) ( dealer.hlt( this ); }
II mas, agora, diz à banca que o jogador te rminou , caso contrário, nada II acontecerá qua ndo o jogador retornar stopPlay( dealer ); }
publi c vo i d re set() { hand.rese tO; }
publi c boolean isBusted() ( return hand.bu st(); }
publiC void addL is tener{ PlayerListener 1 ) ( lis teners.add( 1 ); }
publ iC String toString() { return ( name + ":" + hand.toString() }; }
protected Hand getHand() { return hand; }
protected void notify Lis teners() { Iterator i · l i steners . iterator(); while( i .hasNext() ) { PlayerListener pl • (PlayerListener)i.next() ; pl.handChanged( th is ) ; } }
lU • A chamada de pas sT urn DEVE ser dentro de um método proteg i do. Oealer
Apre nde ndo a combinar teoria e processo
LISTAGEM 15.5
373
P1ayer.java (continuação)
* precisa sObrepor esse compor tamento! Caso contrário, O l aço será infinito.
"/ protected void stopPlay( Dea l er dea l er ) { dea l er.passTurn(); J
protected abstract bool ean hit(); J
LISTAGEM 15 .6
HumanPlaye r .java
pub l ic class HumanPlayer extends Player { private private private private
fi na 1 f i na 1 final final
static static static static
String String String String
, "H" ; STAND , "S" ; MSG • "[H] it or [5] t ay' ; DEFAULT , "inva l id":
HIT
pub l ic HumanPlayer( String name, Hand hand ) I supere name , hand ); J
protected boolean hi t() { while( true ) I Console.lNSTANCE.printMessage( MSG ); String response • Console.INSTANCE.readI nput( DE FAULT ): if( response.equalslgnoreCase( HIT ) ) { return true; I else if{ response.equalsIgnoreCase( STANO ) ) I return f alse : J
II se chegarmos aqui , faz um laço até obtermos entrada significat i va J J J
A classe abstrata Player(Jogador) define todos os comportamentos e atributos comuns a objetos Player (Jogador) e Dealer (Banca). Esses comportamentos incl uem mani pular a classe Hand (Mão), controlar objetos Pl ayerli stener e jogar uma rodada. Pl ayer de fi ne um método abstrato: public bool ean hit(). Durante o jogo, a classe base Pl ayer (Jogador) fa rá uma chamada para esse método, para detenni nar se deve continuar a receber carIas ou parar. Subclasses podem implementar esse método para fornecer seus próprios comporta-
Dia 15
mentos específi cos. Por exemplo, HumanPlaye r (Jogador Humano) pergunta ao usuário se vai conti nuar recebendo cartas ou não ou se va i parar. Quando o objeto Pl aye r (Jogador) acaba de jogar, ele informa o objeto Oealer (Banca), chamando o método pass TurnO de Oeal er (Banca). Quando o objeto Pl ayer (Jogador) chama esse método, Oea ler ( Banca) diz a next Player (Jogador) para que jogue.
Dea 1er - Banca Deal er (Banca) é uma interface que especi fica os métodos extras que um objeto Dea 1er (Banca) vai expor. A Li stagem 15.7 apresenta a interface Dea ler (Banca). LISTAGEM 15.7 Deale r .java pub l ic interface Dealer ( publ ic vo i d hlt( Player jog ador )i publ iC void passTurn( );
I A Li stagem 15.8 apresenta Bl ackjackOealer. LISTAGEM 15.8 BlackjackOealer.java import java . utl1.Arrayl is ti import java. ut i l . l terator; public class Bl ackjackDealer extends Player i mplemen t s Dealer ( private Deckpile ca rdsi private Arraylist players • new Arraylist()i private int player_ l ndex i publi C Bl ackjackDea l er( Stri ng name , Ha nd hand , Deckpl1e cards ) ( super( name, hand )i this.cards • card s i
I pub l ic void passTurn() ( i f( player index != players.size() ) ( Player pl aye r • (Player) players.get( playe r_index )i player_index++i player.play( th is ) i ) el se {
Aprendendo a combinar teoria e processo
375
LISTAGEM 15.8 8lackjackDealer.ja va (continuação) this.play( this li
I I publiC void addPlayer( Player player l I players.add( player )i
I public vold hlt( Player player l { player.addCard( cards . dealUp() )i
I
II sobrepõe para que a banca mostre suas cartas antes que comece a jogar public void play( Dealer dealer ) I exposeCardsO j super.play( dealer )i
I pub l iC void newGame() I II dis tribui as cartas e diz ao primeiro jogador para começa r
d"'(}; passTurnO i
I publ i c void deal () I cards.shuff le(); II reconfigura cada jogador e distribui uma carta aberta para cada um e para si mesma Pl ayer [] player • new Player [players.size()] i players.toA rray( player li for( int i • Oi i < player. length i i ++ ) { player [1].resetO i player [1] .addCard( cards.dealUpO l i
I this.addCard( ca rd s.dealUp() ) i
II dis tribui mais uma ca rta aberta para cada jogador e uma fechada para si mesma for( int i ., Oi i < pl ayer . length i i ++ ) { player (i]. addCard( cards.dealUp() )i
I this . addCard( cards.dealDown(l li
Dia 15
LISTAGEM
15.8 8lackjackDea l er.java (continuação)
} protected vo1d stopPl ay ( Oealer dea ler ) { II não faz nada aqui , na banca, s i mplesmente deixa o jogo parar II se i sso não fo sse sobreposto , chamaria passTurn() e II faria um laço infinito }
protected boo l ean hit() { if( getHand().total() return true; } return false ; }
<-
16 ) {
private vo1d exposeCards ( ) { getHand() . tu r nOver() ; notifylisteners() ; } }
BlackjackOea ler ( Banca21 ) herda de PI ayer (Jogador) porque um objeto Oea ler ( Banca) tam~ bém é um objeto Pl ayer (Jogador). Além dos comportamentos forn ecidos por um objeto PI ayer (Jogador), a banca também contém o objeto PI ayer (Jogador), que di stribui canas para esses objetos PI ayer (Jogador) e diz a cada um para que j ogue, quando chega s ua vez. Quando um objeto PI ayer (Jogador) chama o método passTu rn () de Oealer (Banca), ele sabe deixar o próx imo objeto PI ayer (Jogador) j ogar. A Figura 15. 13 il ustra a interação entre a banca e os jogadores. 8lackjack Dea ler (Banca2 1) sobrepõe seu método stopPI ay () para q ue ele term ine de jogar. A banca também implementa o método hit ( ) para que e le retorne t rue quando a mão fo r menor que 17 e false quando a mi'lo for igual ou maior que 17.
BlackjackGame (Jogo 211 As listagens 15.9 e 15. 10 apresentam as classes Bl ack j ack e Conso l e, respectivamente. LISTAGEM
15.9 81ac kj ack . java
publ; c cl ass Blackjack { publ ic s tat; c vo1d ma in ( St r ing [J args ) Oec kp ile cards
=
new Oeckpi le() ;
I
Aprendendo a combinar teoria e processo
377
LISTAGEM 15.9 8la ckj ack.java (continuação) for{ int i • O; i < 4; i ++ ) ca rds .shuffle () ; Oeck deck • new Deck{ ) ; deck.addToStack ( cards ); cards.shuffle () ;
(
J
Hand dealer hand • new Hand(); Blac kjackDealer dea ler • new BlackjackDealer( "Dealer", dea le r_hand, cards ); Hand human_hand • new Hand(); Pl ayer pl ayer · new HumanPlayer( "Human", human hand ); dealer.addlistener( Conso le.INSTANCE ) ; player.addlistener( Console. INSTANCE ) i dealer . addPlayer( player ); dealer.newGame( ); J J
FIGURA 15.13 II illferaçlio elllre jogadores e a bal/ca.
Banca v inte-e·um
,
O ,
plaV Idealer)
Jogador
>
n.
,, ,
O·,
,,
Jogador
/ hitO
Enquanto estourado e pedindo maiscartu hil()
hit (plaver)
;» addCard (card)
,, ,,
passTurn( )
'r' r'l-
stopPlav()
,, ,, ,,
,, plav (dealer)
U
,,
Dia 15
LISTAGEM 15.10
Console.java
i mpo rt java.io . BufferedReader; i mpo rt java.io . InputStreamReader; impo rt java.io . IOException; publi C cla ss Console implements Playerlistener (
II console si ngl eton public final static Console INSTANCE
=
new Con sole() ;
private BufferedReader in new BufferedReader( new InputStreamReader( System.in ) }; K
public void printMess age( String message ) { System .out.print l n{ message };
I publiC String readlnput( String default_input ) ( String response ; try ( return in.read li ne() ; ) ca tch (IOException ioe) { return default_input;
I I public void handChanged( Player player ) I printMessage( player.toString() };
I
II pri vate para impedir instancia çã o private Console() {I
I A classe Blackjack constrói objetos Dea ler (Banca), Hand (Mão), Deckpile (Maço de cartas),
Deck (Baralho) e conecta todos eles. Uma vez conectados, ela começa o jogo dizendo ao objeto Oea1er (Banca) para que inicie um novo jogo. Consol e é um si ngleton que dá acesso à linha de comando. Ele também recebe dados dos objetos Player (Jogador) e, em seguida, os imprime na tela, sempre que eles são atualizados.
Uma armadilha procedural Se você vem de uma base procedural, poderia ser tentador implementar o método newGame () de BlackjackDeal er como ilustrado na Listagem 15.11.
Aprendendo a combinar teoria e processo
LISTAGEM 15.11
379
Uma impl ementação procedural de BlackjackOea l er
publ1c void newGameO { cards . shuffle();
II II
reconfi gura cada jogador e distribui uma carta aberta para cada um e para si mesma Player [) player • new Player [players . size(»): players.toArray( player ) : for( i nt 1 • O: i < player.length: i ++ ) { player [i).reset(); player [i].addCard( cards.deaIUp() ) ;
I this.addCard( cards .dealUp() ):
II
distribui mais uma carta aberta para cada jogador e uma fechada para si mesma for( int i • O; 1 < player.length; i ++ ) { player [i ].addCard( cards.dealUp() l;
I this . addCard( cards.dealOown() ) :
II
cada jogado r joga e depo i s a banca for( int i • O; i < player.length : i ++ ) player [l).play(this):
I
I exposeCa rd s (); this.play( this ) ;
I Essa implementação e limi na a necess idade do método passTurn () ; entretanto, essa é uma estratégia procedural para o laço do j ogo. Em vez dos objctos PI ayer (Jogador) comuni carem ao objeto Dealer (Banca) que terminaram de jogar, Dealer (Banca) simplesmente faz um laço seqUencia l através dos PI ayers (Jogadores). A Figura 15. 14 ilustra a interação entre Oea 1er ( Banca) e seus objetos PI ayer (Jogador).
Você notará que as interaçõcs da Figura 15. 13 são muito mais dinâmicas do que as interaçõcs que ocorrem na Figura 15. 14. A Figura 15.13 é verdadeiramente um sistema de objetos interagindo. Na Figura 15. 14, Dea l er ( Banca) simplesmente espera que o objeto PI ayer (Jogador) retome e, então, chama seq Uencial mente o próximo objeto PI ayer (Jogador). Não há nenhuma interação além de Oea l er ( Banca) dizer estaticamente para que cada objeto PI ayer (Jogador)jogue em sua vez. Embora essa estratégia func ione, ela não é muito nexível e certamente não é tão orientada a obj etos quanto a estratégia apresentada na Figura 15. 13.
380
Dia 15
FIGURA 15 .14 A illleraç(io procedI/mI
elflre Players Oogadores) e Dealer (bal/ca).
,
,
Banca
Jogador play (deaferl
Jogador loap até estourar
ou parar
---.J hit () hit (playe r) addCard (card) play (deaferl
hit ()
hit (playar) addCard (card)
play (deaferl
,
,, ,,
, ,, ,,
•••
Testando Confonne mostrou o Capítulo 14, "Construindo software confi ável através de testes", os testes devem ser um processo di nâmico. Um conjunto completo de testes está disponível,j unto com o cód igo-fonte, para download. Esses testes consistem em um conj unto de testes de unidade e objetos fa lsificados que testam com pletamente o jogo vinte-c-um. O estudo dos testes será deixado COmO um exercíc io importante para o leitor.
Resumo Hoje, você ana lisou, projetou e implementou um jogo vinte·e·um básico. Usando uma estratégia iterativa, você obteve os resultados que puderam ser vistos rapidamente. No decorrer da semana, você continuará a acrescentar funcionalidades nesse jogo vinte-e-um. Você também aprendeu algumas lições novas hoje. Ao programar, você precisa evitar cair nas annadi lhas procedura is, mesmo que uma estratégia procedural possa parecer a solução nat ural. Você também deve aprender a evi tar a tentação de acrescentar mais detalhes em uma defin ição de classe do que precisa.
Aprendendo a combinar teoria e processo
381
Perguntas e respostas P Se os testes são tão importantes, por que você os pulou ? R Não pu lamos os testes. Todos os côd igos-fonte que podem ser carregados por download estão repletos de casos de teste. O texto pu lou a discussão dos testes por restrições de espaço e eficácia. Não achamos que você teria gostado, se fosse obrigado a ler incontáveis páginas de código de teste. O estudo e o entendimento do cód igo de teste (e, na verdade, todo o código) são deixados como exercicio para o lei tor. P Parece huver muito mais cód igo do que o publicado no capítulo -o que aconteceu? R Há muito mais cód igo. Simpl esmente não é possíve l abordar todo o código efi cientemente dentro do texto. Cons idere o tcxto como o 'fi lme dos destaqucs' do código. Você precisará dedicar uma quantidade considerável de estudo pessoal para entender o código comp letamente. O objetivo destes capít ulos finais é apresentar um projeto global, do qual o código é apenas um componente. A análise e o projeto são igualmente importantes. Você precisará dedicar um tempo extra dentro dessa estrutura, estudando o código, se quiser entendê-lo totalmente.
Workshop As perguntas e respostas do teste são fornecidas para seu melhor entendimento. Veja as respostas no Apêndice A, "Respostas",
Teste J. Liste dois dos padrões de projeto que você viu hoje. Onde os padrões foram usados? 2. Encontre um exemplo de polimorfismo no código-fon te. 3. Encontre um exemplo de herança no cód igo-fonte. 4. Como a classe Oeck (Bara lho) encapsula suas cartas? 5. Como 61 ackjackOea l er (Banca 21) e HumanP1 ayer (J ogador Humano) aluam de forma polimórfica?
Exercícios I. Faça o down load do código-fonte da iteração de hoje. Após tero côd igo, compile-o, faça-o funcionar executando o jogo vinte-c-um e, depois, tente entender como ele funciona. Ter um entendimento lotai do código exigirá algum tempo e paciência. 2. A lição de hoje foi bastante longa. Não há outros exercícios. Examine o código-fome e reveja a lição.
SEMANA
3
DIA Iteração 2 do jogo vinte-e-um: adicionando regras Ontem, você rea lizou a análise e projeto iniciais de um jogo vi lllc·e·uTn . Hoje, você conti nuará esse processo e vai adicionar mais regras no jogo vinte-c- um. Hoje você aprenderá como:
• Modelar os estados do jogo vi nte-c-um •
Usar estados para rem over lógica cond icional
Regras do jogo vinte-e-um Ontem, você construi LI um jogo vinte-c-um simples. O jogo que você projetou e construi u di stribuía canas e pennit ia a um jogador jogar até parar ou estourar. Umjogo vinte-c-um rea l fará um pouco mais. Em um jogo vi nte-c-um real, os ases valem I ou 11 pontos. Osjogadores podem obter um vi nte-e-um, estourar, empatar, perder ou ganhar. A banca nem mesmo jogará se todos os jogadores estourarem ou se receber uma mão que dê vinte-e-um. Hoje, você vai adicionar a lógica necessária para suportar esses e outros recursos adicionai s no jogo. Como sem pre, você começará anali sando os casos de uso.
Dia 16
Análise das regras Para entender com pletamente todas as regras do j ogo vinte-e·um, você precisará rever cada um dos casos de uso de ontem e acrescentar todos os novos casos de uso exigidos. Uma vez identi fi· cados os casos de uso, você precisará atualizar o modelo de domín io.
Análise de caso de uso das regras do jogo vinte-e-um A adição de regras afeta muitos dos casos de uso identificados ontem. Existe também um novo caso de uso: Banca efelllajogo. Vamos começar com o caso de uso Distribuir cartas, que você descobriu ontem: Começando com o prime iro jogador, a banca distribu i uma carta aberta para cada jogador, terminando consigo mesma. A banca repete então esse processo, mas di stribui sua própria carta fechada. A distri bu ição de cartas termina e então o jogo começa, quando a banca tiver distribuído para cada j ogador, inc luindo ela mesma, duas cartas. o
Distribuir cartas I. A banca distribu i uma carla aberta para cada j ogador, incluindo ela mesma. 2. A banca d istribui uma segunda carta aberta para todos os jogadores, menos para ela. 3. A banca distribui uma carta fechada para e la mesma.
•
Condições prévias o
•
o
Novo j ogo.
Condições posteriores •
Todos os jogadores e a banca têm uma mão com duas cartas.
•
A vez do primei ro j ogador que não tenha vinte-e-um (d uas cartas totalizando 2 1), começa.
•
O j ogo cont inua para cada j ogador que não tenha vi nte-c-um.
Alternativa: A banca tem vi nte·e-ulll Se a banca tiver um 2 1 natural, o jogo passará para o cumpri mento. Os jogadores não terão s ua vez.
O caso de uso Distribuir cartas acrescenta agora várias novas condições posteriores, assim como uma alternati va. O importante a notar é que, se a banca tiver vinte-e- um , o jogo term inará automaticamente. Do mesmo modo, qualquer jogador com vinte-e-um não joga. Em seguida, reveja Jogador recebe mais cartas: O jogador decide q ue não está satisfeito com sua mão. O j ogador ainda não estourou, de modo que decide receber mais cartas. Se o jogador não estourar, ele pode optar por receber mais cartas novamente ou parar. Se o jogador estourar, o jogo passará para o j ogador segui nte.
Iteração 2 do j ogo v inte-e-um: adicionando regras
o
385
Jogador recebe mais cartas I. O jogador decide que não está satisfeito com sua mão. 2. O jogador soli cita outra carta da banca. 3. O jogador pode dec idir receber mais cartas novamente Oll parar, se o total em sua mão for menor Oll igual a 2l.
• Condições prévias • O jogador tem lima mão cujo valor tOlal é menor ou igual a 2 J. o O jogador não tem vinte-c-um. o A banca não tem vinte-e-um. o Condições posteriores o Uma nova carta é ac rescentada na mão do jogador. o Alternat iva: O jogador estoura
o
A nova carta faz a mão do jogador ser maior que 21. O jogador estoura (perde); a vez do próxi mo jogador/banca começa. Alternativa: mão do jogador > 21, mas o jogador tem um ás A nova carta faz a mão do jogador ser maior que 21. O jogador tem um ás. O valor do ás muda de II para I, fazendo a mão do jogador ser menor ou igual a 21. O jogador pode decidir receber cartas novamente ou parar.
Digno de nOla é o fato de que um jogador sô pode jogar se ele ou a banca não tiver vinte-c-um. Esse caso de uso também introduz o fato de que um ás pode ter um valor igual a I ou I I, dependendo do que toma a mão mel hor.
Jogador pára também recebe mais algumas cond içõcs prévias. O jogador dec ide que está sat isfeito com sua mão e pára. o
Jogador pára 1. O jogador decide que está contente com sua mão e pára.
o
o
Condições prévias o O jogador tem uma mão cujo valor é menor ou igual a 21. o O jogador não tem vinte-e-um. o A banca não tem vinte-e-um. Condições posteriores o A vez do jogador tcnnina.
Assim como em Jogador recebe mais carias, você precisará alUalizar Banca recebe mais carias: • A banca deve receber mais cartas se o total de sua mão for < 17. Se a banca não estourar, após receber mais cartas, e o lotai de sua mão ainda fo r < 17, ela deverá receber mais car-
Dia 16
•
tas novamente. A banca deve parar em qualquer total >= 17. Quando a banca estourar ou parar, o jogo termina. Banca recebe mais cartas I. A banca recebe mais cartas se sua mão for menor que 17.
2. Nova carta acrescentada na mão da banca. 3. O total é menor que 17, a banca deve receber mais cartas novamente. • Condições prévias • A banca tem uma mão cujo total é menor que 17. • Deve ser um jogador no estado de parada. • Condições posteri ores • Nova carta na mão da banca. • Jogo termina. • Alternati va: Banca esloura •
A nova carta faz a mão da banca ser maior que 21; a banca estoura. Alternativa: Banca pára A nova carta faz a mão da banca ser maior ou igual a 17; a banca pára.
•
Alternativa: Banca lem ás, pára A nova carta faz a mão da banca ser maior ou igua l a 2 1, mas incl ui um ás. O valor do ás muda de I I para I, fazendo o lotai da banca ser 17; a banca pára.
•
Alternativa: Banca tem ás, recebe mais cartas A nova carta faz a mão da banca ser maior ou igual a 21, mas inclui um ás. O valordoás muda de I I para I, fazendo o talai da banca ser menor que 17; a banca recebe mais cartas.
Esse caso de li SO acrescenta várias condições prévias e variantes . A condição prévia, "Deve ser um jogador no estado de parada", signifi ca que a banca só receberá mais cartas se houver um jogador para bater. Se nen hum jogador esti ver parado, isso signifi ca que todos os jogadores estoufaram ou têm vinte-C-um . Além disso, as novas alternati vas levam em conta o tà to de que um ás pode ser contado como J ou como I I. Do mesmo modo, a banca parará automat"icamente, se não houver nenh um oUlrojogador parado. A banca tem uma mão cujo total é >-= 17 e pára. •
Banca pára I. A mão da banca dá um talai maior ou igual a 17 e pára.
• Cond ições prévias • Mão da banca maior ou igual a 17. • Deve ser um jogador no estado de parada.
Iteração 2 do j og o v inte-e-um: adicionando reg ras
•
Cond ições posteriores •
o
387
Jogo tenn ina.
Alternativa: nenhum j ogador parado Se não houver nenhum jogador parado, a banca parará automaticamente, independente da contagem da mão.
Quandoojogo tiver terminado, a banca precisará descobrir quem ganhou, quem perdeu e os em pates. O caso de uso Banca efelllajogo trata desse uso: Após todo o j ogo ter terminado, a banca verifica cada mão e determi na, para cada jogador, se e le ganhou ou perdeu, ou se o j ogo deu empate. •
Banca efet ua jogo I. A banca veri fi ca a mão do primei ro jogador e a compara com a s ua própri a. 2. A mão do jogador é maior que a da banca, mas não estourou; o j ogador ganha. 3. A banca repete essa comparação para todos os jogadores.
•
•
Cond ições prévias •
Cada jogador passou por sua vez.
•
A banca passou por sua vez.
Condições posteriores •
•
Al ternativa: Jogador perde •
•
A banca verifica a mão do jogador e a compara com a sua própria. A milo do jogador é menor que a da banca . O j ogador perde.
Alternativa: Empate •
•
Resultados fi nais do j ogador detenn inado.
A banca verifica a mão do j ogador e a compara com a sua própria. A mão do jogador é igua l à da banca. O jogo deu em pate.
Alternat iva: Banca estoura •
Se a banca estourou, cada j ogador que esti ver parado e com vinte-e-um, ganha. Todos os outros perdem.
Modelando os casos de uso A maior parte das alterações nos casos de uso é simples. Entretanto, pode ser úti l desenhar a seqüência de eventos para Banca elewajogo. A Figura 16 .1 ilustra a seqüência de eventos encontrada no caso de uso Banca ele/lia jogo.
I
388
Dia 16
FIGURA 16 .1
O diagrama da seqiil!l1cia do caso de liSO Banca cfctua jogo.
..*',
*',
*',
Jogado,
~
Jogador
,
Solic:ite mio
~---- - --- --mio- Compara mios
,, ,, ,,, ,, ,
'O,, ,
Informa togado. do resultado I
,, ,, , ,, I
n.
Atualizando
O
Compa re mAas
Informa j ogador do resultado
mão
,, ,,
modelo de dominio do jogo vinte-e-um
Da perspectiva do domínio, esses casos de uso novos e atualizados não o aheram.
Projeto das regras Neste ponto, pode ser extremamente tentador ir diretamente para a implementação. Superfi cial mente, parece q ue você pode implementar as regras através de condicionais. Na verdade, você pode impl ementá- Ias através de cond icionais. A Li stagem 16. 1 apresenta como poderi a ser uma dessas condiciona is. LISTAGEM 16.1 Reg r as condicionais den t r o de Bl ac kjackOealer ( Banca 21)
prot ec t ed voi d stop Pl ay ( Deal er deale r ) {
1/ o j ogo termi nou , i dent i f i ca os ganha do res e os pe rdedores i f ( isBusted () ) { Iterator i .. playe rs . i t era to r () ; whil e( 1. hasNext () ) { Player playe r .. (P l ayer) i . nex tO ; 1f( !player. i sBus ted() ) { Console. INSTANCE . prin tMessage( player. toSt r ing() .. " WINN ER!!" ) ;
I I } else {
Iteração 2 do j ogo v inte-e-u m : adicionando regras
389
LISTAGEM 16. 1 Regras condicionai s dentro de BlackjackDealer (Banca 2I) (cont . )
;t( hasBlackjackO ) (
lterator; • players.iterator()i while( i.hasNext() ) ( Player pl ayer = (Player) i.next() i i f( player.hasBlackjack() ) ( Console. INSTANCE . printMessage( player.toString() + " STANOOF F!!- )i I else ( Console.INSTANCE.p rintMessage( player.toStr1ng() + " LOSER!! " )i
I I
I else ( II a ban ca não estourou e não tem vinte-e - um
M
• " " "
Iterator i • players.iteratorO ; wh1 1e( 1. hasNext() ) ( Player player • (Player) i .next(); if( player.hasBlackjack() ) { Console .I NSTANCE.prin tMessage( player . toString() + WINNER WITH BLACKJACK!!" ); I el se if( pl ayer.isBusted() ) ( Console.INSTANCE.pr intMessage ( player.toStri ng() + BUSTED! !" ); I else if( pl ayer.getHand().greaterThan( getHand() ) )( Console.INSTANCE.p r intMessage( player.toString() + WINNER! !" ); I else if( pl ayer .getHand().equalTo(getHand() ) ) ( Console.INSTANCE. printMessage( player.toString() + STANDOFF! !" ) i I else ( Console.INSTANCE .printMessage( player.toString() + LOSER !!" );
I I I I I É claro que essa condicional trata apenas do cum primento do jogo. A banca precisará de muito mais condicionais para saber se deve ou não começarajogar após a distri buição e se deve ou não deixar que um jogador jogue. Por exemplo, se a banca tiver uma mão com vinte-e-um, o jogo deverá terminar automaticamente. Você precisará de uma condicional para esse e todos os outros desv ios do jogo.
Dia 16
Tal estratégia é frági l, difici I de manter, propensa a erros e simplesmente ho rrível. Ao tratar com condiciona is, você freq Uentemente verá que o acrêscimo de uma nova condicional faz um antigo comportamento fa lhar. Também é extremamente dific il entender código repleto de cond icionais. Boa sorte para aqueles que precisam manter tal bagunça condic ional. As estruturas cond icionais não são pa rt iculannente orientadas a objetos. O uso incorreto de condicionais estraga as div isões corretas de responsabi lidades, que são tão importantes para a POO. Ao usar condic ionais, a banca degenera para uma função procedural que verifica um fi ag no jogador, toma uma decisão e, então, diz ao j ogador o que fazer. A 00 não funciona assim! As cond icionais têm seus usos; entretanto, elas nunca devem extrair responsabilidades de um objeto. Em vez di sso, o conhecimento de se um jogador estourou ou se tem vinte-c-um deve estar contido dentro do próprio objeto Pl ayer (Jogador); então, quando um desses eventos ocorrer, o objeto Player (Jogador) poderá executar a ação correta e informar a BlackjackDeal er ( Banca 2 1), se apropriado. Em vez da banca instruir os jogadores sobre o que fa zer, os jogadores devem usar seus próprios estados internos para tomar essas deci sões. Tal estratég ia mode la mais preci samente o jogo vinte-e-um real. O pri meiro passo para se desfazer das condicionais é perceber quai s eventos e estados di rigem o jogo vinte-e-um. Por todo o j ogo vinte-e-um, os vários jogadores se movem através de estados. Em um ponto o jogador está esperando; depois está j ogando. Após j ogar, o jogador passa para um estado dc espcra ou de estouro. Do mesmo modo, a banca passa da distribuição para a espera por sua vez, para o jogo e, fi nalmente, para a parada ou estouro. Também existem transições de estado alternativas. Após receber s uas cartas, a banca ou o jogador pode mudar automaticamente para o estado de vinte-c-um, se receber uma mão quc totalize v inte-e- um. Para ter total entend imento dos vários estados. assim como dos eventos que movem o jogador de um estado para outro, ajuda modelar os diversos estados através de um diagrama de estado.
Diagramas de estado A UM L defi ne um rico conj unto de notações para modelar diagramas de estado . Em vez de nos perdermos nos deta lhes, usaremos apenas os aspectos necessári os para modelar o j ogo vi nte-e-um. Para os propósitos da mode lagem do j ogo vinte-e-um, ex istem estados, transições, eventos, atividades e condições. No jogo vinte-e-um, um estado é a cond ição de jogo corrente de um jogador. Tais condições incluem espera, j ogo, estouro, parada e vi nte-e-um , dentre outras. As transições ocorrem q uando um j ogador se move entre seus estados. Por exemplo, um jogador muda do estado de j ogo para o estado de estouro, quando ele estoura.
Ite ração 2 d o jog o v inte-e-um : adic io nando reg ras
391
Os eventos são um tipo de estímulo que pode fazer um jogador mudar entre seus estados. Por exemplo, quando a mão do jogador causar um estouro, o jogador mudará do estado de jogo para estouro. A tividades são as ações executadas quando se está em um estado. Por exemplo, ao j ogar no estado de jogo, um jogador optará por receber mais cartas até decidi r parar ou estourar. Cond ições de guarda são uma expressão booleana que coloca algum tipo de restrição em uma transição. Por exemplo, um jogador mudará para o estado de parada, se decidi r não receber mais cartas. A Figura 16.2 apresenta a notação que você usará para mode lar os estados do jogo vinte-e-um.
FIGURA 16.2
A //O/(/çr70 do diagrama de estado.
~;E;"~'d~O~~'~"~'~'~~~'->·I(~E~"~'d~O~] __-;.~~ ( alividade
..
No modelo, as transições são simbolizadas através de setas. As transições podem ocorrer como resultado de um evento ou de uma condição (ou uma combinação dos dois). O evento e a condição devem aparecer na seta de tronsição para que seja óbvio o motivo da ocorrência da transição. Você também notará q ue uma transição pode voltar paro o estado corrente. Tal transição é conhecida como outotrollsiçào. Finalmente, se o obj eto exccuta cena ação quando está no estado, a ação é regi strada como uma atividade dentro do símbolo de estado. É completamente válido paro um estado não ter uma atividade.
Modelando os estados do jogador A Figuro 16.3 apresenta o d iagrama de estado do jogador. O jogador tem ci nco estados princ ipais: Espero, Vinte-c-um , Jogo, Parado e Estouro. O jogador começa no estado Espcra. Após a distribuição de cartas, o j ogador muda para o estado Vinte-c-um o u Jogo, dependendo da mão de canas recebida . Quando é sua vez de jogar, o jogador faz isso (a atividade do estado Jogo). Ao jogar, o jogador dec ide receber mais canas ou parar. Se o jogador decidir parar, ele mudará para o estado Parado. Se o jogador receber mais cartas, mudará para o estado Estouro, se sua mão causar estouro, ou voltará para o estado Jogo, se der para jogar com a mão. Isso continua até que o jogador estoure ou pare.
Dia 16
FIGURA
16.3
O diagrama de eSfado do jogador.
!
Espers
m60 é vinte-e-um [mão __ 211
Vint_um informs barlCll
~
Idá PI1l jogar com s mão Imlo < 211
Jogo recebe cartas ou p'ra
I lhitOJ
h..
Psrsds
I
informs banca d' para jogar com a mão Imão < 211
Estouro Informa banca
Modelando os estados da banca A Figura 16.4 apresenta o diagrama de eSlado da banca. A banca lem seis eSlados principais: Dislribuição, Espera, Vinle-e-um, Jogo, Parado e Estouro. A banca começa no eSlado Distribuição e d istribui cartas para cada jogador e para si mesma. Após a dislri buição, a banca muda para o estado Vinte-e-um ou Espera, dependendo da mão recebida. Enquanto está no eslado Espera, a banca espera que todos osjogadores lerm inem sua vez. Quando lodos osjogadores tiverem tenninado, a banca mudará para o eSlado Jogoe começará sua vez. Assim como o jogador, a banca decide se vai receber mais cartas ou parar, no eSlado Jogo. Ao cOlllrári o dos jogadores, cnlrctanlo, a banca está reslri la a receber mais cartas quando sua mão for menor que 17 e a parar quando sua mão for maior o u igual a 17. Assim como o jogador, se a banca receber mais cartas e estourar, ela mudará aulomalicamente para o eSlado Estouro. Se a banca decidir parar, mudará para o eSlado Parado. Dignas de nota são as alividades nos eSlados Vinle-e-um, ESlouro e Parado. Enquanlo eSlá nesses estados, a banca efelua o jogo e termina de jogar.
o modelo do jogo
vinte-e-um atualizado
Quando os estados do jogo eSliverem modelados e completamente entendidos, você precisará dec idi r como vai encaixá-los no projeto. Comece colocando cada estado do jogo em sua própria classe. Em seguida, você precisará descobri r o que gera os eventos.
393
Iteração 2 do j ogo v inte-e-um: adicionando reg ras
FIGURA 16.4
Odiagramll de eslado da bal/ca.
1 m""
v;nU!-fHIm 1m60- 211
Oillribui .....
1M'. ~ jogador~nc. [da
•.~
p'. jog... c:om a ...... [m'" < 211 [nenhum Jogado, espe'andol
Eope..
1M'. cada
_.
llim_.....
TI'
p,,, joga'
[jogadorn "5perand<>1
.....
Jog~"
Imao,. 16 < 221
'&cabe
C.i: ~ d~
pa'. Jogar c<>m
"mao [mlo< 111
Pa,ado
.flt".l • mio e'ulII 1110"' 0 [mio,. 21[
ESI<>ur<> af.lua
Esse uso do termoeslado se encaixa na defi nição original apresenlada anteriormente. Aqui, você apenas baseia um objeto em tomo de cada estado que o objeto Pl ayer (Jogador) deve ter em determi nado momento. Isso o libera de ter mui tas variáveis intemasd iferentes. Em vez disso, o obj eto estado encapsula perfei tamente todos esses di ferentes valores dentro de um objeto, que tem estado e comportamento. Você perceberá rapidamente que todos os eventos giram em tomo do estado da mão. Assim, você provavelmente deve deixar que o objeto Hand (Mão) gere e envie esses eventos à medida que objetos Card (Cana) sejam adicionados no objeto Hand (Mão). Você também precisará estabelecer um mecanismo para que os estados recebam eventos de Hand (Mão). Os estados em si são muito simples e têm três responsabi li dades. Os estados são responsáveis por: • Executar todas as atividades. • Receber e responder aos eventos. • Saber para qual estado ir. em resposta a um evento ou condição. A Fi gura 16.5 iluSlra o diagrama de classe St ate. Você notará que cada evento tem um método correspondente no State (Estado). O objeto Hand (Mão) chamará um desses métodos, dependendo do estado que Hand (Mão) gostaria de reponar. O objeto State (Estado) também acrescenta um método executeO. Esse método é chamado quando State (Estado) deve executar suas açôes. A Figura 16.6 modela os relacionamentos entre os objetos Pl ayer (Jogador), Hand (Mão) e State (Estado).
Dia 16
FIGURA 16.5
H.ndUst.n.,
O diagrama de c/asse de State (&Iado).
... h. ndPI.yeble O ... handBleckjack O + handBusted II + handChanged I)
o\>
PI.yritate + elCecute Id: Delaer)
FIGURA 16.6 H.naUstene,
O diagrama de classe da • • • •
eSfrllf/lra
State (&/(Ido) .
h.ndPl..,.bl. n hendBltdtjtclc II "-ndBUfled li "-ndCh'''IIed (I
PleyerState He nd • WlHoIdet" \I: H.ndUsl_,1 • ed
,
~
~-
,
Play.,
. _.td : OHwl
,
, ,_bed.
,
,
ImplCl ass
o objeto Pl ayer (Jogador) mantém um objcto 5tate (Estado). Quando for a vez do objeto Pl ayer (Jogador) fazer algo, ele simplesmente executará o método execute () de 5tate (Estado). Então, State (Estado) executará todas as atividades, receberá de Hand (Mão) e mudará para o próximo State (Estado), confonne for apropriado. Uma vez feita a transição, o próx imo State (Estado) executará todas as alividades. receberá de Hand (Mão) e mudará, conforme for apropriado. Esse padrão se repeli rá até que o jogo termine. A Figura 16.7 il ustra o diagrama de classes completo do jogo de cartas vi nte-e-um .
395
Iteração 2 do j og o v inte-e-um: adicionando reg ras
16.7 O diagramll de classes completo de 61 ackjack, FIGURA
C nn""
•
Em bora a ad ição de urna estrutura 5ta te (Estado) seja urna alteração i mportante ocorrida durante essa ileração, outras interraces e classes roram atual izadas para poder supol1ar o relato e a ex ibição das novas condições do jogo.
Implementação das regras Para suportaras novos recursos do jogo, são necessárias mudanças nas classes PI ayer (Jogador), Dealer (Banca), Bl ackjackOeale r (Banca 21) e Hand (Mão). Várias novas classes e interfaces também preci sam ser acrescentadas. As seçôesa seguir examinarnocada mudança importante.
Mudanças na Classe Hand Para suportaras novos recursos dojogo, a classe Hand (Mão) deve relatar seu estado para um receptor. A Listagem 16.2 apresenta a nova interface HandUs t ener.
Dia 16 LISTAGEM 16.2
HandListener.java
public interface Handl is tener I public void handPlayab l e(); publiC void handBlackjack() ; public void handBusted(); public void handChanged(); J
A Listagem 16.3 apresenta a classe Hand (Mão) atuali zada. LISTAGEM 16.3
Hand .java
i mport java.util.Arraylist õ i mport java.uti 1 .lterator; pub l ic cl ass Hand I private private private private
Arraylist cards = new Arraylis t () ; stati c f i nal int BlACKJACK = 21; Handlistener hol der; int number_aces;
public Hand() I II configura o portador como um receptor em branco para que ele não seja nulo . caso II não seja configurado externamente setHolder( new Handlistener() I public void handPlayable() II publi c vo i d handBlackjack() II publi c vo i d handBusted() {I publ ic vo i d handChanged() I} J
); J
publiC void setHolder( Handlistener holder ) I th i s .holder • holder; J
publi C Iterator getCardsO I
Iteração 2 do j ogo v inte-e-um: adicion ando regras
LISTAGEM 16.3 Hand.java (continuação) return cards.iterator(); }
publlC void addCard( Ca rd card ) { cards . add( card }; hol der.handChanged(); i f( card. ge tRank() •• Rank.ACE ) { } if( bustO} (
holder.handBusted() ; return ; }
if( blackjac k() ) ( holder . handBlack jack(); return; }
if ( ca rds.size() ~ . 2 ) ( holder.handPlayab le (); return: } }
pubhc boo l ean isEqual ( Hand hand ) I H(hand.totalO ·· this.totalO) { return true; }
return false; }
pub l iC boolean isGreaterThan( Ha nd hand ) { return th i s . total () > hand. tota 1() : }
publ ic boolean bl ackjackO ( if( ca rds.size() ··2 &&total() •• BLACKJACK ) { return true; }
return fa 1se ; }
public vo1d reset() I
397
Dia 16
LISTAGEM
16.3 Hand.java (continuação) ca rds. clearO; number aces - O;
J
publlC void turnOver() I Ite ra tor i - cards.iterator() ; while( i.hasNext() ) I Card card - (Card) i .nextO; card.setFaceUp( true ,; J J
public Stri ng toString() ( Iterator i • cards .iterator(); String string· ""; while( LhasNext() ) ( Card ca rd - (Card) i . next(); string - string + " " + card.toString(); J
return st r ing;
J publ ic int total () I int total - O; Iterator i - cards.iterator() ; while( i.hasNex t() ) I Card card z(Card) i.next(); total +- card.getRank().getRank() ; J
int temp_aces - number_aces; while( total> BLACKJACK && temp_aces total - total 10; w
J
return tota 1;
J private boolean bust() ( if(total() > BLACKJACK ) { retu rn true;
J return false ; J
J
>
O) I
Iteração 2 do jogo v inte-e-um : adic ionando reg ras
399
Agora, as alterações no método total () de Hand (Mão) tomam possível que um ás tenha o valor I ou II . Do mesmo modo, as alterações no método addCard{} perm item agora que Hand (Mão) informe ao seu receptor das alterações no seu conteüdo, quando elas acontecerem. Final mente, a ad ição dos métodos 1sEqua I {} e lsGrea terThanO possibilita a comparação fáci l e encapsulada de obj etos Hand ( Mão).
Mudanças na Classe PI ayer A maior al teração na hierarquia PI ayer (Jogador) gira em tomo do acrésci mo de States (Estados). A Listagem 16.4 apresenta a nova interface PlayerState. LISTAGEM
16.4 Playe r State.java
pub li c interface PlayerState extends HandListener 1 publi c vo i d execute{ Oealer dealer }; }
PlayerState herda de HandUstener e acrescenta um método executeO. Uma implementação de PlayerState implementará Playe r State, responderá apropriadamente a qualq uer um dos eventos HandU stener e executará s uas atividades dentro de executeO.
o objeto Player (Jogador) mantém uma referencia para seu estado corrente, através da variável current s t at e. O método playO foi alterado para si mplesmente executar o estado corrente: publ1c void play{ Oea l er dealer ) 1 current_state .execute{ dealer }; } Em vez de defin ir algum comportamento dentro do método playO, o objeto P1ayer (Jogador) simplesmente de lega para seu estado. Desse modo, você pode fomecer comportamento novo si mplesmente trocando objetos de d ife rentes estados. A troca por diferentes estados é uma solução muito mais elegante do que a troca através de uma li sta de lóg ica cond icional. As listagens 16.5 a 16.9 apresentam as impl ementações padrão de PI ayer e PI ayerState. Esses estados implementam diretamente os modelos de estado da seção anterior. LISTAGEM
16.5 O estado de espera padrão
private class Waiting implements Pl ayerState { publ ic void handChanged{) I notifyChanged(); }
public voi d handPlayab le () I setCurrentState( ge tP layingState() }; /1 t ransição }
Dia 16
LISTAGEM 16.5 O
estado de espera padrão (continuação)
publiC void handB lackjack() { setCurrentState( getBlackjackState() ); noti fyB lackjack() ;
II
t ransiç~o
J
pu bl ic void hand BustedO 1 II impossível no es tado de espera J
public void execute( Oealer dea ler ) { II não faz nada enquanto espera J J
LISTAGEM 16.6 O
est ado de estouro padrão
private class Busted impl ements PlayerState ( pub l ic void handChanged() ( II impossível no estado de estouro
J
public voi d hand Playable() I II impossivel no es tado de estouro J
public voi d handBlackjack() 1 II impossivel no es tado de estouro J
public void handBusted() 1 II impossível no estado de estouro J
public void execute( Oea ler dea ler ) { dealer.bu sted( Player . thi s ) ; I I termi na J
J
LISTAGEM 16.7 O
estado
vinte-e-um pad rão
private class Blackjack implements Pl ayerState I pub l ic void handChanged{) I Il impossível no estado de vin te-e - um J
public voi d handPlayable{) I Il impossíve l no estado de vinte-e-um J
Iteração 2 do j ogo v inte-e-um: adicionando regras
LISTAGEM 16.7 O estado
vinte -e- um padrão (continuação)
publiC vold handBlackjack() { II lmposs fvel no estado de vinte-e- um J
publlc void handBustedO { II imposs ível no estado de vinte-e - um J
public void execute( Oealer dealer ) { dealer.blackjack( Pl ayer.this ); I I tenni na J J
LISTAGEM 16.8 O estado de parada padrão private class Standing implements PlayerState { publ ic void handChanged() { II impossfvel no estado de parada
J pub l ic void handPlayab le () { II impossfvel no estado de parada J
public vold handB lac kjack O I II impossfvel no estado de parada J
public vold handBusted() { II imposs 1vel no estado de parada J
publi c void execute( Oealer dealer ) { dealer.standlng( Player.this ) ; I I tenni na J J
LISTAGEM 16.9 O estado de jogo padrão private class Playlng imp lements PlayerState { pub l ic void handChanged() I notlfyC hanged();
J
public void handP layab le( ) I II pode ignorar no estado de jogo J
publi c void handBlac kjack () I
401
Dia 16 LISTAGEM 16.9 O estado de jogo padrão (continuação)
II
impossfvel no estado de jogo
} publ ic vo i d handBustedO { setCurrentState( getBus t edState() ); not i fyBus ted () ; } public voi d execute( Oealer dea l er ) ( 1f( hno } ( dealer.hit( Player.this ) ; } el se { setCu rrentS ta te ( getS tandi ngS t a te O noti fyS t anding(); } current_state.execute( dealer ) ; II trans ição }
);
}
Todos esses estados são implementados como classes internas de Pl ayer (Jogador) pois, basicamente, elas são extensões da classe Pl ayer (Jogador). Co mo classes internas, esses estados têm tOlal acesso a todos os métodos e variávei s da classe PI ayer (Jogador). As classes internas permitem que você encapsule e fi cientemente a lógica de estado dentro de sua própria classe, sem ler de danificar o encapsulamento da classe Player (Jogador). Subclasses podem fornecer s ua própria implementação de estado, sobrepondo os seguintes métodos em Pl ayer (Jogador): protected Pl ayerState getBustedState() ( return new Busted() ; } protected PlayerState getStandingStateO ( return new Stan ding() ; }
protected PlayerState getPlayingState() ( return new Playing(); } protected PlayerState getWa i t i ngState() ( return new Waiting{); } protec ted PlayerState getB l ack j ackState() { return new Blackj ac k(); }
protected PlayerState get In i t i alState() return new Wa i tingState(); }
I
Iteração 2 d o jogo v inte-e-um: adic ionando reg ras
403
Desde que os estados usem esses métodos para recuperar os outros estados, as subclasses podem introduzir seus próprios estados personalizados. getInitia lS tateO é usado pe la classe base Player (Jogador) para configurar o estado inicial de Pl ayer (Jogador). Se uma s ubclasse começar em outro estado, ela também precisará sobrepor esse método. Final mente, vários métodos de noti licação roram adicionados na classe Pl ayer (Jogador). Os estados usam esses métodos para inrormar o receptor das alterações. Esses métodos correspondem aos novos métodos encontrados na interrace PlayerListener. Novos métodos roram ad icionados no receptor para suportar a nova runcionalidade dojogo. A Listagem 16. 10 apresenta a interface Playerlistener atualizada. LISTAGEM
16.10 Pl ayerListene r. java
pub l i C interface PlayerListener { publi c vo i d playerChanged( Playe r player l : publ ic vo i d playerBusted( Player player )i publiC void playerBlackj ack( Player pl ayer ) : pub l ic void playerS ta nd ing( Playe r player ): publiC vo1d playerWon( Player pl ayer ); public void playerLost ( Player player ): publiC void playerStandoff( Player pl ayer ); J
Como Console é um Playerlistener, os métodos a seguir roram acresce ntados em Console: pub li c void playerChanged( Player playe r ) { pri ntMessage( player.toS t ring() ): J public void playerSusted( Player player ) ( printMessage( player.toStringO + " SUS TED!!" ) : J public void playerSla ckjack ( Player player ) { prin t Message( playe r. toStringO + " SLACKJACK! ! " );
J
Dia 16
public yoid playerStanding( Player player ) ( printMessage( player.toString() + • STANDING
~
}i
I publiC yoid playerWon( Player playe r ) ( printMessage( player.toStringO + • WINNER!!" );
I public void playerLost( Player player ) ( printMessage( player.toString() + • LOSER !! " }i
I publiC void playerStandoff( Pl ayer player ) { prin tMessage( player.toStringO + " STANDOFF " )i
I Essas alterações perm item que Console apresente todos os pri nci pais eventos do jogo. Alguns métodos novos também foram acrescentados em PI ayer (Jogador): publ i c Yoid winO ( notifyWi nO;
I publiC yoid lose() I noti fyLoseO i
I publiC void standoffO { notifyStandoff();
I publi c void blackjack() I notifyBlackjack() i
I Esses métodos pe rmitem que Oeal er(Banca) informe a PI ayer(Jogador) se ga nhou, perdeu, empatou ou ti nha vinte-e-u m.
Mudanças em Dealer e BlackjackDealer Dealer (Banca) e Bl ackjackOea I er (Banca 2 1) precisam ser atualizados para se encaixar na nova estrutura de estado. A Listagem 16. 11 apresenta a interface Dea 1er (Banca) atua lizada. LISTAGEM 16.11
Oea l er . java
publ i c interface Oealer I II usado pelo jogador para in terag ir com a banca
Iteração 2 do jogo vinte-e-um: adicionando regras
LISTAGEM 16.11
405
Dea l er.ja va (c on t inuação)
publiC void hit( Playe r player };
II usado pelo jogador para se comunic ar com a banca public void blackjack( Player playe r }; public void busted( Player pl ayer }; public void standing( Player player }; }
Pl ayer (Jogador) usa esses novos métodos para relatar o estado para Deal er (Banca). Assim, por exemplo, quando Pl ayer (Jogador) tiver vinte-e· um , chamará o método bl ackjack () de Dea ler (Banca). Então, oea l er (Banca) pode passar a vez para o próximo jogador. Esses métodos são parecidos com o método passTurnO anterior. Eles apenas são mai s específicos. Dea 1er (Banca) usa cham adas para esses métodos, para filtrar os objetos Pl ayer (Jogador) em recipientes, baseado em seu estado. Isso torna o cumprimento dojogo muito mai s fá cil para Dealer (Banca). Por exem plo, aqui está uma implementação de busted() de Bl ackjackDea l er: public void busted( Player pl ayer ) ( busted pl ayers.add( player ) ; play( t his ); }
Os outros métodos funcionam de manei ra semelhante. BlackjackDeal er (Banca 2 1) adi ciona um estado oealerDeal ing. Ela também persona liza mui· tos dos estados padrão de Pl ayer (Jogador). As listagens 16. 12 a 16. 16 apresentam esses estados modificados. LISTAGEM 16.12 O
estado de estoura da banca personal izado
private cl ass oealerBusted implemen t s PlayerState { pu bl ic void han dCha nged() { II impossfvel no estado de estouro }
publ ic vo i d handPlayable() { I I imposslvel no estado de estou ro }
pub l ic voi d handB lackjack() { II imposslvel no es t ado de estouro }
pub l ic voi d handBustedO { II imposslvel no es t ado de estouro }
Dia 16
LISTAGEM 16.12 O estado de estoura da banca pe rsonalizado (continuação) pub l iC void execute{ Dealer dea ler ) I Iterator 1 • standing_players.iterator {): while{ i. hasNext() ) I Player player = (Pl ayer) i .next(); player .winO: J
i • blackjack_p l ayers. iterator() ; while( 1. hasNext() ) I Player player • (Player) i .next() : player.win(): J
i • busted_players.iterator(): while( i.hasNext() ) I Player player (Player) i . next() ; player. l oseO ; lO
J
J J
LISTAGEM 16.13 O estado de banca com vinte -e- um persona l izado pr iva te cla ss OealerBlackjack implements PlayerState { public void handChanged() { noti fyC hangedO: J
public void handPlayable(} { II impossível no estado de vinte-e-um J
publi c void handBlackjack() { II impossível no estado de vinte-e -um J
public void han dBusted () ( II impossível no estado de vinte-e -um J
publ iC void execute( Dealer dealer ) I exposeHand (); Iterator i • players.ite rator () : whl1e( i .hasNext() ) ( Pl ayer pl aye r (Player)i.next() : if( player.getHand () . blackjack() } I player.stando ff() : I else { player.lose() ; K
J
Iteração 2 do j ogo v inte-e-um: adicionando regras
LISTAGEM 16.13 O
estado de banca com vinte -e- um personalizado (continuação)
} } }
LISTAGE M 16.14 O
estado de banca pa rada personalizado
pri vate class OealerStanding implements PlayerState ( publi c v01d handChanged() { II impossfvel no estado de parada }
public void handPlayable() { II impossfve l no estado de parada }
pub l ic vo i d handBlackjack() { II impossfvel no estado de parada }
pub l ic vo i d handBustedO { II impossfvel no estado de parada }
public void execute( Oealer dea l er ) I Iterator i • standing_pl ayers.ite rato r (); while( i.hasNext() ) I Player player = (Pl ayer) i .next(); H( pl ayer.getHand(). is Equal( getHandO ) ) I player .standoff() ; I else if( player.getHand() . isGreaterThan( getHand() ) ) { player.winO; I e I se { player.loseO ; } }
blackjack_players . iterator{) ; while( i .hasNext() ) { Player player = (Player) i . nex t () ; player.win() ; i •
}
busted_p l aye rs.iterator() ; while( i.ha sNext() ) I Pl ayer pl ayer • (Pl ayer) LnextO; p1ayer.loseO; i =
} } }
407
Dia 16
LISTAGEM 16.15 O estado da banca esperando personal izado pr ivate cla ss OealerWa i t i ng i mpl ements PlayerState { public void handChanged() I II i mposs fve l no estado de espera }
publi C void handPlayab le( ) I II imposs1vel no estado de espera }
publ ic vo1d handBlackjack() { II imposs fvel no es tado de espera }
publi C vo i d handBusted() { II impossfvel no estado de espera }
publi c vo i d execute( Dealer dealer ) { ;t( !wa1ting_players . i sEmpty() ) { Player pl ayer • (Player) waiting players.get( O )i wa i ting_players. remove( player ); player. play( dea ler )i ) el se { setCurrentState( getPlayingState () ) ; exposeHand()i getCurrentState().execute( dealer ); II faz a tran sição e executa } } }
LISTAGEM 16.16 O estado da banca distribuindo personal izado private cla ss DealerDealing implements PlayerState { pu blic void han dChanged() ( notifyChanged(); }
publi C vo i d handPl ayab l e() ( se t Current State( getWaitingS tate() ); II tran si çlio }
pub l iC void handB l ackjack() { se tCurrentState( getBlackjackState() li noti fyBl ackjack (l; II transi ção }
public void handBusted() { II imposs i vel no estado de di stribu içao
Iteração 2 do jogo vinte-e-um: adicionando regras
LISTAGEM
409
16.16 O estado da banca distribuindo personalizado (continuação)
}
public void execute( Oealer dea ler ) { deal();
getCurrentState().execute( dealer ) ; II faz a transiçao e executa } }
Você pode notar que Bl ackjackOeal er (Banca 21 ) não define seu próprio estado dej ogo. Em vez disso, ele lisa o estado de jogo padrão de PI ayer (Jogador); entretamo, para usa r seus estados persanai izados, BI ackj ac kOea I er (Banca 2 I) precisa sobrepor os receptores de estado encontrados na classe base PI ayer (Jogador): protected PlayerStale getBlackjackState() { return new OealerBlackjack() ; }
protected PlayerState getBustedState() ( return new OealerBusted(); }
protected PlayerState getStandingState() { return new OealerStanding(); }
protected PlayerState getWaitingState() I return new OealerWaiting(); }
Teste Assim como no código do Ca pítulo 15, "Aprendendo a combinar teoria e processo", uma bateria completa de testes está disponíve l para download no endereço www.samspublishing.com. junto com o cód igo-fonte deste capítulo. Esses lestes consistem em um conjunto de lestes de unidade e objetos fa lsifi cados, que testam completamente o sistema do jogo vinte-e-um. O teste é ullla parte importante do processo de desenvolvimento; entretanto, o estudo do código de teste é deixado como exercício para o leitor.
Resumo Hoje, você conclui u a segunda iteração do jogo vinte-c-um. Fazendo esse exercício, você viu pela primeira vez como poderia usara processo iterat ivo para conseguir paulatinamente uma solução completa.
Dia 16
Cada iteração anterior atua como base ou fundamento para a seguinte. Em vez de começar uma nova análi se ou projeto, hoje você começou construindo os casos de uso e projeto descobertos ontem. Amanhã, você complementará melhor essa base, quando adicionar recursos de aposta no programa do jogo vinte-e-um.
Perguntas e respostas I) Se os estados são tão importantes, por que você quis esperar até essa iteração para incluí-los?
R A iteração inicial era simples. A iteração inicial usava um jogo vinte-e-um bás ico que não detectava mãos de vinte-e-um natural, ganhadores ou pe rdedores (embora detectasse estouros). Não havia motivos para atacar o probl ema com uma solução complexa. Os requi sitos dessa iteração justi ficam uma solução mais com plicada, pois ela acrescenta detecção de vinte-e-um, assim como cumprimento do jogo. P Voeê poderill ter implementado os estados fora de Pl ayer (Jogado r) e 81 ackjackDea1er (Banca 2 1) ou eles devem ser classes in ternas?
R Você pode implementar os estados fora da classe. Mas se você os definir fora de Pl ayer (Jogador), talvez prec ise adicionar alguns métodos novos para que os estados possam recuperar todos os dados de que precisam. Gostaríamos de desaconselhar tal estratégia, por três mot ivos: Primeiro, mover a defin ição de estado para rara da classe não resolve muito. Na verdade, isso ca usa traba lho extra, devido aos métodos que você prec isará adicionar. Segundo, a adição de métodos extras para que os estados possam recuperar dados estraga o encapsu lamento. Finalmente, mover os estados para fora da classe Pl ayer (J ogador) não modela o relacionamento estado/jogador muito bem. Basicamente, os estados são lima extensão de Pl ayer (Jogador). Desse modo, os estados aluam como os cérebros de Pl ayer (Jogador). Ê melhor que os cérebros fiqu em dentro do corpo. ,
E importante nOlar que a im plementação de estados como classes internas funci ona perfe itamente na linguagem Java. Oulras linguagens podem exigi r uma estratégia li geiramente diferente.
Workshop As perguntas e respostas do teste são fornecidas para seu melhor entendimento. Veja as respostas no Apêndice A, ·'Respostas".
Iteração 2 do j ogo v inte-e-u m : adicionando regras
411
Teste I. Quando as condicionais são perigosas? 2. Liste duas maneiras de remover condicionais. 3. A versão de Hand (Mão) apresenlada hoje é melhor encapsulada do que a de ontem. Como a nova versão de Hand (Mão) se encapsu la? 4. Que padrão Hand (Mão) e HandListener implementam? 5. Procure na Web mais informações sobre o padrão State.
Exercícios I. Faça down load do código-fonte da iteração de hoje. Quando você tiver o código, compile-o, faça-o funcionar executando o jogo vinte-e-um e, depois, tente entender como ele funciona. Ter um entendimento total do código exigirá algum tempo e paciência. 2. O cód igo a seguir aparece na definição da classe PI ayer (J ogador): protected yoid notlfyChanged() ( Iterator i ·listeners .iterator(); while( i .hasNex t() ) ( PlayerListener pi • (PlayerListener) i . next(); pl.playerChanged( th is ) ; }
}
protected yoid notify8usted () ( Iterator i · listeners. iterator(); while( i.hasNext() ) ( PlayerListener pl a (PlayerListener) i.next(); pl . player8usted( this ) ; } }
protected yoi d notify8lackjack() ( Iterator f • 11steners.iterator(); wh i le{ i.hasNext() ) ( Playerlistener pi = (PlayerL i stener) i . next() ; pl . player81ackjack( this ); }
}
protected yoid notifyStanding() ( Ite rator i = listeners. iterator(); while( i.hasNext() ) {
Dia 16
Playerlistener pl • (Playerlistener) i.next(); pl.playerStanding( this ); } }
protected yoid notifyStandoff() { Iterator i · l i steners.iterator{); while( i.hasNext() ) { Playerlistener pl z (Playerlistener) i.next(); pl. playerStandoff{ this ); } }
protected void notifyWinO { l terator i • 1 isteners . iterator{) ; whi l e( i.hasNext{) ) { PlayerUstener pl • (Playerlistener) i.next(); pl.playerWon( thi s ); } } protected void notifyloseO { Iterator i = l i steners. ite ratorO; while( i . hasNext() ) { Playerlistener pl • ( Pl ayerlistener) i.next(); pl.playerlost{ this ); } }
Os métodos funcionam. Funcionalmente, não há nada de errado com eles; entretanto, cada método executa exalamente a mesma ação, até o ponto em que uma chamada é fe ita em Pl ayerli stener. Como você poderia usar objetos de modo que precisasse escrever apenas um método de notificação? Projete e implemente uma so lução.
SEMANA
3
DIA Iteração 3 do jogo vinte-e-um: adicionando aposta No capítulo de OIlIem, você viu como pegar lima implementação bastante primit iva do jogo vintc-c-um e fazer sua iteração para obter umjogo mais amadurecido. Hoj e, você vai complementar o mesmo j ogo vinte-c-um, adicionando suporte para aposta simpl es. A lição de hoje dará a você mai s experiência com o processo iterativo, ass im como com ADO e POO ( Projeto Orientado a Objeto). No final da lição de hoje, você também deverá começar a se senti r mai s à vontade com a arq uitclu ra baseada em estado apresentada ontem. Na verdade, um dos exercícios de hoje ped irá para que você ad icione um novo estado no s istema.
Hoj e você aprenderá: •
Como estender a arquitetura de estado do jogo vinte-c-um para ad icionar func ional idade
•
Sobre as vantagens que lima verdadei ra estratégia 00 pode trazer para um sistema, trabalhando em um sistema baseado em 00
Aposta no jogo vinte-e-um A iteração de ontem produziu um jogo vinte-e-um bastante completo. Quase todos os recursos não relacionados com aposta agora fazem parte do j ogo. Hoje, você vai acrescentar alguns dos recursos de aposta ausentes.
Dia 17
Assi m como nas outras lições desta semana, segui remos o processo del ineado no Capitulo 9, " Introdução à AOO (Análise Orientada a Objetos)". Vamos começar explorando os casos de uso de aposta.
Análise da aposta Para entender completamente a aposta, você precisará finalizar os casos de uso Fazer apostas e Dabrar, identi fi cados durante a anál ise inicial do jogo vinte-c-um. Você também precisará rever os outros casos de uso para ter certeza de que eles não precisam de atual iz.."tção. Uma vez que tiver term inado os casos de uso, você precisará atuali zar o mode lo de domínio.
Análise do caso de uso de aposta no jogo vinte-e·um Vamos começa r com o novo caso de uso Jogador faz aposta: Os jogadores começam o jogo com U$$ 1000 no pote. Antes que quaisq uer cartas sej am distribuídas, cada jogador deve faze r uma aposta. Começando com o primei ro jogador, cada um aposta um valor de USS IO, US$50 ou U$$ IOO. • Jogador faz aposta I. Jogador faz uma aposta de US$ IO, US$SO ou US$IOO.
2. J)assa para o próx imo jogador e repete até que todos os jogadores tenham feito lima aposta. • Condições prévias • Novo jogo. • Condições posteriores • Jogador fez aposta. O jogo villle-e-um rea l, como todo jogo, tem suas próprias regras sobre aposta. Essas regras incl uirão uma aposta minima, uma aposta máxima e o incremento da aposta. Neste jogo vinte-e-um , um jogador pode apostar U$$ l 0, US$SO ou U$$ I00. Por simpl icidade, este jogo oferecerá ao jogador uma lin ha de crédi to ili mitada. Cada jogador começará com US$ I000. Quando o jogador esvaziar seu pote, seu saldo se tornará negativo; entretanto, o jogador pode jogar, desde que queira. O outro novo caso de uso de aposta é Jogador dobra: O jogador decide que não está satisfei to com sua mão inicial. Em vez de simplesmente receber mais cartas, o jogador decide dobrar. Isso duplica a aposta do jogador e acrescenta uma carta na mão. A vez do jogador term ina e o jogo passa para o jogador/banca seguinte. • Jogador dobra I. O jogador decide que não está satisfeito com sua mão in icia l.
Iteração 3 do jogo vinte-e-um: adicionando aposta
415
2. O jogador quer dobrar. 3. A aposta do jogador é duplicada. 4. A banca acrescenta outra carla em s ua mão. o
o
o
Condições prévias o
Essa é a mão in icial do jogador e ainda não recebeu mais carias nem parou.
o
O j ogado r não tem vinte-c-um.
o
A banca não tem vinte-e-um.
Condições posteriores o
A mão do jogador tem três carias.
o
A vez do jogador termina.
Alternativa: Jogador estoura A nova carta faz a mão do jogador estourar (perde).
o
Alternativa: a mão do jogador é maior que 21, mas o jogador tem um ás.
A nova carta faz a mão do jogador ser maior que 21. O jogador tem um ás. O valor do ás muda de II para I, fazendo com que a mão do j ogador seja menor ou igual a 21. Os únicos casos de uso prev iamente existentes, afetad os pela adição da aposta, são o caso de uso Distribuir carias e o caso de uso Banca ejeluajogo. Os outros casos de uso permanecem inal terados: Começando com o primeiro jogador, a banca distri bui uma cana abena para cada jogador, terminando consigo mesma. Então, a banca repete esse processo, mas distribu i s ua própria carta fechada. A distribuição termina e o jogo começa quando a banca tiver distri buído para cada jogador, incluindo ela mesma, duas cartas. o
Distribuir cartas I. A banca distribui uma carta aberta para cada jogador, inclu indo ela mesma. 2. A banca distribui uma segunda carta aberta para todos os jogadores, menos para ela. 3. A banca distribui uma carta fechada para si mesma.
o
Condições prévias o
o
Condições posteriores o
o
Todos os jogadores fizeram suas apostas. Todos os jogadores e a banca têm uma mào com duas cartas.
Alternativa: a banca tem vinte-c-um Se a banca tiver um 21 natural. o j ogo passa para o cumprimento. Os jogadores não terão sua vez.
Dia 17
Agora, a distribuição não começará até que cada j ogador tenha feito uma aposta. Vamos ver como a aposta muda o cumpri mento do jogo: Após todo ojogo ser fei to, a banca veri fica cada mão e detenn ina, para cada jogador, se ele ga· nhou ou perdeu, ou se o j ogo deu empate. •
A banca efelua o jogo I. A banca verifica a mão do primeiro jogador e a com para com a s ua própria. 2. A mão do jogador é maior que a da banca, mas não estourou; o jogador ganha.
3. O va lor da aposta é adi cionado ao pote do jogador. 4. A banca repete essa comparação para todos os jogadores. •
•
Condições prév ias •
Cada jogador teve sua vez.
•
A banca teve sua vez.
Condições posteriores •
•
Resultado fi nal do jogador foi detenn inado.
Alternativa: o jogador perde A banca veri fi ca a mão do jogador e a compara com a sua própria. A mão do j ogador é menor que a da banca. O jogador perde. A aposta é retirada do pote do jogador.
•
Alternativa: empate A banca veri fi ca a mão do j ogador e a compara com a sua própria. A mão do j ogador é igual à da banca. O j ogo deu empate. Nada é acrescentado ou subtraído do pote do joga· dor.
•
Alternativa: a banca estoura Se a banca estourou, todo j ogador que esti ver parado e com vinte·e-um ganha. Todos os outros perdem. Os gan hadores recebem o valor apostado.
•
Alternativa: os jogadores ganham com vinte-e·um Se o jogador tiver vinte-e·um e a banca não, o jogador ganha rá e receberá na proporção de 3:2 (por exem plo, se o jogador apostasse US$ IOO, receberia US$ I 50).
J sso
conclui as al terações nos casos de uso. Todos esses casos de uso são relativamente si mples. Os diagramas de interação provavel mente seriam complicados. Vamos ver como esses casos de uso atualizados mudaram o modelo de domínio.
Atualizando
O
modelo de domínio do jogo vinte-e-um
A análise da aposta ex ige q ue você atualize o modelo de domínio, mas apenas ligeiramente. Você precisará adicionar mais um objelo de domínio: Bank ( Banco). Todo objeto Player (Jogador) no jogo tem seu próprio objeto Bank (Banco) pessoal. A Figura 17. 1 ilustra o modelo de do-mínio atualizado.
Iteração 3 do jogo vinte-e-um: adicionando aposta
FIGURA 17.1 O II/Ol/elo de domínio do jogo l·ilfle-e-/lIII.
417
J"" '
, 1... 7
, M'o
, Carta
Projeto da aposta Você deve começara projeto projetando a nova classe Bank (Banco). Quando Bank (Banco) estiver pronta, você precisará descobrir como apostar no jogo. Para os propósitos da Iição de hoje, o caso de uso Jogador dobra é deixado como exercíc io no fina l da lição.
Projetando a classe 8ank Ao começar a projetar Bank (Banco),você deve primeiro identifica r suas responsabi lidades. A Figura 17.2 ilustra o cartão CRC resultante para a classe Bank (Banco). A boa 00 ex ige a divisão correta das responsabilidades. Desse modo, Bank (Banco) é responsável por controlar Iodas as atividades de aposta. Bank (Banco) mantém lodos os detalhes da aposta internamcnte e dá acesso â aposta e ao saldo através de uma interface bem de fi nida. A Fi gura 17.3 ilustra O diagrama da classe Bank (Banco), assim como o relacionamento de Ba nk (Banco) com PI ayer (Jogador).
o projeto da aposta Como se vê, a aposta deve se encaixar bem na arqu itetura de estado que vi mos ontem. O jogador e a banca precisarão de mais um estado para suportar aposta básica. A banca precisará de um estado Co 11 cct i ngBcts (Coletar Apostas) e o jogador precisará de um estado Aposta. As fig uras 17.4 e 17.5 ilustram os novos diagramas de estado para a banca e para o jogador, respectivamente.
Dia 17
FIGURA 17.2
Banco
O ca,.tão CRC de Bank
~~=
(Banco).
Banco
FIGURA 17.3
O diagrama da classe Ba nk
.. pl_, OOBetU: .. pl_SOS.' (I: .. pl_l08e10:
(Bal/co).
.. winO:
.. lose(l: • bh,,:kj.ck(l;
.. mndoffll;
FIGURA 17.4
O diagrama de estado de Oea 1er (Bal1ca).
mio'
I
la mio ' boII pa,a iOlla, Imlo < 21J
(mio> 11 < 221
ijogldo
I
mio' """ pao-I joglf Imlo < 111
• mio estouro ...
Iteração 3 do jogo vi nte-e-um: ad icio nando apost a
41 9
FIGURA 17.5
O diagrama de estado de P/(lj'e,.s
(Jogado,.es).
Apo •• aposta
IJogo I.ito)
Esper.
mio' vint.· ... um (mão __ 2 I I
I!
mão é
boI!
Vint.-e·um
parI! jogar (mio .. 21)
jogo
a mio é booI ""rI Joglr [mio ... 211
Jlhi@
I mio estourou [mio:>2 11
Estoulo
Confonne você pode ver, agora o jogador começa no estado Aposta, enquanto a banca começa no estado Co 11 ec t i ngBets (Coletar Apostas). Quando todas as apostas forem colctadas, a banca passará para o estado Distribuiçâo. como antes. Quando termi narem as apostas, os jogadores passarão para o estado Espera.
Refazendo a hierarquia de Player (Jogador) Neste ponto do projeto, parece que PI ayer (Jogador) e Bl ackjackDea I er (Banca 21) estão divergindo. Embora BlackjackDealer (Banca 21) estenda Player (Jogador), ela não precisa de Bank (Banco). Isso é diferente de HumanPI ayer (Jogador Humano), pois a banca não aposta. Se você adicionar suporte para aposta diretamente em Player (Jogador), BlackjackOealer (Banca 21) herdará lodos os tipos de comportamento inút il , que precisará sobrepor (além de Pl ayer (Jogador) fica r demasiadamente congestionado). Este é um bom momento para refazer a hierarquia de herança PI ayer (Jogador), dividindo os elementos comuns em subclasses. Na nova hierarquia, nenhum suporte para apostas deve ser ad icionado na classe base PI ayer (Jogador). Em vez disso, uma nova classe, Bet t; ngPI ayer, deve herdar de Pl ayer (Jogador) e, então, adicionar os estados e métodos necessários para o suporte de apostas.
Dia 17
B1ackj ackOealer (Banca 21) pode continuar a herdar de Pl ayer (Jogador); entretanto, Hl.manPlayer (Jogador Humano) deve agora herdar de Sett i ngP1 ayer. A Figura 17.6 ilustra a hierarquia resultante. FIGURA 17.6 II hierarquia de
PllJyer
Player ljogadOl).
• • • •
oodCa,d le ; canil play (d ; ,ddlistanar (I; Playerlistane.) win (J
De".,)
. 1058 O + SIDndoff O + blaekjaek O
# gel/nllllllS/lla (J : PI,~,SllIle I hil (I ; boa/fllln
Dealer + hlt (P: PI, ver) + blaekjeek (p : Plavar) + buSled (p ; Plavar) + standing (p: Player) + doneSaning (p: Piava. )
~
~ BetfingPllJyer
BlackjackDealer , hil O : boolean , gM!nilialStata n : PI,y&rStata • -.tdf'I ..... r Ip : Pla.....1 • newGame
O
.. win O • lou ti + l tandOtf II • blacljack CI , getlnill.tSl"e II: PI..... rState
,li« fi
HumanPlayer ... hil () : boolean ... bel ((
o modelo do jogo vinte·e-um atualizado Agora que você terminou o projeto, é uma boa idéia atuali zar o diagrama da classe B1 ackjack. A Figura 17.7 ilustra o diagrama da classe. Agora, você está pronto para passar para a implementação.
Implementação da aposta A implementação da aposta ex igirá a criação das classes Bank e BettingPlayer, assim como alterações em BlackjackOealer, Oea1er e HumanPlayer. Vamos começar com a classe Bank.
Ite ração 3 do jogo v inte-e-um : ad icio na ndo aposta
42 1
A implementação de Bank Confonne você descobriu, Bank (Banco) é responsável por conler um 10lal, assim como por gerendar as apostas. A Listagem 17. 1 apresenta uma passivei implementação de Bank (Banco).
-
FIGURA 17.7 O diagrllllla da classe
Blackj ack.
, 1 ... 7
re<:,be d' _ _
,
, 81nk di.uiblll de
, •
c..,
LISTAGEM 17.1 Bank.java pub l ic class Bank ( private int total; private int bet; pub l ic Bank( int amount ) I tota I • amount; }
public void placelOOBet() { placeBet( 100 ,; }
,
,
Dia 17
LISTAGEM 17.1
Bank.java (continuação)
publiC void placeSOBet() { placeBet( 50 li J
publi c void placelOBetO { placeBe t( 10 )i J
public void winO { total +- ( 2 * bet ) i bet = Oi J
publi c vo i d lose() ( II já extrafdo do total bet .. Oi
J pub l ic void blackjack() { tota 1 +'" ( ( ( 3 * bet ) bet .. Oi
I
2 ) + bet ) i
J publiC void standoff() { t ota l +.. beti bet .. Oi J
public String toString() { return ( "$" + total + ".00" ) i J
private void placeBet( int amount ) { bet .. amounti total . = amounti J
J
Quando o jogador prcdsa fazer uma aposta, ele faz isso através da interface Bank. Digno de nota é o modo como Bank ocu lta completamente os detalhes da aposta. Quando o jogador ganha, perde, atinge vinte-c-um ou empata, ele si mplesmente informa a Bank. Bank faz o resto.
Iteração 3 do jogo vinte-e-um: adicionando aposta
423
A implementação de BettingPlayer Bet t i ngPl ayer preci sa herdar de PI ayer, defi nir um estado Apostá (Bett ing), garant ir que seu estado inicia l seja configurado como o estado Aposta e adicionar suporte para Bank (assim como atua lizá- Io corretamente). A Listagem 17.2 apresenta a nova classe BettingPlayer. LISTAGEM17.2 BettingPlayer.java public abstract class BettingPlayer extends Player { private Bank banki public BettingPlayer( String name, Hand hand, Bank bank ) { supere name, hand )i this.bank • banki
I
// •••••• **.* ••••••••••••• ** ••• *** ••••••••••• *.**.* ••••••••••••• ** •••• *••••••
// comportamento sobreposto pub li c String toString() { return ( getNameO + bank . toString() )i M :
M
+ getHandO.toStringO + -\n" +
I public void win() { bank .winOi super.winO i I public void lose() I bank.loseO; super.lose() j
I publi c yoid standoffO { bank.standoff()i super.standoff()i I pub l ic yoid blackjack() I bank.blackjack()i super.blackjack();
I protected PlayerState getlnitia l State() { return getBettingState()i
I
Dia 17
LISTAGEM 17.2
BettingPlayer.java (continuação)
11**************************************************** ********************** II ad ic ionado recentemente para BettingPlayer protected final Bank getBank() ( return bank; J
protected PlayerState getBettingState() { return new Betting()j J
protected abstract void bet() j private class Betting implemen t s PlayerSt ate ( public vo i d handChanged() ( II 1mpossfvel no es t ado de estouro J
publ ic void handPlayable() ( II impossíve l no estado de estouro J
public vo id handBlackjac k() ( II impossível no estado de estouro
J public void handBusted() ( II impossível no estado de es t ouro
J public vo i d execute( Dealer dea l e r ) { bel ();
setCurrentState( getWaitingState() )j dealer.doneBett1ng( BettingPlayer.th i s ) j I I termi na J J
J
Você também notará que Betti ngPl ayer acrescenta um novo método abstrato: protected abstract voi d bet (). O método bet () é chamado dentro da ativi dadc do estado Betti ng (Aposta). Cada subclasse deve sobrepor esse método, como achar melhor.
Mudanças em Oealer e HumanPlayer A parti r do exame do cód igo de BettingPlayer,você provavel mente notou que um novo método foi adicionado em Oea l er: public voi d doneBet t i ng( Pl ayer p ).
Ite ração 3 do jogo v inte-e-um: adici o nando aposta
425
BettingPlayer chama esse método para informar a Dea l er que ele terminou a aposta. Desse modo, o objeto Dea l er pode saber que o jogador tenninoll e que o próxi mo jogador pode começar a apostar. As alterações em HumanPI ayer são muito sem graça. A Listagem 17.3 lista a nova classe HumanPI ayer. LISTAGEM
17.3 HumanP l ayer.java
public class HumanPlayer extends 8ettingPlayer { private private private private private private private private
f i na I final fina I fina I fina I fi na I final f i nal
static statlc s tatl c static stat i c statlc static static
String St r ing St ring St ring String String String String
HIT
STAND PLAY MSG BET- MSG 8ET- 10 BEl 50 BET 100 DEFAULT
~
~
~
~
~
~
~
~
"H'" • "S"; .. [H] it or [S] tay";
"Place BeqlO] [50] or [100]"; "10"; "50"; "100'" • "inval id'" •
pub l ic HumanPlayer( String name , Hand hand. Bank bank } { supere name, hand. bank ); }
protected boolean hit() { while( true ) { Co nsole . INSTANCE.printMessage( PLAY_MSG }; String response s Console.INSTANCE . readInput( DEFAULT ); lf(response.equals lgnoreCase( HIT ) ) { return true ; J else if( response . equalsIgnoreCase( STAND ) ) { return fal se ; }
II II
se chegarmos até aqul , faz um s i gnifi cativa
laço até obtermo s entrada
} }
protected void bet() ( while( true ) I Console.INSTANCE.printMessage( BET MSG ); St ring response • Conso l e.INSTANCE . readInput( OEFAULT ) ; if( response.equa l s( BET_10 ) } { getBank(} . placel0Bet() ; return; }
Dia 17
LISTAGEM 17.3
HumanP1ayer.java (continuação)
if( response.equal s ( BET_50 ) ) ( getBank(}.place50Bet(); return ; J
if( re sponse.equal s ( BET_IOO } ) { get Ban k(}. place IOOBe t(}; return; J
/1 se chegarmos até aqui, faz um laço até obtermos entrada /1 significativa J J J
Agora, HumanPlayer herda de BettingP layer, em vez de diretamente de Playe r. HumanPl ayer também fornece uma implementação de bet (). Quando bet() é chamado, ele consulta a linha de comando para ver retorno do usuário.
Mudanças em 81ackjackDealer Bl ack jackOeal er (Banca 2 1) implementa o novo método doneBett i ng D, definido em Dealer. Quando esse método é chamado, Blackj ackDea ler pega o jogador e o insere em um rec ipiente de jogadores esperando. BI ackjac kDea 1er também define um novo estado: Dea 1erCo I Iec t i ngBets . A lém disso, Dea I erCo 11 ect ; ngBet s atua como o novo estado ini cial de Bl ackj ackDea I er. A Listagem 17.4 apresenta o novo estado. LISTAGEM 17.4 O
novo estado de DealerCollectingBets
pr;vate cl ass OealerCollectingBets imp lements PlayerState \ publi c vo i d handChanged() i II imposs fve l no estado de aposta
J publ ic void hand Playable () ( II impossfvel no estado de aposta
J
pub l ic void handB lackjack () t /1 imposs fve l no estado de aposta J
public void hand Bus tedO { II i mposs fve l no es tado de apost a
Iteração 3 do jogo v inte-e-um: ad icio nando apost a
LISTAGEM 17.4 O
427
novo estado de OealerCollectingBets} (continuaçào)
publiC vold execute{ Dealer dea l er } I if( !betting_pl ayers.isEmpty() ) { Player player • (Pl ayer) betting players.get( O } ); betting pl ayers .rernove( pl ayer )j player . play( dealer }; I else I setCurrentState( getDealingState() ) j getCurrentState().execute( dealer ) ; II faz a trans ição e executa J J J
o novo estado chega ao fi m e diz a cada jogador para q ue aposte. Digno de nota é que esse estado não faz laço. Em vez di sso, a atividade é executada sempre que um jogador indica que acabou de apostar. Esse comportamento está defi nido dentro do método doneBettingO: public void doneBetting( Player player ) { waiting_playe rs.add( player ); play( thls ) j J Lembre-se de que uma chamada a play() executa o estado correntc.
Miscelânea de mudanças A única outra alteração digna de nota é o falo de que o método getInitialStateO de Player agora é declarado como abstraIo na classe base PI ayer. Tornar o método abstrato funciona como uma forma de documentação que pennite a qualq uer um, que faça subclasses da classe, saber que deve fornecer sua própria definição de estado inicial. A prática de tornar um método abstrato para que ele funcione como uma forma de documentação é uma maneira eficiente de estabe lecer um protocolo de herança.
Um pequeno teste: um objeto falsificado Como sempre, casos de teste estão disponiveis junto com o código-fonte; entretanto, é interessante dar uma olhada em um uso inteligente de objetos fal sificados. A Li stagem 17.5 apresenta um objeto fa lsi fi cado Deckpile que garante q ue a banca recebe um vinte-e-um. LISTAGEM 17.5
Oea l erBlackjackP i le.java
publ i C class DealerBl ack jack Pile extends Deckpile pr; vate Card [] cards j
I
Dia 17
LISTAGEM 17.5 OealerBlackjackP i le.java (continuação) private lnt index· -I: public OealerBlackjackPi l e() { ca rd s " new Ca rd (4]; card s (OJ • new Ca rd ( Suit. HEARTS , Rank.TWO ) ; cards ( lJ • new Card( Suit. HEARTS, Rank.ACE ) ; cards (2J • new Card( Suit. HEARTS, Rank. TH REE ): card s (3J • new Card( Suit.HEARTS. Rank.KING );
I publi C vo i d shuffle() ( II não faz nada
I publ ic Card dealUp() ( index++; cards[index].setFaceUp( true ) ; return cards [index]:
I pub l iC Card dealOown O ( index++; return cards (index]:
I public voi d reset() ( I I não faz nada
I I Você pode usar esse objeto falsificado para testar se o jogo responde corretam ente quando a banca recebe uma mão de vi nte-e-um. Esse objeto fa lsificado realmente prepara o baral ho para roubar no jogo.
Resumo Hoje. você concl uiu a terceira iteração do projeto do jogo vinte-e-um - resta apenas mais uma! Neste capít ulo, você viu como poderia estender a arquitetura de estado para suportar aposta simples nojogo. Você também viu que às vezes é necessário rever e refazer lima hierarq uia, quando novos requisitos se apresentam. Refazer pensadamente representa um pouco de trabalho extra alllecipado; refazer quando apropriado tende a compensar quando você prosseguir.
Iteração 3 do jogo vinte-e-um: adicionando aposta
429
Como você refez as hierarquias agora, a base de código será muito mai s fác il de entender, estender e manter posteriormente. Amanhã, você colocará uma UI gráfica no jogo.
Perguntas e respostas P I'or que você não modelou o cumprimento como um estado? R Você poderia ter modelado o cum primento como um estado; entretanto, um estado de cumprimento teria signi fi cado ruído no projeto. As atividades dentro dos estados Busled, B/ackjack e Slanding podem efet uar o jogo adequadamente. Além disso, esses estados podem efetuar o jogo mu ito especificamente.
Se a banca fizesse a transição para um estado de cumprimento, ela perderia suas informações de estado anteriores. Se você efetuar dentro de um estado específi co, entretanto, a banca poderá fazer a contagem dojogo facilmente, pois saberá em que estado term inou.
Workshop As perguntas e respostas do teste são fornecidas para seu melhor entendimento. Veja as respostas no Apêndice A, ·' Respostas".
Teste I. Como você pode estabelecer protocolos de herança efic ientemente?
2. A lição sobre herança, na Semana I, d isse que as hierarquias de herança são freqlien temente descobertas e não planejadas desde o início. Qual hierarquia você descobriu hoje?
3. Dada a Pergunta 2 do teste, por que você deve esperar para realizar a abstração até ter fe ito algo algumas vezes? 4. Hoje, você refez a hierarqu ia Pl ayer. Liste duas vantagens que você obteve fazendo as alterações.
Exercícios I . Faça download do código-fonte da iteração de hoje. Quando você tiver o código, compile-o, faça-o funcionar executando o jogo vinte-e-um e, depois, tente entender como ele funciona. Ter um entendimento talai do código exigirá algum tempo e paciência.
2. Projete e implemente o caso de uso Jogador dobra. Baseie sua solução no código-fonte da lição de hoje.
SEMANA
DIA
3
• •
Iteração 4 do jogo vinte-e-um: adicionando uma GUI Até aqui nesta semana, você anal isou, projetou e construi u um jogo de cartas vi nte-c-um. Trabalhandoa partir de uma implementação simples e faze ndo iterações para obter um aplicativo mais complexo, você ad icionou regras e recursos de aposta no jogo. Hoje, você continuará o processo iterativo e fa rá melhorias na camada de apresentação do jogo. Hoj e você aprenderá como: •
Aplicar aná lise, projeto e im plementação ao escrever interfaces com o usuário
•
Aplicar o padrão MVC no jogo vinte-c-um
Apresentação do jogo vinte-e-um Até aqui, a ün ica interface para ojogo de canas vinte-e-um tcm sido uma rudimentar interface com o usuário (U I) baseada em linha de comando. Não foi falado muito a respeito dessa UI. Na verdade, mu ito pouco, se é que houve, análise ou projeto aplicado a UI, apenas foi dito que você usará o padrão MVC. Em vez de fazer a análise e o projeto da UI de linha de comando, a UI mais simples possível foi criada para pennitir que você interagisse com o sistema do jogo vinte-e-um. Durante o desenvolvimento, você freqOentemente verá que precisa desenvolver materiais de suporte, como objetos stubs ou partes de interação do sistema com o usuário, como a UI. Freq üentemente, esses materiais não serão orientados pela análise. Em vez disso, esses itens são orientados por necessidades que se apresentam durante a implementação. No caso do jogo vin-
Dia 18
te-e-um , você precisa absolutamente de uma maneira para interagir com o sistema; entretanto, escrever uma UI gráfica desde o início si mplesmente não era prático. Como a UI de linha de comando não se destinava a fazer parte do sistema fina l, não houve necessidade de realizar análise adiciona l para ela. Hoje, você fará uma última olimização na UI de linha de comando origi nal e depois passará para a análise, projeto e implementação de lima UI gráfica (GU I) completa para ojogovinte-e-um.
Otimizações da linha de comando Antes de passarm os para o trabalho na GUI do jogo vinte-e-um, é interessante fa zer uma última otim ização na UI de linha de comando.
É urna inconven iência ter de reiniciar o jogo cada vez que você quer j ogar. A Listagem 18. 1 apresenta um novo método principal que permite a você jogar quantos jogos vinte-c-um quiser, sem ter de reiniciar.
LISTAGEM 18.1 Blackja ck.java publi c cla ss Blackjack I public stati c vold ma i n( String [] args ) I Dec kpil e cards • new Deckpile(} i for{ i nt i = O; i < 4; i ++ ) I cards .shuffle{) ; Oeck deck • new Deck()i deck.addToStack ( card s )i cards.shuff1e() ; J
Hand dea ler hand = new Hand(); BlackjackDealer dealer • new BlackjackDealer( "Dealer" , ca rd s ); Bank human_bank • new Bank{ 1000 ) ; Hand human_hand • new Hand{); Pl ayer player· new CorrmandLinePl ayer{ "Human" , human_hand, human bank );
dealer.addL istener{ Console.INSTANCE ); player. addL istener( Console. INSTANCE ); dealer.addPlayer{ playe r )i do
I
dealer.newGame(); I while( playAga in () };
Iteração 4 do jogo vinte-e-um: adicionando uma GUI
433
LISTAGEM 18 .1 Bla ckjac k.j ava {conti nuação}
Consol e. INSTANCE.print Message { MThank you for playing'M } ;
} pri vate sta t ic boolean pl ayAgai n{} { Conso le . INSTANCLp ri ntMessage ( "Would you like to pl ay again? [Y) es
[Nl o· }; St ri ng response • Conso l e.INSTANCE.read I nput{ "i nvalid" }; if( response . equa ls IgnoreCase{ "y" } ) { return true; } return fal se ;
} }
Adicionar essa funciona lidade no método principal do jogo vinte-c-um tem um valor prático, pois ela nos ajuda a detectar todos os erros que possam estar ocu ltos no programa, quando você jogar várias vezes. Por exem plo, cada mão prec isa ser correlamente reconfi gurada, antes do pró-ximo jogo. Levantando todos os erros agora, você não será pego pelo erro posterionncnte e não achará. que a nova GUI é a culpada.
Análise da GUI do jogo vinte-e-um Para completar a aná lise da GU I, você deve rea lizar a análise de caso de uso, exatamenle como fez durante as iterações anteriores. Ao se realizar a análise da GU I, também é fundamental que você se sente com seus clientes, usuári os e especialistas na util ização, para projetar o layout da GUI. Na realidade, quanto menos palpite você der como desenvolvedor no layout da GU I, melhor. Todo mundo tem sua própria especialidade. Como descnvolvedor, sua espec ialidade normalmente é anal isar probl emas, projetar soluções e implementar essas soluções. Quando você se sentar com seu cliente, va i descobrir como ele quer sua OUI configurada. Infeli zmente. os especialistas em uti lização, clientes e usuários não aparecem na fonna conveniente de um livro; portanto, você precisará passar sem eles hoje. Em vez disso, trabalharemos em um esboço da te la, antes de passarmos para o projeto.
Casos de uso da GUI Ao contrário dos casos de uso do jogo vinte-e-um que você analisou durante as iterações anteriores., os casos de uso da GU l não afetam o domínio do jogo em si. Em vez disso, os casos de liSO da GUI ajudam a estabe lecer como o usuário vai manipular o jogo vinte-e-um através da UI.
Dia 18
Desse modo, o primeiro caso de uso que você precisa invest igar é aquele que inicia um novo • Jogo: Quando o programa está iniciando pela primeira vez ou o jogador acabou de jogar um jogo, ele pode optar por jogar um novo jogo. • Novo jogo com GU I I. O jogador clica no botão New Game e um novo jogo começa. • Condições prévias • o jogador deve ter acabado de iniciar o programaOll acabado de tenninar um jogo. • Condições posteriores • Novo jogo iniciado. Conforme você pode ver, esse caso de uso não altera o domínio do jogo vinte-e-um . Ele sim plesmente configura as regras básicas da UI. O próximo caso de LISO da UI analisa as apostas: Os jogadores começam o jogo com US$ I000 em seu pote. Antes que qualquer carta seja distribuída, cada jogador deve fazer uma aposta. Começando com o primeiro jogador, cadajogador aposta um valor de US$IO, USSSO ou US$ IOO. Se um jogador ficar abaixo de USSO, ele ainda poderá jogar. O valor em seu pote é refletido como um número negativo. • Jogador faz aposta com GU I I. O jogador se leciona um dos seguintes níveis de aposta: 10.50 ou 100 e faz a aposta imediatamente. 2. A aposta passa para o jogador seguinte e se repete até que todos os jogadores tenham feito uma aposta. • Condições prévias • Novo jogo in iciado. • Condições posteriores. • O jogador fez uma aposta. • A banca pode começar a dar as cartas Você ainda precisa de suporte para dar mais cartas e para parar. O próximo caso de uso identi fica o ato de dar mais cartas: O jogador decide que não está satisfeito com sua mão. O jogador ainda não estourou; portanto, ele decide receber mais cartas. Se ojogador não estourar, ele pode optar por receber ma is cartas novamente ou parar. Se o jogador estourar, o jogo passa para o jogador scguinte. • Jogador recebe mais cartas com GU I I. O jogador decide que não está satisfeito com sua mão. 2. O jogador clica no botão Hit, que solicita outra carta da banca.
Iteração 4 do jogo vinte-e-um: adicionando uma GUI
435
3. O jogador pode optar por receber mais cartas novamente ou parar, caso o total em sua mão seja menor ou igual a 2 1. o
o
Cond ições prévias o
O jogador tem uma mão cujo valor total é menor que 2 1.
o
O jogador nào tem vinte-c-um.
o
A banca não tem vinte-e-um.
Condições posteriores •
•
Uma nova carta é acrescentada na mão do jogador.
Alternativa: Jogador estoura A nova carta faz com que a mão do jogador seja maior do que 21. O jogador estoura (perde). Começa a vez do jogador seguinte/ banca.
•
Alternat iva: a mão do jogador é maior do que 2 1, mas ele tem um ás A nova carta faz com que a mão dojogador seja maior que 21. O jogador tem um ás. O valor do ás muda de II para I, fazendo a mão do jogador ser menor ou igual a 2 1. O jogador pode optar por receber mai s cartas novamente ou parar.
Se um jogador não quer receber mai s cartas, ele deve parar. O próx imo caso de uso analisa o uso da QU I para parar: O jogador decide que está satisfeito com sua mão e pára. o
Jogador pára. 1. O jogador decide que está contente com sua mão e clica no botão Stand.
o
•
Condições prévias •
O j ogador tem uma mão cujo valor é menor ou igual a 21.
•
O j ogador não tem vinte-e-um.
•
A banca não tem vinte-e-um.
Condições posteriores •
A vez do j ogador termina.
E, por último, mais não menos importante, você precisa considerar a saída do j ogo: O jogador decide que não quer mais jogar e sai. •
Jogador sai do j ogo com QU I I. O jogador clica no botão Quit.
2. O jogo se fec ha. o
Condições prévias •
O jogo não deve estar em andamento (nenhum jogo foi iniciado ou o jogo foi concluído).
1 436
•
Dia 18
Condições posteriores •
Desligamento do j ogo.
Modelos visuais de GUI Com base nos casos de uso enumerados na seção anterior, você precisará projetar o layout da GUI. A Figura 18. 1 apresenta uma possível GU I que preenche todos os requisitos descobertos durante a análi se.
FIGURA 18 .1 li CU! dojogo \'il1le-e-lIl11.
Deale r (Banca)
Ica na ll cana l " · Nome do jogador S Banco
I· .. El Icana
II I I USS10 I I I Hit I I Stand I INewG.me I I Qu i. I USSóO
US5100
Existem alguns comportamentos ad iciona is da GUI com que você pode traba lhar agora. A Figura 18.2 ilustra o status dos botões quando o usuário inicia o jogo pela primeira vez.
FIGURA 18 .2 O stafll.\" inicial
do.\" balões.
US$10
I
Hit
II
II
US$SO
S!a nd
II
US$l 00
I lN_Gamai I
I Quit
I
Todos os botões são vi síveis quando o jogo começa; entretanto, apenas New Game e Quit estão ativos. A Figura 18.3 ilustra o status dos botões após um clique em New Game.
I
FIGURA 18 ,3
O stallls dos bOIOes após clicll/" em New Game.
I
Hit
US$1O
II
USS50
II Stand I I
II
New
US5100
Gam.1I
I Qui!
I
Ite ração 4 do jogo vinte-e-um: adicionando uma GUI
437
o jogador deve fazer uma aposta após clicar em New Game. Como resultado, apenas os botões de aposta estão ati vos. A Figura 18.4 ilustra o status dos botões após ter fe ito a aposta. FIGURA 18.4
I us." II
O SllIfltS dos botões após !a=er
IIIIIlt
H't
II
U"",,
Stand
II
II uss", I
New Gama i
I
Qu 't
apoS/(I.
Após fazer uma aposta, um usuário pode receber mais cartas ou parar. Assim, apenas os botões Hit e Stand estão ati vos. Todos os outros botões são desativados. Os botões permanecem nesse estado até que o jogador pare ou estoure, no ponto em que o jogo termina os botões voltam para o status ilustrado na Figura 18.2. Após a conc lusão de um jogo, as cartas permanecem na te la até que o usuário clique em New Game. As cartas são então removidas da tela. Durante o jogo, a mão gráfica do jogador é atual izada, quando uma carta é distribu ída para ele. Esse layout de OU I afetará fortemen te os modos de visua lização que você vai projetar na próxima seção.
Projeto da GUI do jogo vinte-e-um Projetar as classes que compõem uma OU I não é diferente de projetar qualquer outro tipo de classe. Você deve identi fi car as classes individuais e suas responsabilidades. Usando a Figura 18. 1 como ponto de partida, você pode gerar uma lista inicial de classes. Você precisará de uma classe para a tela principa l, uma classe para ver um jogador e uma classe para visua li zar as opções do jogador.
Cartões CRC da GUI Uma sessão de cartão CRC pode ou não ser garantida aqu i. Tudo depende de seu nível próprio de bem-estar. Para uma GU I maior, você definitivamente desejará passar por um número de sessões, para garantir que tenha fei to um bom traba lho na divisão das responsabilidades. Para nossos propósitos, a OUI do jogo vinte-e-um é suficientemente simples para que se possa pular uma sessão de CRC completa. Em vez disso, listaremos as responsabilidades aqui.
PlaverView PI ayerViewé responsável por visualizar um objeto Player no jogo vi nte-e-um. O modo de visualização deve apresentar a mão, o nome e o saldo do pote de Player (se aplicável). Pl ayerView é
si mplesmente um veiculo de visual ização. Desse modo, ele não exige um controlador. Ele preci sa simplesmente receber e apresentar seu objeto PI ayer.
Dia 18
OptionView e OptionViewController Opt i onVi ew é responsável por visualizar as opções do jogador humano. Opt ionVi ew também preci· sa responder â interação do llSlk'irio, de modo que exige um controlador: Opt i onVi ewContro 11er.
CardView CardVi ew é responsável por visualizar os objetos Card individuai s. CardVi ew não é interativo; as· si m, ele não exige um controlador. PlayerV i ew usará CardV i ew para vi sualizar Hand.
BlackjackGUI B1ackjackGU I é responsável por agregar e apresentar todos os diversos modos de visual ização. Como BlackjackGUI at ua como um shell simples, ele não exige um controlador.
Miscelânea CardVi ew precisará de um modo para mapear um objeto Card em uma imagem, para exibição. Você pode implementar um longo iflelse ou case para mapear o objeto Card dentro de CardView; entretanto, tal estratégia é horrível (para não mencionar que é lenta). Em vez de criar uma estrutura condicional, você deve faze r uma subclasse de Oeck e Cardo Você pode chamar as duas classes resullantes de VDeck. e VCard, respcçt ivamente. VCard receberá um argumento construtor extra, o nome de um arquivo bitmap. VOeck construirá objetos VCa rd. Como você passa Oeckpi 1e para Bl ackjackDealer, em vez de permit ir que BlackjackOeal er crie seu próprio maço de cartas, pode passar as carias visuais de fo nna transparente para a banca. Você também precisará criar um novo jogador humano para a OU I. Esse novo objelo GU IPl ayer pode herdar diretamente de Bet t i ngPl ayer; entretanto, ele precisará fornecer seus próprios esta· dos Aposta e Jogo personal izados. Em vez de basear uma decisão no método hi t () ou bet(), o objeto GUIP l ayer precisará obter essa informação da OU I. Como resultado, você prec isará de métodos que a OU I possa chamar para apostar, receber mais cartas e para r. Quando esses métodos forem chamados, eles colocan1o o jogador nos estados corretos e com un icarão qualquer informação para a banca. Ao todo, você precisará adicionar ou sobrepor os seguintes métodos em GUIPlaye r: placel()'BetO. place50Bet() , placelOOBet(), takeCard() , stand(), getBettingState() e getPlayingState().
Estrutura da GUI •
As vezes, ao se trabalhar com uma GUI, aj uda esboçar o modo como as panes se encaixarão. Como a própria GUI é visual, seu esboço pode ser realmente um pouco mais poderoso do que os diagramas de classe padrão. A Figura 18.5 visualiza Pl ayerView.
Iteração 4 do jogo vinte-e-um: adicionando uma GUI
439
FIGURA 18.5
Vislfoli:olldo
Pl ayerV i ew.
Você vê que, na Figura 18.5, PI ayerVi ew é constituído de vários objetos CardView. PI ayerView também desenha uma borda em torno de si mesmo, com o nome e o saldo do pote (se aplicável) de PI ayer, no canto superior esquerdo. Fe lizmente, o pacote Java javax . swing .JPanel fornece toda a funciona lidade que você precisa para dispor componentes, assi m como para desenhar uma borda rotu lada. Continuando, a Figura 18.6 visual iza OptionView. Oplion View
FIGURA 18.6
Visl/a!i:olldo
OptionView.
Controles de aposta
r - - - - - - - -
-A.- - - - - - - -
--I
II II 1\ :, , ~ :I II I I G.mel I I: :
U5$10
Hit
U5$5O
Stand
U5$100
N8W
Qui!
:_\ ___ ~ ___ ~f!: ___ ~----~ ~ Controles do jogador
Controlei do ioga
OptlonView é si mplesmente um conjunto de botões. Uma combinação de javax.swing.JPanel (para an inharas botões) e j avax. swi ng .JButton deve fornecer tudo que você precisa para implementar esse modo de visualização. A Figura 18.7 reúne visualmente todas as partes. As fi guras anteriores devem ajudar a visualizar como todos os modos de visualização se encaixam. Entendercomo as partes se encaixam pode ajudar durante a implementação de urna GU J.
Refazendo Agora que existem dois tipos de jogadores humanos -GU I e CLUJ - provavelmente faz sentido renomear HumanPlayer como ConmandUnePlayer. Você deve fazer essa alteração hoje.
Dia 18
FIGURA 18.7 A j(me/a I>rincifXIl dividida por 1II0dos de ris/lali=açc7o.
Modo de visualização da carta: vislvel
GUI do jogo vinte-e·um
-------------------, I
Modos de visualização do jogador
Banca
,, ,,
,, ,,
<------~'
~---
--------
---
______ I
-----------------, I Nome do jogador $ Banco
,
Icarta I l ca rta M odo de visueli zaçêo de opçêo
I
~--
I...
--------
----
_
_____ I
Diagrama de classes da GUI Agora que todas as novas classes estão identificadas, você pode mode lar a estrutura de classes resultante. Assim como o diagrama de classes do Capitulo 17, ·· lteração 3 do jogo vinte·e-um: adicionando aposta", o modelo aprese.uado na Figura 18.8 focaliza a estrutura.
Implementação da GUI do jogo vinte-e-um Ao se implementar uma OU I, em geral, é mais fácil traba lhar de ba ixo para cima. Nesse sent ido, você deve implementar na seguinte ordem: VCard, VDeck, CardView, PlayerVlew, OptlonView, Opt i onVi ewCont ro 11er, GU I P1 ayer e B1 ac kjackGU I. Vamos exam inar os destaques de cada classe.
Implementando VCard, VDeck e CardVi ew VCard tem uma implementação relativamente simples, pois apenas acrescenta mais um atributo à classe Cardo A Listagem 18.2 apresenta a nova definição da classe VCard.
Iteração 4 do jogo v inte-e-um: adicionando um a GUI
FIGURA 18.8
441
BI"CkjlM'k GUI
A esrrulllra de classes da GUI.
Jt..bel
,
2. .B ,=:-= ,
1
r:-:---:-:-=---' Option ViewConlro II••
• uibe
,
comrola
VCfld
manipu la.
recebe dto
..,.nipul, a r,..,.be dto
B.ttingPl. "..
diouibul dto
" I diciona cartas 1m QU IPI ..... .
LISTAGEM 18.2 VCard.java pub l ic class VCard extends Card { String image; publi C Vcard( Sui t suit, Rank rank , String image ) ( supere sui t , rank ); t his. image .. image; }
publ ic Str i ng getl mage () { i f( i s Fa ceUp() ) I return image;
Dia 18
LISTAGEM 18.2 VCard.java (continuação) } else { return N/ bltmaps/ empty pile.xbm N: } }
}
A implementação de VOeck é si mples. Para criar objetos VCa rd. em vez de Card, você preci sará sobrepor Ométodo bui l dCards() de Deck. Para sobrepor o método, primeiro você precisará mudar o método para protegido em Oeck. Originalmente, o método era privado. A Listagem 18.3 moma uma li stagem parcial da implementação de VOeck. LISTAGEM 18.3 VOeck.java pub l iC cla ss VOeck extends Oeck { protected void buildCards() {
II Isso
ê horrível , mas ê melhor do que a alternativa laço/ if/ else
Ca rd () deck • new Card (S2): setOeck( deck l: deck [O] • new Vcard ( SuH.HEARTS , deck [1] • new Vcard( Suit. HEARTS , deck [2] • new Vca rd ( Suit. HEARTS, deck [3] • new Vcard ( Suit. HEARTS, deck [4] • o•• Vcard( Suit. HEARTS, deck [5] • new Vcard( Sui t.HEARTS, deck [6] • new Vcard( Suit.HEARTS, II restante cortado por brevidade
Rank . TWO , "/ bi tmaps/ h2 " ): Rank. THREE, N/ bitmaps / h3N ) ; Rank . FOUR, "/b itmap s/ h4~ ): Rank . FIVE , "/bHmaps/ hS- l: Rank.S IX, "/ bHmaps/ h6" ,; Rank.SEVEN, "/bitmaps/h7" ) ; Rank . EIGHT, "/bitmaps/h8" ):
Para a GU I, usaremos um conjunto de bitmaps que está contido no diretório bitmaps,j unto com o download do cód igo-fonte. Os nomes dos bitmaps seguem uma convenção de atribuição de nomes especíl1ca; ponanto, você também pode implementar bui 1dCards () como um laço. Embora seja horrível, simplesmente codificar os valores é um pouco mai s fácil de entender (e manter).
Iteração 4 do jogo vinte-e-um: adicionando uma GUI
ALERTA
443
Codificar a cri ação da carta também pode não ser a solução mais fá cil de manler . O problema é que cada estratégia que você possa adol ar tem uma deficiência. VDeck é um exemplo de uma dessas ocasiões em que você deve fa zer uma escolha entre d ois males e conv iver com ele. A solução delineada anterior mente é fa lha. devido aos erros inerentes na d igit ação de t odas as chamadas. Além d isso. se o construtor mudar. você precisará atualizar cada chamada . Co m o altern ativa. você poderia fa zer um laço pela rep resen tação de List de Ranks. Tal sOlução o o briga a supo r uma ordem específica dos elementos na li sla (para que você possa gerar corretamente o nome de arqui vo da imagem). Se a ordenação mudar. o laço será desfeito misteriosamente. Qualquer um qu e mantiver o código terá dificuldade para encon trar a fonte do erro. Evi ta mos a estratégia do laço porque alterações em uma classe não rela cionada poderia danificar VOeck.
CardVi ew ex ibirá o bitmap VCa rd. j avax . swi ng. JLabel fornece a funci onalidade necessária para exibir um bitmap. A Listagem 18.4 apresen ta a im plementação de CardVi ew. LISTAGEM 18.4
Ca rdV iew.java
. . *: i mpor t Javax. SWlng. . t .*; i mport Java.aw publiC cla ss Ca rd View ex tends JLabel { private ImageI con icon; publi c CardView( VCard card ) I getImage( card.get Image{) ) ; setIcon( i con ) ; setBackground( Colo r.white ) ; setOpaque( tru e );
,
pri vate voi d get Image( String name ) I ja va.ne t.URL url • thi s.ge tClass().getResource ( name ); icon • new Image Icon( url ) ;
,
,
CardVi ew recebe um objeto VCard, extrai o caminho do bitmap, converte O cami nho em uma url, cri a um ImageIcon e ad iciona o ícone em si mesmo. Isso é tudo que você precisa para carregar e exibi r um bi tmap!
Dia 18
Implementando PI ayerVi ew PJayerView exibirá qualquer subc lasse de pj aye r. Ao contrário de OptionView, que você verá na próx ima seção, pj ayerV l ew só precisa apresentar o objeto PI ayer; ele não aceita interação do usuário. Como resu ltado, a implementação é muito simples. A Listagem 18.5 apresenta o método que é chamado quando o objeto PI ayer muda. LISTAGEM 18.5 O código atual izado de PlayerView publiC void playerChanged( Player player ) ( border . setTitle( player.getName() ) ; cards . removeAll(); Hand ha nd • player . getHa nd() ; Iterator i • hand.getCa r ds() ; while(i . hasNext{) ) ( VCard vcard • (Vcard) i.next() ; JLabel card • new Ca rdView( vcard l ; cards.add( card li
I reval i dateO; repai nt () i
I Como você pode ver, o método pl ayerChangedO extrai os objetos VCard de PJayer e cria um CardView para cada um . Finalmente, ele adiciona o modo de visualização em si mesmo, para que o objeto VCard seja apresentado. A implementação apresentada aqui não é a mais eficiente, pois ela cria um novo CardVi ew para cada objeto VCard, sempre que o objeto PI ayer muda. Uma implementação mais efic iente poderia usar alguma cache do modo de visualização. Como você está usando objetos, pode mudar a implementação para outra mais eficiente, a qualq uer momento. O desempenho parece estar normal, de modo que a sobrecarga da ad ição do uso de cache simplesmente não va le a pena neste ponto. Pl ayerVi ew também precisa apresentar o resultado do jogo de Pl aye r. A Listagem 18.6 apresenta dois métodos que são chamados no fi na l do jogo de Pl ayer. LISTAGEM 18.6 Um exemplo dos métodos PJ ayerL i stene r de Pl ayerVi ew public void pl ayerBusted( Player pl ayer ) ( border.setTitle( pl ayer.getNameO + " BUSTEO!! " )i card s .repa i nt()i
I publ i c void pl ayerBlackjack( Player player ) {
Ite ração 4 do jogo vinte-e-um: adicionando uma GUI
LISTAGEM
,
445
18.6 Um exempl o dos métodos Player Listener de Pl ayerV i ew (continuação)
border.setTit le( playe r.getNameO + " 8lACKJACK !" ); cards.repaf nt( );
Esses métodos con fig uram a borda do modo de visualização com o resullado do jogo. PI ayerListener defi ne mais do que dois métodos, mas assim como os dois listados aq ui, a impl ementação dos métodos de PI ayerVi ew segue um padrão semelhante para todos. Exam ine o código- font e, se você esti ver interessado em ver a lista intei ra de métodos de atuali zação.
Implementando OptionView e OptionViewController Opti onView herda de JPanel e acrescenta vários botões em si mesmo. Opti onV iew não recebe do modelo. Em vez di sso, Opt i onViewControll er recebe do modelo e ativa ou desativa os botões em OptionView, conforme for apropriado. Nenhuma das duas classes é muito interessante do ponto de vista da implementação. Se você estiver interessado nos detalhes especificas, faça download e leia o cód igo.
Implementando GUIPlayer GU IPI aye r provavel mente é a classe mais interessante dessa iteração. Ao implementar uma GUI, você deve lembrar que toda interação com o usuário é assíncrona - ela pode surgir a qualquer momento. Escrever um jogador de linha de comando foi muito fácil . Você s6 teve que sobrepor hl te) ou bet() para que ele fosse lido da linha de comando. Como a linha de comando fi ca bloqueada até receber a entrada do usuário, o jogador foi muito fáci l de implementar. Um jogador com GUI é um pouco mais dificit de esc rever. Em vez de chamar um método e bloquear até recebermos a entrada, GU IP layer prec isa esperar até que o usuári o dec ida cl icar em um botão. Como resultado, todos os estímu los vêm de/ora do jogador. Em resposta a essa rea lidade, você precisa adicionar vários métodos que a GU I possa chamar para GUIPl ayer. A Li stagem 18.7 lista os métodos de aposta que você deve ad icionar. LISTAGEM
18.7 Métodos de aposta de GU I Player
II esses métodos de aposta serão chamados pe lo controlador da GUI II para cada um: faz a aposta co rreta, muda o es tado, permite que a II banca sai ba que o jogador t erminou de aposta r public void pl acel08et() ( get8an kO . pI ace 108et () ; setCurrentState( getWaitingState() ) ;
Dia 18
LISTAGEM 18.7 Métodos de aposta de GUIPlayer (continuação)
,
dealer.doneBetting( this );
publiC void pla ce50Bet() I getBank() . place50Bet(); setCurrentState( getWaitingState() ); dealer.doneBetting( this };
,
pUblic void placelOOBet() { getBank().placelOOBet(); setCurrentState( getWa i tingState() ); dea l er.doneBetting( th1s );
,
Você notará que esses métodos precisam fazer apostas e configurar o usuário com o estado correto. A Listagem 18.8 li sta os métodos para receber mais cartas e para parar. LISTAGEM 18.8 Métodos para receber mais cartas e para parar de GUIPlayer
II takeCard será chamado pelo control ador da GUI , quando o jogador II decidi r receber mais cartas public void takeCardO { dealer.hit( thi s );
,
II stand será chamado pelo controlador da GUI . quando o jogador optar II por parar, quando a parada mudar de estado , deixa o mundo saber, e então II diz à banca public void stand{) { setCurrentState( getStand1ngState() l; notifyStanding(); getCurrentStateO .execute( deale r );
,
Assim como os métodos de aposta, os métodos da Listagem 18.8 devem executar sua ação e atualizar o estado. Como o estado não pode simplesmente chamar hi t () ou bet() para jogar ou apostar, você precisará fornecer alguns estados Jogo e Aposta personalizados. A Listagem 18.9 apresenta os metodos getPlayingStateO e getBettingStateO sobrepostos. LISTAGEM 18.9 Métodos de obtenção de estado sobrepostos de GU I Player protected PlayerState getPlayingState() I return new Playing();
Iteração 4 do jogo vinte-e-um: adicionando uma GUI
LISTAGEM 18.9
447
Métodos de obtenção de estado sobrepostos de GU IPl ayer (conto)
I protected PlayerState getBett i ngState() I return new Betting();
I Sobrepondo esses métodos, GUIPlayer pode fornecer seus próprios estados personalizados. A Listagem 18. 10 apresenta o estado Jogo personalizado de GUIPl ayer. NOTA
LISTAGEM 18.10
Métodos como getPlayingState() e getBettingStateO são métodos faclory.
Estado de Jogo personalizado de GUIPlayer
private class Playing implements PlayerState I pub l iC void handPlayab l e() I II não faz nada I public void handBlack jack() I setCurrentState( getBl ackjackState() ); notifyBlackjack (); getCurrentS t ate().execu te( dea l er ); I publi c vo i d handBusted() ( setCurrentState( getBustedState() ); not ifyBusted () ; getCurrentState().execute( dealer };
I public void handChanged() ( notifyChanged();
I public void execute( Dealer d ) { II não faz nada aqui , as ações virão da GUI, que é II externa ao estado, mas quando eventos chegarem. certifica-se de II impor a transi ção de estado imediatamente
I I
Dia 18
Quando executado, o estado Jogo personalizado não faz nada. Em vez disso, GU IPlayer precisa esperar pela interação assíncrona da GUI. Você notará que o esradoJogo ainda faz transiçôesem resposta aos eventos de Hand. A Listagem 18.1 1 apresenta o estadoAposla personalizado. Você notará que esse estado não faz nada. Em vez disso, GUIPl ayer deve esperar que ojogador pressione algum botão na GUI. Quando
isso acontecer, o botão chamará o método de aposta correto em GUIPlayer. LISTAGEM
18.11 Estado Aposta personalizado de GUIPlayer
private class Bett1 ng implements P1ayerState { pub1ic void handChanged() { II impossível no estado de estouro
I public void handPlayab1e () { II impossível no estado de estouro
I publ ic void handBlackjackO { II impossível no estado de estou ro
I public void handBusted{) { II impossível no estado de estouro
I public void execute( Oea l er d ) ( II não faz nada aqui , as ações Vl rao da GUI, que é II externa ao estado, pois nenhum evento vem como parte da II aposta: o estado precisará ser mudado externamente pa ra este estado
I I
Reunindo tudo com BlackjackGUI B1 ackjackGU I cria e ex ibe o sistema de jogo vinte-c-um. A Li stagem 18. 12 deslaca o método setUpO de Blac kjackGUI. LISTAGEM
18.12 Método setUpO de BlackjackGUI
private void setUp() { BlackjackOealer dea1er • getOealer(): PlayerView vI • getP1ayerView( dealer ) : GUIPlayer human = getHuman() ; P1ayerView v2 z getPlayerView( human ); PlayerView [] views • { vI , v2 }:
Iteração 4 do jogo vinte-e-um: adicionando uma GUI
LISTAGEM 18.12
449
Método setUp() de BlackjaçkGUI (continuação)
addPlayers( views )i dealer.addPlayer( human li addOpt ionV iew ( human , dea ler )i }
o método setupO cria cada jogador, os modos de visualização e coloca tudo junto. Os outros métodos principalmente constroem os vários objetos. Se você estiver interessado no código-fonte completo, exam ine o cód igo. A Figura 18.9 ilustra a tela fina l do jogo.
FIGURA 18.9
;/ GVI dojogo ,·ill/e-e-lIl11.
Resumo Hoje, você viu o padrão MVC aplicado a um programa rea l. Às vezes, ajuda ver um exemplo estendido, para poder entender completamente um padrão. A lição de hoje também apresentou a questão de que uma GU I não é algo a ser pensado posteriormente. A GUI merece o mesmo nível de análise e projeto que qualquer outra parte de um sistema. A lição de hoje conclui o jogo vinte-e-um. Amanhã, você verá um projeto e implementação alternativos da GUI do jogo vinle-e-um .
Dia 18
Perguntas e respostas P Você mencionou anteriormente que não deve simplesmente anexar a GUI no final. Bcm, essa foi a última iteração e estamos adicionando uma GUI . Isso não vai contra o que você disse anteriormente?
R Não, absolutamente, não ! Quando dissemos ' anexar', queríamos dizer adicionar uma GUI sem realizar qualquer projeto. Nós planejamos uma GU I desde o início. No começo, dissemos que poderíamos usar MVC. Esse foi todo o projeto que prec isamos realizar, até estarmos fin a lmente prontos para adicionar a GUI. Uma vez prontos para ad icionar a GU I, fizemos mais um projeto, em uma iteração totalmente dedicada à GUI. Dizer anteriormente que usaríamos MVC e adicionar um mecani smo observador era tudo que prec isávamos fazer para saber que pudesscmos s uportar uma GU I. P Onde/quais estão/são as diferentes partes do MVC (encontramos menção de vários modos de \'is ualização e de um controlador)? Desenvolva o que são o modelo e o controlador.
R O modelo é o sistema. Neste caso, Bl ackjackOea l er, BettingPl ayers etc., consti tuem a camada do modelo. O projeto pedi u apenas um contro lador mu ito a dizer sobre controladores.
Opt "i onViewCon t rol l er -
assim, não havia
Workshop As perguntas e respostas do teste são fornecidas para seu melhor entendimento. Veja as res postas no Apêndice A, " Respostas".
Teste I. Como você usou herança e polimorfismo para introduzir uma carta " vi sual"? 2 . Na lição sobre herança, foi mencionado que, se um método não é necessário para as classes externas e não exi stem requisitos para uma subclasse usar o método, então você deve defini-lo como privado. Se uma subc lasse precisar dele, você pode torná-lo protegido nesse momento, mas não anteriormente. Encontre um exemplo dessa recomendação no projeto do jogo vinte-c-um .
Ite ração 4 do jogo vinte-e-um: adic ionando uma GUI
451
Exercícios I. Faça downl oad do código-fonte da iteração de hoje. O cód igo está dividido em dois direlórios separados: mvc_9u i e exercise_2. mvc_gui contém o cód igo da GUI que foi criada durante a iteração de hoje. exerci se_2 contém os arqu ivos que você precisará para o Exercíc io 2, ass im como as soluções. Estude o cód igo cm mvc gui. Tente entender como tudo func iona e depois complete o Exercício 2. 2. O Exercício 2 do Capítulo 17 pediu para que você adicionasse o recurso de apostar em dobro no j ogo vi nte-e-um. Você precisa adicionar esse recurso no jogo novamente. Desta vez, ad icione-o na versao gráfica do jogo que você carregou por downl oad para o Exercício I. O download inclui lodosos arquivos que você precisará para começar este exercício.
SEMANA
3
DIA Aplicando uma alternativa ao MVC Ontem, você ana lisou, projetou e implementou uma GUI para o jogo vinte-e-um. Hoje, você vai usar uma outra estratégia para o projeto e implementação da OUI. l-laje você aprenderá: •
Sobre uma alternativa para o padrão de projeto MVC
•
Co mo apl icar uma OU I altemat iva no jogo vinte-c-um
•
Quando basear s uas QUIs no MVC e quando não fazer isso
Uma GUI alternativa do jogo vinte-e-um Ontem, você criou uma OUI para o jogo viIHe-e-urn baseada no MVC. O MVC é apenas uma estratégia para o projeto de GU I. Hoje, você va i reprojetar e reimplementar a GU [, usando uma estratégia diferente. A estratégia que você vai empregar hoje é uma especialização do padrão de projeto PAC (Presellfalioll Absfraclion Con/rol). Assim carnoo padrão de projeto MVC, o padnl0 de projeto PAC divide o projeto da GUI em três camadas separadas: •
A camada de apresentação, que exibe o sistema
•
A camada de abstração, que representa o sistema
•
A camada de controle, que monta todos os componentes da camada de apresentação
Dia 19
As semel hanças do PAC com o padrão de projeto MVC são ilusórias. Na verdade, o PAC segue uma fi losofia totalmente di ferente daquela seguida pelo padrão MVC.
As camadas do PAC A camada de abstração do PAC é semel hante ã camada de modelo no padrão MVC. A camada de abstração abriga a func ionalidade básica do sistema. É essa funcionalidade que a camada de apresentação exi be e manipula. A camada de abstração também é responsável por dar acesso aos objetos do níve l de apresentação. No PAC, as funcionalidades das camadas de visua lização e controladora do MVC não são dividi das; cm vez disso, essas duas entidades são combinadas dentro da apresentação. A camada de apresentação é responsável por exib ir e manipular a camada de abstração, assim como por responder ã interação do usuário. Corno o comrole e o modo de visualização MVC são combi nados na camada de apresentação, o controle tem um objetivo totalmente diferente no PAC. No PAC, o controle monta todas as diferemes apresentações. Ele não recebe e responde â interação do usuário, corno o controlador MVC.
A filosofia do PAC
o MVC faz todo o possível para desacoplar completameme cada parle de seu projeto. Quando você lisa o padrão MVC, é fâcil trocar para novos modos de visual i7..açãoem seu sistema, a qual quer momento; assim, usaro padrão MVC proporciona a você muita liberdade sobre como exibe seu sistema. Entretanto, o Capítulo 13, "00 e programaçâo de interface com o usuário", informa que a maior liberdade tem o preço do encapsulamento. A estratégia do PAC é diferente. O PAC não desacopla as camadas de apresentação e abstração. Em vez disso, as duas camadas sâo forte mente acopladas. Isso não quer dizer, por exemplo, que PI ayer estenderá JComponen t diretamente . Isso quer di zer que a camada de abstração criará e retornará sua apresentação. Assim , PI ayer e sua apresentação ai nda são dois objetos separados. Para obter uma aprese ntação diferente de uma parte da abslração, você prec isará al terar a defini ção da abstração para que ela relorne um objeto de apresentação d iferente. É um pouco mais d ifiei l alterar a apresentação ou fornecer dois ou mais modos de visua lização difere ntes do mesmo sistema. A construção da GU I é mais fáci l, entretanto. Quando a camada de controle montar a tela, ela solicitará a cada membro da camada de abstração sua apresentação. Tudo que a camada de controle precisa fazer é adicionar essa apresentação na tela principal. Não há modo de visual ização e controlador para reuni r.
Aplica ndo uma alte rna tiva ao MVC
Quando usar o padrão de projeto PAC A suposição s ubjacente do PAC é que você não precisará fornece r vãrios modos de visua lização do sistema. Em vez disso, quando usa o PAC, você precisa cert ifi car-se de que o sistema tenha apenas uma interface bem definida. Se seu sistema vai ter apenas uma interface, o PAC pode fornecer uma alternativa mu ito elegante ao MVC.
O PAC tem várias vantagens. Como a camada de abstração pode criar sua apresentação, você não precisa destrui r o encapsulamento de seu sistema. Em vez disso, você pode defini r as classes de apresentação como classes internas. Como lima classe interna, a apresentação pode ter talai acesso a s ua classe de abstração progeni tora. Quando ela precisa visualizar a abstração, pode acessar e exibir diretamente o estado da abstração.
O uso de PAC si mpli fi ca a comun icação entre a apresentação e sua abstração. Quando a abstração mudar, ela pode simplesmente chamar um método de atua lização na apresentação que tiver criado. Quando você usa o padrão PA C, pode considerar a apresentação como urna extensão direta da abstração. Atuando na apresentação, você atua d iretamente no sistema s ubjacente. De certa fo rma, a apresentação age de forma muito parecida com um proxy para o sistema. Tal manipulação direta simplifica muito o projeto global.
Analisando a GUI PAC do jogo vinte-e-um Para aplicaro padrão de projeto PAC na aUI do jogo vinte-e- um, você pode simplesmente reuti liz.1r a análise que fez ontem. Nada muda quanto à análise, pois você decide usar o padrJo PAC em vez do MVC.
Projetando a GUI PAC do jogo vinte-e-um Para o jogo vinte-e-um ex iste apenas uma interface principal: uma aU I. Você não vai distribu ir esse jogo como um ap licativo HT ML da Web (embora você pudesse transformá- lo em um applet fac ilmente) ou como um POA; assim, é seguro usar o padrão de projeto PAC. ,
E importante notar que nada o obriga a remover os mecanismos receptores que já construiu no sistema. Ainda é possível ter uma aUI baseada em linha de comando e uma aU I baseada no PAC, simultaneamente. Na verdade, através do uso cuidadoso de subclasses, você pode deixar Iodas asdefin ições de classe originais intactas. Quando você quiser uma aU I, pode si mplesmente instanciar as classes que suportam uma OUI. Quando você quiser um jogo de linha de comando. pode instanciar as classes ant igas. Escolher MVC ou PAC não o impede necessariamente de usar o outro. Assi m como no Capitulo 18, " Iteração 4 do j ogo vinte-e-um: ad icionando uma aUI", você pode pegar o projeto e implementação completos do Capitulo 17, " Iteração 3 do jogo vinte-e-um: adi-
Dia 19
cionando aposta", como base para a nova GU I. O que você precisa fazer é descobrir q ua is dessas classes precisam de seus própriosobjetos de apresentação. Quando você tiver essas classes identificadas, precisará projetar a camada de abstração. Q uando isso estiver fei to, você poderá projetar a camada de controle.
Identificando os componentes da camada de apresentação Para identificar os componentes da camada de apresentação, ajuda fazer alguns esboços da GU I. Desta vez, você deve associar partes da tela à classe subjacente, em vez de associá-Ias a um modo de vis ualização separado. A Figura 19.1 isola uma part e da GU I. Dissecando cui dadosamente esse segmento da tela, você pode identifica r alguns componentes da apresentação.
FIGURA 19. 1
Um .çegmelllo da tela.
Jogador
M'o , --- .\ -Noma - - L - - - - - -, , ,,- __ , , , ,, , r rta r , , ,, ,r ____ , ---- - - - -, Carta
:8 :D···:
, ,, , ,
,, ,
~ -- ------ ----------
Dividi ndoo segmento da tela em partes, você pode ver que Card, Ha nd e Player precisal""ao fornecer objetos de apresentação. A Figura 19.2 d isseca a parte restante da te la.
FIGURA 19.2
Os botões (/(1 GUI.
Jogador
----~--------------- ,,
I
Hit
USS10
II
II
US$50
Stand
I
II
New
US0100
Gam,ll
I
Quit
-- ------ -- ----------Todos OS botões pertencem ao objeto Player humano; portanto, a classe que representa o jogador humano prec isará estender a apresentação de PI ayer e adicionar botões. GU IP l aye r deve ter um projeto igual àq uele criado para o Capítulo 18. Em vez de repetir esse projeto aqui, volte e leia a seção " Implementando GUI Player" , no Capítulo 18, se você precisar de um lembrete. A
Aplica ndo uma alternativa ao MVC
única d iferença entre a GUIPlayer do Capítulo 18 e esta é q ue esta também fornecerá uma apresemação de si mesma.
Projetando os componentes da camada de abstração Na seção anterior, você identificou Card, Hand e as diversas subclasses de PI ayer necessárias para fornecer uma apresentação delas mesmas. Para cada uma dessas classes, você precisa criar uma subclasse de abstração. Em part icular, você prec isa ter uma subclasse de BlackjackOeale r, BettingPlayer, Hand c Ca rdo Além di sso, você precisa criar uma GUIPl aye r, como no Capítulo 18. Entretanto, essa GUI Pl ayer tam bém precisa fornecer uma apresentação. A Figura 19.3 ilustra a hierarquia de herança Player res ultante.
19.3 A hierarquia dI! abslraç(io FIGURA
PlIly9r
Pla)'er (Jogador) . 81acJcjacJcOeala
BettingPlllyer JPanal
VBlockjackPlayer
JPanel
1
,
V8ettingPlsyer
cria
, ,
tria
OealerView
GUIPlayer cria
BettingView
, ,
tontém
GUIVllJw
,~,~
Você precisa criar uma s ubclasse VBlackjackDealer BlackjackDealer. Você também preci sa criar uma s ubclasse VBetti ngPl ayer Bett i ngPl ayer. Essas s ubclasses adicionarão s uporte para criar e retornar objetos de apresentação. A Figura 19.4 il ustra as hierarq uias Hand e Car
Dia 19
FIGURA 19 .4
A hierarquia de absrraç(io Hand (Meio) e
Card (Ca,.w).
JllI bel
J llIbel
Você precisará criar lima subclasse YCard Card, assim como um a subc lasse VHand Hand. Essas
subclasses visualizarão Card e Hand, respectivamente. Como no projeto de ontem, você também precisa de um VDeck. O VDeck criará um maço de objetos VCard .
Projetando a camada de controle O controle é uma classe relativamente simples. Uma instância do controle recuperará um objeto VBla ckjackOea le r, assim como os vários jogadores. De cada um desses objctos, o controle soli citará um objeto de apresentação. O controle pegará esse objeto de apresentação e o ad icionará na ex ibição. Você precisará projetar um mecanismo que o console possa usar para solicitar à camada de abs-
tração a apresentação de seus objetos. A estratégia mais fácil é defin ir uma interface - vamos chamá-la de Di sp' ayab 1e. Di sp 1ayab I e tem um método: pub I i c JComponen t vi ew (), que recupera a apresentação de um objeto. Cada classe de abstração que fornece uma apresentação precisará implementar esse método. As figuras 19.5 e 19.6 mostram as hierarquias alual izadas. Agora, as classes de abstração percebem a interface Displayable.
Usando o padrão Factory para evitar erros comuns Existe um pequeno problema na hi erarqu ia resultante: nada o impede de criar um objeto Deckpi1e de objctos Card que não possam ser ex ibidos e passá-los para a banca. Os relacionamentos com capacidade de substituição permitem tal substituição. Infelizmente, você experimentará erros de tempo de execução, se misturare com binar as classes GU I e não-GU I incorretamente. O Capitulo 12, "Padrões avançados de projeto", apresentou o padITio de projeto Abstract Factory. Um motivo para usar esse padrão era para garantir que um conjunto de objetos fossem usados juntos. Isso o impede de usar objetos incompatíveis juntos.
Aplica ndo uma alte rnativa ao MVC
459
FIGURA 19 .5
A hierarquia de absfraçtio Pl ayer alllali:ada.
BllICkjackDealer
8ertingPfayer
Displayab le • vi!Wf}:JCompot1enr
VBlackjackPlayer
,
JPanal
,
V8ertingPIByer
,
8otringViow
cria
DealerView
GUIPlayer
, ,
conlém
cria=-_~~~~~
,
,
Mio
FIGURA 19 .6
A hierarquia de abslraçc70 Hand e Card allfali: ada.
VCard
JLebel
1
,
conlém
,
JLlbel
t..__{C~'~'d~V~"~W}.__~oo=nlém HandView
Você pode usar factory para garanl irque os objelos correiOS sejam usadosjunlos. Você precisará criar factory que retome um objeto VBl ackjackOeal er e um objeto GUIPlayer que tenham sido instanci ados com os tipos corretos de argumentos. Quando o controle for recuperar os jogadores e a banca. ele deverá fazer isso apenas através de factory. Essa camada extra garantirá que todos os objetos sejam instanciados corretamente.
Dia 19
Implementando a GUI PAC do jogo vinte-e-um A lição de ontem mostrou que, ao se implementar uma GU I, freqUentemente é mais fácil trabalhar de cima para baixo. Segui ndo essa recomendação, você deve implementar na seguinte ordem : VCard, VHand, VBettingPlayer, VBlackjackGUI e GUIPlayer. Vamos examinar cada implementação.
Implementando VCard e VHand VCard herda de Card e se representa através da classe interna: CardView. A Li stagem 19.1 apresenta a imp lementação de VCard. LISTAGEM 19.1
VCard. java
public class VCard extends Card implements Oisplayable { private String image; private CardView view; publiC Vcard( Suit suit. Rank rank . String image ) ( supere su i t. rank ) ; this.image · image; view · new CardView( getImageO ); J
publiC void setFaceUp( boolean up ) ( super.setFaceUp( up ); vi ew. changed O; J
public JComponent view() ( return view; J
private String getlmage() ( if( isFaceUp() ) ( return image; ) else { return M/bitmaps/ empty_pile.xbm-;
J J private class CardView extends Jlabel {
Aplicando uma alternativa ao MVC
LISTAGEM 19.1
46 1
VCard.java (continuação)
publiC Ca rdView( String image ) I setlmage( image )i setBackg round( Co l or.white li setOpaque( true ); J
public void changed() { se tI mage( get lmage() )j J
private void set lmage( String image ) { java . net.URL url z this .getClass().getResource(image l i Imagelcon icon • new ImageIcon(url l i se tI con( icon l: J J J
Essa implementação de VCard é muito parecida com aquela apresentada anlerionnente, com exceção da classe de apresentação interna. Na criação, VCard cria e contém um modo de apresentação de si mesmo. Você também notará que o novo atri bula agora fica completamente encapsulado dentro de
VCard . Para lima entidade externa exibir a imagem, ela precisa solicitar um modo de visualização a VCard. Além disso, quando a carta é virada, VCa rd diz automaticamente ao seu modo de apresentação para que se atualize, chamando changed () no modo de visua lização. Ao contrário do padrão MVC. todo o controle é mantido dentro da própria abstração. VHand é semelhante a VCard. Na cri ação, VHand cria um modo de apresentação de si mesmo. A Listagem 19.2 apresenta a implementação de VHand. LISTAGEM 19.2
VHand. java
pub l i C cl ass VHand extends Hand implements Displayable { private HandV iew view • new HandV iew()i pub 1i c JComponent vi ewO { return view ; J
II você precisa SOb repo r addCa rd e reconfigurar para que quando a II al teração se propague para o modo de visualização
~o
mudar, a
Dia 19
LISTAGEM 19.2 VHand.java (continuação) public void addCard{ Ca rd card ) I super.addCard( card ); view.changed(); J
public void reset() ( super . reset () ; view.changed(); J
private class HandView extends JPanel ( public HandView () ( super{ new Fl owLayou t(FlowLayout . LEFT ) ,; setBackground{ new Color{ 35 . 142. 35) ,; J
publiC void ch anged() ( removeAll (); Iterator 1 • getCards O; while{ i.hasNext() ) ( VCard card • (Vcard) i .next() ; add( ca rd . view() ,;
J revalidateO;
J J J
Assim como VCard, VHand diz ao seu modo de visualização para que se atualize quando VHand mudar.
Implementando VBetti ngPl ayer
o conceito por trás de VBet ti ngPl ayer é o mesmo de VHa nd e VCard. A Li stagem 19.3 apresenta a im plementação de VBett i ngPl ayer. LISTAGEM 19.3 VBettingPlayer.java publi c abstract class VBettingPlaye r extends BettingPlayer imp lement s Disp layabl e ( private BettingV1ew v1ew; publi C VBettingPlayer( String name, VHand hand, Bank bank ) {
Aplica ndo uma alte rnativa ao MVC
LISTAGEM 19.3 VBettingPlayer.java (continuação) supere name. hand. bank )i }
publi c JComponent view() { if(view z. null ) I view • new BettingView( (VHand)ge tHand() ); addLi stener( view ) ; }
return view ; }
pri vate class BettingView extends JPanel implements Pl ayerListener I private TitledBorder border ; public BettingView( VHand hand ) { supere new FlowLayout( Fl owLayou t. LEFT ) ) : buildGU I( hand.view() ); }
pub l ic void playerChanged( Player p ) { St r ing name • VBettingPl ayer.this.getName(): border.setTille( name ); repaint()i }
publi c void playerBusted( Playe r p ) { String name • VBettingPlayer. t his.getName() ; border . setTitle( name + BUSTED!!") i repaint(): M
}
II o resto dos métodos PlayerListener foi cortado por brevidade II todos eles seguem o mesmo pad rão : vej a a listagem completa no II código-fonte private void bUildGUI( JComponent hand ) { border· new TitledBorder{ VBettingPlayer.this.getName() ) ; setBorder( border ) i set8ackground( new Color( 35 . 142 . 35 ) ) : border.setTitleCol or( Col or.b l ack ) ; add( hand ): } } }
Dia 19
VBetti ngPl ayer cria seu modo de visualização e o configura como um receptor. Quando o jogador muda, o modo de visualização sabe atualizar-se automaticamente. De interesse é o método bUl l dGUI (). O método bu i IdGUI () configura o modo de visualização. Você notará que, em vez de pegar cada carta na mão e constru ir um modo de visualização, Bet-
t i ngV iewsimplesmente pega o modo de visualização de VHand e o insere em si mesmo. VHand gerenciará a exibição das cartas. Tudo que Bett i ngView tem de fazer é inseri r o modo de visua lização em si mesmo e manter o status do título atualizado.
Implementando VBl ackjackDea 1er VB I ackjackDeal er funciona exatamente como VBett i ngPI ayer. A Listagem 19.4 aprese nta a impl ementação de VBlackjackDea l er. LISTAGEM 19.4
VBlackjackOealer . j ava
pub l ic class VBlackjackDealer extends BlackjackOealer implements Oisp l ayabl e { private Oea l erView view; pub l iC VBlackjackDeale r ( String name , VHand hand , Oeckpile ca rds ) { supere name , hand , ca rds ); J
publi c JComponent view() { if( view •• nul l ) ( view new Oea l erV iew( (Vhand) ge t Hand() }: addL i stener( view l i E
J
return viewi J
private TitledBorder border i public DealerView( VHand hand ) { supere new Flow Layout( Fl owLayou t.L EFT ) ) i String name • VBlackjackOealer.this.ge t Name() ; borde r • new TitledBorder( name l : setBorde r ( borde r ); setBackground( new COlor(35 . 142. 35 ) ); border.set TitleColor( Color.bl ack ) ; add( hand.view() li repaintO: J
publ ic void playerChanged( Pl ayer p } {
Aplica ndo uma a lte rna tiva ao MVC
LISTAGEM 19.4
VBlackjackOea l er . java (continuação)
String name z VBlackjackDealer.this.getName(); border.setTitle( name ): repaintO; }
public void playerBusted( Player p ) { St ring name • VBlackjackDea l er.this .getName(); border.setTitle( name + ~ BUSTEO!!" }; repaintO; }
II o res to dos métodos Playerlistener foi cortado por brev i dade II todos eles seguem o mesmo padrao : veja a listagem completa no II código- fonte } }
Dea 1erVi ew recebe as alteraçôcs cm vaI ackjackDea 1er. À medida que essas alteraçôcs ocorrem, o modo de visualização atualiza seu título. VHand cuida de manter o modo de visual ização da carIa alualizado.
Implementando GUIPlayer Todos os aspectos não relativos a GU I da classe GUIPlayer são iguais àqueles apresentados ontem. Assim como as outras classes da camada de abstração, GUIPlayer defi ne urna classe de apresentação interna. Essa classe com bina as classes Opt i onVi ew e Opt 1onV i ewCont ro 11 er do Capítulo 18. O código de apresentação não é tão diferente das classes de modo de visualização e controle originais. Para ver a listagem completa, faça download do código-fonte do dia de hoje, no endereço www.samspublishing . com.
Reunindo tudo com o Controle A ntes de criar o controle, você precisa criar uma factory de jogador. A Listagem 19.5 apresenta a implementação de VPl ayerFactory. LISTAGEM 19.5
VPlayerFactory.java
public cla ss VPlayerFactory I private VBlackjackDealer dealer; private GUIPlayer human; private Deckpile pile:
Dia 19 LISTAGEM 19.5 VPlayerFactory.java (continuação) publiC VBlackjackDealer getDealer() { II cri a e retorna apenas um ;t( dealer •• null } ( VHand dea ler_hand = getHand() ; Deckpi l e cards z getCards(); dealer · new VBlackjackDealer( "0 ea l er R. dealer_hand . cards }: }
return dealer: }
publiC GUIPlayer getHuman() ( II cr ia e retorna apenas um if( human a. null ) ( VHand human_hand ~ getHand() ; Bank bank • new Bank( 1000 ); human· new GUIPlayer( "Human". human_hand . bank. getDealer()}: }
return human; }
pub l i C Deckpile getCards() { II cria e retorna apenas um if( pile •• null } ( pile · new Oeckpi l e(); for( int i • O; i < 4; i ++) ( pil e.shuffleO ; Oeck deck = new VOeck() ; deck.addToStack( pile ) ; plle.shuffleO; } }
re tu rn pile ; }
private VHa nd getHand() ( return new VHand(): }
}
VPlayerFac t ory garante que VBlackjackOea ler e GUI PI ayer sejam inslanciados corretamenle. A Listagem 19.6 apresenta o método setu p () de BlackjackGUI: o controle.
Aplica ndo uma alternativa ao MVC
LISTAGEM 19.6 O
467
método setUp() do contro l e BlackjackGUI
public class Blackj ackGU I extends JFrame {
II CORTE !! parte do código omitido por brevidade private JPanel pl ayers
z
new Jpanel( new Gridlayout( O, 1 ) )i
private void setupO { VBlackjackOealer dealer
z
factory.getOealer();
GUIPlayer human • facto ry.getHuman(): dealer . addPlayer( human ); players.add( dealer.view() ); pl ayers.add( human.view() ) ; getContentPane().add( players, Borderlayout.CENTER );
I I setUp () simplesmente recupera cada jogador, adiciona seus modos de vi sualização em si mesmo e conecta a banca aos jogadores. Compare isso com a Listagem 19.7, o método setup() de ontem. LISTAGEM 19.7 O
método setUp() de MVC BlackjackGU I
priva te voi d setUpO { Blackj ackOeal er dealer • getOea ler () ; PlayerView vI • getPlayerView( dealer ); GUIP l ayer human • getHuman(); PlayerV i ew v2 • getPlayerView( human ); PlayerView [] views - { vI, v2 ); addPlayers( views ) ; dea l er.addPlayer( human ); addOptionV i ew( human , dealer li
I Parece que simplesmente solicitar um modo de visualização da abstração é muito mais simples do que criar e juntar os vários modos de visualização da versão MVC.
Dia 19
Resumo Hoje, você viu uma alternativa ao MVC. Se seu sistema é re lat ivamente estável e tem uma UI bem defin ida, a estratégia PAC pode oferecer uma solução mais elegante do que o MVC. Mesmo que você precise suportar várias interfaces, a lição de hoje mostra como é possível usar herança para separara camada de abstração da funcionalidade básica do sistema. Assim, para suportar várias interfaces, você precisa apenas criar uma subclasse para cada ti po de apresentação.
Perguntas e respostas P Se o PAC oferece lim a escolha melhor, por qu e fizemos a implementaçíio MVC ? R O PAC simpl esmente ofe rece uma alternativa. Uma não é necessariamente melhor q ue a outra. Trata-se apenas de uma decisão de projeto. O rato é que você encontrara o MVC em muitas implemen tações. Você pode encon trar o PAC, mas é mui to menos prováve l. Assim , abordar o MVC pri meiro é mai s prático. Pes· soalmente, tendemos a privilegiar o PAC. Tenha as duas opções em mente. O que você nunca quer fazer é codificar sua lógica corporativa (classes como BettlngPl ayer) diretamente como um componente da GUI. Por exemplo, Bett i ngPl aye r nunca deve estender JComponent (ou algum outro componente da GUI) diretamente. O MVC e o PAC fornece m um mecanismo que evita mi sturar seu modelo e a GU I. Os padrões apenas adotam estratégias diferentes. O que você escolhe depende de seu projeto e de sua eq ui pe de projeto.
Workshop As perguntas e respostas do teste são fornecidas para seu melhor entendimento. Veja as respos· tas no Apêndice A, " Respostas".
Teste I. Quais são as três camadas do padrão de projeto PAC? 2. Dê uma breve descrição de cada uma das camadas do PAC. 3. Como você pode usar herança para desacoplar a OUI das c lasses de sistema subjacentes? 4. Antes de usar o PA C, quais características seu projeto deve exibir? 5. Mesmo que você usasse PAC portada esta lição, como ainda poderia fornecer uma UI de linha de comando para o sistema? 6. Como o padrão factory foi usado neste capítulo?
Aplica ndo uma a lte rnativa ao MVC
Exercícios I. Faça download do cód igo·fonte da iteração de hoje. Quando você ti ver a cód igo, com pi· le-o, faça-o fu nc ionar executando 81 ackj ackGUI e, depoi s, tente entender como ele fu nci· ona. Ter um entendimento talai do código ex igirá algum tempo e paciência. 2. O Exercício 2 do Capítulo 17 pediu para que você acrescentasse aposta em dobro no jogo vi nle·c·um. Você prec isa adicionar aposta em dobro no j ogo novamentc. Desla vez, adicione-a na versão gráfi ca do jogo que você carregou pordownl oad para o Exercício 1.
SEMANA
3
DIA Divertindo-se com o jogo vinte-e-um Nos úhimos dias, você trabalhou em um projeto de POO bastante intenso. Hoje, você vai dar o troco e se divertir com o jogo vinte-c-um, usando polimorfismo para adicionar vários jogadores não-humanos ao j ogo. Você também verá como a 00 se presta para faze r simuladores. Hoje você aprenderá como pode: •
Usar polimorfismo para adic ionar jogadores no j ogo vinte-e-um
•
Usar 00 para criar simuladores
Divertindo-se com o polimorfismo o jogo vinte-c-um
possibi lita que até sete jogadores joguem em determinado momento. Até agora, o j ogo que você criou incluía apenas um jogador humano e a banca. Fel izmente, Opolimorfismo permite que você adicione jogadores não-humanos no jogo.
Criando um jogador Para criar um novo jogador não-humano, basta criar uma nova classe que herde de Betti ngPlayer. Tudo que sua nova classe precisa faze r e implementar os dois métodos abstratos a seguir:
I 472 • •
Dia 20 public boo l ean hit() pub lic void bet()
o componamento que você fornecer para esses dois métodos delenni nará como o jogador jogará na sua vez. Você não preósará alterar quaisquer estados ou sobrepor quaisquer outros métodos. Os estados padrão sabem corno usar os métodos que você im plementa. Quando você tiver acabado de defi nir a nova classe de jogador, pode alterar B1 ackjackGUI para criar o jogador e ad icioná-lo no jogo.
o jogador seguro Vam os criar um novo jogador: SafePl ayer. SafePl ayer nunca recebe mai s cartas e sempre aposta a menor quantia permitida. A Listagem 20 .1 apresenta a definição de SafeP1 ayer. LISTAGEM
20.1 SafeP1ayer . ja va
pub li c cla ss SafePlayer exte nd s BettingP1ayer { pub l iC SafeP1ayer( String name , Hand ha nd, Bank bank ) { supere name , ha nd, bank ) ; }
public boolean hit() { return false ; }
publiC void bet() { getBank().p l ace l OBet() ; } }
Você notam que hi t () sempre retorna fal so; SafePlayer nunca receberá mais canas. Do mesmo modo, Sa fePlayer sempre chama place l OBet O.
Adicionando SafePlayer na GUI Adicionar Sa fePl ayer no jogo é relativamente si mples. Primeiro, adic ione o seguinte método em BlackjackGUI: LISTAGEM
20.2 O método getSafePlayer()
private Player getSafePlayerO { II retorna o quanto for sol i citado Ha nd safe_hand • new Hand(); Bank sa fe_bank • new Bank( 1000 ) ;
Divertindo-se com o jogo vinte-e-um
LISTAGEM 20.2
,
O
método getSafePlayerO (con t inuação )
return new SafePl ayer ( MSafe", safe_hand , safe bank );
getSafePl ayer O é um método factory que instanci a objetos Sa fe Pl ayer. Quando você ti ver o método, pode atualizar se tUp() para que ele adicione o novo jogador no jogo. A Listagem 20.3 apresenta o método se tU pO atualizado. LISTAGEM 20.3 O
método setUp() atualizado
priva te void se tUp() { BlackjackDealer dealer • getOea ler( ) ; PlayerVi ew vI • getPlayerView( deale r ) ; GUIPlayer human • getHuman() ; PlayerView v2 • getPlayerView( human ) ; Player sa fe getSa fePlaye r (); Pl ayerView v3 • getPlayerView( safe ); K
PlayerV i ew {J views z I vI , v2, v3 }; addPl ayers( views ); dea ler.add Playe r( human ); dealer.addPlayer( sa fe );
,
addOptionView( human, dea l er );
Você também desejará alterar o método principal da GU I, para que ele torne ajanela um pouco maior, a fim de que o novo jogador possa caber. A Fi gura 20.1 ilustra o novo jogador, conform e ele aparece na GUI. Aqui, o jogador é adi cionado corno um segundo jogador que joga após o ser hum ano. Nada o impede de deixar que o jogador não-humano jogue primeiro.
Aperfeiçoamento Você pode ad icionar qualquer número e tipo de objeto Bett; ngPl ayer no jogo. Quando o númcro de opções de jogador ficar maior, tal vez você queira fornecer ao usuário uma caixa de diálogo que permita configura r a mistura de jogadores. No mínimo, você desejará tornar o método setUpO protegido, para que subclasses possam sobrepor o método. Quando protegido, você pode escrever subclasses que criem jogos com diferentes misturas de jogadores.
Dia 20 FIGURA 20 .1
A CUI comeI/do Irês
jogadores.
POO e simulações Confonne mencionado em uma lição anterior, quando você escreve um sislema de POO, na verdade está escrevendo um simulador vivo de algum problema real. Nesla semana, você escreveu um sistema que sim ula o jogo vinte-e-um . Alê aqui , o jogo lem sido centrado em um jogador humano. Você quer uma pessoa com a qua l possa jogar o jogo, embora o sistema não se preocupe se há um ser humano jogando ou não. Para o sislema, todos os jogadores são objetos Plilyer. O sistema cuida apenas que existam objetos Player.
Criando di feren tes tipos de jogadores e adicionando-os ao sistema, você pode ter um jogo vinte-c-um que jogue sozinho, se m interação humana: um si mulador do jogo vinte-e-um. Um simulador do jogo vinte-e-um pode ser útil por vários motivos. Ta lvez você qui sesse criar jogadores que util izassem diferentes estratégias para ver quais delas func ionam melhor. Talvez você esteja fazendo pesqui sa de IA e queira escrever uma rede neural que aprenda ajogar perfeitamente um jogo vinte-e-um. Você pode usar um simulador do jogo vinte-c-um para atingir todos esses objetivos. No capít ulo e nos exercícios de hoje, você vai criar vários jogadores para sabcrse pode ganhar da banca com o passar do tempo.
Os jogadores do jogo vinte-e-um Você já viu um objeto SafePlayer. Será interessante ver como ele se desempenha com o passar do tempo. Além de SafePl ayer, vamos defi nir:
Divertindo-se co m o jogo vinte-e-um
•
FlipPlayer: um j ogador que alterna entre receber mais cartas e parar.
•
OneHaplayer: um jogador que sem pre recebe mais cartas a cada vez.
•
SmartPl ayer: um jogador que
NOTA
para com qualquer mão maior do que
I I.
Você pode notar a falta de casos d e uso para esses jogadores. Como exerclcio, talvez você queira trabalhar nos casos de uso para os jogadores. Entretanto, um principio deste livro t em sido realiza r apenas a quantidade de análise que f aça sentido e valorize seu entend imento do problema. Em nossa opinião, os casos de uso não ajudariam no seu entendimento neste caso e pareceria mais com a cri ação de documentação simplesmente.
Implementando FHpPlayer A implementação de Fl i pPl ayer é um pouco mais complicada do que a de SafePl ayer. A li stagem 20.4 apresenta a implementação de Fl i pPl ayer. liSTAGEM
20.4 FlipPlaye r . ja va
publi c cl ass FlipPlayer extends BettingPlaye r ( private boolean hit • false; private boolean should_hit_once : false; pub l ic Fl ipPlayer ( String name , Hand hand, 8ank bank ) ( super( name , hand, bank ) ; }
public boolean h1t() ( if( should_hit_once &&l hit ) { hit .. true; return true; }
return false; }
publiC yo i d resetO ( supe r. reset() ; hit • fal se; should hit once = lshould hit once ; }
-
pub l ic yoid bet() I getBank().pl acelOBet()i } }
Dia 20
F1 i pPl ayer precisa manter dois fl ags booleanos. Um flag informa ao jogador se ele deve receber mais cartas toda vez e o outro controla se o jogador recebeu mais cartas durante essa rodada. Os flags garantem que o jogador só receba mais cartas uma vez a cada outro jogo.
Para penniti r que toda a lógica booleana funcione, você prec isará sobrepor O método reset () , para alternar o estado booleano shoul d_hi t_once. Sobrepor reset O dessa maneira garante que o jogador só receba ma is cartas a eada outro jogo.
Implementando OneHitPl ayer OneHi tPl ayer é semel hante na implementação a Fl i pPl ayer. Ao contrário de Fl i pPl ayer, OneHi tPl ayer receberá mais cartas a cada jogo, mas apenas uma cana. A Listagem 20.5 apresenta a im plementação de OneHi tPl ayer. LISTAGEM
20.5 OneHitPl ayer. java
pub l ic cl ass OneHitPlayer extends BettingPlayer { pr;vate boo l ean has_hi t
z
false ;
pub l ic OneHitPlayer( String name , Hand hand , Sank bank ) { supere name, hand. bank )i }
publ ic boolean hitO ( if( !has_hit ) { has_hit • true; return true; }
return false j }
public vo1d reset() { super.reset() j has hit • false; }
publiC vo i d bet() ( getBankO . pla celOBet(); }
}
Novamente, você precisa sobrepor reset () para que ele li mpe o flag has_ hi t. Esse fl ag garante que o jogador só receba mais uma carta na rodada.
Divertindo-se com o jogo vinte-e-um
477
Implementando SmartPl ayer Sma r lPl ayer tcm uma implementação muito simples. A Listagem 20.6 apresenta a implementação. LISTAGEM
20.6 SmartP l ayer . java
public cl ass SmartPlayer extends BettingPlayer ( publiC SmarlPlayer( String name , Hand hand, Bank bank ) ( supere name , hand , bank ): J
public boo lean hit() ( if( get Hand() . total() return false :
>
II ) (
J
return true : J
pub l ic void bet() ( getBank().placeIOBet():
J J
SmartPl aye r veri fi ca o total de seu objeto Hand. Se o total for maior que onze, o jogador pára. Se for menor, o jogador recebe mais cartas.
Configurando o simulator Para transformar o jogo em um simulador, você pode simpl esmente alterar o método principal encontrado na classe Bl ackjack. Aqui , copiaremos e renomearemos a classe como Bl ackjackSim. A Listagem 20. 7 apresenta o novo si mulador. liSTAGEM
20.7 BlackjackSim . java
pub li c class BlackjackSim { pub l ic stat1c void main( Stri ng (] args ) ) ( Console. INSTANCLpr intMessdge( "How many t imes should the simula tor play? " l: String response • Consol e. INSTANCE.readlnput( "invalid" ): int loops = Integer.parselnt( response ) : Oeckpile cards • new Deckpi l e() ;
Dia 20
LISTAGEM 20.7 8lackjackSim .java (continuação) for( int i .. O: i < 4: i ++ ) cards .shuffle() : Oeck deck .. new Deck{}; deck.addToStack( cards ); cards.shuffle();
(
J
II
cria uma banca Hand dealer hand .. new Hand() ; BlackjackDealer dealer .. new BlackjackDealer( "Oealer", dealer_hand , cards ) ;
II
cria um OneH it Player Ba nk one_bank .. new Bank( 1000 ) i Hand one_hand .. new Hand(l; Player oplayer " new OneHitPl ayer( "OneHit", one_hand , one_bank l i
II
cria um SmartPlayer Bank smart_bank .. new Bank( 1000 ) ; Hand smart_hand • new Hand() i Player smpl ayer .. new SmartPlayer{ "Smart" , smart_hand, smart_bank l;
II
cria um SafePlayer Bank safe bank .. new Bank( 1000 l i Hand safe_hand .. new Hand(); Player splayer .. new SafePl aye r ( ·Sa fe", safe_hand, safe_bank li
II
cria um FlipPlayer Bank flip_bank .. new Bank( 1000 } i Hand flip_hand .. new Hand() i Player fplayer .. new FlipPlayer( "Flip" , flip_ha nd, f l ip_bank li
II
reúne todos os jogadores dealer.addL i stener( Console .I NSTANCE l i oplayer.addListener( Conso l e.INSTANCE l i dealer . addPlayer{ oplayer }i splayer,addListener( Console . INSTANCE l i dealer.addPlayer( splayer li smplayer.addListener( Console . INSTANCE li dealer.addP l ayer( smplayer li fplayer.addlistener( Consol e. INSTANCE li dea ler.addP l ayer( fplayer li i nt
counter .. O;
Divertindo-se co m o jogo vinte-e-um
LISTAGEM 20.7
8lackjac kSi m. java (continuação)
whlle( counter < loops ) { dealer . newGame(); coun ter ++; J J
J
o simulador primeiro o consu ltará quanto ao número de vezes a j ogar. Quando ele tiver essa informação, criará um objeto 81 ackj ac kDea 1er, um OneHitPl ayer, um Sma rtPl ayer e um FI l pPl ayer. Então, ele conectará esses jogadores no Console e os adicionará na ba nca. Q uando tiver terrn inado a con fi guração, ele jogará o jogo pelo número de vezes que você tiver especifi cado.
Os resultados Após executar o simulador algumas vezes, com 1000 jogos por execução, torna-se fác il ver como os j ogadores se comportam. Aq ui estão os resultados obt idos, do maior para o menor:
• SmartPl ayer •
SafePlayer
•
FhpPlayer
•
OneHHPlayer
Parece que, desses quatro j ogadores, permanecer com uma mão que é ma ior que II é a me lhor estratégia. Na verdade, SmartP l ayer foi o unico jogador a ficar com din hei ro após os 1000 • Jogos. SafePl ayer vem em um segundo lugar próx imo, mas acabou perdendo di nhe iro. E, faça o que fizer, não receba mai s cartas. Esses jogadores perderam mais dinheiro do que ti nham em seu pote inicial.
NOTA
Nenh um desses jogadores acabou com mais dinheiro do que com eço u. Os jogadore s só diferiram na velocidade com que perderam seu dinheiro.
Resumo Você aprendeu uma lição im portante hoje: não siga nenhuma das estratégias de hoje ao jogar vinte-c-um . Você perderá todo seu dinheiro!
Dia 20
Você também viu pela primeira vez como o polimorfismo permite escrever software à prova do futuro. Você pode introduzi r tipos de jogador no sistema, sem ter de alterar o sistema básico. Esses tipos dejogador nem mesmo eram considerados, q uando vocêconslruiu o sistema inicial.
Perguntas e respostas P Há um motivo pclo qual você não tcnha usado a CU I como base deseu simulador? R Poderíamos ter usado a GU I, em vez do conso le. Poderia ter sido interessante ver osjogos ci ntilarem. Nonna lmente, um simulador não tem UI. Em vez disso, o simulador mostrará algumas estatísti cas no final. Também ex istem limites práticos para uma G U!. As GU ls demoram alguJ1ltem po para alUa li zar. Jogar 1000 jogos visuais poderia demorar um pouco ma is do que jogá-los na linha de comando. Várias versões de Swing também sofrem de estouro de memória. Esses estouros poderiam surgir e atrapalhá-lo, caso você executasse 10.000 jogos visuais. Para propósitos de teste, entretanto, você poderia considerar o uso da GUI como base para o sim ulador.
Workshop As perguntas e respostas do teste são forneci das para seu melhor entendimento. Veja as respostas no Apêndice A, " Respostas".
Teste I. Como o polimorfi smo permitiu jogar o jogo sem um jogador humano? 2. Qual estratégia de aposta você nunca simulará?
Exercícios I. Faça download do código-fonte da iteração de hoje, a partir do endereço www,samspu bllshing . com. O cód igo está dividido em quatro diretórios separados: gui o simulation, exerci se 2 e exercise 3.
-
-
9U1 contém uma GU I que tem um jogador humano e um SafePl aye rU.
simu l ation contém o código de simu lação. s imul ati on tem os seguintes jogadores: OneHi tPl ayer, Fl i pP1 ayer, Sa fePl ayer e SmartPl ayer. exerci se 2 e exerci se 3 contêm os arquivos que você precisará para os exercícios 2 e 3, ass im como as soluções.
-
Dive rtindo-se co m o jo g o v inte-e- um
Estude o cód igo de gui e simula t or. Tente entender como tudo funciona. Quando você t iver feito isso, complete os exercícios 2 e 3.
2. O download contém todos os arquivos iniciais que você precisará p<1ra completar este exercício. Os arquivos in iciais faze m alterações no método hi t declarado pela primeira vez em PI ayer. As alterações adic ionam Dea ler como um parâmetro. O mêtodo getUpCard fo i adi cionadoem Oea l er para que você possa obter a carta aberta de Oea I er. Em urnjogo vi nte-e-um real , os jogadores podem ver a carta aberta da banca. Você pode usar essa in formação para tornar alguns movim entos mais inte ligentes. Para este exe rc ício, escreva um ou dois j ogadores novos, que base iem s ua decisão de receber ma is cartas em seu próprio total, assim como na carta aberta da banca. Aqui estão duas s ugestões, mas sinta-se livre para impl ementar seus própri os j ogadores, adici oná- Ias 110 simulador e ver como eles reagem : KnowledgeablePlayer
Knowl edgeabl ePI ayer deve basear a dec isão de se vai ou não receber mais cartas de acordo com as seguintes regras: Independentemente de tudo, se o tota l da mão for maior que 15, pára. Se o total da mão for I I ou menor, recebe mais cartas. Se a mão for 15 ou menos e maior que 11 , baseia a decisão de receber mais cartas na carta da banca. Se a carta da ba nca for maior q ue 7, recebe mais cartas; caso contrário, você deve parar. OptimalPlayer Opt ima 1PI ayer está t110 próx imo da perfei ção, que você pode não consegui r diferenciar ent re mãos fáce is e difíceis. (A d ifere nciação é deixada como excrcíc io para o leitor). Opt i ma I PI ayer deve basear a decisão de se vai a li não receber ma is carias nas seguintes regras: Se o total da mão for maior ou igual a 17, pára. Se o tota l da mão for 11 ou menos, recebe mai s cartas. Se o total da mào for 16, baseia a decisão de receber ma is cartas ou parar na carta aberta da banca. Se a carta aberta for 7, 8 Oll 9, recebe mais cartas; caso contrário, pára. Se o total da mão fo r 13, 14 ou 15, baseia a decisão de receber mais cartas ou parar na carta aberta da banca. Se a carta aberta fo r 2, 3, 4, 5 ou 6, pára; caso contrário, recebe mais cartas. Se o tota l da mão for 12, baseia a decisão de receber mais cartas ou parar na carta aberta da banca. Se a carta aberta fo r 4, 5 ou 6, pára; caso cont rário, recebe mai s cartas.
Dia 20
3. O download contém todos os arqu ivos iniciais necessários paravocê complelareste exercic io. Os arquivos iniciais adicionam suporte para dobrar a aposta nas classes. Além disso, o novo método doubleOown agora aceita um objeto Oealer como argumento. As alterações adicionam Dealer como um parâmetro em doub 1eDown. O método getUpCard foi adic ionado em Dealer para que você possa obter a carta aberta de Dea 1er. Em um jogo vinte-e-um rea l, o j ogador pode ver a carta aberta da banca. Você pode usar essa informação para tornar alguns movimentos mais inteligentes. Para este exercicio, escreva um ou dois j ogadores novos que baseiem sua decisão de receber mais cartas ou dobrar em seu próprio lotai, assim como na carta aberla da banca. Aqui estão duas sugestões, mas sinta-se I ivre para impl ementar seus próprios jogadores, adicioná-los no simul ador e ver como eles se comportam: KnowledgeablePlayer Para receber mai s cartas, siga as regras delineadas no Exercício 2. Para dobrar, siga estas regras: Se o lotai da mão for 10 ou 11, dobra. Em todos os outros casos. não dobra. OptimalPlayer Para receber mais cartas, siga as regras delineadas no Exercic io 2. Para dobrar, siga estas regras: Se o total da mão for 1 I, sempre dobra. Se o total da mão for 10, baseia a decisão de dobrar na carta aberta da banca. Se a carla aberta for 10 ou um ás, dobra; caso contrário, não dobra. Se o total da mão for 9, base ia a decisão de dobrar na carta aberta da banca. Se a carta aberta fo r 2, 3, 4, 5 ou 6, dobra; caso contrário, não dobra. Em lodos os outros casos, não dobra.
SEMANA
3
DIA
o último quilômetro Parabéns! Você chegou à última lição deste livro. Você percorreu um longo caminho. Agora, você deve ler a base necessária para ter êxito ao continuar seus estudos de POO. l-laj e, elimi naremos algumas arestas e depois o enviaremos por seu cami nho ! Hoje você aprenderá sobre: •
Refazer o projeto do jogo vinle-c-um para reutilizar em outros sistemas
•
As vantagens que a POO trouxe para
•
Realidades do selor que podem impedi r sol uções totais 00
O j ogo
vinte-c-um
Amarrando as pontas Você abordou muitos assuntos durante as três últimas semanas. Você começou considerando a tcoria básica da 00 e chegou em um proj eto inteiramente baseado cm POO. Agora, você já deve
ter uma boa idéia do que POO realmente significa. Antes de concluirmos o livro, ent retanto, restam três problemas para abordar: •
Refazer o projeto do j ogo vinte-e-um para reutil izar em outros sistemas
•
Um levantamento das vantagens que a
•
Uma palavra sobre as realidades do selar e
roo trouxe para o sistema de j ogo v inte-c-um Poo
Dia 21
Refazendo o projeto do jogo vinte-e-um para reutilização em outros sistemas Existe um pequeno problema relacionado ao projeto do jogo vinte-c-um que precisamos exami nar. Esse problema não têm impacto sobre o jogo vinte-c-um, mas poderia ter impacto negativo em outro sistema 00, se c[c reutilizar o projeto incorretamente. No Dia [5, você viu duas soluções altemativas. Em uma solução, a banca fa z um laço por seus jogadores dizendo a cada um para que jogue depoi s que o jogador anterior tcnn inar. Então, voeê viu uma outra estratégia mais baseada em objetos para o laço do jogo. Em vez da banca percorrer osjogadores um por um e dizer a eles o que fazer, a banca 00 iniciava cada jogador c esperava que ele dissesse quando tinha acabado de jogar. Isso ofereceu uma solução mais limpa, poi s você deixava por conta do jogador dizer à banca quando tivesse terminado - somente então é que a banca continuava. Também se viu que esse projeto era abso lutamente necessário para a GUJ fun cionar; caso contrário, um jogador humano retornaria instantaneamente e, se a banca estivesse fa zendo o laço, diria ao próx imo jogador para prosseguir. O jogador humano nunca teria sua vez na estratégia procedu ral!
o problema do projeto Existe um peq ueno problema na estratégia delineada nas lições. Vamos acompanhar as chamadas de método em um jogo onde ex iste um jogador e uma banca. Os dois jogadores param durante sua vez. A Listagem 21 .1 representa um acom panhamento de Iodas as chamadas de método no jogo. A pilha termina quando um método retorna. L ISTAGEM 2 1 .1
Um acompanhamento da pilha de método
BlackjackSim.main 81ackjackDealer . newGame PI ayer. pI ay 81ackjackOealerSOealerCollecting8ets . execute Player.play 8et tingPlayerSBetti ng.execute 8lackjackOealer. done8etting Player . play 81ackjackDea l erSOealerCo l lec t i ng8ets.execute 81ackjackDeal erSOea l erOeal ing.execute 81 ackj ackOeale rS DealerWaiting.execute Playe r .play PlayerSPlaying . execute Pl ayerSStanding.execute Bl ackjackOealer. sta nding
o último quil ô m etro LISTAGEM 21 .1 Um acompa nhament o da pilha de mé todo (continuação)
Player . play Black jac kOealer$DealerWaiting.execute PlayerSPlaying.execute BlackjackDea ler$DealerStanding
o problema é s uti!. Nen hum dos métodos retoma até que a banca termine s ua vez! Os métodos chamam-se uns aos OUlros rec ursivamente. Assim, por exemplo, o método noti fyChanged da Listagem 2 1.2 não será executado até que o jogo corrente tennine. LISTAGEM 21 .2 Um método que não será chamado antes que o jogo corrente termine
pub l ic void execute( Dealer dealer ) { if( hit( dealer) ) { dealer . hit( Player.this ): I el se ( se tCurrent State( getStandingSt ate() ); notifyStandingO: } cu rren t_state.execute( dealer ) ; II transição
II
não será chamado até que a pilha se desenrole!! !! notifyChanged();
}
No jogo vinte-e-um, isso não é problema, pois existe um limite de sete jogadores e a pilha de chamada de métodos se desenrola após cada jogo. Você tam bém pode codificar cu idadosamente no caso de problemas como o demonstrado na Listagem 21.2. Entretanto, imagine um si mu lador com centenas ou milhares de objetos que seguem o projeto do jogo vinte-e-um . Se esses objetos chamarem um ao o utro recursivamente, mesmo que a pilha finalmente se desenrole, você poderia acabar ficando sem memória. De qualq uer modo, cada chamada de método alocará mais memória. Se você seguir esse projeto inalterado, seu sistema não func ionará ou exigi rá muito mais memória do que o absolutamente necessário.
Costurando uma solução com linhas de execução (threadsl Felizmente, existe lima solução : as linhas de execução (threads). Embora uma di scussão completa sobre o uso de linhas de execução esteja bem fora dos objctivos deste livro, você vai ver como pode usá-Ias para resolver o problema da chamada de métodos rapidamente.
Dia 21
NOTA
Quase todo sistema não-trivial com partilhará duas características: • Eles usarão lin has de execução • Eles terão lógica de estado O sistema de jogo vinte-e-u m compartilha essas duas características.
Uma linha de execução é simplesmente um caminho de execução através de seu programa. Até agora, o sistema do jogo vinle-e-um tem uma (mica linha de execução. A Figura 21. 1 ajuda a visualizar essa linha de execução única.
FIGURA 2 1.1 O sisfema flojogo l'illle-e-ulII com lI/IIa úllica linha de e.recllç(io.
Sistema do jogo vinte· e-u m com uma única linha de execuçAo
Uma linha deexe(:uçêo
Comoo jogo vinte-e-um usa apenas uma linha de execução (ele tem somente um linha de execução), essa linha de execução faz tudo. O uso de linhas de execução penni te que você crie várias linhas de execução através de seu programa. Criando várias linhas de execução, seu programa pode fazer muitas coisas diferentes, simultaneamente. A Figura 2 1.2 ajuda a visualizar duas linhas de execução percorrendo o sistema do jogo vi nte-c-um. Usar linhas de execução no sistema do jogo vinte-e- um pode perm iti r que um método retorne imed iatamente. Vamos ver um exemplo si mples de linha de exec ução da linguagem Java. A Li stagem 2 1.3 apresenta urna linha de execução simples que imprime " Hello Worl d!". FIGURA 2 1.2 O sisfema do jogo l'ime-e-IIIII COIII múlfiplas linhas de execlIÇlio.
Sistema do jogo vinte-e·um com múltiplas linhas de eXBCuçAo
"-
"-~
/
"/
"-
,
Duas linhas de eXlCuçAo
o último quil ôm etro lISTAGEM2 1 .3
487
"Hell o Worl d!" com múltiplas linhas de execuç!o
publ i c class HelloWorld ( publ ic void sayHello() I System.out.println( "He l lo Worl d!" }; J
public static void main( String [] args ) I final HelloWorld hw • new HelloWorldO ; Runnable runna ble • new Runna bl e() I publ ic yoid run() { hw. sayHe11 00 ; J J;
Thread t hread • new Thread{ runnab 1e ) ; thread. s tartO; System .out. pr intln( "A11 Oone! " ) ;
J J He ll oWorld é ela própria uma classe simples que tem um método: sayHell o. sayHel lo imprime uma mensagem na linha de comando. ,
E no método main que as coisas ficam interessantes. Pri meiro, esse método instancia Hell~ Wo r1d. Então. ele cria uma classe Runnab l e anônima. Os objetos Runnab1e têm um método: run. O método run diz à li nha de execução o que fazer quando fo r iniciada. Neste caso, ele di rá à instância de He 11 oWor1d para que imprim a sua mensagem. Após cria r Runnable, O método principal instancia um Thread Java . Quando você cria um objeto Thread, precisa passlí-lo para um objeto Runnab1 e. O método run de Runnabl e diz ao objeto Thread o quê fazer quando você disser a Thread para que comece (s tart). Após iniciar o objelo Thread, o método ma;n imprime sua própria mensagem. Você pode ficar surpreso quando ver o método princi pal ser executado. A Figura 21.3 apresenta a saída de He 11 oWor 1d. Ao executar He11oWorld, você verá que "Ali Done" é impresso antes de "Hel io World !" a chamada de Thread .s ta r t não bloqueia como outras chamadas de melado. Como sta r t in icia uma nova linha de execução, ele reloma automaticamente. Após chamar st art, você tem duas linhas de execução no programa Hel loWorld. Apenas acontece que o método ma i n impri me sua mensagem antes que a nova linha de execução tenha uma chance de chamar sayHell o.
Dia 21 FIGURA 21 .3 A saida de He 11 oWor 1d.
Você pode usar o rato de que start não bloqueia para corrigir a defi ciência do projeto no jogo vinte-e-um. A Li stagem 21.4 apresenta um novo estado Wa i ti ng para B1 ackjackDeal er, que inicia cada jogador em sua própria linha de execução. LISTAGEM 21 .4 Dea 1erWai ti n9 com 1i nha de execução private cla ss DealerWaiting implements PlayerState ( publ ic void handCha nged( ) { II imposs (ve l no estado de espera }
pub l ic void handPlayable () { II 1mpossfvel no es t ado de espera }
public void handBlackjac k() { II i mposs (vel no es tado de espera }
public voi d handBusted() 1 II impossível no estado de espera }
public vo i d execute( fina l Dealer dealer ) { if( !waiting_p layers . isEmptyD ) { final P1ayer player z (Player) waiting_players .get( O }; wa i ting_players. remove( player }; Runnable runnable : new Runnable() { publ ic vo i d run() { player . play( dealer ); }
}; Thread thread new Th read( runnable ); thread. s tart () ; } el se { setCurrent Sta te( getPlayingSta te() ); exposeHand () ; getCurrentState().execute( dea le r }; II faz a transi çao e executa K
o último quilômetro
489
LISTAGEM 21 .4 OealerWal ting com 1inha de execução (continuaçào) } } }
Iniciando cada jogador em sua própria linha de execução, o mélodo de estado execute de Bl ackj ackOea 1er pode retornar imediatamente, desenrolando assim a pilha. Isso interpõe algumas dificuldade s, se você fi zer um laço com as chamadas de newGame, pois agora newGame retornará antes que o jogo tenha real mente term inado. Se você fizer o laço, iniciará outro jogo antes que o último tenha tcrminado e, então, enrrentará todos os ti pos de problemas desagradáveis. Você pode resolver esse problema dizendo a 81 ackjackOeal er quantas vezes deve razer o laço. No finai de cada jogo, ele pode verificar se precisa jogar novamente.
NeTA
o uso de lin has de execução é apenas uma maneira de resolver o problema da recursividade. Apresentamos uma solução com linhas de execução aqui para que você as visse um pouco.
O problema do laço/ linha de execução levanta algumas preocupações. Você também pOderia criar um objeto GameTable que iniciasse e parasse as linhas de execução. Então, Oea 1er poderia receber o est ado da tabela e distribuir cartas, receber mais cartas, efetuar o jogo, etc., com base no est ado. Entretanto, tal estratégia é um pouco mais complicada do que simplesmente usar linhas de execução para os jogadores quando eles começam. Você t ambém poderia se desfazer da recursividade através de iteração pelos jogadores.
A boa nova é que, se você não fize r o laço, como na GUI, pode usar linha de execução racilmen-
te, sim plesmente mudando o estado de Oea 1erWai ti n9 de Bl ackjackDea ler! O código-ronte carregado por download contem versões com linhas de execução da aul. É fáci l usa r linhas de execução no jogo vin te-e-um, pois apenas uma linha de execução de jogador é executa da em dado momento . Você não tem mu itas linhas de execução de jogador sendo executadas concomitantemente. O uso de lin has de execução se torna complicado quando muitas linhas de execução são executadas concom ita ntemente e compartilham os mesmos dados!
Identificando as vantagens que a POO trouxe para o sistema do jogo vinte-e-um A primeira semana mostrou alguns dos objetivos e vantagens da POO. Para recapitular, a POO tenta produzir so ftwa re que é: 1. Natural
2. Confiável
Dia 21
3. Reutilizável 4. Manutenível 5. Extensível 6. Oportuno A POO trouxe cada uma dessas vantagens para o sistema do jogo vinte-e-um . O sistema do jogo vinte-e-um atinge cada um dos objetivos da POO: •
Natural : o sistema do jogo vinte-e-um modela naturalmente um jogo vinte-e-um.
O sistema dojogo vinte-e-um existe nos termos de um jogo vinte-e-um real . O sistema do jogo vinte-e-um é constituído de objetos P1 ayer, um 81 ackj ac kDea1er, objetos Ca rd, Deck e um DeckPile. Corno você vê, o jogo vi nte-c-um é uma simulação viva do domínio do jogo vinte-e-um. • Con fi áve l: o sistema do jogo vinte-e-um é confiável.
•
Através de uma combinação de testes cuidadosos e encapsulamento, você criou um sistema do jogo vinte-c-um confiável. Como você iso lou conheci mentos e responsabilidades e os colocou no lugar onde pertencem, pode fa zer mel horias no sistema sem se preocupar com um impacto negativo em partes não relacionadas do sistema. Relll ilizável: o sistema do jogo vinte-e-um é reutilizável. Como esse foi o primeiro jogo de cartas que você escreveu, não houve muita ênfase na escrita de uma estrutura dejogo de cartas abstrata. Em vez disso, você escreveu um jogo vinte-e-um. Como resultado, o jogo não é completamente reutilizável; entretanto, classes como Card, Deck e Deckpile podem ser reuti lizadas em praticamente qualquer jogo de cartas.
Além di sso, muitas das idéias do projeto são reutilizáveis em muitos problemas. À medida que você escrever mais jogos de cartas, poderá abstra ir mai s e criar uma estrutura totalmente reut ili zável. • Manuten ível : o sistema do jogo vinte-e-um é manutenível. Encapsulando conhecimentos e responsabilidades no lugar onde eles pertencem, fi ca simples fazer alterações em lim a parte do sistema sem ter um impacto negativo em outras partes não relacionadas do sistema .
•
Tais divisões tornam possível fazer melhorias no sistema a qualquer momento. Você também viu pela primeira vez como a herança e o polimorfismo tornam possível adicionar novos jogadores no sistema, a qualquer momento. Extensíve l: o sistema do jogo vinte-c-um é extensível. Você viu pela primei ra vez como pode adicionar novos jogadores no sislema. Além disso, através de uma herança cuidadosa. você pode inlroduzir novos tipos de cartas (como cartas visuais) e mãos. O processo iterativo mostrou como um sistema de POO pode ser extensível.
o último quilômetro •
491
Oportuno: o sistema do jogo vinte-e-um é oportuno . Você produziu um jogo vinte-c-um completo em quatro iterações mana. Veja como ele é oportuno!
o tempo de uma se-
Realidades do setor e POO As lições deste livro ti veram como pressuposto o fato de que você estava inic iando seus projetos de POO desde o princípio. Quando você começa desde o principio, não precisa integrar sistemas de backend legados, não-OO. Você não precisa reut il izar bibliotecas proced urai s. Você pode começar do começo e tudo que usar pode ser 00. Você verá que um projeto de POO independente é raro. Na maioria das vezes, você precisará interagir com componentcs não-OO . Peg ue o caso dos bancos de dados relacionais. Os bancos de dados relacionais não são particularmente ori entados a objetos e os bancos de dados orientados a objctos ainda são raramente usados fora de alguns nichos do setor. A própria linguagem Java não é totalmente orientada a objetos. A con fi ança cm primitivas não-OO faz você rea lizar alguma codificação não-OO, de tempos em tempos. Ao se deparar com essas reali dades, é melhor trabalhar para empacotar esses aspectos não-OO em um empacotador orientado a objetos. Por exemplo, ao tratar com bancos de dados relacionais, ajuda escrever uma camada de persistência de objetos. Em vez de ir diretamente a um banco de dados e reconstitui r seus objetos através de várias consultas SQL, a camada de persistência pode fazer esse trabal ho para você. Real mentc não é possível abordar aqui cada tipo de sistema não-OO que você va i encontrar. Mas teríamos sido negligentes se não apontássemos essas realidades antes de mandá-lo apli car POO. Vai demorar muito tempo, antes que todo sistema legado seja convertido para uma arquitetura baseada em objetos (se é que isso vai acontecer). Você deve estar preparado para essa eventualidade e pronto para tratar com e la da forma mais elegante possível.
Resumo Você termi nou ! Em três semanas curtas, este livro forn eceu a você uma base sólida em POO. O resto fi ca por sua conta. Agora você tem conhecimento suficiente para começar a aplicar POO em seus projetos diários. Boa sortc!
Perguntas e respostas P Por que você esperou até agora para nos dizer a respeito de linhas de execuçào? R O problema do projeto não afela o jogo vinte-e-um. Apresentar os possíveis problemas antecipadamente leria confundido a queslão.
Dia 21
É importante que você perceba as deficiências do projeto, assim como uma possível solução. O uso de linhas de execução também é um assunto avançado. Usar linhas de execução no jogo vinte-e-um foi muito fá cil. Mas usa-las em outros aplicativos pode não se mostrar tão simples.
P O que pode tornar o uso de linhas de execução difíci l? R Se você tiver mllltiplas linhas de execução compartilhando dados, uma linha de execução poderá alterar os dados c dan ificar outra li nha de execução. Tais problemas de concorrênc ia são ext remamente difíceis de projetar, implementar e depurar.
Workshop As perguntas e respostas do teste são forn ecidas para seu melhor entend imento. Veja as respostas no Apêndice A, ;'Respostas".
Teste I. Como as li nhas de execução tratam do problema da chamada de método recursiva?
Exercícios I. Faça down load do código-fonte da iteração de hoje. O código está dividido em quatro di retórios separados: threaded _he 110_worl d, threaded _mvc _gui, threaded _pac _9ui e threaded_simulador. Estude o cód igo e certifique-se de entender como ele funci ona. 2. Seu estudo de POO não deve terminar com este livro. Gere uma lista de assuntos sobre os quais você gostaria de aprender mais. Classifique esses assuntos em ordem de im portância, pesq uise a Web para encontrar materiais e comece a estudar!
SEMANA
3
Em revisão Agora, você concluui fi terceira e última semana deste li vro e nos lJltimos sete dias, aprendeu a desenvolver seu próprio jogo vinte-c-um 00. O Dia 15 apresen tou as regras básicas do jogo vinte-c-um. Você desenvolveu uma li sta de casos de LISO em potencia l e se lecionoll alguns de les para desenvolver na pri meira iteração do jogo. Você segu iu o processo de projeto, da análise, de implementação e teste, e no final do d ia ti nha uma versão funciona l do jogo vinte-c-um que distribuía cartas e o deixava jogar.
No Dia 16, você completou a segunda iteração do jogo. Você adicio nou mai s funcionalidade, corno a capacidade de dctcnninar os resultados do jogo. Ao fazer isso, você aprendeu a respeito dos estados e como usá- los para melhorar seu projeto.
O Dia 17 mostrou a você como completar uma outra iteração do jogo vinte-e-um - a aposta. Ao fazer isso, você viu como poderi a estender a arq uiletura de estado para suportar apostas simples no jogo. Você também viu que, às vezes, é necessário revere refazer uma hierarquia, quando novos requisitos se apresentam. Embora o fato de refazer apresente um pouco de trabal ho extra antecipado, refazer quando apropriado tende a valer a pena, quando você prossegue. No Dia 18, você concl ui u ojogo vinte-e-um, adicionando uma OU I. Para isso, você reviu o padrão MVC discutido em um capitulo anterior. O Dia 19 forneceu uma OU I alternativa, usando o padrão PAC, àquela desenvolvida no Dia 18.
Isso ajudou a refinar scu entendimento de quais padrões são apropriados para cenários específicos. Durante o Dia 20, você reviu os conceitos do polim or fismo que perm item escrever software fi prova do futuro. Você se divertiu um pOLlCO, quando ad icionou vá rios jogadores não-humanos ao sistema e transformou o jogo vinte-e-um em um simulador. Mexendo com est ratégias de vários jogadores, você aprendeu o que não deve fazer ao participar de um jogo. Você também aprendeu que pode introd uzir tipos de jogador no sistema sem ter de alterar o sistema básico. Esses ti pos de jogador não foram nem mesmo considerados quando você construiu o sistema inicial. Finalmente, no Dia 2 1, você aprendeu sobre linhas de execução. O capítulo também abordou todas as arestas do projeto e apresentou uma discussão sobre a 00 pura, em oposição ao que você provavelmente verá no mundo real.
Dia 21
As lições desta semana aprofundaram seu entendimento de 00 e, além disso, você acabou com um divert ido e demorado jogo vinte-e-um 00 para provar isso. Após concluir este livro, você tem a base necessária em 00 para começar a desenvolver software 00. Tudo de que você precisa agora é prática e experiênc ia. Boa sorte. Para mais recursos, o Apênd ice D. " Bibliografia selecionada", romece um ponto de partida para mais informações sobre 00.
Apêndices A Respostas B Resumo do Java C
Referênc ia da UML
O Bibliografia selecionada E
Listagens do código do jogo vinte-e-um
ApÊNDICE Respostas Dia 1 Respostas do teste Respostas do teste 1. Como urna di sc iplina de software , a programação procedura l decompõe um programa em dados e proced imentos para manipular esses dados. A programação procedural tem uma natureza seqüencial. Li stas de chamadas procedura is que são executadas seqilêncialmente or ientam O nuxo de um programa procedural. Um programa procedura l termi na após chamar sua última procedure. 2. A programação procedural fo rnece a um programa uma estrutura g loba l: dados e procedimentos. Os proced imentos também o ajudam a ver como deve programar uma tarefa . Em vez de esc rever um ún ico bloco de processamento grande, você di vide os procedimentos em subprocedimentos. Os procedi mentos fornecem um nível de reutil ização. Você pode criar bibliotecas de procedimentos reutilizáveis. 3. A programação modu lar acopla dados e procedimentos forteme nte, para manipularesses dados em unidades conhecidas como mód ulos. Os módulos ocultam o runcionamento e a representação de dados internos de um programa. Entretanto, a ma ioria das linguagens modulares ainda penn ite que você use esses módulos cm um ambiente procedura l.
Apêndice A
4. A programação modular oculta a im plementação e, assim, protege os dados de manipulação inconsistente ou imprópria. Os módulos tam bém fornecem uma estrutura de nível mais alto para um programa. Em vez de pensar em tennos de dados e proced imentos, os módulos pennitcm que você pense em um nível comportamental conceituaI. 5. A programação procedura l e a programação modu lar têm s uporte limitado para a reutil ização. Embora você possa reutilizar procedimentos, eles são altamente dependentes de seus dados. A natureza global dos dados no mundo procedural torna a relltil ização di fici l. Os procedimentos podem ter dependências dificeis de quantificar. ,
Os módulos em si são prontamente reutilizáveis. E possível pegar um módu lo e usá-lo em qualquer um de seus programas. Entretanto, os módulos limitam a reutil ização. Seu programa só pode usar os módulos diretamente. Você não pode usar um módulo existente como base para um novo módulo. 6.
roo é uma di sciplina de so ftware que modela o programa em termos de objetos do mundo real. A POO divideo programa em vários objetos inter-reJac ionados. Ela complementa a programação modu lar, s uportando encaps ulamento, assim como eliminando defici ências da reut ilização através da herança e deficiências de tipos através do polimorfi smo.
7. As se is vantagens da
roo são programas que são:
Natura is Confiáve is Reutili záve is Manuteníveis Extensíveis Oportunos 8. A POO é natural. Em vez de modelar os problemas em termos de dados ou proced im entos, a permite que você modele seus programas nos termos do problema. Tal estratégia o li bera para pensar nos termos do problema e focalizar o que está tentando faze r. Isso reti ra o foco dos detalhes da implementação.
roo
9. A classe define todos os atributos e comportamentos comuns de um grupo de objetos. Você usa essa definição de classe para criar instâncias desses objetos. Um objelo é uma instância de lima classe. Seus programas manipul am esses objetos. Um objeto executa componamentos. Você tambem pode chamar os comporta mentos de um objeto de interface pública dele. O utros objetos podem exercer q ualq uer comportamento na interface de um objelo.
Respostas
10. Os objetos se comunicam através do envio de mensagens de uns para os o utros. Chamar um método é si nônimo de fazer uma chamada de método Oll procedim ento. 11. Um const rutor é um método que defi ne como se cria uma instância de objeto. O uso do construtor instanciará um objelo e o to rnará disponível para seu programa. 12. Um acessor é um comportamento que dá acesso aos dados internos de um objeto. 13. Um mutante é um comportamen to que pode alterar o estado interno de um objelo. 14. thi s é uma reCerência que cada instância tem para si mesma. A referência thi s dá à instância acesso às suas variáve is e comportamentos internos.
Dia 2 Respostas do teste e dos exercícios Respostas do teste I.
o encapsulamento é natural. O encapsulamento pennite que você mode le o software em termos do problema e não nos tennos da implementação. O encapsulamento leva a um software confiável. O encapsulamento oculta o funci onamento interno de um componente de softw are e garante q ue ele seja acessado corretamente. O encapsulamento permite que você isole e va lide a res ponsabilidade. Quando você ver um componente agir corretamente, pode reutilizá-lo com confi ança. O encapsulamento proporciona a você um software reutilizáve l. Como cada componente de software é independente, você pode reutil izarocomponente em muitas si tuações diferentes. O encapsulamento leva a um código manutenível, pois cada componente é independente. Uma alteração em um componente não danificará outro componente. Assim , a manutenção e os aprimoramentos são simplificados. O encapsu lamento torna se u software modular. As alterações em uma parte de um programa não danificarão O código em outra parte. A modularidade permite que você faça correções de erro e melhorias de func ional idade sem danifi ca r o restante de seu código. O encapsulamento leva a um desenvolvimento de cód igo oportuno, pois remove acoplamento de código des necessário. Freqüentemente, dependências ocultas levam a erros que são dificeis de encont rar e corrigir.
2. Abstração é o processo de simplificar um problema dificil. Quando você começa a resolver um problema, não se sobrecarrega com cada detalhe que envolve o domínio. Em vez disso, você o simplifica, tratando dos detalhes pertinentes à formulação de uma solução. A área de trabalho gráfica de seu computador é um exem plo de abstração. A área de trabalho oculta completamente os detalhes do sistema de arquivo.
Apêndice A
3. Uma implementação defi ne como um componente fornece um serviço. A implementação defin e os detalhes internos do componente. 4. Uma interface defi ne o quê você pode fazer com um componente. A interface oculta completamente a implementação subjacente. 5. Uma interface descreve o que um componente de software faz; a implementação diz como o componente faz isso. 6. Sem uma divisão clara, as responsabi lidades se misturam. Responsab iIidades misturadas levam a dois problemas relacionados. Pri meiro, o código que poderia ser centra lizado se torna descentraI izado. A responsabi Iidade descentralizada deve ser repetida ou reimplementada em cada lugar onde ela é necessária. Lembre- se do exem pl o BadItem apresentado anteriormente.
É fáci l ver que cada usuário precisaria reimplementar o código para ca lcul ar o tota l ajustado de um item. Sempre que você reescreve a lógica, abre a possibi lidade de erros. Você também abre seu código para um liSO incorreto, pois a responsabilidade de manter o estado interno não está mais den tro do componente. Em vez disso, você coloca essa responsabi lidade nas milos de o utros. 7. Um tipo é um elemento da linguagem q ue representa alguma unidade de cálculo ou com portamento. Se as linhas de cód igo são frases, os tipos são as palavras. Normalmente, os tipos sào tratados como unidades independentes e atômicas. 8. Um TAO é um conj unto de dados e um conj unto de operações sobre esses dados. Os TAOs nos pennitem defini r novos tipos de linguagem, ocultando dados internos e o estado por trás de uma interface bem definida. Essa interface apresenta o TAO como uma úni ca unidade atôm ica. 9. Ex istem várias maneiras de obter ocu ltamento de impl ementação e cód igo fracamente acoplado. A resposta fáci I é usar encaps ulamento. Entretanto, um encapsulamento eficaz não é acidente. Aqui estão algumas d icas para o encapsulamento e fi caz: •
Acesse seu TAO apenas at ravés de uma interface de métodos; nunca perm ita que estruturas internas se tomem parte da interface püblica.
•
Não forneça acesso às estruturas de dados internas; abst raia todo o acesso.
•
Não forneça acesso inadvertido às estruturas de dados internas, retornando acidentalmente ponteiros ou re ferências.
•
Nunca faça suposições a respeito dos outros tipos que você usa. A não ser que um comportamento apareça na interface ou na documentação, não conte com ele.
•
C uidado ao escrever dois tipos intimamente relacionados. Não se permita programar acidentalmente com base em s uposições e dependências.
10. Você preci sa conhecer algumas annadilhas da abslração.
Respostas
Não caia na parali sia da abstração. Resolva os problemas que você encontrar primeiro. Resolver problemas é s ua pri ncipal tarefa. Veja a abstração como um bónus e não como o objetivo final. Caso contrário, você vai se deparar com a possibi lidade de prazos fi nais perdidos e abstração incorreta. Existem momentos para abstrair e momentos em que a abstração não é apropriada . A abstração pode ser perigosa. Mes mo que você tenha abstraído algum elemento, isso pode não fun cionar em todos os casos. É muito dificil escrever uma classe que sati sfaça as necessidades de todos os us uários. Não coloque em uma classe mais detalhes do que são necessários para resolvero prob lema. Não queira resolver todos os prob lemas. Resolva o problema que está a sua fren te e depois procure maneiras de abstrair o que você fez.
Respostas dos exercícios I. Um possível TAD de pil ha:
pub lie interface Stack { publlc void push( Obj ect obj ) ; pu bli c Obj ec t pop (); pub l i c boolean isEmpty() ; pu bl i c Objec t peek(); } 2. A pil ha é mel hor implementada como uma lista encadeada isoladamente, com um ponteiro de frente. Quando coloca ou ret ira um elemento, você usa o ponte iro de fren te para encontrar o primeiro elemento. 3. Examinando a resposta do Exercício I e a implementação do Exercício 2, você vê que a interface era adequada. A interface nos proporciona as vantagens que qua lquer interface bem definida o ferece. Aqui está uma lista breve das vantagens: •
A interface de fine a pi lha como um ti po. Estudando a interface, você sabe exatamente o que a pilha furá.
•
A interface oculta completamente a representação interna da pilha.
•
A interface defin e c laramente as responsabilidades da pilha.
Dia 3 Respostas do teste e dos exercícios Respostas do teste I. Aeeount tem dois mutantes: depositFunds() e wi thdrawFund s( ). Aceount tem um aeessor: getBa laneeO.
Apêndice A
2. Existem dois tipos de construtores: aqueles que têm argumentos e aqueles que não têm (noargs). Accou nt. do Laboratório 2, tem os dois tipos de construtores. 3. (Opcional) Publ i c é ace itáve l no caso de Boolean, pois as variáveis são constantes. Ter acesso públ ico às constantes não estraga o encapsulamen to, pois isso não está expondo a implementação para liSO externo. Além disso, O uso de valores Bool ea n constantes para verdadeiro e fa lso economi za memória. Não há necess idade de instanciar suas próprias cópias de Boo l ean. Você pode simplesmente compartilhar essas constantes de instâncias gl obai s. 4. As instânc ias de Card são imutáveis. Seria mais eficiente definir 52 constantes Card uma para cada carta. Não há necess idade de instanciar várias representações da mesma , carta, mesmo quando existem várias instânc ias de Deck. E perfe itamente seguro compartilhar as mesmas instânc ias de Card entre os maços de carta. 5. Ao projetar suas classes, você deve perguntar-se o que torna ' isso' uma classe? Espec ificamen te, lembre-se da di scussão sobre como as classes classificam obj etos relacionados. Como as cartas são rel acionadas? Todas as cartas contêm um número, um naipe e como exibi -Ias.
o número ou o naipe não torna uma carta um tipo di fere nte de carta . Ela ai nda é uma carta de pôquer. As cartas de pôquer simplesmente poderiam ter va lores diferentes. Assim como um mamífero marrom ainda é um mamífero, um l O de copas ai nda é apenas uma carta. ,
As vezes, pode ser extremamente dificil decidiroque devec o que não deve ser uma classe. Contudo, existe uma regra geral que você pode seguir. Se você ver que o comportamento de um objeto muda fundam entalmente quando o va lor de um atributo muda, as chances são de que você deva criar c lasses separadas: uma para cada va lor possível desse atributo. Claramente, o valor da carta não muda seu comportamento de nenhuma manei ra fundamental. 6. A divisão correta de responsabilidades toma o projeto de Deck, Dea l er e Card mais modular. Em vez de urna única classe grande, as cartas de pôquer se dividem perfeitamente em três classes. Cada classe é responsável por fazer seu lraba lho e ocu ltar essa implementação das outras classes. Como resultado, essas classes podem mudar sua implementação fac ilmente, a qual quer momento, sem prejud icar qualq uer um de seus usuários. Com classes separadas, você também tem a vantagem de q ue pode reutilizar a classe Car d separada das classes Deck e Dea l er.
Respostas
Respostas dos exercícios I.
Aqui está uma possível solução para o Exercício I: public class DoubleKey { private Object keyl. key2;
II
um construtor noargs public DoubleKeyO { keyl z "keyl "; key2 '" "key2"; }
II II
um construtor com argumentos deve procurar e ma nipular caso nulo public DoubleKey( Object keyl. Object key2 ) { th is .keyl .. keyl; th1s.key2 = key2; }
II
acessor publi c Object getKeyl() { return keyl ; }
II II
mutante deve proc urar e manipular caso nulo publi c void se tKeyl( Object keyl ) { th1s.keyl = keyl; }
II
acessor publ ic Object getKey2() { return key2; }
II II
mutante deve procurar e manipular caso nulo public void setKey2( Object key2 ) { thi s . key2 = key2; }
II
os doi s métodos a seguir são exigidos para . . .. 11 trabalhar cor retamente como um keyif passado para HashMap ou Hashtable pub li c boolean equals( Object obj ) {
Apêndice A
il( Ih is •• obj ) ( return true; }
if( thi s .getC l ass O "" obj. getC l ass () ) { DoubleKey dk " ( Doub l eKey )obj; if( dk . getKey l().equa ls ( getKeyl() ) && dk . gelKey2 () . equa 1s ( gelKey2 () ) ) { return true; } } return false; } public int hashCode () { return keyl.hashCode{) + key2.hashCode(); } }
2. Aqui está lima possível solução para o Exercício 2: pub li c class Deck ( private java.util.Li nkedlist deck ; publi c Deck() { bui l dCardsO ; } public String dis play() ( int num_cards " deck . size() ; String display .. ""; i nt cou nter '" O; for( int i ,. O; i < num_cards; i ++ ) { Card card " (Card ) deck.get( i ); display " display + card . display()+ " "; counter++ ; if( counter "" 13 ) { counter .. O; display " display + "\ n"; } } return display; } publi c Card get( int index) ( if( index < deck . size() ) { return (Card) deck.ge t( index }; }
Respostas return nul1; }
pub l i c void replace( int index. Card card ) { deck.set( index. card )i }
public int sizeO { return deck.size() i }
publ ic Card removeFromFront() { if( deck.sizeO > O } ( Card card • (Card) deck .removeFirst()i return ca rdi }
return nul1; }
public void returnToBack( Card card ) { deck. add( card l ; }
pr ivate void bu i l dCards() { deck "'new j ava.util.linkedlist(); deck.add( new Card( Card . CLUBS. Ca rd.TWO ) ) i deck.add( new Card( Card . CLUBS . Ca r d.THREE ) l; deck.add( new Card( Ca rd. CLUBS . Card .FOUR ) ) i deck.add( new Card( Card . CLUBS. Card . FIVE ) ); II definição comp l eta cortada por brevidade II veja a listagem comp l eta no código - fonte }
}
Dia 4 Respostas do teste e dos exercícios Respostas do teste I. A reutilização simples não fornece nenhum mecanismo para reut il ização além da instanciação. Para reutil izar cód igo diretamente, você precisa reconar e co lar o código que deseja reuti lizar. Tal prática resulta em várias bases de cód igo que diferem apenas em um pequeno número de maneiras. Um código q ue não possu i herança é estático. Ele não pode serestend ido. Além disso. um cód igo estático é limi tado quanto ao ti po. Um cód igo
Apêndice A
estático não pode compart ilhar tipo. Assim, você perde as vantagens da capacidade de conexão de tipo. 2. A herança é um mecanismo interno para a reutil ização segura e a extensão de defin ições de classes preex istentes. A herança penn ite que você estabeleça relacionamentos 'é um ' entre as classes. 3. As três formas de herança são: Herança para reutili zação de implementação Herança por d ifere nça Herança pa ra s ubstituição de tipo 4 . A impl ementação da herança pode cegar um desenvolvedor. A reuti lização da implementação nunca devc ser o ún ico objeti vo da herança. A substituição de tipo sempre deve ser s ua primeira prioridade. A herança para reutili zação cega leva a hi erarquias de classes que simplesmente não fazem sentido . 5. A programação por diferença é uma das fo rm as de herança. Isso signi fica que, quando você herda, programa apenas os recursos que diferenciam a nova classe da antiga. Tal práti ca leva a classes menores e incrementais. As classes menores são mais fáce is de ge• renc.ar. 6. Os três tipos de métodos e atri butos são: Sobrepostos Novos Recursivos Um atributo ou método sobreposto é um atri buto o u método declarado na progeni tora (ou ancestral) e reimplementado na filha. A filha altera o comportamento do método ou a defin ição do atributo. Um método ou atributo novo é um método ou atributo que aparece na filh a, mas não na progenitora ou ancestral. Um atributo ou método recursivo é de fin ido na progenitora o u na ancestra l, mas não é redefinido pe la filha. A filha sim plesmente herda o método o u atributo. Quando é feita uma chamada para esse método ou atribulo na filha , a chamada sobe na hierarquia até que seja encontrado alguém que saiba como manipulá-la. 7. A programação pela diferença fornece as menores classes que definem um número menor de comportamemos. Classes menores devem conter menos erros, ser mais fáceis de depurar, mais fáceis de manter e mais fáceis de entender.
Resposta s
A programação pela diferença permite que você programe de fo rma incrementa l. Como resu ltado, um projeto pode evoluir com o passar do tempo. 8. A11 Permi ss ion, Bas i cPermi ss i on e Un reso 1vedPermi ss i on são todas fi lhas de Penni ss i on. SecurityPenniss i on é descendente de Permission. Penn ission é a classe-raiz. Al I Permi 5S i on, Unreso 1vedPenni ss; on e Securi tyPermi ss i on são todas classes folhas, pois elas não têm fi lhas. Sim, Penni ss10n é ancestral de SecurityPennission. 9. Herança por substituição de tipo é o processo de defin ir relacionamentos com capacidade de substituição. A capacidade de substituição permite que você subst itua uma descendente por uma ancestral , desde que não precise lIsar quaisquer novos métodos defi nidos pela descendente. 10. A herança pode destruir o encapsulamento, dando a uma subc lasse acesso inadvert ido à
representação interna de uma superc lasse. A destruição inadvertida do encapsulamento é uma anlladi lha que pode apanhá-lo sorrateiramente. A herança fo rnece silenciosamente a uma classe filh a acesso mais liberal à progenitora. Como resul tado, se os passos corretos não forem dados, uma fi lha poderia ter acesso direto à im plementação da progenitora. O acesso direto à implementação é tão perigoso entre progenitora e fil ha quanto entre dois objetos. Muitas das mesmas armadilhas ainda se aplicam. Evite a destruição do encapsu lamento tomando a implementação interna privada. Faça implementação interna privada apenas dos métodos absolutamente necessários para uma subclasse. Na maioria das vezes, as filhas devem exercitar apenas a interface pública da progeni tora.
Respostas dos exercícios I. Toda subclasse lerá acesso direto à representação interna de Point. Ta l acesso irrestrito destrói o encapsulamento c abre a classe para os prob lemas tratados na pergunta 10. Remediar a si tuação é tão fáci l quanto tornar x e y privados. Note que essa classe Pai nt é mode lada de acordo com java.awt.Point.
Apêndice A
Dia 5 Respostas do teste Respostas do teste lo Em CheekingAeeount, publie double withdrawFunds( double amount ) é um exemplo
de método redefin ido. CheckingAeeount sobrepõe withdrawFunds() para que ele possa cont"rOlar O número de transaçôes. Em BankAeeount, publ i e double getBal anee() é um exemplo de método recursivo. O método aparece na progenitora, mas nenhuma das subclasses o redefine. Entretanto, as subclasses o chamam. Finalmente, o método publ ie double getlnterestRate() de SavingsAeeount é um exemplo de método novo. O método aparece apenas na classe Savi ngsAeeount e não na progenitora. 2. Você usa ria uma classe abstrata para herança planejada. Uma classe abstraIa fornece às suas subclasses um indício do que ela prec isará redefinir. As classes abstratas garantem que suas subclasses usem a classe base corretamente. 3. O Laboratório 3 mostra 'tem um'. Deck tem objelos Cardo DoubleKey, do Laboratório 2, também tcm duas St rings. 4. Os laboratórios preservaram o encapsulamento ocultando lodos os membros de dados. Se você exami naras soluções, verá que todos os dados são pri vados. Por exemplo, a classe BankAeeount declara o saldo (balance) como privado. Em vez disso, cada classe dá acesso à representação dos dados através de uma interface bem defin ida. 5. SavingsAeeount é um exemplo de especialização. Ela se especializa em relação à sua progenitora, BankAeeount, ad icionando métodos para configurar, consultar e aplicar juros na conta. 6. O Laboratório 3 usa herança para reut ilizar o comportamento básico defi nido pela classe BankAccount. BankAccount de fi ne uma implementação comum para sacar fundos, depositar fundos e consultar o sa ldo. Através da hera nça, as subc lasses de conta obtêm essa implementação. O Laboratório 4 começa apresentando um caso de herança para reuti liz.1ção da implementação, mas tennina usando composição, para obter uma forma dc reutilização mais limpa.
Dia 6 Respostas do teste e dos exercícios Respostas do teste I. Inc lusão
Respostas
Paramétrico Sobreposição Sobrecarga 2. O polimorfismo de inclusão permite que você trate um objelo como se fosse um tipo di fere nte de objeto. Como resultado, um objeto pode demonstrar muitos tipos diferentes de comportamento. 3. Os polim orfi smos de sobrecarga c paramétrico perm item que você modele algo em nível conceituaI. Em vez de se preocupar com os tipos de parâmetros que algo processa, você pode escrever seu código de forma mais genérica. Em vez di sso, você modela seus métodos e tipos em nível conceitua i do que e les fazem e não para que fazem . 4. Uma interface pode ter qualquer número de implementações. Programando para uma interface, você nào fica vinculado a nenhuma im plementação especítica. Como resultado, seu programa pode usar automaticamente qualquer implementação que apareça. Essa Iiberdade de implementação permite que você troque entre diferentes implementações para mudar o comportamento de seu programa. 5. Quando você sobrepõe um método, o polimorfismo garante que a versão correta do método seja chamada. 6. Polimorfi smo ad-Iroc é outro nome para sobrecarga. 7. A sobrecarga permite a você de fin ir um nome de método várias vezes. Cada definição difere simplesme nte no número e nos tipos de argumentos. A sobrecarga expressa um comportamento diferente porque você sim plesmente c hama o método. Você não precisa fazer nada para garantir que a versão correta do método sej a cha mada. A sobrecarga permite a você modelar um método em nível conce ituai do que ele faz. A natureza polim órfica da sobrecarga cuida dos argumentos específi cos. 8. O po limorfi smo paramétrico pcnnite a você escrever tipos e métodos verdadeiramente genéricos, adiando as de finiç ões de tipo até o momento da execução . Esse tipo de po limorfi smo permi te que você escreva cód igo rea lmente natural, pois pode programar tipos e métodos muito genéricos e conceituais. Você escreve esses tipos e métodos a partir da visão conceituai do que eles fa zem e não do que especificamente fa zem com ela. Por exempl o, se você programar um método compare ( [T] a, [T] b) . pensará em lermos do conceito mai s alto da comparação de do is objetos de tipo [T] . Os argumentos de tipo [TJ simplesmente precisari am compart ilhar uma estrutura comum, como < ou um mélodo compare( ). O ponto impo rtante é que você escreve si mplesmente um método e ele pode comparar mu itos tipos diferentes de objetos. 9. O po limorfi smo nomlal mente incorrerá em um custo na efic iência. Algumas fonnas e implementações de polimorfi smo ex igem verificações e pesquisas em tempo de execução. Essas verificações são dispendiosas, quando comparadas com as linguagens estaticamente tipadas.
Apêndice A
o polimorfis mo tenta fazer o desenvolvedor quebrar a hierarquia de herança. Você nunca deve mover funciona lidade para cima na hierarquia, si mplesmente para aumentar as oport unidades para comportamento polimórfico. Quando trata um subtipo como se ele fosse o tipo base, você perde o acesso a todos os comportamentos adicionados pelo subtipo. Assim, quando você criar um novo subtipo, prec isará garantir que a interface do tipo base seja adequada para a interação com seu novo subtipo, em métodos que trabalham com o tipo base. 10. A herança efi cnz tem um impacto direto no polimorfismo de inclusão. Pnra nproveilar a capacidade de conexão oferec ida pelo poli morfismo de subtipo, você deve ter lima hierarquia de objetos correta. O encapsulamento evita que um objeto fique vincu lado a um a implementação especifica. Sem encapsulam ento, um obj eto poderia se tornar facilmente dependente da im plementação interna de outro objeto. Tal acoplamento fone toma a substi tuição difíc il, se não impossível.
Respostas dos exercícíos 1. Imagine um programa que escreva seu status na linha de comando ao ser executado. Na linguagem Java, você poderia si mplesmente usar Sys tem .write. pr i nt l n D. para escrever essas mensagens na tela . E se você quisesse que essas mensagens fosse m gravadas cm um arquivo? E se você quisesse que essas mensagens fossem enviadas para uma GU I de alarme em outro computador? Obviamente, você precisaria alterar seu código, conforme os req uis itos mudassem. E se seus requisitos exigissem que você oferecesse suporte para ambos ao mesmo tem po? Em vez de um ou outro, você quer permit ir que o usuário escol hn onde va i escrever, através de um argumento de linha de comando. Sem pol imorfismo, você precisaria programar casos para cada tipo de escrita. Com o polimorfi smo, entretanto, você pode simplesmente declarar uma clnsse chamada 1og, que ten ha um método de escrita. Subclasses podem especificar onde as mensagens são gravadas. Você pode ad icionar novos subtipos em seu progra ma a qualq uer momento. O programa sabera automati camente como usar os novos subtipos, desde que você programe para a interface 109. Assim, você pode trocar para o novo comportamento de log a qualquer momento. 2. int i
=
2 + 3. 0
Dependendo da defin ição de +, essa instrução pode ser coerciva. Aqui , a instrução tenta somar um número inteiro e um número real. Ela pega o resultado e o coloca em um a variável 1nt. Dependendo da linguagem, o compilador pode converter o inteiro 2 em um nímlero rea l, efetuar a operação aritmética e. em seguida. convener o resultado novamente para um inte iro.
Respostas
Essa instrução é interessante, pois e la também pode demonstrar uma instância de sobrecarga. + pode sobrecarregar as seguintes operações: +(real ,real) +(integer . integer) +(integer . real) Em qualquer caso, você tem pol imorfi smo ad-floc, pois, em vez de um método pol imórfic o, você tem vários métodos polimórfi cos ou conversão. 3. Sobrecarga: Considere java . util . SimpleTimeZone. SimpleTimeZone define os doi s métodos sobrecarregados a seguir: setEndRu 1e e setS ta rtRu 1e. Como resultado, esses métodos respondem diferentemente, dependendo do número e dos tipos de entrada. Polimorfi smo de inclusão: Cons idere java. iO.Writer. A classe abstrata Wri ter define vários métodos para gravar dados. Correntemente, a linguagem Java define várias subclasses de Wri ter: BufferedWri ter, CharArrayWriter, Fi 1terWriter, OutputStreamWriter, PipedWriter, PrintWriter e StringWrite r . Cada subclasse define o comportamenlo dos métodos el ose, f lush e write (um método sobrecarregado, a propósito). Ao programar, você deve escrever seus objetos e métodos para agirem em instâncias de Wri te. Desse modo, você pode trocar para diferentes subc lasses, dependendo de como deseja que os dados sej am gravados. Se você programar dessa maneira, Wri ter expressará vários comportamentos di fe rentes, dependendo da im plementação subjacente que esteja sendo usada.
Dia 7 Respostas do teste Respostas do teste I. o método observeO de
PsychiatristObject é um exemplo de sobrecarga de método.
2. O método observe () i Iustra muito bem o problema da sobreca rga . Sempre que você adicionar um novo subtipo, precisará ad icionar outro método sobrecarregado. À med ida que o número de mélodos aumentar, você desejará encont rar uma manei ra de ad icionar uma função com um em seus objelos, para que possa tratá-los genericamente e remover os métodos sobrepostos. 3. São dois passos para adicionar novo comportamento em uma hierarquia poli mórfica. Pri meiro, crie o novo ti po. Segundo, al tere seu programa de modo que ele possa eriar instâncias do novo tipo. Você não deve ter de mudar nada ma is, a não ser que precise tirar proveito de algum recurso especial da nova classe.
Apêndice A
4. O método exami ne() de Psychi at ri stObjec t é um exempl o de polimorfismo de inclusão. Ele pode trabalhar com qualquer subtipo de MoodyObject. 5. Você pode eliminar as condicionais atacando os dados que fazem parte das condicionais. Se os dados não são um objeto, transforme-os em um objeto. Se os dados são um objeto, adicione um método que forneça o comportamento necessário. Uma vez feito isso, peça ao objeto para que faça a lgo, não faça algo nos dados. 6. O polimorfismo de inclusão permitirá que um método funcione para o tipo de argum ento e qua lquer s ubtipo. Você não precisa de um metodo d iferente para cada s ubtipo. Ter apenas um método dim inui o número de métodos que, de outra forma, você precisaria. Isso também simplilica a adição de novos recursos. 7. Na 00, você não deve pedir os dados de um objelo. Em vez disso, você deve pedir a um objeto para quc faça algo com scus dados. 8. As condicionais o obrigam a quebrar o relacionamento deli neado no Exercício 7. Quebrar o relacionamento o obriga a misturar responsabilidades, pois todo usuário precisará saber o quê os dados representam e como manipulá-los. 9. Se você se encontraratua li zando várias condi cionais, semprc que adiciona um novo t ipo, então a condiciona l é um problema. Se você se encontrar escrevendo a mesma cond icionaI em vários lugares (ou chamando um método que tenha a cond icional), então a condicional é um problema.
10. O polimorfismo permite que você trate um s ubtipo como se ele fosse o supertipo. Entretanto, o poli morfismo permite que você use o comportamento do tipo subjacente real. O pol imorfis mo faz parecer que osupertipo manifesta muitos comportamentos diferentes.
Dia 8 Respostas do teste e dos exercícios Respostas do teste I. A UML é a Unified Model ing Language. A UML é uma linguagcm de modelagem padrão do setor.
2. Uma metodologia descreve como projetar software. Uma linguagem de modelagem ajuda a capturar esse projeto graficamente. Uma metodologia freqüentcmente conterá sua própria linguagem de modelagem. 3. O laboratório demonstra um relacionamento de dependência. 4. Você pode fazer duas afirm ativas sobre MoodyObject. MoodyObject tem um método chamado queryMood (). MoodyObject também é uma classe abstrata. O nome em itá lico ind ica que a classe é abstrata .
Respostas
5. O relacionamento Employee, do Laboratório I, é um exemplo de dependência. O método payEmployeesO de Payroll depende da interface públ ica de Employee. 6. Cada um desses símbolos transmite informações de visibi lidade. + é públ ico, # é prote· gido, e - é pri vado. 7. Um objcto Queue e seus elementos são um exemplo de agregação. 8. O obj eto Deck tem muitas cartas. Entretanto, se você destruir O baralho, deverá destrui r as cartas. O objeto Deck é um exemplo de relacionamento de composição. 9. Basta deixar um nome de classe ou método em itâlico para mostrar que ele é abst rato. 10. O objet ivo tinal da modelagem é transmitir seu projeto. Conseqüentemente, você não deve se preoc upar com o uso de toda notação de modelagem disponível. Em vez disso, você deve usar a notação mínima que ainda transm ita sua mc nsagem com êxi to. I I. Uma associação modela relacionamentos estruturais entre objetos. A agregação e a composição são subt ipos da assoc iação que modela relac ionamentos ' todo/ parte'. Uma agregação é um relacionamento estrutural entre pares. A composição é um relacionamento estrutural onde a parte não é independente do todo. A parte não pode existi r separadamente do todo. 12. Mode le uma associação quando o objetivo de seu modelo for modelar os papéis entre os objetos. Use agregação ou composição quando você estiver tentando transmitir o projeto est rutural.
Respostas dos exercícíos I. Fir.
FIGURA A .1
Uma fila.
+ enqullulI (obj' ObjOClI: yold + dequllua ( I' ObjllCt + ,sEmpty (I' boolean + pook (I: ObjOCl
2. FIGURA A .2
Um relaciollamemo de composiçt1o A belllalCol",éia.
COJrT\6i.
1
• Aba lha
Apêndice A
3. ..~
FIGURA A .3
VIII ndaciollalllelllo de agregaç(io Banco/Collfa Bal/cária.
1
• ConuEüru:ân.
4. FIGURA A .4 A associaç(io COlllpradorlCOIllel""iol1re.
compra dor
compra de
Comprador
•
vendedor
•
Comercianle
• vende para
vendedor
~
comprador
•
Comercianle
Comp-rador
5. FIGURA A .5
Emplo YH
A hieml"qllia Employee.
- tifSl...na me : Slring -Iasename: Slrlng - wage : double + getWage ( ) : double + gelFirslName ( ) : String + getLastName ( ) : String + calculatePay (): double + printPaychllCk ( ) : String + calculate80nuli ( ): double
7 Comml"lonedEmployee - oommission : double - unidades: inl + addSalfl'S (unilli inl) : void + resetSales ( ) : void + calculatePav () : double + calculateBonu$ (I: double
I haure : inl + addHo ur. (hou,..: inl) : void + resetHours ( ): void + calculataPay ( ) : double + calculateBonus ( ) : double
Resposta s P'fson. lityObject
FIGURA A .6
A hierarquia • spelk ( I : String
Persona l ; tyObject.
OptimlsticObJec:t
Introv.rtedObJec:t
PeulmllllcOb}ed
+ speak ( I: String
• spellk ( I :
Strin~
• spellk ( I : String
Dia 9 Respostas do teste e dos exercícios Respostas do teste I. Um processo de software d ispõe os vários estágios do desenvolvimento do software. 2. Um processo iterativo é um processo que permite a você voltar e refazer o u melhorar continuamente o produto de iterações anteriores. Um processo iterativo adota uma estratégia iterativa e incrementa l para o desenvolvimento de software . Incremental significa que cada iteração acrescenta um pequeno aumento na funcionalidade. Não tão pequena para passar desapercebida, mas não tão grande para ser deixada de lado por ser demasiadamente d is pendiosa. 3. No fina l da AOO você deve ter um bom entendi mento dos requisitos do sistema, assim como do vocabulário do domínio do sistema. 4 . Os requi si tos informam o que os usuários querem fazer com o sistema e quais tipos de respostas eles esperam receber. Os requ isitos são aqueles recursos que o sistema deve ter para resolver deterrn inado problema . 5. Um caso de LI SO descreve a interação entre o usuári o do sistema e o sistema. O caso de uso descreve como o usuário uti lizará o sistema a partir de seu ponto de vista. 6. Você deve dar os seguintes passos para defi nir seus casos de uso: I. Identi fi car os alores 2. Criar uma lista pre liminar de casos de uso 3. Re fin ar e nomear os casos de uso 4. Defin ir a seq üência de eventos de cada caso de uso 5. Mode lar seus casos de uso 7. Um ator é algo que interage com o sistema.
Apêndi ce A
8. Você pode fazer as segui ntes perguntas para ajudar a encontrar atares: • Quem principal mente vai usar o sistema? • Exi stem outros sistemas que usarão o sistema? Por exemplo. existem usuários não-humanos? • O sistema vai se comunicar com qualquer outro sistema? Por exem plo, já existe um banco de dados que você precisa integrar? • O sistema responde a estímu los não gerados pelo usuário? Por exemp lo, o sistema precisa fazer algo em dctcnninado diade cada mês? Um estímulo pode serprovenicnte dc fontes norma lmente não consideradas ao se pensar do ponto de vista puramente do usuário. 9. Um caso de uso pode conter e utilizar outro caso de LI SO, ou estender outro caso de uso. Um caso de uso também pode ser uma variante de outro caso de li SO. 10. Uma variante de caso de uso é um caso especial de um caso de uso mais gera l. I I. Um cenário é uma seq Uência ou flu xo de eventos entre o usuá rio e o sistema. 12. Você pode modelar seus casos de uso através de diagramas de intcração e diagramas de atividade. Ex istem dois tipos de diagramas de interação: diagramas de seqUência e de colaboração. 13. Os diagramas de seq i.iênc ia modelam a seqüência de eventos com o passar do tempo. Um diagrama de colaboração modela as interações entre os atares de um caso de uso. Os dois tipos de diagramas são diagramas de interação. Entretanto, cada um adota um ponto de vi sta diferente em relação ao sistema. Use diagramas de seqüência quando quiser controlar eve ntos e diagramas de colaboração, quando quiser destacar relacionamentos. Os diagramas de at ividade o ajudam a modelar processos paralelos. Use diagramas dc at ividade quando qui ser transmiti r o fato de que um processo pode ser executado em parale lo com outros processos, durante um cenário de caso de uso. 14. Um modelo de dom ínio apresenta várias vantagens. O mode lo de domín io pode servir como base ou esqueleto de seu mode lo de objetos. Você pode usar essa base como um início e construir a part ir de la. Os mode los de domínio também fornecem um vocabulário com um e um entendimento do problema. 15. Os casos de uso o ajudam a entender o sistema, seus requi sitos e seus usos. Os casos de uso podem aj udá-lo a planejar as iterações de seu projeto. Final mente, os casos de uso o ajudam a definir seu mode lo de domínio.
Respostas
5 17
Respostas dos exercícios 1. Alguns outros casos de uso: •
Remover lIem: um usuário pode remover um item do carrinho.
•
Exclui r Usuário: um admin istrador pode remover contas inativas.
•
Premiar Usuário: o sistema pode recompensar cl ientes freqüentes, oferecendo descontos dinamicamente.
2. O usuáriose leciona um item do carrinho de com pras. O usuári o remove O item selecionado do carrinho de compras. •
Remover Item .
I. O usuári o convidado se lec iona um item do carrin ho de compras. 2. O usuári o conv idado pede ao carrinho para que remova o item: • •
Condições prévias. O carrinho contém um item para remover.
•
Condições posteriores.
•
O item não aparece mais no carrinho.
•
Alternati va: Operação Cancelada.
O usuário pode optar por cancelar a transação após o passo I. 3. Os dois casos de LISO a seguir são variantes do caso de uso Pesq uisar o Catá logo de Produtos: •
Os usuários convidados podem pesquisar o catálogo de produtos.
•
Os usuários convidados podem procurar um item específico.
Os doi s casos de
LISO
a seguir são variantes do caso de uso Assinar Not ilicações:
•
Os usuários reg istrados podem assinar notifi cações.
•
Os usuários regi strados podem assi nar várias listas de mensagens.
4. Existem muitos outros obj etos de domínio. Aqu i estão alguns: Administrador, Li sta de Produtos Destacados e Lista de Pedidos.
Dia 10 Respostas do teste e dos exercícios Respostas do teste I. Existem três vantagens em um projeto fonnal. Um proj eto fo rmal o aj uda a descobri r quais objetos aparecerão em seu programa e como e les vão interagir ou se enca ixar.
Apêndice A
Um proj eto o ajuda a prever muitos dos problemas de projeto que apareceriam durante a implementação. É mu ito mais fácil corrigir um projeto antes que seja codificado. Finalmente, um projeto ajuda a garantir que todos os desenvolvedores estejam na mesma página; caso contrário, você corre o risco de que cada desenvolvedor desenvolva partes incompatíveis. 2. POO é o processo de construir o modelo de objetos de uma sol ução. Di lo de outra manei ra, POO é o processo de decompor uma solução em vários objelos constitui ntes. 3. O modelo de objetos é o projeto dos objetos que aparecem na solução de um problema. O modelo de objelos final pode conter muitos objelos não encont rados no domín io. O //1 0dela de objelos descreverá as responsabil idades, relacionamentos e estrutura dos vários objetos. 4. Si mpl esmente não é possive l prever cada decisão de projeto, antes que você a tome, e isso nem sempre vale a pena. Algum projeto pode ser deixado até a construção. Além disso, você não quer ser pego tentando criar o projeto perfeito. Você precisa começar a cod ificar, algum dia. 5. As partes signi ficativa s são aqueles aspectos do sistema que alteram completamente sua estrutura ou seu com portamento. Essas são as partes que realmente importam, quando você cod ifica a solução. Uma mudança em uma parte arquitetônica importante mudará a estrutura da solução. 6. Os cinco passos básicos do POO são: I. Gerar a lista inicial de objetos.
2. Refinar as responsabilidades de seus objetos através de ca rtões CRC. 3. Desenvolver os pontos de interação. 4. Detalhar os relacionamentos entre os objetos. 5. Construir seu mode lo. 7. Começar com o domínio para gerar sua lista inicial de objetos. Cada objeto e cada atar do domínio deve se tornar uma classe em seu model o de objetos. Sistemas de tercei ros, interfaces de hardware, relatórios, telas e dispos itivos também devem se tornar classes. 8. Um projeto completo capturará as responsabilidades de cada objeto, assim como a estrutura e os relacionamentos do objeto. Um projeto mostrará como tudo se encaixa. 9. Os cartões CRC oaj udam a identi ficar responsabil idades e colaboraçõcsde cada classe. 10. Colaboração é o relac ionamento de delegação entre dois objetos. Você pode considerar uma colaboração como um relacionamento cliente/servidor enlre dois obj ctos.
Respostas
I I. Praticamente, as responsabilidades se trad uz irão em métodos. Os relacionamentos se traduzirão em uma estrutura; entretanto, um entendimento g lobal das responsabi lidades o aj udará a dividir as responsabil idades eficientemente entre os objetos. Você precisa evitar ter um conjun to pcqueno de objetos muito grandes. Através do projeto, você garanti râ a dispersão das responsabilidades. 12. Um cartão C RC é uma fi cha de arquivo 4x6 que o ajuda a descobri r as responsabi lidades e colaborações de um objeto, explorando casos de uso. 13. Você está intencionalmente lim itado pelo tamanho de um cartão CRC. Se você verificar que está ficando se m espaço, são boas as chances de que sua classe esteja faze ndo coisas demai s. 14. Você deve usar cartões C RC durante os estágios iniciais do desenvolvi mento, especialmente quando você ainda for in ic iante no desenvolvimento 00. Os cartões CRC sc prestam para pequenos projetas ou para uma pequena seção de um projeto maior. Você só deve usar cartões CRC para descobrir res ponsabilidades e colaborações. Não tente descrevcr relacionamentos complexos através de cartões CRe . 15. Os cartõcs C RC não funcionam bem para projetos grandes ou para grupos de desenvolvimento. Um grande número de classes pode atrapalhar uma sessão de cartões CRC. Desenvolvedores demais também podem dan ificar uma sessão de cartões CRC. 16. Um ponto de interação é qualquer lugar onde um objeto use outro. 17. Em um ponto de interação, você deve considerar transformação de dados, alteração fut ura, interfaces c o uso de agentes. 18. Um agente é um objeto que faz a intenned iação entre dois ou mai s objetos para cumprir algum objetivo. 19. Você vai definir as dependências, associações e general izações. Detalhar esses relacionamentos é um passo importante, pois isso define como os objetos se encaixam. Isso tam bém dcfine a est rutura interna dos vários objetos. 20. Você poderia criar diagramas dc classe, diagramas de atividade e diagramas de interação para mode lar seu projeto. A UML também define diagramas de objeto e d iagramas de estado.
Respostas dos exercícios I. As instâncias de Shopp i ngCart terão a responsabil idade g lobal de conter itens. Especificamente, um objeto Shoppi ngCart pode adicionar um item em si mesmo, remover um item de si mesmo e permitir que um obj eto externo selec ione um item, sem removê-lo.
Apêndice A
Dia 11 Respostas do teste e dos exercícios Respostas do teste I. Uma classe adaptadora transforma a interface de um objeto naquela esperada por seu programa. Um adaptador contêm um objeto e delega mensagens da nova interface para a interface do objeto contido. 2. O padrão Iterator descreve um mecanismo para fazer laço pelos elementos de uma colcção. 3. Você usaria o padrão Ite r ator para conter lógica de navegação em um local, fornecer um modo padrão de percorrer coleções e ocultar do usuári o a implementação da co lcção. 4. O padrão Adapter descreve um mecan ismo que permi te transformar uma interfa ce de objetos. 5. Você usaria o padrão Adapter quando precisasse utilizar um objeto que tivesse uma interface incompatível. Você também pode usar empacotadores preventivamente, para isolar seu código das mudanças de APL 6. O padrão Proxy intennedia de fonna transparente o acesso a um objelo. Os proxies adicionam um procedimento indireto no uso do objeto. 7. Você usaria o padrão Proxy sem pre que quisesse intenned iar o acesso a um objeto de maneira que uma si mples referência não pennite. Exemplos comuns inc luem recursos remotos, otimi zações e limpeza gera l de objeto, como contagem de referência ou reunião de estatísticas de util ização. 8. Nessa situação, você pode usar o padrão Adapter para criar uma interface independente daquela fornec ida pe la Sun, IBM ou Apache. Criando sua própria interface, você pode permanecer independente da APl ligeiramente diferente de cada forneced or. Empacotando a bibl ioteca, você está li vre para trocarde bibl ioteca a qualquer momento, seja para migrar para uma nova versão ou para trocar de fornecedo r, pois você controla a interface do adaptador. 9. Nessa situação, você pode usar o padrão Proxy para ocultar a identidade do objeto que armazena os dados dos objetos que o chamam. Dependendo da localização do cliente, você pode instanciar um proxy interligado em rede ou um proxy local. De qualquer modo, O restante do programa não notará a diferença; portanto, todos os seus objetos podem usar uma interface proxy sem ter de se preocupar com a implementação subjacente. 10. O padrão Proxy não muda uma interface, no sentido de que ele não retira nada dela. Entretanto, um proxy está livre para adicionar mais métodos e atributos na interface.
Respostas
Respostas dos exercícios I.
LISTAGEM 11 .13 ShoppingCart .java public class ShoppingCart 1 java.util.linkedlist items : new java.uti l .lin kedlist() ;
r·* ad iciona um item no carrinho * @param item o item a ser ad i cionado
./
pu bl ic void add ltem( Item item ) { items . add ( item);
I
r·* remove o item dado do carrinho * @param item o item a ser removido
./
pub l ic void removeItem( Item item) ( items. remove( item )i
I
r·* @return int o número de it ens no carrinho ./
public int getNumberltems() ( return items.size()i
I
r·* recupera o item indexado * @param i ndex o lndice do item * @ret urn Item o item no lndi ce
./
public Item get ltem( int index) { return (Item) items.get( index )i
I
pub l ic I te rator iterator() ( II Arraylist tem um método iterator() que retorna um ite rator /1 ent retanto, pa ra propÓS itos de demonstração , ajuda ver um iterato r /1 simples return new Cart l terator( items )i
I I
521
Apêndice A
LISTAGEM 11 .14
CartIterator.java.
public class Cartlterator implements lterator { private Object [] items; private int index; public Cartlterator( java . util.linkedlist items ) ( this.items • items.toArray() ; }
public boo l ean isOone() { if( index >z items.length ) { return true ; }
ret urn false; }
publ iC Object curre ntItemO ( if( lisOone() ) I return items [index]; }
return null; }
publiC void next() { index++ ; }
publiC void f irst() { index· O; }
}
2. Tornando o adaptador mutante, você pode usar o mesmo empacotador para empacotar muitos objetos diferentes e não precisa instanciar um em pacotador para cada objeto que precise ser empacotado. A reutilização de empacotadores faz melhor uso da memória e Iibera se u programa de ter de pagar o preço da instanciação de muitos empacotadores. LISTAGEM 11 .15
MutableAdapter.java
publ ic cla ss MutableAdapter extends MoodyOb ject I private Pet pet; publi C Mu tableAdapter( Pet pet ) {
Respostas
LISTAGEM 11 .15
Mutab leAdapte r.java (continuação)
setPet( pet ); }
protec ted St ri ng getMood () I II imp lementando apenas porque é ex i gido II por MoodyObject, pOis também sobrepõe queryMood II não precisamos disso return pet.speak(); }
publi C yoid queryMoodO I System . out.println( getMoodO ); }
public yold se tPet( Pet pet ) I this.pet • pet; } }
Dia 12 Respostas do teste e dos exercícios Respostas do teste 1. Uma classe empacotadora transfonna a interface de um objeto naquela esperada por seu programa. Um empacotador contém um objeto e delega mensagens da nova interface para a interface do objeto contido.
2. O padrão Abstract Factory fornece um mecanismo que instancia instâncias de classe descendentes específicas, sem reve lar qual descendente é real men te criada. Isso permite conectar, de forma transparente, diferentes descendentes em seu sistema. 3. Você lisa o padrão Abstract Factory para ocultar os detalhes da instanciação, para ocultar
a classe de objetos que é instanciada e quando quer que um conjunto de objetos seja usado junto. 4. O padrão Singleton garante que um objeto seja instanciado apenas uma vez. 5. Você usa o padrão Singleton quando quer que um objeto seja instaneiado apenas uma
vez. 6. Usar consta ntes primitivas não é uma estratégia de 00 para programação, pois você tem de aplicar um significado externo à constante. Você viu quantos problemas o desdobramento da responsabilidade poderia causar!
Apêndice A
o padrão Typesafe Enum resolve esse problema, transformando a constante em um objeto de nivel mais alto. Usando um objeto de nível mais alto, você pode encapsular me lhor a responsabil idade dentro do objeto constante. 7. Você deve usar o padrão Typesafe Enum quando se encont rar declarando constantes públicas q ue devem ser objetos propriamente di tos. 8. Não, os pad rões não garantem um projeto perfeito, pois você poderia acabar usando um padrão incorretamente. Além disso, usar um padrão corretamente não significa que o restante de se u projeto seja vál ido. Muitos projetos válidos nem mesmo contêm um padrão.
Respostas dos exercícios I.
LISTAGEM 12.19 Bank.java publi c class Bank { private java.util.Ha shta ble accounts
=
new java.uti l . HashtableO;
private statlc Bank ins tance; protected Ba nkO {) publi c stati c Ba nk getlnstance(){ i f( ins t ance •• nu l l ) I in stance • new Bank(); }
re t urn in stan ce; }
public vo i d addAccoun t( String name , BankAccount ac count ) { aceounts .put( name , aeeo unt ) ; }
pu bl ic double tota l Ho l ding s( ) { doubl e total = 0.0 ; java.ut il .Enurnerati on enum" ac counts.elemen t sO ; wh il e ( enum.hasMoreElernents() ) I BankAceount aeeount (BankAccount) enum. nextE l ement(); tota l +- aecount.getBalance(); E
}
return tota l; }
Respostas
LISTAGEM 12.19 Bank.java (continuação) publiC int totalAccounts() ( return accounts .size(): J
publi C void deposite Str i ng name , double ammount ) ( BankAeeount aeeount • retrieveAeeount( name ): if( aeeount ! - null ) ( aeeount .deposi tFunds( ammount ): J J
publie double balanee( String name ) { BankAeeount aeeount • ret r ieveAeeount( name ): if( aeeount ! - null ) ( return aceount.getBalance{): J
return 0.0: J
private BankAecount retrieveAccount( String name ) ( return (BankAecount) accounts .get{ name );
J
J 2.
LISTAGEM12.20 Level .java publie fina l elass Level { public public publie public
fi nal fi nal fi na 1 fi na 1
statie stat1e slatie statie
Level Level Level Level
NO ISE INFO WARNING ERROR
new new • new • new • •
Level ( Level ( Leve 1 ( Level (
private int level: private St ring name ; private level( int level, String name ) { this.level • leve l : thiS.name • name : J
publlc int getlevelO {
O, "NOISE" ): 1 , "INFO" ): 2, "WARNING" ): 3, "ERROR" ):
Apêndice A
LISTAGEM 12.20 level. j ava (continuação) return level; }
public String getName() I return name ; } }
LISTAGEM 12.21 Error .java pub l ic class Error 1 pr;vate Level level ; public Error( Level level ) { this.level ·level ; }
pub l iC Level getLevel() 1 return level; }
publiC String toString() { return level.getName (); } }
3. A solução consiste em uma factory abst rata de conta bancária (escrita como uma interface; entretanto, ela também pode ser uma classe abstrata) e em uma Factory concreta de conta bancária. A factory tem um método para criar cada tipo de conta bancária. Essa factory oculta os deta lhes da instanciação e não necessariamente o subtipo do objeto.
LISTAGEM 12.22 AbstractAccountFactory.java pub li c inte rface AbstractAccountFactory { publ iC CheckingAccount createCheckingAccount( double i nitDeposit . int trans. doubl e fee ) ; pub l ic OverdraftAccount createOverdraftAccou nt( double in i tOeposit. double rate ); public RewardsAccount createRewardsAccount( double i nitDepos;t. double interest . double min };
Respostas LISTAGEM
527
12.22 AbstractAccountFactory .java (continuação)
publiC SavingsAccount createSavingsAccount( double initBalance. double interestRate ); public TimedMaturityAccount createTimedMaturityAccount( double initBalance. double interestRate. double feeRate );
I
LISTAGEM
12.23 ConcreteAccountFactory,java
public class ConcreteAccountFactory implements AbstractAccountFactory ( publiC CheckingAccount createCheckingAccount( double initOeposit . int tra ns . double fee ) { return new Check i ngAccount ( i ni tDepos i t . tra ns . fee ) ; I publ i c OverdraftAccount createOverdraftAccount( double initDeposi t . double rate) ( return new OverdraftAccount( initOeposit . rate );
I public RewardsAccount createRewardsAccount( double initOeposit. doub le interest. double min } I return new RewardsAccount( initOeposit. interest. min };
I publi c SavlngsAccount createSavingsAccount( double initBalance. double interestRate ) I return new SavingsAccount{ i nitBalance . intere st Rate );
I publi C TimedMaturityAccount createTimedMaturi tyAccount( double i nit8alance. double interestRate . double feeRate ) 1 return new TimedMatu r ityAccount( ini t Balance. interestRate. feeRate ) ;
I I
Apêndice A
Dia 13 Respostas do teste e dos exercícios Respostas do teste I. A análise, projeto e implementação de uma UI não são diferentes do restante do sistema. A UI deve ter consideração igual durante todas as fases do desenvolvimento. Antes de tudo, você deve certificar-se de não ignorar as considerações sobre a UI. 2. Você deve desacoplar as Uls para que o sistema e a UI não se tomem entrelaçados. É dificil fazer alterações na UI, quando entre laçada com a funci onal idade básica do sistema. Também é imposs ível com partil haro sistema com outras Uls ou tipos de UI, quando a UI e o sistema estão entrelaçados.
3. Os três componentes são o mode lo, o modo de visuali zação e O controlador. 4. O padrão PA C e o Document/V iew Model são duas alternativas para o padrão MVC.
5. O modelo é a camada da tríade MVC que gerencia o comportamento básico e o estado do sistema. O controlador usa o modelo para instigar o comportamento do sistema. O modo de visualização usa o modelo para recuperar informações de estado para exi bição. O modelo também fomece um mecanismo de notificação de alteração. O modo de visualização e o controlador podem usar esse mecanismo para estar a par das mudanças de estado no modelo. 6. O modo de visua lização é o membro da tríade MVC responsável por apresentar o modelo para o usuário. 7. O controlador é responsáve l por interpretar os eventos gerados pelo usuári o. O controlador instiga o comportamento no modelo ou no modo de visual ização, em resposta a esses eventos.
8. Um sistema pode ter muitos mode los. Um modelo pode ter muitos modos de vi sualização diferentes. Um modo de vi suali zação pode ter um controlador e um controlador pode controlar apenas um modo de visua lização.
9. Ineficácias podem ser encontradas no modelo do modo de visua li7.ação c do controlador. Um modelo deve ev itar notificações de alteração de estado desnecessárias. Os modos de visualizaçi'lo e controladores devem colocar os dados cm cac he, quando possível. 10. O padrão MVC supõe um modelo estável e uma apresentaçi'lo variável. 11 . Um resumo muito detal hado da história e da moti vação por trás do padnl0 MVC está em "A ppl ical ions Programming in Smal ltalk-80(TM): I-Iow to use Model- View-Conlroller (MVC)", de Steve Burbeck, Ph .D. Você pode encontrar uma cópia no endereço http: //st-www.cs. ui uc.edu/users/sma rch/ st-docs/ mvc.html
Respostas
Então, qual é o objetivo dessa pergunta? Bem, a resposta proporciona a você uma perspectiva importante da moti vação por trás do pad rão MVC. Lendo a história, você também notará que o padrão MVC fo i desenvolvido inicialmente como parte da Smalltalk. Agora seu uso é encontrado em quase qualquer li nguagem. Isso traz um ponto importante: os padrões não são ut ilizações de linguagem - eles são padrões que funcionam em qualq uer linguagem com os recursos requisitados. A MVC não está relacionada com Java ou Smalltalk. Está relacionada a um projeto que transcende a linguagem de implementação.
Respostas dos exercícios I. A listagem 13.1 1 apresenta a nova classe Employee. LISTAGEM 13.11 Employee. j ava import java.util . ArrayLi st : 1mport java. ut 11 . Itera tor : publi C abstract c1ass Emp10yee { private private private private
String firs t_name : Str ing l ast_name ; double wage: ArrayList 11steners • new Arrayli st() :
publi C Employee(Str ing first_name,S t ring las t _name,doub l e wage) { thi s . first_name· first_name : this.last_name a las t_name : t hi s .wage • wage; }
pu bl iC double getWageO { re turn wage: }
publ ic vo i d setWage( double wage ) { this.wage • wage ; updateObservers() ; }
pub l ic String getFirstName() { r eturn fir s t_name ; }
publi c St ring getLastName() I
Apêndi ce A
LISTAGEM 13.11
Empl oyee.java (continuação)
return last_name ; }
publiC abstract double calculatePay() ; public abstract doub l e calcul ateBonus() ; publ i c vold prlntPaycheck(l ( Stri ng full_name • last_name + ", " + first_name; System.out.printl n( "Pay: " + fu ll name + " $ " + calculatePay() ); }
public vold reglster( Observe r o ) { listeners . add( o l ; o.update() ; }
pub l ic void deregister( Observer o ) ( listeners.remove( o ) ; }
private void updateObservers() ( Iterator i • l isteners . iterator() ; while( i.hasNext() ) ( Observer o • (Observer) i.nex t (); o.update(); }
} }
2. A Listagem 13. 12 apresenta a nova implementação de BankAccountController. LISTAGEM 13.12
BankAccountControl 1er .java
pub l ic class BankAccountController implements Ba nkActivity l istene r { private BankAccountView view ; private BankAccountMode l mode' ; public BankAccountCont roller( BankAccountView view , BankAccountModel model )
I this.view • view; this .modelo '" mode]; }
Respostas
531
LISTAGEM 13.12 BankAccou nt Controll er. java} (c ontinuação) publiC vold wlthdrawPerformed( BankAct l vityEvent e ) ( double amount • e.getAmount(); model.withdrawFunds( amount ); }
public void deposltPerfonmed( Ban kAc ti vityEvent e ) ( doubl e amoun t a e.getAmount() ; model .depositFunds( amount ); } }
ESIa versão de BankAccountControl l er ê muito mai s fácil de ler do que a origi nal; entretanto, o modo de vis ualização agora é muito mai s complexo.
Dia 14 Respostas do teste e dos exercícios Respostas do teste I. Erros de digitação, erros na lógica ou enganos bobos cometidos durante a codificação podem surgir. Os erros também podem res ultar da interação incorreta entre objetos ou de falhas no projeto ou na anál ise. 2. Um caso de teste é o bloco de construção dos testes. Cada forma de teste é constituída de casos de teste e cada caso de teste testa um aspecto do sistema . 3. Você pode basea r seus casos de teste em teste de caixa preta ou caixa branca. 4. Os testes de caixa branca são baseados na estrutura do código- fonte s ubjacente. Os testes de ca ixa branca são projetados para atingir 100% de cobertura do código testado. Os testes de caixa preta são baseados na especifi cação. Os lestes de ca ixa preta veri fi cam se o sistema se comporta conforme o esperado. 5. As quatro formas de teste são: teste de unidade, leste de inlegração, {este de ~·istellla e teste de regressão. 6. Um teste de uni dade é o dispositivo de teste de nível mais baixo. Um teste de unidade envia uma mensagem para um objeto e verifica se recebe o resultado esperado. Um teste de unidade só deve verificar um recurso por vez. 7. O leste de integração confirm a se os objetos interagem corretamente. O teste de sistema verifica se o sistema se comporta conforme definido nos casos de uso e se e le pode manipular uso imprevisto nonnal mente.
Apêndice A
8. Você não deve deixar as testes para o fim. Testarenq uanto você desenvolve o aj uda a encontrar erros imediatamente. Se você deixar os testes para o final , haverá mais erros e eles serão mais difíceis de rastrear e corrigir. Testar enquanto você desenvolve também toma mais faci l alterar seu cód igo e pode melhorar seu projeto. 9. A validação manual ou visual é propensa a erros. Você deve evitá-Ia o máximo possível. Em vez disso, você deve contar com um mecanismo automático para validação dos testes de unidade. 10. Uma estrutura define um modelo de domínio reut ilizável. Você pode usar as classes des-
se modelo corno base para seu apli cativo especí fi co. I I . Um objeto falsificado é um substituto simplista de um objelo real , que o ajuda a fazer o
teste de unidade de seus objetos. 12. Os objctos fal sificados permitem que você faça o teste de unidade de suas classes iso la-
damente. Eles também abrem possibilidades de teste que, de outra forma, seriam di ficei s ou impossíveis de fazer. 13. Um erro surge de uma fa lha ou de um defeito no sistema. Uma condição de erro, por outro lado, nãoé um erro, mas sim uma condição para a qual seu sistema deve estar preparado e deve manipular normalmente. 14. Ao escrever seu código, você pode garantir a qualidade através dos testes de unidade, do
tratamento correto de exceções e através de documentação correta.
Respostas dos exercícíos I. Cookstour dará idéias sobre o projeto e as noções por trás de JUnil.
2. A Li stagem 14.12 apresenta um poss ível teste de unidade. LISTAGEM 14.12 HourlyEmp l oyeeTesLja va
import juni t. framework .TestCase : i mport junit.framework.Assert ; public cl ass HourlyEmployeeTest extends TestCase { private Hou rlyEmp loyee emp: private static f i na l St r ing FI RST NAME private static fi nal String LAST NAME private statlc fi nal double WAGE protected void setUp() {
= = =
-FNAME" ·• -LNAME"·•
500 . 00 ;
Respostas
LISTAGEM 14.12
HourlyEmpl oyeeTest . java (con t inuação)
emp • new Hou rl yEmp loyee( FIRS T_NAME. l AST NAME . WAGE ); }
publiC yoid test_calculatePay() ( emp .addHours( 10 ); doubl e expected • WAGE * 10; assertTrue( "incorrect pay calculation" . emp.calculatePay() • • expected ); }
publi C HourlyEmployeeTest( String name ) ( supe re name ); } }
Dia 15 Respostas do teste e dos exercícios Respostas do teste I. PlayerListener é um exemplo do padrão observável.
Consol e é singlcton. Ele implementa o padrão si ngleton. Rank e Sui t implementam o padrão Typesa fe Enum. 2. 81 ackjackDea 1er trata de HumanPlayer de forma polimórfica como um P1 ayer. Você p0deri a criar jogadores não· humanos e B1 ackjackDea1 er saberia como jogar virue·e· um com eles. 3. Pl aye r!B1ackjackDealer/HumanP1ayer é um exemplo de hierarquia de herança. 4. Deck encapsul a comp letamente os objelos Card que contém. Ele não fornece quai sq uer métodos de obtenção ou configuração. Em vez disso, Deck adiciona seus objetos Card em objetos Deckpile. 5. B1 ackjackDea 1er e HumanP1 ayer atuam de fonna polimórfi ca., fornecendo suas próprias ver· 5ÕeS personal izadas do método hi t(). Quando o método p1 ay() chamar hi t() , o comporta· mento do método p1ay() variará de acordo com a implementação subjacente de hi te).
Respostas dos exercícios I. Sem resposta. 2. Sem resposta.
Apêndice A
Dia 16 Respostas do teste e dos exercícios Respostas do teste I. As estruturas cond icionais são perigosas quando retiram responsabilidade de um objeto e a colocam em outro lugar. O comportamento pertence ao objeto e não é distribuído por todo o programa . Uma lógica distri buída o obriga a repetir lógica por todo o seu programa, em vez de tê- la em apenas um lugar. As est ruturas condicionais também são perigosas porque tornam mais difíc il testar um objeto e cobrir todas as comb inações de caminhos através do objeto. 2. Antcrionncnte, você viu que podia usar polimorfismo para remover estruturas condicionais. Hoje, você vi u que pode usar uma combinação de polimorfi smo e estado para remover estruturas cond icionai s. O estado é um modo excelente de im plementar regras. 3. A versão anterior de Hand exig ia que você mesmo comparasse os objetos Card de Hand para veri ficar se dois objetos Hand são iguais ou se um o bjeto Hand é maior do que out ro. Agora, Hand faz essa verificação para você, sem comprometer seu estado interno. Hand fa z out ra coisa para se encapsular. Agora, Hand infonna aos receptores de mudanças de estado. Como Hand coloca prontamente suas infornlaçôes de estado nos receptores, nâo há moti vo para que um objeto interessado faça uma consulta em Hand para conhecer seu estado. 4. Hand e Handlistene r implementam pad rão observador. 5.
Sem resposta.
Respostas dos exercícios I. Sem resposta. 2. O segredo desse problema é perceber que todos os métodos levam as mes mas açôes para cima, até o ponto onde uma chamada é feita em Pl ayerl i s t encr. A solução é empacotar essa chamada em um objeto. Considere o método noti fyl i stener() a seguir: protected void notifylisteners( Not"ifyHelper helper ) { I terator i = listeners. i terator() ; while( LhasNext() ) { Playe rl istener pl = ( Playe r listener )i.next() ; helper.notifylistener( pl l; } }
Respostas
Note que esse método é exalamenle igua l ao ant igo not i fyChangedO ou not i fyBusted() , exceto quanto a uma diferença. Em vez de chamar um método diretamente em PI ayerl i stener, o método noti fyl i steners() delega a chamada para um objeto fiot i fyHel per. A Listagem 16.17 apresenta NotifyHe l per. LISTAGEM
16.17
O
estado de espera da banca personalizado
protected i nterface Not i fyHelper I publ ic void not ifylistener( Playerlistener pI )i J
A interface NotifyHelper define um método: notifylistenerO. Os implementadores decidirão qual método vão chamar em Playerlistener. Ao todo, você precisará defini r sete implementadores de Not 1fyHe I per, uma im plementação para cada método na interface PI ayerl i stener. A Listagem 16.18 apresenta essas sete implementações. LISTAGEM
16.18 As impl ement açõe s de Not HyHe lper .
protected cl ass NotifyBusted imp leme nts Not ifyHe l pe r { public void notifyl i stener( Pl ayerl istener pI ) { pl . playerBusted( Pl ayer. th is l i )
J protected cla ss NotifyBlackj ack imp l ements NotifyHe lper I publi c void notifyl i stener( Playerlistener pI ) { pl .playerBla ckj ack( Player . this )i } J
protected cl ass Noti fyWon implements Not i fyHelper ( publ ic vo i d notifylistener( Playerl istener pI ) ( pl.playerWon( Pl ayer. thi s ) i ) J
protected cl ass Not1fylost implements NotifyHelper ( publi c vo i d notify l i stener( PlayerListener pI ) I pl.playerlost( Player.thi s )i ) J
protected cla ss NotifyCh anged imp lements NotifyHelper I publ ic vo i d notifylistener( Playerlistener pI ) I pl . playerChanged( Player.this ) ; ) J
protected cla ss NotifyStanding imp lements NotifyHelper I public void not ifyli stener( Player lis tener pI ) ( pl.playerStand i ng( Playe r. th i s ); )
J protec ted cl ass NotifyStandoff implemen ts Noti fyHe l per I
Apêndice A
LISTAGEM 16 .18 As implementações de NotifyHe 1pe r .
(continuoçào)
pub1iC vold notifyListener( PlayerListener pI ) ( p1.playerStandoff( P1ayer . this ); I
,
Agora, em vez de chamar not i fyCh anged () ou not i fyB1 ac kj ac k(), você chamaria not i fylisteners( new NotifyChanged() ) ou notifyListeners( new NotifyBlack jack() ). Se você acha que essa é uma boa solução ou não, é uma questão de gosto pessoal. Emretan to, e la remove os métodos notifyXXX redundantes.
Dia 17 Respostas do teste e dos exercícios Respostas do teste 1. Tomar abstrato um método protegido é uma boa maneira de estabelecer um protocolo de herança. 2. Após a análise e o projeto de hoje, uma nova hierarquia PI ayer foi descoberta. Como os requisitos se apresentaram através dos casos de uso, a necessidade de uma nova hierarquia de herança se apresentou. 3. A programação por especulação raramente funciona. As hierarquias que você precisará para mode lar corretamente um domín ioabslralo se apresentarão após você ter traba lhado com um domínio por algum tempo. Se você lentar abstrair um domínio sem trabalhar com esse dom ín io, es tará supondo uma solução. 4. Refazendo a hierarquia, você ficou com um modelo que representa mais detalhadamente o j ogo vi nte-e-um . O código também é mais fác il de entender. Colocar responsabi lidade ext ra na classe base Player teria resultado em um código difici l de entender.
Respostas dos exercícios I. Sem resposta. 2. Dobro é apenas outro estado de Betti ngPI ayer. Você sabe q ue ele precisa ser um estado separado, porque Pl ayer deve reagir de uma fonna d ifere nte ao evento handPlayable. Em vez de permanecer no estado Jogo, PI ayer deve fazer a transição para o estado Para-
do. A Figura A. 7 ilustra o novo diagrama de estado para um objeto Bett ingPl aye r que pode dobrar.
Respostas
537
FIGURA A.7 O diagrama de estadQ de
BettiogPlayer (lluali: lIdo. • mio 6 ,,1!>!&+Um (mio In ld.1 • dobto(
/ ' d' ~r. )011" IÜ ~r.l<>g.r
CClm' 11'110
com.m'" ( m'CI~2 1 (
(m6C1~2 1 (
• mio .1t01lroU (11'160>21)
• mto •• Iourou (mio>21(
Você também precisa alterar o estado Jogo para que ele possa faze r a transição para o estado Dobro. A Listagem 17.6 apresenta os novos estados de Bett i ngPl ayer.
LISTAGEM 17.6
Ooub 1i ngDown e Jogo
pri vate clas s OoublingOown implements PlayerState { publlc void handChanged() { notifyChanged() ;
I public vo i d handPlayable() { setCurrentState( getStandingS t at e() ) ; notifyStand1 ng() ;
I public vo i d handBlackjack( ) { II imposs ivel no estado de dobro
I pub l i c vo i d handBusted() { setCurrentState( getBustedState() ) ; not ifyBus ted () ;
I pub l ic void execute( Deale r dea l er ) { bank .doub l eOown(); dea l er.hit( BettingPlayer . t hi s ) ; getCurrentState().execute( dea l er );
I
I 538
Apêndice A
LISTAGEM 17.6
Doub llngDown e Jogo (continuação)
I private class Be tterPl ayi ng i mplements PlayerState ( public voi d handChanged{) ( not ifyC hanged();
I public void handPlayable(} ( II pode ignorar no estado de jogo
I public v01d handBlackjack() ( II imposs fvel no estado de jogo
I publi C void handBusted() { setCu rrent State ( getBus tedS ta te () ) ; not ifyBusted () ;
I publ ic void execute( De aler dealer ) ( if{ getHand().canDoubleDown() &&doub leDown() ) ( se tCurrent State( getDoublingDownState() ); getCurrentState().execute( deale r ); return;
I if( hit()
I (
dealer.hit{ Bett i ngPlayer.this ) ; I else ( setCurrentS tate ( getStandingState() ); notifyStanding() ; I getCurrentState().execute( dealer ); II transição I
I Você também prec isará atualizar HumanPl ayer para que ofereça a opção de dobrar. A Listagem 17.7 apresenta a classe HumanPlayer atualizada. LISTAGEM 17.7
Huma nPlayer .java
publiC cla ss HumanPlayer extends BettingPlayer { private private private pri vate private
f inal fi na 1 fi na1 fi nal fi nal
static static static stat ic stat i c
String St r ing St r i ng St r i ng String
= MH"· HIT • STAND - ·S"· PlAY_HSG '" "[H]it or (S]tay" ; BET_MSG = MPlace Bet: [10] [50] or [100]" ; OD_MSG = MDouble Down? [V] es [N]o";
.
Respostas
LISTAGEM 17.7
private priva te private private pri vate pr ivate
HumanPlayer.java (continuação)
fi na 1 final f i nal fi na I fi na I fi nal
static static stati c statlc static stati c
String String String String Str i ng Str ; ng
BEl_lO = MIO"; BEl_50 = MSO"; BEI_I00 = MIOO M; NO " "NN; YES = "Y"; OEFAULI = "invalid";
publ ic HumanPlayer( String name , Hand hand. Bank bank ) { supere name , hand. bank };
I protected boolean hit() ( while( true ) ( Console.INSIANCE . printMessage( PLAY_MSG ) : String response • Conso l e.INSTANCE.readInput( DEFAUlI ); if( response . equals IgnoreCase( HII ) ) { return true: lelse if( response.equalsIgnoreCase( SIAND ) ) I return fal se ;
I
II se chegarmos até aqui . II sign ifica tiva
faz
um laço até obtermos entrada
I I protected boolean doubleOown() I while( true ) I Conso le.lNSIANCE.printMessage( OO_MSG ) ; String response • Console.INSIANCE.readInput( OEFAULI ,; if( re sponse .equal sI gnoreCase( NO) ) { return fal se; } el se if( response.equalsIgnoreCase( YES ) ) I return true ; I II se chegarmos até aqui, faz um laço até obtermos entrada II significativa I
I protected void betO ( while( true ) ( Conso le . INSTANCE.printMessage( BET_MSG ): Str i ng response " Conso le. INSTANCE . read Input( DEFAULT l: if( response.equals( BEl_lO) ) I getBank().placelOBet():
I 540
Apêndice A
LISTAGEM 17.7
HumanPlayer.java (continuação) return ;
I if( response.equal s ( BEl_50) ) ( getBank().place50Bet() ; return;
I if( respon se,equa ls( 8EI_IOO) ) { getBank ( ).place l OOSet() : return ;
I
/1 /1
se chegarmos atê aqui, faz um l aço atê ob te rmos entrada s i gnificativa
I I I
Dia 18 Respostas do teste e dos exercícios Respostas do teste 1. Para introduzir um Vcard, você pode si mplesmente faze r lima subclasse de Card oPara colocá- Ia no jogo, você pode faze r uma subclasse de Oeck e, em segu ida, sobrepor bui 1dCards para que a s ubclasse crie objetos VCa rd, em vez de objetos Cardo 2. Originalmente, o método bu i 1dCa rds de Deck era privado. Quando veri ficamos que uma subclasse prec isava sobrepor o comportamento de bui 1dCards, o tornamos protegido.
Respostas dos exercícios I. Sem resposta. 2. O projeto e im plementação inici ais que fizemos para o Exercício 2 do Capítulo 17 ainda são vál idos. Dobrar é ape nas outro estado em Sett i ngPl ayer. Você deve começar escrevendo o código para este exercício, adicionando o novo estado Ooubl i ngOown em Bet tlngPlayer. Você também precisará fazer as mesmas alterações fe itas no Capítu lo 17, nas classes Bank e Hand. Quando fi zer essas alterações, você precisará atualizar GUI Pl ayer, assim corno Opt i onView e Opt ionViewCont roll er . Antes de fazer as alterações exigidas nas classes de modo de visualização e de con trolador, examinaremoS as alterações ex igidas em SettingP layer, Hand e Bank. A Listagem 18.13 destaca as alterações que foram feitas na classe Hand.
Respostas
LISTAGEM
541
18.13 Destaques das alterações feitas em Hand
publ i c boolean canDoubleOown() { return ( ca rds. slze() ··2 );
,
o novo método canOoubleDown penn ite que Be tt i ngPl ayer veri fi que o objeto Hand para ver se o jogador pode dobrar. Você também prec isa adicionar um novo método doubl eDown na classc Bank. A Listagem 18. 14 apresenta esse novo método. LISTAGEM
18.14 O novo método doubleDown
pub l ic void doubl eDownO ( placeBet( bet ) ; bet • bet .. 2;
,
o método doubleOown dobra a aposta corrente. Para poder adicionar a fu nção de dobrar, você precisa adici onar um novo estado em Bet tingPl ayer. Você sabe que precisa de um novo estado porque Bett 1ngPl ayer tem de tratar do evento handPlayable, especial mente ao se dobrar. Normalmente, um evento handPl ayable sign ifica que o jogador pode receber mai s canas novam ente, se quiser. Ao dobrar, Ojogador deve parar imediatamente, após a operação (caso ele não estoure). A Listagem 18.15 apresenta o novo estado de DoubleDown. LISTAGEM
18.15 The New Doub l eDown Stat e
private class DoubllngDown implements PlayerState ( public void handChanged() { noti fyChanged( );
,
publ ic vo i d handPlayable () { setCurrentState( getStandingSta t e() ) ; notify Standi ng();
,
pub l ic void handB lac kjack() { II impossfve l no estado de dobro
,
pub l ic void handBus t ed() { setCurrentState( get Bust edSta t e() }; not i fyBusted();
,
publi c void execute( Dea l er dea l er ) {
Apêndice A
LISTAGEM 18.15 lhe New OoubleOown State (continuação) bank.doubleOown(); dealer.hit( BettingPlayer.this ); getCurrentState().execute( dealer ) i J
J Quando executado, esse novo estado dirá ao objelo Bank para que duplique a aposta, pedi rá à banca para que distribua mais cartas para o jogador e, então, fará a tran sição para o próximo estado. O estado seguinte será parado ou estouro, dependendo do evento enviado para o estado por Hand. Para obter o estado Doubl i ngDown (Dobro), você precisará fazer algumas alterações no estado Jogo. A Li stage m 18.16 apresenta o novo estado BetterPlaying. LISTAGEM 18.16 O novo estado BetterPl ayi ng private class BetterPlaylng implements PlayerState ( public void handChanged() I notifyChanged();
J
pub li C vo ld handPlayable() I II pode ignorar no estado de jogo
J
publi c vold handBlack jack() ( II impossfvel no es tado de jogo J
public voi d hand8usted() I setCurrentState( getBustedS tate() ) ; notHyBustedO; J
public void execute( Dealer dealer ) ( if( getHand() . canDoubleDown() &&doub l eDown() ) ( setCurrentS tate( getDoublingDownState() ); getCurrentState().execute( dealer ); returni J if( hit()
J (
dealer.hit( BettlngPlayer.this ,; ) else I setCurrentState( getStandingState() ) i notifyStanding();
J
getCurrenlState().execute( dealer ) ; II transição J
J
Respostas
Quando executado, o estado BetterPl aying primeiro verifica se o jogador pode dobrar. Se assim for, o estado chamará o método doub l eOown de Betti ngPl ayer (você precisará adicionar um método abstrato doub leOown em Betti ngPl ayer). Seo método indicar que o jogador gostaria de dobrar, o estado BetterPl ayi ng configurará o estado corrente como Ooub 1i ngDown e fará a transição para ele. Se o jogador não quiser dobrar, Oestado continuará ejogará nonnalmente. Veja todas as alteraçõcs no código-fonte. Existem algumas outras pequenas alterações, como a adição de um método getOoubl eDownState em BettingPl ayer. Esse método efetivamente adiciona a possibilidade de dobro no sistema. Agora, você precisa al ualizar GU I PI ayer, Opt i onView e Opti onVi ewControll er. A boa nova é que você não precisa fazer quaisquer alterações nos estados de GU I PI ayer. Esses estados não devem fazer nada, pois eles prec isam esperar até que o jogador humano cl iq ue em um botão. Você precisa implementar o métod o doubleOown. A Li stagem 18.17 apresenta o método. LISTAGEM
18.17 O método doubleDown
protected boolean doubleOown() I setCurrentState( getOoubl i ngOownState() ): getCurrentState().execute( dealer ): return true; }
o boião da GUI pode chamar esse método, caso o usuário decida dobrar. O método configura o estado corrente como o dobro e, em seguida, fa z a transição para ele. O estado manipula o restante. Todas as alterações restantes prec isam entrar em Opt i onVi ew c Opt i onVi ewControll er. Você precisa principa lmente adicionar um botão de dobro no modo de visualização e garantir que ele seja ativado pe lo controlador imediatamente após a aposta e desativado assim que o jogador pressionar esse botão, o balão de parada ou o de receber mai s cartas. Não se preocupe mui to, se você não entender totalmente o código da GU !. O importante é que você entenda como a operação de dobro é adicionada no sistema e as idé ias básicas por trás do modo de visualização e do controlador.
Dia 19 Respostas do teste e dos exercícios Respostas do teste 1. As três camadas são: aprese,,/ação, abstração e eDil/role.
Apêndice A
2. A apresentação é responsável por exibir a abstração, assim como por responder à interação do usuário. A abslraçãoeseme lhante ao modelo na MVC. A abstração representa o sistema básico.
O controle é responsáve l por pegar as diversas apresentações ecombiná-Ias em um modo de visualização. 3. Você pode usar herança para faze r uma subclasse de cada uma das classes de sistema que exigirão uma apresentação. Desse modo, você não enxerta uma classe de apresentação diretamente na c lasse de sistema. Em vez disso, você pode adicionar a classe de apresentação na s ubc lasse. Usa ndo herança dessa maneira, você pode fornecer vários modos de visuali zação do mesmo sistema. Sempre que você precisar de um modo de visuali zação diferente, bastará fazer uma s ubclasse das classes que preci sa ex ibir e fazê- Ias criar uma nova apresentação. Quando você precisar reun ir todas as classes como um ap licat ivo, bastará garant ir a instanciação das subclasses corretas. ,
4. E, melhor usaro PAC em um sistema estável com requisitos de interface bem defin idos. As vezes, a estratégia da herança pode fa lhar para projetas mai s complicados. Q uando a herança fa lhar, você terá de enxertar a apresentaçãodiretamente na classe de sistema. Tal eventualidade lorna d incil servir muitos modos de visualização diferentes. 5. Você consegu iu manter duas Uls porque de ixou a estrutura de observador intacta. Nada o obriga a remover a estrutura, apenas porque você utiliza PAC. Na verdade, Sett ingView e Oea l erView usam o mecanismo Pl ayerl i stener para fi car a par das alterações no jogador e na banca. 6. O padrão factory foi usado para garant ir que as instânc ias de classe corretas fosse m usadas juntas. Em particular, você deve tomar ocuidado de usar um objeto VOeck quando utilizar as classes que criam uma apresentação.
Respostas dos exercícios I. Sem resposta. 2. O projeto e a implementação ini ciais que você realizou para o Exercício 2 do Capitulo 17 ainda são vá lidos. Dobro é apenas outro estado em Sett i ngPl ayer. Você deve começar a escrever o cód igo deste exerc ici o, adicionando o novo estado Ooubl i ngOown em Bett ingPl ayer. Você também precisará fazer as mesmas alteraçõcs que fez no Capitulo 17, nas classes Bank e Hand. Quando você fize r essas alterações, precisará alualizar GUIPlayer e sua classe de apresentação. Antes de faze r as alterações exigidas nas classes da GU I, vamos examinar as alterações exigidas em BettingPl ayer, Hand e Bank.
Respostas
A Listagem 19.8 destaca as alterações que foram feitas na classe Hand. LISTAGEM 19.8
Destaques das alterações feitas em Hand
publlc boolean canDoubleDownO { 2 }; return ( cards.size() ZE
I
o novo método canDoubleDown permite que Bett; ngPl ayer verifique o objeto Hand para ver se o jogador pode dobrar. Você também precisa adicionar um novo método doubleDown na classe Bank. A Listagem 19.9 apresenta esse novo método. LISTAGEM 19.9
O novo método doubl eOown
pub l ic void doubl eDown() ( placeBet( bet ); bet • bet ... 2;
I
o método doubleDown dobra a aposta corrente. Para adicionar a aposta em dobro, você precisa adicionar um novo estado em Bett; ngPI ayer. Você sabe que precisa de um novo estado, porque Bett; ngPlayer tem de tratar do evento handPI ayabl e de uma maneira especial quando se dobra. Nonnalmente, um even· to handPlayable significa que o jogador pode receber mais cartas novamente, se qu iser. Ao dobrar, Ojogador deve parar imediatamente após tê-lo fei to (caso ele não estoure). A Listagem 19.10 apresenta o novo estado Doub 1eOown. LISTAGEM 19.10 O novo estado DoubleDown private class DoubllngDown 1mplements PlayerState { public void handChanged() { notifyChanged();
I public void handPlayable() { setCurrentState( getStandingS tate() ) ; notlfyStanding();
I public void handBlack jac k() { II 1mpossfvel no estado de dobro
I publ ic void handBustedO { setCurrentState( getBustedState() l ;
Apêndice A
LISTAGEM 19.10 O novo estado OoubleOown (continuação ) not1 fyBustedO i }
public voi d execute ( Dea l er dea ler ) { bank .doubleDown()i dealer.hit( BettingPlayer.this ) i getCurrentState() . exec ute( dea ler )i }
}
Quando executado, esse novo estado diz a Bank para que duplique a aposta, pede à banca para que di stribua mai s cartas para o jogador e, em seguida , faz a transição para o próximo estado. O estado seguinte será parado ou vai estourar, dependendo do evento que for envi ado para o estado por Hand. Para obter o estado Doub 1i ngDown, você precisará fazer algumas alterações no estado Jogo. A Li stagem 19. 11 apresenta o novo estado BetterPlayi ng. LISTAGEM 19.11 O novo estado Jogo private cla ss BetterPlaying implements PlayerState ( publi c vo id handChanged{) { noti fyC hanged()i }
public voi d handP layab le( ) { II pode ignora r no estado de jogo }
public voi d handBl ackjack() I II impossfvel no estado de j ogo }
publi c vo i d handBu sted() { setCurrent State( ge tBustedStateO }; not ifyBu sted () i }
publi c yo i d executeI De aler dealer ) { if( getHand (), canDoubleDown() && doubleDown() ) ( setCurrentState( ge tDoubl i ngDownState() )i getCurrentState().execute( dealer l i returni } if( hitO } I
dealer .h it( Betti ngPl aye r . th i s li ) else ( se t Curren tSta te( getS tandingState () ) i notifyStanding() ;
Respostas
LISTAGEM
547
19.11 O novo estado Jogo (continuação) }
getCurrentState().execute( dealer ); II trans ição }
}
Quando executado, o estado BetterPl aying primeiro verifica se o jogador pode dobrar. Se assim for, o estado chamará o método doub 1eDown de Betti ngPlayer (você precisará adicionar um método abst rato doubl eDown em Bett i ngPl ayer). Se o método indicar que o jogador gostaria de dobrar, o estado BetterPl ayi n9 configurará o estado corrente como Doub 1i ngDown e depoi s fará a transição para ele. Se o jogador não quiser dobrar, o estado continuará e jogará normalmente . Veja todas as alterações no cód igo- fontc; cx istem algumas outras pequenas alterações, como a adição de um método getooubleOownState em BettingPl ayer. Você efeti vamente adicionou a capaci dade de dobrar no sistema. Agora, você precisa atuali zarGU IPl ayer e sua classe de apresentação, para que possa suportar a aposta em dobro.
A boa nova é que você não precisa fazer quaisquer alterações nos estados de GU IP l ayer. Esses estados não faze m nada, pois eles precisam esperar até que o jogador humano clique em um botão. Você precisa implementar o método doub 1eOown. A Listagem 19.12 apresenta o metodo. LISTAGEM
19.12 O método doub 1eOown
protected boolean doubleDown() f setCurrentState( getOoublingOownState() ) ; getCurrentState() .execute( dealer ) ; return true; }
o botão da GUI pode chamar esse método, caso o usuário decida dobrar. O método confi gura o estado corrente como dobro e, em seguid a, faz a transição para ele. O estado trata do resto. Todas as alterações restantes precisam entrar na classe de apresentação: GUIView. Você precisa adicionar um botão de dobro, cert ificar-se de que ele seja ativado após a aposta e desalivado assim que o jogador pressionar esse botão, o botão de parar ou de receber mais cartas.
Apêndice A
Não se preocupe mu ito, caso você não entenda 100almente o código da GUI. O importante é que você entenda como a capacidade de dobrar é ad icionada no sistema.
Dia 20 Respostas do teste e dos exercícios Respostas do teste I. Através dos relacionamentos com capacidade de substituição e do pol imorfismo, você pode criar qualquer subclasse de Bett i ngPl ayer que queira e adicioná-la ao jogo.
O jogo não sabe a d iferença entre um jogador humano e um jogador automático não-humano. Como res ultado, você pode configurar o jogo sem nenhum jogador humano.lmp lementando o método hi t nas subclasses de Pl ayer, e las poderão j ogar sem intervenção humana. 2. Você nunca deve seguir a estratégia de OneHHPlayer.
Respostas dos exercícios I. Sem resposta.
2. Suas soluções podem variar. As listagens 20.8 e 20.9 apresentam a implementação de Knowl edgeablePlayer e OptimalPlayer, res pecti vamente. LISTAGEM
20.8
Knowl edgea b 1ePl ayer .java
publ i c class KnowledgeablePlayer extends BettingPl ayer
I
publiC KnowledgeablePlayer(Stri ng name,Hand ha nd ,Bank bank} super( name , hand , ba nk };
I
I publi c boolean hit ( Dealer deale r ) { i nt total · get Hand O . tota 1 () ;
Card card • dealer.getUpCard();
II
nunca recebe mais cartas , não importa qua l, se total> 15 if( total> 15 ) { return fal se ;
I
II
sempre recebe mais ca rta s para 11 e menos if( tolal < z 11 ) I return l r ue ;
I
Respostas
LISTAGEM 20.8
Knowl edgeab 1ePl ayer .java (continuação)
II 1sso deixa ll. 12. 13. 14 II baseia a decis~o na banca i f( card.getRank().getRank() return true;
>
7 ) (
J
return false; J
publi C yo i d bet() 1 getBankO .place l OBet(); J J
LISTAGEM 20.9
OptimalPlayer.java
publiC cla ss Op timalPlayer extends BettingPlayer ( publ ic Opt imalPlaye r( Stri ng name, Hand hand , Bank bank ) ( supere name , hand, bank ); J
publi c boolean hit ( Oeal er dealer ) { i nt total · getHandO.totalO; Card card • dealer.getUpCard(}; i f( total >- 17 ) { return fal se; J
;t( total· · 16 ) {
if( card.getRank(} •• Rank.SEVEN I I card.getRankO == Rank.EIGHT II card . getRank() •• Rank.NINE ) { return true; I else ( return fa 1se; J
J
; f( total·· 13 II total == 14 II total if( card. ge tRank( ) •• Rank.TWO II card.getRank() •• Rank.THREE I I
==
15 ) (
I 550
Apêndice A
LISTAGEM
20.9
Opt imal Player . java (continuação)
card.ge tRank () == Rank.FOUR II card .getRank() •• Rank.FIVE I I card .getRank{) •• Rank.S IX ) { return false; } else { return true; } } if( total·· 12 ) ( if( card. getRank() ••
'ao'. 'OU, II Rank . FIVE I I
card.get Rank(} == card.getRank{) •• Rank . SIX ) { return false ; } el se { return truei } }
return true; }
publiC void bet() I getBank().placel0Bet{); }
}
Então, corno esses jogadores se companam? Em nossos lestes, Knowl edgeablePlayer e OptimalPlayer executam melhor do que SmartPl ayer, apresentado no capítulo. Comparando um com outro, Opt lmal PI ayer executa me lhor. Contudo, com o passar do tempo, ambos ainda perdem dinheiro, apenas que mu ito lentamente. 3. Suas soluções podem variar. As listagens 20.10 e 20 .1 1 apresentam a implementação de Knowl edgeabl ePl ayer e Optima 1PI ayer, respectivamente. LISTAGEM
20.10 Knowl edgeab 1ePl ayer .java
public cla ss KnowledgeablePlayer extends BettingPlayer I public KnowledgeablePlayer(String name , Hand hand, Bank bank) ( super( name . hand. bank ); }
Respostas
LISTAGEM 20 .10
Knowl edgeab 1ePl ayer .java (continuação)
publiC boolean doubl eDown( Oealer d ) { i nt total· getHand (). to ta 1(): if( total·· 10 Il total .... 11 ) ( return t rue: J
return false : J
public boolean hit( Oealer d ) ( int total " getHand() . total () ; Card c " d.getUpCard():
II nunca recebe mais cartas , não importa qual , se total> 15 if( tota l> 15 ) { return fal se : J
II sempre recebe mais ca rt as para 11 e menos if( total <" II ) {
return t rue:
J
II isso deixa 11, 12. 13. 14 II baseia a decisao na banca if( c.getRankO .getRank() > 7 ) (
return true; J
return fal se : J
publi C yo id bet() { getBank() . place10Bet(): J
J
LISTAGEM 20.11
OptimalPlayer. java
publ i C class Opt imalPlayer extends BettingPl aye r ( public OptimalPlaye r ( String name , Hand hand. Bank bank ) { super ( name, hand, bank ) ;
551
Apêndi ce A
LISTAGEM 20.11 OptimalPlayer . java (continuação) }
publ i c boolean doubleOown( Oealer d ) I int total = getHandO.tota l O; Card c • d.getUpCard(); if( total · · 11 ) I return true: }
if( total·· 10 ) { if( c.getRankO.getRankO != Ra nk.TEN.getRankO && c.getRank() ! = Rank.ACE ) I return true; }
retu r n fa I se: } if( total --9 ) I if ( c. getRank O ,... Rank. TWO c.getRankO •• Rank . THREE c.getRankO •• Rank.FOUR
II II II II
c. getRank O •• Rank . FIVE c. getRank O •• Rank . SIX ) { return true: }
retu rn fa l se; }
return false : }
public boolean hit( Dealer d ) { i nt to ta 1 .. getHand () . tota I O ;
Card c .. d.getUpCard() : if( tota l >= 17 ) {
return ta I se : }
if( total .. 16 ) { if( c.getRank() .... Rank.SEVEN J J
c.getRank() == Rank.EIGHT II c .getRank() == Rank.NINE ) { return t rue : } e l se { retu rn fa l se; }
Respostas LISTAGEM 20.11 Opt imal Player . java (continuação) }
if( total ·· 13 Il total "· 14 II total if( c .ge t Rank () •• Rank.TWO I I c.getRankO == Rank. THREE I I c.getRank() == Rank.FOUR I I c .getRank O •• Rank.FIVE I I c .getRank O •• Rank.SIX ) { return false; } else { return true;
==
15 ) {
}
} if( tota l·" 12 ) { if ( c. getRank O
Rank . FOUR II c.ge tRankO •• Rank.FIVE II c.ge tRank() •• Rank.SIX ) { return false; } else I return true; u
} }
return true; }
public voi d betO I get8ank().placeI08et(); }
}
Em nossos testes, Knowl edgeabl ePI ayer e Opt imal PI ayer executam melhor do que as versões do Exercício 2. Contudo, com o passar do tempo, ambos ainda perdem dinheiro - apenas muito lentamente.
Dia 21 Respostas do teste e dos exercícios Respostas do teste I. Você pode cu idar do problema da chamada de método recursiva colocando cada jogador em lima linha de execução. Quando você chamar Thread.s t art, a chamada retornará imediatamente, ao contrário de um método normal. Como o método reloma imed iatamente, a pilha de chamada de métodos pode desenrolar e retomar.
Apêndi ce A
Respostas dos exercícios 1. Sem resposta .
2. As respostas dependerão dos interesses pessoais. O Apêndice D, "B ibliografia se lecionada", apresenta unta lista de recursos excelente, na q ual você pode basear a cont in uação de seus estudos.
ApÊNDICE
• •
Resumo do Java
o Java Developer's Kit: J2SE 1.3 SDK o JDK (Java Developcr's Kit) da Sun Microsystems fornece o ambiente para todo o desenvolvi-
mento Java. Com o passar dos anos, a Sun mudou o nome do kit de desenvolvimento de JDK para Java 2 Standard Edition (J2SE) Software Developmenl Kil (SDK; entretanto, os objetivosdas ferramentas e das bibliotecas permanecem os mesmos - aj udar os desenvolvedores em seus esforços para escrever soRware de qual idade, independente da plataforma, c orientado a objetos).
Muitos ambientes de desenvolvimento integrados (IDEs) populares incorporam o SDK , além de um editor e depurador poderoso. O Forte da Sun e o J Builder da Borland fornecem gratuitamente versões mais simples do IDE; entretanto, para o objetivo desta discuss~o, a abrangência será limitada ao uso do SDK com um editor de textos como o TextPad, NOlepad ou vi. A Sun fornece o J2SE SDK para várias plataformas: • Windows NT, 2000, 95, 98, ME • Sun Solaris • Linux Você pode obter o SDK para outras platafonnas, como HP ou AIX, com o forneced or da plataforma apropriada. O J2SE SDK para Windows será utilizado como exemplo. A instalação e confi guração em outras platafonnas vai variar pouco em relação aos procedimentos di scut idos a • segUIr.
Apêndice B
Os desenvolvedores podem fazer download do J2SE SDK no si te Web J2SE da Java50ft, no endereço javêI . sun.comj j2sej 1.3. Siga os links apropriados para fazer download do J2SE 1.3 SDK. Além do J2SE SDK, você deve faze r download da documentação da APl J2SE. Embora não seja exigida. a documentação da APl é tremendamente útil. A documentação da APl J2$E fornece documentação detal hada de atributo, método e classe que até os desenvolvedores de Java altamente experientes considerarão úteis.
Configuração do ambiente de desenvolvimento A JavaSoft empacota o Windows J25E 1.3 SDK em um pacote InstallShie ld . Quando você fizer download do arquivo, execute-o e percorra as caixas de diálogo para instalar o SDK no diretório de destino apropriado. (O diretório de destino padrão é C: \jdkl. 3.) Instale todos os componentes, quando so licitado. Isso exigirá cerca de 54MB de espaço no disco rígido. Quando term inar, você deverá ver a seguinte estrutura de di retório em seu diretório de destino de instalação: C,\jdk1.3
\bi n \demo \include \include-old \j re \ll b
o pacote de instalação apenas distribui OSDK nos diretórios apropriados. Para in iciar o desenvolvi mento, você deve confi gurar as seguintes variáveis de ambiente. •
JAVA HOME
•
PATH
•
CLASSPATH
Primeiro, con fi gure JAVA HOME com o diretório instalado apropriado. Por exemplo, você executaria o segu inte na linha de comando: set JAVA_HOME z c:\jdk l .3 Em seguida, configure o PATH: set PATH=%PATH%;%JAVA_HOM E%\bin Finalmente, configure o CLASSPATH. O ClASSPATH infornlará o compilador e a máquina vi rtual onde deve procurar os arquivos de classe compilados. Em geral, esse caminho é o mesmo da raiz da árvore-fonte. Escolha c : \projects\src\java\ cla sses. Assim, você configurará o CLASSPATH: set ClASSPATH -c: \ projects \s rc \java\c lasses A Figura B. I demonstra as configurações de ambiente que acabamos de descrever.
Aesumo do Java
557
FIGURA B.1
COI/figural/do o ambiellfe.
A maior parte dos out ros fornecedores empacotará suas bibliotecas na forma de arqu ivos jar ou zip. Para usar essas bi bliotecas, você deve anexar a localização do arquivo jar no CLASSPATH. Por exemplo, para usar c : \projects\ 1i b\mycl asses . jar, você deve executar o seguinte: set
CLASSPATH ~%CLASSPAT H% ; c :\projects\lib\myclasses.jar
Você deve anexar cada arquivo jar em CLASSPATH, antes de usar.
Panorama das ferramentas do SDK Além de forne<:er as bibliote<:as Java, o Java SDK fornece várias ferramentas necessárias para o desenvolvimento. As ferrame ntas mais usadas são:
• javac • j ava •
Jar
•
javadoc
Você precisará das outras ferram entas para recursos adicionais, como cham adas de métodos remotos e interface Java nativa.
Compilador Java: javac j avac , o compi lador Java, compila código-fonte Java em byte code. Quando você digitar j avac FirstProgram.java, o compilador gerará um arquivo FirstProgram.class no diretóri o corrente - supondo que o compilador não detecte nenhum erro dentro de seu cód igo Java. Se um program a Java utilizar bibl iotecas de terceiros, o compilador tentará local izar essas bibliotecas a part ir do CLASSPATHespecificado; entretanto, você pode optar por modi fi car a CLASSPATH ao com pilar. A opção -C 1asspath permite aos desenvolvedores substituir o ClASSPATH. Você também pode especi fi car diferentes diretórios de origem e destino. através de -sourcepath e -d , respectivamente. A opção -sourcepath especi fi ca uma nova localização para arquivos-fonte de entrada. Assim, você pode optar por compilar arq uivos-fonte loca-
Apêndice B
lizados em um lugar diferente do d iretório corrente. A opção -d infonna o com pi lador para que deposite os arquivos .c1ass no caminho especificado, em vezde fazê-lo no di retório corrente. Você não precisará das opções -c1asspath, -sou rcepath ou-d para os exercícios deste li vro.
Interpretador Java : java j ava, o interpretador Java, fornece o ambiente de tempo de execução. Ele interpretará e executará os arquivos de classe compi lados. Para executar um programa Java, digite: Java FirstProgram Note que o comando omite a extensão .c1ass oO interpretador anexa, c1 ass automaticamente no nome da classe. Assim como no compi lador, você pode espec ificar opções de linha de comando para o interpretador. Algumas das opções usadas mais freq üentemente são: •
- classpath, para especificar um cam inho de classe diferente daquele defini do no ClASSPATH
•
-OpropName"'propValue . para especifica r propriedades de sistema
Essas e outras opções do interpretador, entretanto, estão fora dos objet ivos desta di scussão.
Utilitário de compactação de arquivos Java: j ar o ut ilitário jar gera arquivos compactados java (jar). Os arq uivos jar s."Io equivalentes aos arquivos zip, compactando arquivos para um tamanho menor e fornecendo uma mane ira conveni ente de distribuir classes java compiladas. Para criar um arq ui vo compactado, basta chamar: jar cvf Você também pode especi ficar todos os arqui vos jar e s ubdiretórios de determinado diretório, através de: jar cv f Note que todos os arquivos jar devem ler
° su fixo ,j ar.
Se você quiser ver o conteúdo de um arquivo jar, chame: jar tvf O comando jar tvf ex ibirá o tamanho, adala de inserçãoe o nome de arq uivo que está no arquivo com pactado. Mais opções estão descritas na documentação do SDK Java.
Aesumo do Java
Documentação Java e o gerador de documentação:
javadoc Todos os desenvolvedores já ouviram o mantra da documentação de código. A JavaSoft fac ilita o processo de documentação, fornecendo uma ferrame nta para gerar documentação HTML amigável para o usuário. O desenvolvedor precisa uti lizar apenas tags padrão, ao escrever comentários de atributos, métodos e classes, e depois chamar a ferramenta javadoc com as opções apropriadas para gerar documentação de APl . A documentação do SDK Java forn ece infonnações elaboradas sobre a fe rramenta e o processo j avadoc ; entretanto, vamos abordar alguns recursos básicos para começar. Os desenvolvcdorcs devem primeiro documentar seu código com os comentários e tags apropriados. Seria ót imo se a fe rramenta de documentação pudesse adentrar nosso código e decifrar exatamente o que estávamos pensando quando escrevemos aquele método cm part icular; ent retanto, a ferramenta javadoc não é suficienlemenle avançada para ler a mente. Em Java, ex istem três níveis principa is de documentação: classe, método e atribulo. A ferramenta javadoc reconhecerá comentários que começam com j ** como um comentário javadoc. Os comentários terminam com * j . Para comentários de classe, normalmente você verá: •
~author
especifica o autor da classe. Você pode ter mais de um autor; en-
tretanto, cada autor deve começar com a tag @author. •
~version
versão> especifica a versão da classe. Alguns soft wares de controle
de versão fornecem tags que gerarão o número automaticamente. • ~see fornece links para outras classes, para mai s informações. Você pode ter mais de uma referênc ia; entretanto, cada referênc ia deve começar com a tag@see. Uma doc umentação de classe seria semelhante a:
r'
*
• * * * *
@author Mi chael C. Han @author Tony Sln t es @version 1. 0 @see SecondJa vaCla ss
./ Os comenlários de método usam as tags anteriores, além de: • @param descreve os parâmetros do metodo. • ~retu rn descreve o valor de retorno do metodo.
•
~excep t 1on
descreve todas asexceçõcs lançadas pelo mé-
todo corrente.
Apêndice B
Uma documentação de método seria semelhante a:
/H *
coment~rios
•
do mé t odo>
* @param valuel parSmetro obtido pe l o método de teste * @param value2 segundo parâmetro obtido pelo mé todo de tes t e * @return ve rdadei ro se valuel :: va l ue2 * @except i on NumberFormatException lançado se valuel ou val ue2 i nte i ros
n~ o
forem
./ Os comentários javadoc de atri buto tendem a não ter tags espec iais. Em vez d isso, você deve denotara bloco de comentário corno um comentário javadoc. A seguirestá um exempl o de comentário de atributo javadoc:
/H * Atributo de classe para conter o estado da operação de comparação anterior
./
Cercadinho Java: seu primeiro programa Java Para ajudar a confi rmar sua instalação do SDK e praticar com as ferramenta s descritas até aq ui, você vai escrever o infame exemplo He ll oWorld. Pri mei ro, crie um arquivo chamado Hello Wor 1d . java, na raiz da origem. Se estiver usando a raiz da origem sugerida (\projects \s rc \java \classes), crie o arquivo sob c:\projects\src\ java \classes. Após criar o arquivo, você pode começar a escrever seu pri mei ro programa Java. A Li stagem 8.1 contém o exemplo HelloWorld em Java. LISTAGEM
B.l
HelloWorld.java
/H * Um programa hell o world pa ra confirmar que o SDK foi con f igurado corretamente. * Também utilizado para ajudar a demonstrar as ferramentas bãsicas do SDK •
•
*@ *@
author Michael C. Han version 1.0
./ publi c cla ss HelloWorld {
/H * Método principa l do programa. Todas as classes Java executá ve i s devem * conter esse método .
•
* @param argument os passados da linha de comando
Aesumo do Java
LISTAGEM 8 .1
561
HelloWorld.java (continuação)
'1
public static void main(String [] args) I HelloWorld helloTest : new HelloWorld(); System.out.pri ntln(hel loTest .sayHel l o(»; System.out.pri ntln(M"); System. out . pri nt ln(helloTest.sayHi(» ; }
;-
.
... Construtor da classe padrão
•
'1
public HelloWorld() { }
I"
... Método para dizer hel Io para o chamador ... @return String dizendo "Helio"
'1
publ ic String sayHell o() { return "HelIo"; }
I"
... Método para dizer hi para o chamador ... @return String dizendo "Hi! "
'1
public String sayHi() I return "H1!"; }
}
Compilando e executando Para compilar a classe, execute: javac HelloWorld no diretório-raiz da origem. Se tiver êxi to, você verá um arq uivo HelloWorld .class gerado 110 mesmo di retório. Em seguida, execute o programa, digitando: java HelloWorld
Apêndice B
Se o programa Hell oWorl d executar com sucesso e impri mir "Hello" e "Hi! ", como mostrado na Figura 8.2, você configurou seu SDK corrctamentc; enlretanlO, se você ver o segui nte ao exeçular java Hel l oWorld, Excep t ion in thread "main" java. lang.NoClassOefFoundError:Hell oWorld ou não configurou o CLASSPATH correlamente ou colocou oarq uivo Hel loWorld.java em um lugar que não é a raiz da origem. Em qualquer caso, confinne pri meirose o CLASSPATH contém a raiz da origem (\projec ts\ sr c\ja va\cl asses) e, em seguida, confi rme se o arquivo He ll oWorld.java está na raiz da origem . FIGURA 8 .2
Compilando e exeC"II/(lI1do
HelloWorld.
• Criando um arquivo . Jar
Em seguida, lente executar o ulil itário jar nos arquivos do diretório. Na ra iz da origem , execute: jar cvf hel l o.jar *.java *.class Você verá um arq ui vo hello.jar gerado. Para con finn ar o conteúdo de hello.jar, execute: jar tvf hello.jar Você verá dois arquivos-Hel loWorld .java e HelloWorld. class- na listagem, como na Figura 8.3. Em seguida, exclua HelloWorld.java e HelloWorld. class da raiz da origem (\projects\s rc \java\classes). Final mente, você precisa executar: jar xv f hel lo.jar Isso extra irá os arquivos Hel loWorl d.java e He ll oWorld .class na ra iz da origem (veja a Figura B.3).
Resumo do Java
FIGURA 8 .3
Criando, /is/ando e ex/rail/do hello.jar.
Gerando javadoc Se você examinar o código·fonte de HelloWorld, notará comentários no estilo javadoc para a classe e para os métodos da c lasse. Para gerar a docum enlação, crie o diretório de documentação. Sob c : \projects, crie um direlÓ· rio docs e depois execute: javadoc - public - d c:\projects\docs HelloWorld
o comando depositará a documentação HTML no diretório c : \projects\docs para todos os mé· todos públicos da classe Hel l oWorl d, como se vê na Figura B.4. Para ver a docum entação, abra c : \p ro jects\docs\ index, html em um navegador da Web. Observe a semel hança de estil os en· tre a documentação gerada e a documentação da APl J2SE da Sun.
FIGURA 8 .4
Tela do geração de jamdoc
de He 11 oWorl d.
Apê nd ice B
Mecânica da linguagem Java Após segui r os passos anteriores, você deve ter um ambiente de desenvolvimento Java totalmente configurado, assim como um entendimento das ferramentas básicas do SDK. De posse do ambiente de desenvolv imento, agora é hora de escrever algumas classes Java.
Classe Java simples A segui restá a versão simplificada do programa He11 olior1 d apresentado na seção anterior. Agora que você sabe como faz para com pilar e executar programas Java, pode exami nar o código-fonte com ma is detalhes: pub l ic class Simp1eHe11oWorld { public stat i c void main ( String ar gs E] ) { String hi • new String(" Hello All lO) ô System.out.print1n(hi) ô
I I A primeira pa lavra-chave utilizada é publ ic. A palavra pub1 ic é denominada modificador de acesso. Semelhanlemente à linguagem C++ ou SmallTalk, a li nguagem Java fomece mod ificadores de acesso para especi ficar quem pode acessar um método, atributo ou classe em particular. O acesso público garante o acesso a todos aqueles que qui serem usar uma classe. método ou atributo em particular. Os outros mod ificad ores de acesso são protected, private e nivel de pacote. Nível de pacote é algo especial na linguagem Java. Basicamente, um modi fi cador de nível de pacote garante o acesso a todas as classes dentro do mesmo pacote ou di retório. A próx ima palavra-chave do exemplo de classe é c1 asso Em Java, uma classe é o fundamento básico e os blocos de construção de programas. Ela é o encapsu lamento de variávci s de dados ou atributos e operações, funçõe s, ou métodos. Tudo em Java deve residir dentro de uma classe. O nome do exemplo de classe é Simp1eHe11oWor 1d. Em Java, o cód igo-fo nte dessa classe deve residir em um arqu ivo chamado Si mp 1eHe 11 oWor 1d. j ava. Se a classe residir em qua lquer outro arqu ivo, o comp ilador rec lamará que "c 1ass Simp 1eHe 11 oWor 1d is pub 1i c, deveria ser dec larado em um arquivo com nome Simp1eHelloWorl d.java". O arquivo de classe compilado reside em SimpleHel1oWo r ld.c1ass. A classe Si mp 1eHe 11oWo r1d também contém pub 1i c s ta t i c voi d ma i n (Stri ng args []). Ignore os mod ificadore s static e vo i d. A linguagem Java exige um método principal, se você quiser executar uma classe Java através do comando java. Você pode optar por adicionar mais métodos e operações na classe; entretanto, sem um método com essa assi natura, você não pode executar a classe. Observe as chaves ({}) e pontos-e-vírgu las ( ô) no código-fonte. Em Java, as chaves designam blocos de programa. Um método e uma classe devem começar e tenni nar com uma chave.
Aesumo do Java
Assi m, você deve tomara cuidado especial de terminar chaves de abertura com chaves de fechamento. Os pontos-e-vírgulas designam o fina l de uma instrução. Se você é programador de C/C++, então j á está fam iliarizado com eles e sabe que deve fecha r todas as instruções com ponlos-c-vírgulas. As palavras-chave static e void descrevem um método. A palavra-chave void é o valor de retorno. Seme lhantemente às outras linguagens, os métodos Java têm um valor de reto rno. Neste caso, o método princ ipal não retorna nenhum valor. A palavra-chave statlc designa o método como acessível através da classe. Em outras palavras, você pode chamar um método estático de uma c lasse, sem criar uma instância da classe. As duas linhas de código a seg uir, encapsuladas pelas chaves do método principa l, representam o m ;010 do programa. String hi • new String(" Hello All System .out.print ln( hi);
U )
;
Aqui, uma referência chamada hi aponta para uma instância construída da classe Stri ng. Como em SmallTa lk ou Ctt, o operador new cria a instância. A instância de St ring contém o valor "Hello All". Ela recebe o valor como parâmetro do construtor. Na linha seguinte, o objeto System imprime o valor de hi no console do sistema. Note também os pontos-e-vírgulas no fina l de cada li nha de cód igo. A fa lha em tenllinar instruções de programa ou declarações de variável com um ponto-e-virgula resultará em erros de compilação.
Tipo de Dados A li nguagem Java, assim como C++ e Smal ltalk, é fortemente tipada. Assim , ao declarar variáveis e valores de retomo, você deve especificar a variáve l ou tipo de retomo. A linguagem Java contém oito tipos primit ivos. A Tabela R I lista os ti pos primitivos vál idos em um programa Java e o numero de bits assoc iados ao tipo: TABELA B . 1
Tipos primitivos J ava e tamanhos de armazenamento
Tipo
Núm ero de bits
byte
8 bits
short
16 bits
char
16 bits
i nt
32 bits
fl oat
32 bits
doub 1e
64 bits
10ng
64 bits
boo lean
1 bi t
Apêndice B
Na maioria dos cenários, você pode usar i nt para representar inteiros e float para representar valores de ponto fl utuante; ent retanto, certos tipos de dados podem exigir espaço de armazenamento maior. Por exemplo, talvez você queira expressar o número de mi lissegundos desde 1970. Esse número é sufi cientemente grande para ex igir um valor l ong, em vez de um inteiro. Você pode representar um valor 1ong como IOOOOOOOOOL. A letra L pos-fixada denota o número como long. Analogamente, você pode representar valores f loat como 4.3405F. Se você for programador de C++, sabe que nessa linguagem os caracteres são ASCI I. A linguagem Java, entretanto, usa Unicode para representar caracteres. O padrão Unicode usa 2 bytes para representar caracteres, em oposição a I byte usado pelo padrão ASC II. Isso pennite a representação de um conjunto de caracteres maior para propósitos de internaciona lização. Por exemplo, a ma ioria das li nguagens As ian exige armazenamento maior do que o padrão ASC II . A linguagem Java não o impede completamente de usar ASC II. Em vez disso, você tem a escolha de usar um dos dois. Por exemplo, você pode usar seq Uências ASC II com uns, como \n para nova linha ou \ t para tabulação. Em certas ocasiões, talvez você queira converter um tipo numérico cm outro, como de i nt para 1ong. A Iinguagem Java fornece conversão automática entre tipos numéricos, se a conversão não levar a nenhuma perda de precisão. Ela converterá um valor int de 32 bi ts automaticamente para um valor long de 64 bits; entretanto, você deve especifica r explicitamente a conversão de ti po, para converter de um va lor 10ng para um valor i nt ou de um valor doub 1e para um valor i nt. A li nguagem Java ehama essa operação de conversão. Se você não converter ao rea lizar uma conversão de redução de precisão, o compilador rejeitará. As linhas a seguir demonstram uma conversão explícita: lon9 valuel • 40000L õ in t va I ue • (int)valuel õ float value2 z 4.003F; double value3 • value2; Observe que a conversão de fI oat para double não ex ige conversão explícita, poi s um valor flo at tem 32 bits e um valor double tem 64 bits. Assim , a operação aumenta a precisão.
Variáveis A lém de ser forteme nte tipada, a linguagem Java também leva em consideração letras maiúSCUlas e minúscu las. ConseqUentemente, a linguagem Java considera duas variáveis com posicionamento de letras maiúsculas e minúscu las di fere nte corno duas variáve is separadas. Por exemplo, variableOne e variab l eone são duas declarações de variável diferentes. Um nome de variável Java deve começar com uma letra e conter quaisquer caracteres alfanuméricos. Os caracteres alfanu méricos podem ser qualquer caractere Unicode que denote uma letra em qualquer idioma; entretanto, o nome não pode conter quaisquer símbolos, como $, %. & etc. As seguintes palavras reservadas não podem ser utilizadas:
Aesumo do Java abs t rac t case canst daub1e f ina l1 y generic import inte rface null private return swi tch throws void
boolean ca tch cont inue else f1 oat goto inner 10ng operator protected sho rt synchroni zed transient volatile
break char defaul t extends for if
567
byte c1ass do
f i na 1 fut ure implements int
instanceof native outer public static this
package rest super throw
try
,ar
new
while
A seguir estão alguns exemplos de declarações de variável: int valuel; doubl e Value2; float _va l ue3 ; cha r VALUE4 , Value5 ; Observe a última declaração de variável. A linguagem Java permite várias declarações de tipo de variável por linha. Nesse cenário, VAlUE4 e Va 1ue5 são valores char. Para declarar múltiplas variáveis, use uma vírgula para separar cada nome de variável e ternline com um ponto-e-vírgu la. Também existem modificadores de atributo que você pode adicionar em uma declaração de variável. Por exemplo, as variáveis podem ser declaradas pub 1i c, pri va te ou protec ted, para controle de acesso. Uma variáve l tam bém pode ser declarada stôtic ou final, ou ambos. Uma variável sta t i c é acessível através da classe, enquanto uma variável fi na 1 não pode ser med ificada. Embora você tenha poucas restrições quanto aos nomes de variável , é altamente aconselhável segu ir alguma fo rma de convenção de atri buição de nomes ou padrões de codifi cação. Os padrões de codi fi cação levam a um cód igo unifonne e, assim , melhoram a legibi lidade do cód igo. Uma convenção de atribuição de nomes de variável comum é colocar a primeira letra de toda palavra em maiúscul a. exceto a primeira pal avra. Os nomes seriam como: int anlntegerValue; cha r aCharValue; Outra convençi'lo é prefixar todas as variáveis privadas e protegidas com um sublinhado. Assim, as variáveis seriam como: private in t _value; pr;vate char _aCharValue: Você deve seguir os pad rões de codificação de sua empresa ou seguir um padrão de codi fi cação
comum.
Apêndice B
Constantes As constantes podem ser consideradas como um tipo especial de variável. Assim como em outras linguagens. as constantes Java são variáveis com valores que não mudam. Você pode declarar uma constante anexando as palavras-chave stati c final no inicio da declaração de variável . Exemplos de declarações de constante incluem: pri vate s tati e final String _NAME_VALUE pr; vate stati e final i nt _AN_INTEGER_CONSTANT publi c stati e fina l String PARAMETER_NAME proteeted static f i nal ehar _TYPE_CHAR_VALUE
=
"MyName"; 1; "MyParam";
~
'c';
~
~
Observe que os nomes de constante estão em letras maiúscu las. Embora não seja um req ui sito, uma prática normalmente seguida entre os desenvolvedores de Java é nomear as constantes com todas as let ras maiúscul as. Você pode acessar essas constantes através de .. Por exemplo, se as constantes anteriores forem declaradas em MyC l ass, você poderá acessar a constante PARAMETER- NAME através de MyCl ass. PARAMETER- NAME. Essa é a única constante públ ica declarada e, assim, essa é a única constante di sponível fora de MyCl ass ; entretanto, você pode usar qualquer uma das outras constantes dentro de MyCl asso Você também pode usar a conSlante _TYPE _CHAR_ VALUEem todas as subclasses de HyC1ass. Ao usar constantes dentro da mesma classe, você pode omilir o prefixo . Por exemplo, para acessar _NAME_VALUE dentro de HyCla ss, você pode fazer referência à constante como MyCl asso _HAME_ VAlUE ou simplesmente NAME VALUE.
Operadores A linguagem Java fornece vários operadores para operações aritméticas e booleanas. Aqueles que estão fam ili arizados com a sintaxe da linguagem C/C++, devem conhecer a sitHaxe desses operadores. Conforme mencionamos anteriormente, Iodas as instruções Java devem terminar com um ponlo-e-vírgula. A linguagem Java tem os operadores +, I, -,. normais para operações aritméticas. O operador = é usado para atribu ição. Além disso, a linguagem Java suporta o operador de módul o ou resto, %. Por exempl o, 15 / 3 é igua l a 5, enquanto 15 % 3 é igual a O. Você também pode usar operadores aritméticos ao ini cializar variávei s. Por exemplo, inicializar x como n + 2 é semelhante a: int x • n + 2; Você também pode opiar por usar uma sinlaxe abreviada ao efetuar operações na mesma variável. Por exemplo, para incrementar x por n, você pode optar por escrever:
x .. n
+
x;
Você tam bém pode adotar uma estratégia abreviada e escrever:
Aesumo do Java
569
A linguagem Java também oferece s uporte para exponenciação. A linguagem Java não fornece um operador. Em vez di sso, você usa o método pow, fornecido na classe java. lang. Hath. assim, para elevar x à potência n, a expressão seria semelhante a: int y • Hath.pow(x, n}: A linguagem Java também fornece operadores de incremento e decremento, como em C/C++. Para incrementar x por 1, você pode escrever o seguinte :
x z X + I; x +" I; A me lhor maneira, ent retanto, é escrever: x++',
o mesmo se aplica ao opemdor de decremento: x_O; Para os dois operadores, ex istem duas fonnas. Você pode colocar o operador antes ou depois da variáve l. Por exemplo, para decrementar x, você pode se expressar como segue:
x_O: -- x; A localização do operador diz quando ele é efetuado. Em uma expressão aritmética, colocar o operador antes da variável incrementará (ou decrementará) a variável primeiro, antes da avaliação da expressão. Se você colocar o operador depois da variável, a expressão será avaliada pri meiro. Pegue como exemplo o seguinte trecho de código: i nt x i nt y i nt k i nt j
" 5: .. 6; "++x; "Y++:
I I' pós I I'pós
esta expressão , k • 6 e x • 6 esta expressão , J • 6 e y • 7
Como você pode ver, o posicionamento do operador de incremento ou decremento é fundamenta l. Tome o cuidado especial de usar os operadores apropriadamente. Os operadores booleanos da linguagem Java são mu ito parecidos com os operadores booleanos de outras li nguagens de programação. Ao comparar maior que ou menor que, a linguagem Java fornece> e <, respecti vamente. Além disso, <= e >= denotam menor ou igual a e ma ior ou igual a, respectivamente. Isso se aplica à comparação de tipos primitivos, como valores inteiros (integers), long, double, caracteres, etc: intx"S: inty "6 : boolean z .. x < Yi
/1
Z .. verdadeiro
Apêndice B
Para fazer comparação de igualdade, a li nguagem Java fornece dois operadores d iferentes. Você pode usar o operador •• para com parar tipos primitivos; entretanto, ao com parar dois obj etos (como String, MyObject), você deve usar o método equalsO,de fi nido para o objelo. Se você usar = por engano, para comparar dois obj eros, isso na verdade com parará a referência dos dois objetos e não os objelos reai s. A não ser que você esteja comparando as mesmas referênc ias de obj eto, esta operação retornará fa lsa: String X " String y. boolean z " boolean w "
new S tring(~My Stri ng ") ; new String(UMy St r i ng"); (x •• y); II z • falso (x.equals(y)) ; II w ~ verd adeiro
Para fazer comparação de desigualdade, a linguagem Java usa o operador!. A desigualdade enIrc lipos primitivos usará! = e entre dois objetos será !objl.equals(obj2). Você tam bém pode optar por usar && e (ou), respectivamente).
II
para verificar cond icionais (operações and (e) e or
Estruturas condicionais As estrut uras cond icionais são uma parte importante de qualquer linguagem de programação. Assim como ClC++, a linguagem Java fornece o operador i t l el se. A sintaxe. assim como em C/C ++, é como segue: if (condição) { .. . llc omportamento para quando a condição for verdadeira } el se { .. .11 comportamento para quando a condição for fal sa }
Você pode optar por ter um único; f , i f je1se ou ; f com várias instruçôcs e 1se. A seguir está um exemplo de instrução i f j e 1se com vários el se: if (, •• y)
I
x -- : } e l se if (x > y) x '" 2; } el se ( , I 2; )
I
Observe as chaves que denotam o iníc io e o final de um bloco i f. Embora as chaves não sejam exigidas, se você tiver apenas uma instrução no bloco i t , usá- Ias nessa situação é recomendado. Fazendo isso, você pode evitar dores de cabeça em potencial posteriormente, quando decidi r
Aesumo do Java
571
I
adicionar mai s instruções no bloco i f. Se você se esquecer de adicionar chaves nesse momento, terá que depurar para encontrar as chaves ausentes. A linguagem Java, assim como C/C++, fornece um operador ternário para reduzi r a expressão lf/else. Uma expressão temária é como segue: z =( x>y)? 3:1 ; (x > y) é o teste condicional. O pri mei ro valor após? representa o valor de z se a condicional for verdadeira. O segundo valor representa o valor de z se a condicional for falsa. Seguindo o exemplo anterior, se x • 5 e y " 7, então z .. 1. A condicional é falsa (5 z recebe O segundo valor na expressão, I.
>
7) e, ass im,
laços ou estruturas de repetição Os laços ou estruturas de repet ição são um componente importante para qualquer linguagem de programação. A li nguagem Java, ass im como muitas outras, fornece urna variedade de mecanismos de laço. Um laço usado com umente, o laço for, tem a seguinte si ntaxe: for (i nt x • O; x < 8; x++ ) { l/_al guma ope ra ção em la ço
I A restrição do laço, x, é definida dentro dos parênteses e iniciali zada como zero. A instrução seguinte testa a restrição para garanti r que ela esteja dentro dos Iimites necessários. Neste caso, x deve ser menor que 8. A última instrução incrementa a restrição do laço. Outro laço comumente usado, o laço whi 1e, tem a seguinte si ntaxe: while (condição for verdadeira) { / l . exec uta alguma operação
I
o laço continuará a executar, dcsdc que a condição dentro do teste entre parênteses seja verdadeira.
Classes e interfaces da linguagem Java
blocos de construção
Em Java, você não pode fazer nada sem classes e, até certo ponto, interfaces. As classes compreendem os blocos de construção básicos da linguagem. Assim, quando escrever seu próprio programa, você terá que criar classes e também usar classes de bibliotecas já existentes. Talvez você também precise construir interfaces para melhor expressar seu projeto através do programa.
Apêndice B
Usando classes já existentes Para usar classes j á existentes no S DK Java, você precisará criar uma instância da classe, inicializá- Ia e depois trabalhar com as instâncias. Para uti lizar a classe existente j ava. uti 1. StringBuffer, você criaria uma instância ou objeto dessa classe: St ri ngBuffer sbTest • new St r i ngBuffer(); Ago ra que você já criou um novo Stri ng Buffer para usar, pode operar no objeto. Por exemplo, talvez você quisesse anexar outra String no buffer. sbTest.append(" Th is is a te st St r ing") ; Talvez seja preciso criar várias instâncias da mesma classe, para executar as o perações necessárias. Assim, você pode criar quantos objetos Str i ng Buffer forem necessários: StringBu ff er sbTest2 • new Stri ngBuffer(); Agora, você criou uma nova instância de um objeto StringBuffer, identificado pe la referência sb Tes t2. Você também pode atribu ir uma variável a outra, através do sina l ::::. 1sso configurará as duas variáveis para que apontem para o mesmoobjelo. Assim, as operações exe<:utadas nas duas variáve is afetarão o mesmo objeto. Tome como exemplo as instruções a seguir: StringBuf fer sbTest· new St r i ngBuffer(); StringBuffer s bTe st2 • new Stri ngBuffer(); sbTest '" sbTes t 2 ô sbTes t. append( NTest String 1. " ); sbTest2.append( aTes t St r i ng 2 .- )ô System.out. prin tl n("Output f or s bTes t = " +sbTest.toSt ringO)ô System.out.println("Output f or sbTest2 '" " +sbTest2. t oSt r ing(} )j Como você configura sbTest • s bTest2, as operações de anexação em sbTest e sbTes t 2 modificarão o mesmo objeto s ubjacente. A saída do trecho de código anterior será: Output for s bTe st = Test Stri ng I. Tes t String 2 . Output for s bTest2 " Te s t String I.Test String 2.
Criando suas próprias classes A sintaxe Java para um a classe é: public cla ss Cl assName { l/métodos e atri butos da c lasse
I Todos os métodos e atribulas da classe residirão dentro das chaves. Todas as classes também devem residir em um arq uivo chamado ClassName.java. Exami ne o trecho de cód igo do arquivo Product.Java, apresentado na Listagem 8 .2.
Aesumo do Java
8.2 Trecho de Product.java
LISTAGEM
lO>
•
* @author Michael C.Han ... @vers i on 1. 0
'1
public class Product (
lO> •
... @param argumentos passados da l i nha de comando
'1
pub l ic stat i c void main (Stri ng [] arg s) ( Product prod • new Product("wi dgetl" , "Acme Inc" , "Mas ter Widge t". "This is the master widget product to so l ve all problems!", 90 . 10f);
System .out.println(prod.getProductId())j System.out.printl n(prod. getManufa ct urer () ); System. out .p r i nt ln (prod.getProduct Desc()) ;
I pri vate St ring prodName , productld. manufacturer , desc i f loat pri ce i
lO> •
... @param argumentos passados da linha de comando
'1
pu bl ic Product(String product ld . String manufact urer , Stri ng prodName. String desc, float pr i ce ) ( this.prodName ~ prodName ; this.productld = productld j th1s.manufacturer ~ manufacturer; t his.desc a desc i this.price = price ;
I
lO> •
... @re turn i d do produ t o
'1
publ ic St ring getProductld() I retur n productIdj
I
I 574
Apêndice B
LISTAGEM
8 .2
Trecho de Product.java (continuação)
• *
~return
nome do produ to
'1 pub l ic St ring getProduc tName() { return prodName; J
I" •
* @ret urn nome do fabricante
'1 public String getManufacturer() ( return man ufact urer; J
I"
• *
~retu r n descriç~o
do produt o
'1 publ ic St r ing getProductDesc() { return desci
J
I" • * @return preço do produto
'1 public fl oa t ge tPri ce() ( return price ; J
I"
•
* @param price - novo preço desse produto
'1 publ ic void setP ri ce(float price) { this.price • price ; J J
A classe Product tem um método construtor com cinco parâmetros: product name, manufa cturer, product id, descri ption e price. O construtor efetua as operações necessárias para iniciali zar O objeto. Neste caso, você inicializa as infonnações do produto designadas. Um construtor deve seguir as regras que c itaremos:
Aesumo do Java • O construtor deve ter o mesmo nome da classe. • Um const rutor pode receber qualquer número de parâmetros. Um construtor sem parâmetros é chamado de construtor padrão. • Um construtor não retorna valores. • Um construtor só pode ser chamado através da palavra-chave new (por exemplo, Product prod = new Produc t ( . .. ). •
Uma classe pode ter mais de um construtor (ou, mais comumente conhecidos como consfrufores sobrecarregados).
Note o uso da palavra-chave thi s dentro do construtor. A pa lavra-chave thi s opera sobre a instância corrente da classe. No construtor, você usa a palavra-chave t hi 5 para ev itar confund ir os atributos privados com os parâmetros do construtor. Além dos construtores, a classe Produ ct também tem quatro (Icessore~' OUlltéfodos de obfel1ção. Os acessores fornecem acesso de leitura aos atri butos da classe Product. Para mod ificar atri butos da classe, você deve usar métodos chamados modificadores ou de cOI/f iguração. A classe Product tem um modificador, setPri ce, para mod ificar o preço de um produto. Observe que todos os atributos da classe Product são privados e apenas um, pr i ce, pode ser modificado após um objeto Product ser cri ado. É importante lembrar que as classes não devem expor seus atri butos internos, a menos que o usuário da classe tenha motivos legít imos para ler e/oll modi fi car o atributo. ConseqUentemente, você fornece acesso aos atri butos através de mod ificadores e acessores. A classe Product tem apenas métodos de acesso público. Você pode optar por implemcntar mélodos com outros niveis de acesso; você pode optar por im plementar métodos pri vados ou prote• gidos, além dos métodos públicos. E importante lembrar que os métodos privados são acessíveis dentro da classe que está implementando e que os métodos protegidos são acessíveis tanto dentro da classe que está im plementando quandto de todas as suas subclasses. Ao decidir-se sobre os níve is de acesso para um método, lem bre-se das seguintes diretrizcs para métodos privados: • Um método pri vado não traz preocupações para os usuários da classe. • O método mudará se a implementação da classe mudar. Lem bre-se também do seguinte em relação aos métodos protegidos: •
Um método protegido não traz preocupações para os usuários dessa classe; entretanto, as classes que estendem fun cionalidade da classe que está implementando exigirão acesso ao método.
• O método atende aos requisitos fu ncionais de todas as subclasses.
Interfaces Assi m como as classes, as interraces são recursos básicos da linguagem Java. Ao contrário das cl asses, entretanto, as interfaces não defi nem atri butos e métodos para uma classe. Em vez disso,
Apêndice B
as interfaces fornecem definições de método que podem ser implementados pelas classes. I)ara aqueles que estão familiarizados com e++, as interfaces serão semelhantes às defin ições de classe dentro de arquivos de cabeçalho. Dê uma olhada em uma interface para objetos que podem ser ordenados ou comparados:
I"
* Interface para comparar a i gua l dade de dois objetos
,
'I public interface Comparable {
I" * * * *
Compara esse objeto com um objeto desejado. Se o corrente for maior , então retorna I, o objeto toCompare é maior . então retorna - 1. Se os dois objetos forem ig ua is . retorna O @param toCompare - objeto para comparação * @retu rn - 1 se toCompare > this. O se toCompare •• this . * 1 se thi s > toCompare
'I public int compare(Object toCompare);
I Essa interface reside dentro do SDK Java. Ela defi ne os métodos que todos os objelos de tipo Comparable devem implementar. Neste caso, todos os objetos Comparabl e devem implementar o método de comparação. De forma simples, uma interface é um contrato que uma classe que esteja implementando deve cum prir. Se uma classe implementa uma inter fa ce em particular, ela pro-mete imp lementar todos os métodos designados pela interface. Atualmentc, a linguagem Java fornece suporte apenas para herança simples. Em outras palavras, uma classe Java s6 pode estender uma classe Java. Conseqüentemente, se você optar por class ificar um objcto de Cl assA c C1 assB (herança múlti pla), então só podcrá fazer isso através de interfaces. Usando interfaces para sim ular herança múltipla, a linguagem Java recupera grande parte da fu ncional idade fornecida pela herança múltipla. Parte da outra func iona lidade perdida poderia scr recuperada parcialmente, através de métodos de projeto, como composição de objeto. As interfaces também têm várias outras propriedades. Você não pode instanciar uma interface através de new. Sua classe pode implementar várias interfaces. Por exemplo, a classe Product pode implementar as interfaces Sortable e Clonable: publi c cla ss Product imp1ements So rtab le . Clonable As interfaces também podem estender outras interfaces. A interfa ce Sortable também pode estender uma interface Collectible. Uma classe implementando a interface Sortabl e também deve cumprir o contrato da interface Collec t i bl e: public interface Sortable extends Cl onable
Aesumo do Java
577
Classes internas e classes internas anônimas Os arquitetos da linguagem Java acrescentaram o concei to de classes internas com a Java 1. 1. Uma classe interna é uma classe declarada dentro do escopo de uma classe pública. Porque você deve usar uma? As classes internas têm as seguintes vantagens imponantes: •
Um objeto de uma classe interna pode acessar quaisquer atributos, privados ou não, da classe que está encapsulando. • As classes internas anónimas simplifi cam tarefas, comocallbacks e tratamento de eventos. • As classes internas são extremamente úteis para a criação de objctos que armazenam dados, que não têm nenhum significado fo ra do contexto da classe que os empacota, corno uma chave de hashing especializada para uma cache. Fora da cache, a chave de hashing não tcm nenhum significado. Assim , você cria uma classe interna Ha shKey dentro doescopo da classe ObjectCache. Aqui está como a cl asse Ob j ectCache com a cl asse interna HashKey seri a: i mport java.util . Map ; i mport java.util.Has hMap ; publi C cla ss Obj ectCache ( private Map cache ; publ iC Object Cache() ( cache · new Ha shMap(); }
pub11C void add(String oid , St r ing objName, Object obj) ( Ha shKey key • new HashKey(oid , objName); cac he. put(key , obj) ; }
publ ic Object get( String oi d, St ring objName ) { Has hKey key = new Hash Key( oid. objName); Object ob j · cache.get( key); return obj ; }
// .... Mais métodos ... pri vate cla ss HashKey { private Stri ng oi d, objName ; publiC Ha shKey(St r ing oid , St ring objName) I th i s.o id • oid; th i s .objName = objName ; }
Apêndice B
pub l iC String getOi d() { r eturn oi d ; }
public String getObjName() { return objName; }
public boolean equals(Object obj) { if (obj instanceof HashKey) { HashKey key = {Ha shKey)obj; return (key.g etOidO.equals(getOidO ) && key.getObjName{).equa ls{ getOb j Name{) ) ) ; }
return fal se ; }
public int has hCode() { return 17; } } }
Nas classes internas, assi m como nas classes normais, você pode acessar os atributos da classe interna com a palavra-chave t his; entretanto, você também pode acessar os atributos da classe que está encapsulando. Você prec isará usar a palavra-chave ou ter para acessar esses atributos. As classes anóni mas são uma forma es pecial de classes internas. As classes anónimas são mais predominantes na escrita de código de tratamento de eventos. Tome como exemplo o trecho de cód igo a seguir, para tratar de eventos de ação em um botão OK: publi c class FooF rame {
II JButton ok .. new Jbutton("ok"); ok.addActionLis t ene r(n ew ActionListener{) { public vo i d actionPerformed(Action Event evt) { II real iz a al gum tratamento de evento para o bota o ok } }} ; }
11-
Embora a sintaxe seja um pouco com plicada, as classes internas anónimas permitem q ue você reduza o numero de classes em seu projeto. Em vez de uma classe ou classe interna para cada manipulador de evento, você colocaria as dedaraçõcs de d asse em linha. Isso economiza muito tempo; entretanto, dev ido à natureza complexa do cód igo, classes internas anónimas em dema·
Aesumo do Java
sia reduzirão a legibilidade do código. Conseqüentemente, se você tiver de real izar muito tratamento de evento, talvez queira usar alguns padrões de proj eto (como o padrão Command) para melhorar seu projeto e, assim, esclarecer sua implementação.
Resumo De modo algum este resumo é uma introdução completa à linguagem Java. Entretanto, ele fornece informações suficientes, dentro do contexto deste texto. Se você quiser obter mais infonnações, ex istem vários sites na Web que fornecem exercícios dirigidos aprofundados sobre os fundamentos da linguagem Java. Um recurso excelente para desenvolvedores in iciantes e experientes é o JDC (Java Developer's Connection), local izado no endereço developer.java . sun. com. Esse site fornece muitos exerc ícios d irig idos e software de acesso adiantado para desenvolvedores de Java.
ApÊNDICE Referência da UML Referência da UML Este apêndice fornece lima referência rápida para a nOlação UML usada por todo O livro.
Classes A UML representa uma classe através de uma caixa dividida em três seçôes. A seção superior contém O nOme, a seção do meio contém os atributos e a seção inferior contém os métodos.
A Figura C. I ilustra como a UML representa uma classe.
Objeto A UML representa objeto igual a uma classe. A única diferença é que o nome do objeto é sublinhado e os métodos são omitidos. Os atri butos também podem exib ir um valor.
Visibilidade A Figura C.2 ilustra como a UML representa visibil idade de atri buto e método: •
+ representa visi bi lidade públ ica
•
# representa visibil idade protegida - representa visibilidade privada
•
Apêndice C
FIGURA C.1 UII/a c/asse UMI..
Nome r:: da classe
--
---~~~,,
_____ . Atribulol
,, Operaç6es
FIGURA C.2
Visibilidade d(! método e atributo.
Vislbllldade +pl.lblic_anr 'protecte-(Canr -private-_attr +pl.lblic_op,( ) 'p-rotecte-(Copr( ) -private_op,( I
Classes e métodos abstratos As classes e métodos abstraias são simbolizados pelo nome abstrato em itál ico. A Figura C.3 dá um exem plo de classe abstrata.
FIGURA C.3
Uma cltlSse (/bsll·(l/a.
Você tam bém pode adicionar a restrição {abs tract} após o nome. Usar o rótul o de restrição aj lida ao se desenhar um d iagrama à mão.
Notas •
As vezes, um a nota tornam um modelo mais inleligível. Na UML, uma nota é semelhante a uma nota adesiva e é ligada ao elemento que a eSlá recebendo com uma linha tracejada. A Figura C.4 ilustra a nota UML. Você pode anexar uma nota em qualquer parte de seu modelo UML.
583
Refe rência da UML
FIGURA C .4
A
1I01a
U:\IL +addAccount I ) +totalHold ings f) +totalAccounts ( ) +deposil ( ) +balanc::e I )
----_ .
B!!nk cont6m vllri!lS conl!!S' fo rnece operaç6es pa ra manipular essas c::onlas.
Estereótipos Um estereóripo é um elemento da UML que permite a você estender o vocabu lário da própria li nguagem UML ou class ificar uma marcação. Um estereót ipo consiste em uma palavra ou frase inclu ída entre «e> >. Você coloca um estereótipo acima o u ao lado de um elemento existente . A Fi gura C.5 ilustra um estereót ipo que defin e um tipo de método.
FIGURA C.5
O estereótipo UML «acc::essoO'» +getBalanc::el) +depositfundsll +withdrawFund'()
Relacionamentos Um relacionamelllo descreve como as classes interagem umas com as outras. Na UM L. um relacionamento é uma conexão entre dois ou mai s elementos de notação. Na UML, um relacionamento é normalmente ilustrado através de uma linha ou de uma seta entre as classes.
Dependência Em um relacionamento de dependêllcia, um obj eto é dependente da espec ificação de outro obj eto. Se a especificação mudar, você prec isará atualizar o objelo dependente. Na UML, você representa uma dependência como uma seta tracejada entre as classes dependentes. A Figura C.6 ilustra a notação de dependência da UM L. O relacionamento infonna que Cl assA depende de Cl as sB.
FIGURA C.S
Um relaciol/amel/to de depel/del/da simples.
I
c....
Claq B
I
Apêndice C
Associação Uma associação indica q ue um obj etocontém outro. Nos termos da UML, quando se está em um relacionameOlo de associação, um objeto está conectado a outro. Na UML, você representa uma assoc iação como uma li nha que conecta as duas classes. Diz-se que uma associação que não tem seta é bidirecional. Uma seta sign ifica que o relacionamento funciona apenas em uma di rcção. A Figura C.7 ilustra a notação de associação da UML. O relacionamento d iz que Cl assA está associada a Cla ssB.
FIGURA C .7
I
Um re/(/c ;OI/(//IIeIlfO de associaç(;o.
ClusA
I
CI ..sB
I
I
Papéis A UML pennite que você denote o papel de cada classe na associação. O papel da associação é a
parte que um objelo desempenha em um relacionamento. A Figura C.8 ilustra a notação UML para o papel.
FIGURA
C.8
I
O pape/lia UML.
j)II~1 B
I
I
Multiplicidade A UML permi te que você denote a multiplicidade da associação. A multiplicidade indica quantos objctos podem tomar parte na instância de uma associação.
O intervalo dos valores de mult"iplicidade está listado na Tabela C. I. TABELA C.1
Valores de multiplicidade
NOfaçüo
Valor
I
Um
•
Qualquer número
1.. *
Pelo menos um
X .. y
Qualquer número de valores no interva lo x a y
A Figura C.9 il ustra a notação UML para multiplicidade.
Ref erência da UML
FIGURA C.9
AJlllliplicidade.
Cln sA
I
,I
I·
Agregação A UML fornece notação para agregação. Uma agregação é um tipo especial de associação que modela relacionamentos 'tem um ' de todo/parte entre pares. Você modela uma agregação como uma linha com um losango aberto na extremidade relat iva a ' todo/parte '. A Figura C. lO ilustra a notação UML para agregação. ClusA
FIGURA C.10
I
Agregaçeio.
Composição A UML fornece notação para composição. Uma composição é um tipo especial de associação que modela relacionamentos 'tem um' de todo/parte entre classes que não são pares. A parte não é independente do todo em um relacionamento de composição. Você mooela a composição como uma linha com um losango fechad o na extremidade relativa ao 'todo/parte' . A Figura C I I ilustra a notação UML para composição. FIGURA C.1 1
ComposiÇfio.
I
a ....
I·
I
I
Generalização ,
Um relacionamento de generalização existe entre o geral e o especí fico. E a herança. A generalização é sim bolizada através de uma li nha cheia com uma seta fec hada e vazada. A Figura C, 12 il ust ra a notação UML para generalização,
~
FIGURA C.12
Generali:açeio.
a ....
CI. asA
I
a.""
I
Apêndice C
Diagramas de interação Os diagramas de interação modelam as interações entre os objelos.
Diagramas de colaboração Os diagramas de colaboração representam as mensagens que os objetos enviam uns para os outros.
Cada objeto é simbolizado como uma caixa no diagrama. Uma li nha conecta cada objeto que in· terage. Sobre essa linha, você escreve as mensagens que os objetos enviam e a direção dessas mensagens. Os diagramas de colaboração destacam os relacionamentos ent re os atores. A Figura C. 13 ilustra o diagrama de colaboração UML. FIGURA C.13
Objelol
Um diagrama de colabo/"açr7o. Ob!e!03
Objelo2
Diagramas de seqüência Os diagramas de seqUência modelam a seqUência de eventos em um cenário, com o passar do tempo. Cada objeto no diagrama é si mbol izado na parte superior do diagrama como uma caixa. As linhas que saem das caixas representam a linha da vida do objeto. As mensagens são passadas entre os objctos. A Fi gura c' 14 ilustra o diagrama de seqUência UML . FIGURA C.14
Vm diagrama l/e seqiiimcia.
I
Objeto1
I
ApÊNDICE Bibliografia selecionada o domínio dos objetos só pode vir com o tempo, a prática e o estudo. Nenh um livro pode ensi· ná-Io tudo que há a respeito da programação orienlada a objetos. Este Apênd ice apresenta lima lista de recursos de 00 classificados. Use esta lista como guia para seus próx imos passos no estudo e na aplicação de POO. Embora não seja importante que você investigue cada recurso, esta li sta pode conduzi-l o para mais informações sobre os assuntos que você achar interessan tes.
Análise, projeto e metodologias Beck, Kent. Ex/reme Prograllllllillg ETplailled: Embrace Change. Boston: Addison-Weslcy, 2000. Baoch, Grady. Objecl Oriellled Ana/ysis And Design: Wilh Applicaliolls. Reading: Addi· son- W esley, 1994.
Baoch, Grady, James Rumbaugh e IYar Jacobson. The Unified Modeling Langl/age Use,. Guidc. Reading: Add ison-Wesley, 1999. Fowler, Martin e Kendall Scott. UML Dislilled: A BriefG/lide to fhe Standard Ohjecl Modeling Language. 2'" ed . Readi ng: Addi son-Wcsley, 2000. Johnson, Ralph E. e Brian Foote. "Designing Reusable Classes:' JOIl1"l1a/ of Objecl Orienled Programming 1.2 (Junho/Julho 1988): 22-35. Liberty, Jessc. Beginnillg Objecl Orienled Ana/ysis and Design. Ollon, UK: Wrox, 1998.
Apêndice D
Programação com C++ Stroustrup, Bjame. TI/e C+ + Programmillg Lallguage. Special ed . Readi ng: Addison-Wcsley, 2000.
Padrões de projeto Buschman, Frank, Regi ne Meunier, Hans Rohnen, Peler Sommerlad e Michael Slal. Paltern-Oriellted Software Architecture: A System of Patterns. C hi chesler: Wiley, 1996. Garnma, Erich, Richard I-Ielrn , Ralph Johnson e John Vlissides. Design Pallel'llS: E/emenls of Rel/sab/e Object-Oriel/fed Software. Read ing : Addison- Wesley, 1995.
Princípios e teoria geral da 00 Booch, Grady. Object Oriel1led Analysis AI/d Design.· With Applicatiolls. Reading: Addison-Wesley, 1994. BlId, Ti molhy. Ali /ll/rodllctiOIl lo Object Oriel1led Programming. 2'" ed . Read ing: Addison-Wesley, 1997. Fowler, Martin. Refa ctoring: /mprovillg lhe Design of Edsling Code. BOSlon: Addison-Wcsley, 2000. Rum baugh, James. " Disinhcrited! Examples of Misuse of Inherilance." Joumal ofObjecl Orienled Programming 5.9 (Feverei ro 1993): 22-24. Se idewitz, Ed. "Controll ing Inherilance". Joul'I1al ofObject Oriellled Programming 8.8 (Janeiro 1996): 36-42. Taenzer, David, Murthy Ganti e Sunil Podar. "Objecl-Orienled Software Reuse: The Voyo Probl em." ./OII1'11al ofObjecl Oriented Programming 2.3 (setembr%utubro de 1989): 30-35.
Teoria "Hard Core" (mas não deixe isso assustá-lo! ) Cardelli, Luca e Peter Wegner. "On Understandi ng Types, Dala Abstraclions and Polymorphism". Compllting SlIr\leys 17.4 ( 1985): 471-523. Danfonh, Scott e Chris Tomlinson. 'Type Theories and Objecl Oriented Programming" . ACM Computing SlIrveys 20. 1 ( 1988): 29-72.
Bibliografia selecionada
Snyder, Alan. "Encapsulation and lnheri lance in Objecl Orienled Prograrnrn ing Languages" . Proceedings ofthc 1986 OO PSLA Conference on Object Oricnted Programming Syslems, Languages and App1icalions. Sigp/all Nolices 21.11 ( 1986): 38-45. Wegner, Peler e Stanley B. Zdonik. " Inheritance as an Incremental Modificat ion Mechanis m or What Likc is and isn' l Likc". ECOOP 1988: 55-77.
Programação com Java Bloch, Joshua. Efleclive .Java Programming Langl/age Guidc. Boslon: Add ison- Wes ley. 200 1. Eckel, Bruce. Thinking i/1 Java . 20d cd. Upper Saddle River: Prenlice Hall , 2000. Warren, Nige l e Philip Bi shop. Java in Praclice: Design S/y/es alld Idiolllsfor Eflec/ive Java. Harlow, UK : Addi son- Wesley, 1999.
Miscelânea Brooks, Frederick P., Jr. Tlle Mylhica/ Man-Mo1l1h. Anniversary ed. Rcading: Addison- Wesley, 1995. Scame, Joh n. Scarne' s Encyclopedia ofCard Games: Ali lhe Rules for Ali thc Games You' lI Want to Play . Nova York: Harper, 1983.
Smalltalk Budd, Timot hy. A Liule SII/(III/alk. Reading: Addison-Wesley, 1987. Goldberg, Adele c David Robson. Small/aik-80: Tlle Language. Reading: Addison-Weslcy, 1989.
Teste Beck, Kent. Ex/reme Progralllllling Exp/ained: Embrace Change. Boston: Addi son- Wesley,
2000. Mackinnon, Tim, Stcvc Frceman e Philip Craig. "Endo-Testing: Unit Tcst ing \\Ii th Mock Objects". Artigo apresentado na confe rencia eXtreme Programming and Flexiblc Processes in Software Engineeri ng - XP2000, Cagl iari, Sardinia, Itál ia, junho de 2000.
ApÊNDICE Listagens do código do jogo vinte-e-um Nos dias 15 a 21 , você construiu um jogo vinte-c- um completo. Muitos dos exercícios também pedi ram para que você adicionasse mais fu ncionalidade ao sistema. Esta listagem de códi go-fonte apresenta a versão final do jogo vinte-c-um. A versão fina l combina cada recurso que foi adicionado nos dias 15 a 2 1.
Este apêndice contém a listagem completa de todo o código. O código foi empacotado para evitar arq uivos-fon te duplicados. O que pode ser comparti lhado é compm1 il hado. O que não pode ser com partilhado não é compmt il hado.
O cód igo-fon te contém uma OU I MVC, urna OUI PAC, urna UI C U e um si mulador. O código-fonte fo i d ividido nos segu intes pacotes: •
blackjack.core
•
blackjack.core.threaded
•
blackjack.exe
blackjack.players • bl ackjack. ui • bl ackjack .ui.mvc • bl ackjack .ui .pac •
Apê ndi ce E
blackjack.core blackjack .core contém todas as classes comuns encontradas nas listagens E.I a E.14. Essas classes constroem o sistema do jogo vi nte-e-um básico. liSTAGEM
E.1
Bank.java
package blackj ack. corej public class Bank ( private int total ; private int bet ; publiC Bank( int amount ) í tota I • amount ;
I pub l ic vo i d doubleOown() í placeBet( bet ) ; bet - bet * 2-,
I publ ic void placelOOBet() í placeBe t( 100 );
I public void place50Bet() ( placeBet( 50 ) ;
I publi C vo i d pl acel0Bet() í placeBet( 10 ) ;
I publiC vo i d win() I total +- ( 2 * bet ) ; bet - O;
I pub l ic void l ose () I /1 já ext raído de tota l bet • O;
I publi C void blackjack() I
Listagens do código do jogo vinte-e-um
LISTAGEM E .1
593
Bank.java (continuação)
total +- ( ( ( 3 • bel ) / 2 )+ bel ); bel - O·• J
publiC void standoff() I total +- bet; bel - O; J
public String toString() { return ( "$" + total + " . 00" l; J
private void placeBet( inl amount ) { bet - amount; tota 1 _ a amount ;
J J
LISTAGEM E .2
BettingPlayer.java
package blackjack . co re; public abstraet elass Bett i ngPlayer extends Player { private Bank bank; publiC BettingPlayer( St r ing name, Hand hand. Bank bank 1 { super( name , hand li this.bank a banki J
11********·*******·······********************·******** ****** ••• ************* Ilcomportamento sobreposto publ ic Stri ng toString(l { return ( super . getName() + lO: " + getHandO.toString() + "\n" + bank.toStr ing() )i
J publie Stri ng getName() { return ( supe r.getName() + " " + bank.toString() l i J
publ ie vo id wi nO {
I 594
Apê ndi ce E
LISTAGEM E.2
BettingPlayer .java (continuação)
bank .winO: super.winO: J
publi C void l ose() { bank.loseO: super .lose() ; J
public vo i d standoff() { bank. s tandoff () : super . standoff(): J
publ ic vo i d blackjack() { bank.blackjack O: s uper.bla ckjack() : J
protected PlayerState geUnitia l StateO { return get8ettingState();
J protected Playe rSta te getPlayingState() { return new 8etterPlaying(): J
11**************************************************** **********************
Iladicionado recentemente para BettingPlayer protected final 8ank get8ank() { return bank: J
protected PlayerStale getBettingState() { return new Betting():
J protected PlayerState getDoublingDownState() { return new DoublingOown():
J protected abstract void bet(): pro tected abst ra ct boolean doubleDown( Dealer dea l er ); private class Betting implements PlayerState {
Listagen s do código do jogo vinte-e-um
LISTAGEM
E.2
BettingPlayer.java (continuação)
publiC void hand Changed() I II impossfve l no estado de estouro J
publiC void handPlayab le() I II impossfve l no estado de estouro
J public void handBlackjack() { II impossfvel no estado de estouro J
pub li c void handBusted() { I I impossfvel no estado de estou ro J
public void execute( Dea l er dealer ) { bet () ;
setCurrentState( getWaitingState() ); dealer.doneBetting( BettingPlayer.this ); II tenni na J
J
private class DoublingDown implements PlayerState I public void handChanged( ) { notifyChanged(): J
publiC void handPlayable() { setCurrentState ( getStandingState() ): notifyStanding(): J
pub l iC void handBlackjack() { I I impossfvel no estado de dobro J
pub li C void handBusted() { setCu rrentState( getBustedState() ): notifyBustedO ; J
publi c void execute( Dea le r dealer ) { bank .doubleDown(); dealer.hit( BettingPlayer.this }; getCurre ntState{).execute( dealer ): J
J
private class BetterPlaying implements PlayerState I
595
I 596
Apêndice E
E.2
LI STAGEM
Bett ingPlayer .java (continuação) publiC "oid handChangedO { not i fyChanged () ;
I public "oid handPlayable(} { Ilpode ignorar no es tado de jogo
I publ i c void handBlackjack(} { Ilimpossf"el no estado de jogo
I public void handBusted() { setCurrentS tate( getBustedSta te() }; notifyBu sted O j
I pub li c "oid execute( Dealer deale r ) { i f( getHand().canDoubleDown( ) && doubl eDown( dealer ) ) { setCurrentS tate( getDoubl ingDownState(l ); getCurrentState().execute( dea1er l ; return;
I i r( hit( dealer ) ) {
dealer.hit( BettingPlayer.this }; ) else { setCurrentState( getStandingState() ); notifyStanding();
I getCurrentState().execute( dealer ) ; II transição
I I I
LI STAGEM
E.3
B1 ackjackDea 1er . java
package bl ackjack.core; import java.util . ArrayList ; import java.uti 1. Iterator ; pub l ic cl ass BlackjackDealer extends Player implements Oea l er { private Oeckpi l e cards ; priva te ArrayLi st pl ayers
=
new ArrayList() ;
Listagen s do código do jogo vinte-e-um
LISTAGEM E.3
597
8lackjackDeal er.java (continuação)
protected ArrayList waiting_players; protected Arraylist betting_players; private ArrayList standing_players; private Arraylist busted_players ; private Arraylist bla ckjack_players; public 81ackjackOeal er( String name. Hand hand, Oeckpile cards ) { super( name, hand ) ; thls.cards • cards; J
//**********************************************************************
//Métodos Que os jogadores podem chamar public void blackjack( Player player ) { blackjack_playe rs.add( player ); play( thls ); J
publiC vold busted( Player playe r ) { busted_players.add( player ); play( this )i
J publiC void standing( Pl ayer player ) { standing_pl ayers.add( player ); play( this ); J
public void doneBetting{ Player player ) { waiting_players . add{ player ); play( this ) i J
public void hit( Player player ) { player.addCard( cards.dealUp() ); J
public Card getupCard{) { Iterator 1 • getHandO.getCards{)i while( i.hasNext() ) { Card ca rd = (Ca rd) i.next(); if( card.lsFaceUpO ) { return card; J J
I 598
Apêndice E
8lackjac kDea 1 er. java (continuação)
LISTAGEM E.3
II
não deve chegar aqui return nu11;
}
II···················································· .................. II Métodos de conf iguração do jogo pub1ic void addPlayer( Player pl ayer ) { players . add( pl ayer ); }
public void reset() ( super,reset(); Ilconfigura os baldes do jogador waiting_p l ayers new Array List() ; standing_players • new ArrayList() ; busted_p1ayers • new ArrayList(); b1ackjack_players a new ArrayL is t(); betting_players new ArrayList() ; betting_players .addA1l(p1ayers ): 3
E
cards . reset O; lterator i • players.iterato r (): while{ i.hasNext() ) ( Player p1ayer : (Player) i .next {) ; player,reset{); } }
pu bli c void newGameO ( reset O; I I vai! play( this ); }
11······························***********·********** **** •• ******.***** public void dea l () ( cards.shuffle(); II reconfigura cada jogador e distribui uma carta abert a para cada um e II para si mesma P1ayer [] player : new Player (waiting players.size{)]: waiting-p1ayers.toArray{ player }; for{ int ; • O; i < player.length; i ++ ) { player (1] . addCard( cards .dealUp() ): }
this.addCard( cards.dealUp() ) :
Listagen s do código do jogo vinte-e-um
599
LISTAGEM E.3 8lackjackDea 1er. java (continuação)
II distribui mais uma carta aberta para cada jogador e uma fechada para II s1 mesma for( in t i • O; i < player.length: i •• ) { player[i] .addCard( cards.dea lUpO ): J
this.addCard( cards .dealDown(l l: J
protected boolean hit( Dealer dealer ) { if( standing_players .size() > O && getHand().total() return true:
<
J
return false õ J
protected voi d exposeHand () ( getHand().turnOver(); notifyChanged():
J protected PlayerState getBlackjackState() ( return new OealerBlackjack(); J
protected PlayerState getDealingState() ( return new OealerDealing(l; J
protected Pl ayerState getCollectingBetsState() I return new OealerCo ll ectingBets() ; J
protected PlayerState getBustedState() { return new DealerBusted(): J
protected PlayerState getStandingState() { return new DealerStanding() : J
protected PlayerState getWaitingStateO { return new DealerWaiting() : J
protected PlayerState getInitialState() { return new Oea lerCollect i ngBets():
J private class DealerCollectingBets implements Pl ayerState ( public void handChanged() {
17 ) {
I 600
Apê ndi ce E
LISTAGEM E.3
8la ckj ackDea 1er. java (continuação)
II
impossivel no estado de aposta
I public void handPlayable() { II imposslvel no estado de aposta
I pu bli c void handBlac kjackO ( II impossfvel no estado de aposta
I publ ic void handBu sted() ( II impossfvel no estado de aposta
I public void executeI Oealer dealer ) ( 1f( !betting_players. isEmptyO ) ( Player pl ayer s( Player )betting_players.get(O) ; bett l ng_players.remove( player ); player.play( dealer ) ; I else ( setCurrentState( getOealingState() ) ; getCurrentState() . execute( dealer ); II faz a trans i ção e executa
I
I I private cl ass OealerBusted implements Pl ayerSt ate ( publ i c void hand Changed() ( II imposshe l no estado de est ouro
I public vold handPlayab l e() ( II impossfvel no estado de estouro
I pub l ic void handB l ackjack() ( II impos sf vel no estado de estouro
I pub l i c vol d handBu st ed() ( II impossfvel no estado de estouro
I publl c vo id executeI Oea l er dealer ) ( I terator 1 • standi ng_pl ayers. i terator() ; while( i.ha sNext() ) { Pl ayer pl ayer =( Pl ayer ) i .next() ; player.wlnO;
I i s blackjack_players. i terator( ) ; wh l1 e( i .hasNext() ) {
Listagen s do código do jogo vinte-e-um
LISTAGEM E.3
8lackjackDea 1er. java (continuação) Player player = (Player) 1. next(); player.blackjack(); J
i = busted_players.iterator() ; while( i.hasNext() ) { Player player z (Player) i.next(); player.lose() ; J J J
private class DealerBlackjack implements PlayerState { publie void handChanged() { not i fyChanged () ; J
publie void handPlayable() { II impossfve l no estado de vinte-e-um J
publie void handBlackjaek() ( II impossfve l no estado de vinte -e- um
J publie void handBusted() ( II impossfvel no estado de vinte-e-um
J publie void execute( Dealer dealer ) ( exposeHand(); Iterator 1 • pl ayers.iterator(); while( i.hasNext() ) ( Player player =(Player) Lnext(); ;t( player .getHandO .blackjackO ) { player.standoff() ; ) else { player.loseO ; J
J J J
private class DealerStanding implements PlayerState { publie void handChangedO ( II impossfve l no estado de parada
J publ ie void handPlayable() { II impossfvel no estado de parada
J publiC void handBlackjack() { II impossfvel no estado de parada
601
I 602
Apê ndi ce E
LISTAGEM
E.3 8la ckj ackDea l er.java (continuação) }
public void handBu sted () { II impossfve l no estado de parada }
publiC void execute( Oealer dealer ) I Iterator i • stand i ng_players. iterator(); while( i.hasNext() ) I Pl ayer player • (Player) i .next(); if( player .getHandO. i sEqual( getHand() ) ) I player .standoff() ; ) else if( player .getHand() . isGreate rThan(getHand() ) ) I player.w;n{) j ) el se I player . lose() ; } }
i • blackjack_players.iterator() ; wh ile( i.hasNext( ) ) I Pl ayer player • (Pl aye r) LnextO; player.b l ackjack()i }
i • bu sted_players . iterator(); whi le( i.hasNext() ) ( Player pl ayer : (Player) i .next () i pl ayer.lose (); } }
}
private class OealerWaiting i mplements Pl aye rState I public void handChanged () { II impossí vel no estado de espera }
public vo i d handPlayable( ) { II impossíve l no es t ado de espera }
publ ic voi d handBl ackjack() { II i mpossfvel no estado de espera }
publiC voi d handBu sted() I II impossíve l no estado de espera }
publi c vo i d executeI Oealer deale r ) ( i f(!wait i ng_pl ayers .i sEmpty() ) I Player pl aye r • (Pl ayer) wa i t i ng_players .get(O), waiting_players.remove( player li
Listagens do código do jogo vinte-e-um
LISTAGEM E.3 8lackjac kDea l er.java (continuação) player.pl ay( deale r ) i ) else { setCurrent State( getPlayingState() ); exposeHand () i getCurrentState() . exec ute( dealer ) ; Il faz a transição e executa } } }
private class DealerDeal ing implements Playe rState { pub l ic void handChanged() { noti fyChanged() i }
public void handPlayable() { setCurrentState( ge tWa i tingState() li II tran si ção }
publiC void ha ndBlackjack() { setCurrent Sta te ( ge t Bl ackj ackS ta te () ) i notifyBlackjack() ; II transição }
publ ic vo i d handBustedO { II impossfvel no estado de di stribui ção }
public void execute( Deal er dea l er ) { dealO ; ge tCurrentS tate( ) .execu te( dealer ) i II faz a transição e executa }
} }
LISTAGEM E.4
Ca rd.java
package blackjack.core; pub l iC class Card { pri vate Rank ran k: private Suit suit; pri vate boolean face_uPi public Card( Suit suit . Rank rank ) {
603
I 604
Apêndice E
LISTAGEM E.4
Ca rd. java (continuação)
th'is.sult • sult: thls . rank • rank : J
publ'ic Sult getSuit() { return SUl t: J
public Rank getRank () { return rank; J
public void setFaceUp( boo l ean up ) { fa ce_up • up; J
publ iC boolean isFaceUpO { return face_up:
J public String toStr'ing() { i f( ! isFaceUpO ) { return -Hidden" :
J return rank.toString() + suit .toSt ring() ; J
J
LISTAGEM E.5
Dea l er.java
package blackjack.core; publi c interfa ce Oealer { II usado pelo jogado r para 'i nteragir com a banca publ ic void hl t( Player player l:
II usado pelo jogador para comun i car estado public publ ic public publ ic
vold vo i d vo i d void
blackjack( Playe r player l: busted( Player playe r l ; stand'i ng( Pl ayer player l; doneBetting( Player playe r ):
pub l ic Card getUpCard();
J
à
banca
Listagens do código do jogo vinte-e-um
LISTAGEM E.6
Oeck.java
package blackjack . core i import java. ut il .Iteratori impo rt java. ut il.Random i public class Oeck 1 private Ca rd [] deck ; pr i vate int index ; pu bliC Deck() ( buildCardsOi
I publ ic vo i d addToStack( Deckpil e stack) ( stack .addCards( deck )i
I pro tected voi d setOeck( Card [] deck ) ( t hi s. deck • deck;
I protected voi d buildCardS( ) 1 deck • new Card[52] i Ite ra tor suit s • Suit.SUITS.i terator() i int coun ter • Di while( su its . hasNext() ) { Sui t sui t • (Suit) suits .next()i Iterator ranks • Rank.RANKS.iterator() ; whi l e( ranks.hasNext() ) { Rank rank • (Rank) ranks.next()i deck(counter] " new Ca rd( suit. ra nk ) i counter++ i
I I
I I
605
I 606
Apê ndi ce E
LISTAGEM E.7
Oeckpi l e.java
package bl ackjack . core ; import java.util.Arraylist; import java.util.lterator; import java . util.Random; publi c class Oeckpile ( private Arrayllst stack· new Arraylist()j private int i ndex; private Random rand z new Random(); public void addCards( Card [] ca rds ) ( for( int i • O; i < cards.length ; i ++ ) { stack . add( cards [i) ) ;
,
,
pub l ic void shuffle() { reset(); randomize() ; randomizeO; randomi ze () : randomizeO;
,
publ ic Card dealUp() { Card card z dea l (); i f( ca rd !z nul l ) { card.setFaceUp( true );
,
,
return card;
publiC Card deal0ownO { Card ca rd z deal(); if( ca rd ! - null ) { card . set FaceUp( false );
,
,
return ca rd;
public void reset() { index z O; Iterator 1 • stack.iterator();
Listagens do código do jogo vinte-e-um
LISTAGEM E.7 Oeckpi l e.java (continuação) while( 1. hasNext( ) ) I Card card • (Card) i. next O: card.setFaceUp( fa l se ) : J J
private Card deal() I if( in dex ! - stack.size() ) I Card card • (Card) stack.get( i ndex ): index++; return card: J
return null; J
private void randomlze() { int num_cards • stack.size(); for( int i • O: i < num_cards: i ++ ) I int index - rand.nextlnt( num_cards ) ; Card card_i • (Card) stack .get( i ): Ca rd card_index • (Card) stack . get( index ): stack .se t ( i, ca rd_index ): stack.set( i ndex, card i ):
J J
J LISTAGEM E.a
Hand.java
package blackjack.core: import java . utl1 . ArrayList : import java.uti l. lterator: pub li C cl ass Hand { private private private private
ArrayList cards - new ArrayList() : sta t ic final int BLACKJACK : 21; HandListener hOl der: i nt number_a ces :
public HandO I setHolder( new HandUstenerO {
607
I 608
Apêndice E
Hand.java (continuação)
LISTAGEM E.a
publiC pub l ic public publ ic
void vo id voi d void
handPlayable() {} handBlackjackO {} handBustedO {I handChangedO {I
} }; }
public vo1d setHolde r( HandListener holder ) { this.ho l der • holder i }
public Iterator getCardsO ( return cards.iterato r(); }
publ iC void addCard( Card card ) ( cards.add( card )i holder.handChanged(); if( ca rd. getRank () •• Rank .ACE ) { number_aces++: }
if(
b,stO ) I ho l der.handBusted(); return;
}
if( blackjack() ) ( holder.handBlackjack(); return ; J
if ( ca rd s.size () >= 2 ) { holder.handPl ayable() ; return ; J J
pub li c boolean canOoubleOown() { return ( cards.size() == 2 );
J publ iC boolean isEqua l ( Hand hand ) { if( hand . totalO •• this.total{) ) {
Listagens do código do jogo vinte-e-um
LISTAGEM E.a
Hand.java (continuação)
return true ; }
return false; }
public boolean i sGreaterThan{ Hand hand ) I return thi s.total() > hand.total(); }
public boolean bla ck jack() ( if( cards. sizeO ,,= 2 && total () return true;
BLACKJACK )
}
return fal se ; }
publ iC void resetO { cards. clearO ; number aces " O; }
public void turnOver() I Iterator i " cards.ite rator (); while( i.hasNext() ) I Card card " (Card) i.next(); card.setFaceUp( true ); }
}
public String toString() ( Iterator i " ca rds.iterator(); String string " ''''; while( i.hasNext() ) ( Card card = (Card) i.next (); string " string + " " + cardo toString() ; }
return string ; }
public int total0 ( int total = O; Iterator i " cards.ite rator(); while( i.hasNext() ) ( Card card " (Card) LnextO i total +. card.getRank().getRank();
I
609
I 610
Apêndice E
LISTAGEM E.a
Hand.java (continuação)
}
in t temp_aces • number_aces ; while( total> BLACKJACK && temp_aces > O ) I total = total - 10; temp_aces-- ; }
return total; }
private boolean bustO { if( total() > BLACKJACK ) { return true ; }
return false; } }
LISTAGEM E.9
Hand Liste ner .java
package blackjack.core; public interface HandLlstener I publi C void handPlayab le( ); public void handBlackjack() ; publiC void handBusted(); public void handChanged(); }
LISTAGEM E.10
Pl ayer.java
package blackjack.core ; import java.ut ll.ArrayList; import java.utll.Iterator; publ i c abstract class Player I private Hand hand;
Listagens do código do jogo vinte-e-um
LISTAGEM E.10
Player.java (continuação)
private Stri ng name : private ArrayList l isteners • new ArrayList(); private PlayerState current_state : publi C Pl ayer( String name, Hand hand ) ( this.name • name: this.hand • hand; setCurrentState{ getlnitialState{) );
I publi C void addCard{ Card card ) 1 hand.addCard( card ):
I publ ic void play( Oeale r dealer ) { current_state . execute( dealer }:
I public void reset() { hand. reset O; setCurrentState( getlnitialState() ); notifyChanged():
I publi C vo1d addlistener( Playerlistener 1 ) ( listeners.add( 1 );
I public String getName() 1 return name:
I publi c Stri ng toString() 1 return (name + ": " + hand.toStringO ):
I publ ic void win() 1 notifyWinO :
I publiC void lose() ( not ifyLoseO :
I public vo1d standoff() I
611
I 612
Apêndice E
LISTAGEM E.10
Player.java (continuação)
no t 1fyStandoff() : }
publl C void blac kjac k() not 1 fySl ackj ack () : }
I
public Hand getHand() { return hand: } protected void notifyChanged() I Iterato r i · l i s t ene rs.iterator(): while( i.hasNext() ) 1 PlayerListener pl .. (PlayerListe ner) i.next(); pl.playerChanged( this ) : } } protected voi d notifyBusted() ( Iterator i .. li steners.iterator(); while( i.hasNext() ) ( Pl ayerli s tener pl .. (PlayerListener) i .nextO : pl . playerSusted( th is ); } }
protected yoid notifyBlackjack() { Iterator i • hsteners .iterator(): while( i.ha sNext() ) { PlayerListener pl : ( Playerlistener) i . next(); pl.playerBlackjack( this ): } } protected void notifyStanding() ( Iterator i · l i s teners . i t erator() : while( i .hasNextO ) ( Pl ayerListener pl .. (PlayerListener) i .next() ; pl.playerStand i ng( th is ) : } } protected yoid notifyStandoff() ( Iterator i • l i steners.iterator() :
Listagens do código do jogo vinte-e-um
LISTAGEM E.10
Player.java (continuação)
while( 1. hasNext( ) ) I Playerlistener pl • (PlayerListener) i .next(); pl . playerStandoff{ this ); J J
protected void notifyWin(} I Iterator i • listeners.iterator{); while( 1. hasNext() ) I PlayerListener pl • (Player l istener) i .next{); pl.playerWon{ this ) ; J J
protected vo l d not1fyLose() { Iterator i ·listeners.iterator(); while( i .hasNext() ) I Playerlistener pl z (PlayerListener) i .next(); pl .pl ayerlost( this };
J J protected fina l void setCurrentState( Pl ayerState state ) I current_sta te • state; hand . setHolder( state ) ; J
protected final PlayerState getCurrentStateO I return current_statei J
protected PlayerState getBustedState() { return new Busted(); J
protected PlayerState getStandingState() I return new Standing() ; J
protected PlayerState getPlayingState() { return new Playing(} ;
J protected Playe rState getWaitingState() ( return new Waiting();
613
I 614
Apêndice E
LISTAGEM E.10
Pl ayer.java (continuação)
}
protected PlayerState getBlackjackState{) I return new Blackjack{); }
protected abstract PlayerState getInitia IState(): protected abstract boolean hit( Dealer deale r ); private class Waiting implements PlayerState I public void handC hanged() { not i fyChanged () : }
publi c void handPlayable{) { setCurrent Sta te{ ge tPlayingState() l: II transição }
public void handBlackjack{) { setCurrentS ta te ( getBl ackj ackS ta te () ); notifyBlackjack(); II trans i ção }
pub l ic void handBusted{) { II impossível no estado de espera }
public void execute{ Dealer dea ler ) { II não faz nada enquanto espera } }
private class Busted implements PlayerState { public void handC hanged {l { II impossíve l no es tado de estouro }
public void handPl ayable() { II imposs fve l no estado de estouro }
pUblic voi d handBlackjack{) I II impossíve l no estado de estou ro }
publi c vo i d handBusted{) I II impossível no estado de estou ro }
publ ic void exec ute( Dealer deal er ) I
Listagen s do código do jogo vinte-e-um
LISTAGEM E.10
Player.java (continuação)
dealer.busted( Player.this ); II tennina } }
private class Blackjack implements PlayerState ( public void hand Changed{) I II impossfvel no estado de vinte -e-um }
public void handPlayable() I II impossfvel no estado de vinte-e-um }
public void handBlackjack() { II impossfvel no estado de vinte-e - um }
public void handBu sted() { II impossfve l no estado de vinte-e-um }
public void execute( Dea ler dealer ) ( dealer . blackjack{ Player.this ); II tennina } }
private class Standing imp lements Pl ayerState I public void handChanged() { II lmpossfvel no estado de parada }
public void handPlayable() { II lmpossfvel no estado de parada }
public void handBlackjack{) { II lmpossfvel no estado de parada }
public void handBusted{) { II impossfve l no estado de parada }
public void execute( Dea l er dealer ) ( dealer.standing{ Player . this ) ; II tenni na } }
private class Playing implements Playe rState I public void handChanged() ( notifyChanged() ; }
public vold handPlayable() I
615
I 616
Apêndice E
LISTAGEM E.10
Pl ayer.java (continuação)
II pode ignorar no estado de jogo }
public void handBlackjack() I II impossivel no estado de jogo }
publ ic void handBustedO I setCurrentS tate( ge tBustedState() ) ; notifyBusted(}i }
public void execute( Deale r dealer ) { if( hit( dealer ) } { dea l er.hit( Pl ayer.this }i } else ( setCurrentState( getStandingS t ateO ); notifyStanding{) i }
current_state.execute( dealer ) i II transiçao } }
}
LISTAGEM E.l1
PlayerUstene r. java
package blackjack .co rei public interface PlayerListener { public void playerChanged( Player player }; publi c void playerBusted( Player player l i publi c vofd playerBlackjack ( Player pl ayer }; publ ic yoid playerStanding( Player player } ; pub l ic yoid playerWon( Player player li public void playerLost( Playe r player )i public void playerStandoff( Player player li }
Listagens do código do jogo vinte-e-um
LISTAGEM E.12
PlayerState.java
package bl ackjack . core : public interface PlayerState extends Handlistener I publiC void execute( Oealer dealer ):
I
LISTAGEM E.13
Rank.java
package blackjack. core : import java.util . Collections: import java.util . list : import java.util.Arrays; public final class Rank { publiC public public public public public pubh c public publ ic public public public public
static stati c static stati c static stati c stati c static static static static static stat i c
final final final f ina l fi nal fi na 1 f ina l final final final fina l final fi na l
Rank Rank Rank Rank Rank Rank Rank Rank Rank Rank Rank Rank Rank
lWO THREE FOUR FIVE
• new " oew " oew • "ew 51' " oew SEVEN • "ew EIGHT • "e, NINE • ne, TE' " ne' JACK • "e, QUEEN - "e, KING " new ACE " "ew
Rank( Rank( Rank( Rank( Rank( Rank( Rank( Rank( Rank( Rank( Rank( Rank( Rank(
2 , "2" };
3, 4, 5, 6,
I; I; I; I; I; "S" I ; "9" I ;
"3 " "4 " " 5" "6" 7, "7"
S, 9,
lO, "10" ):
l O, "J" I ; lO, "Q" I ; 10, " K" I ; 11, "A" I ;
private static final Rank [] VALUES " I TWO, THREE, FOUR , FIVE , SIX , SEVE N, EIGHT, NINE, TEN , JACK , QUEEN , KING , ACE J:
II fornece uma lista não modif ic áve l para fazer l aço publ ic static final List RANKS ~ Co 'l ec t ions .unmodifiablelist( Arrays.asList( VALUES ) }: private fina l int rank: private fi na 1 String disp lay ; private Rank( int rank , String disp l ay ) { this.rank z rank;
617
I 618
Apêndice E
LISTAGEM E.13
Rank.java (continuoção)
thiS.display • displaYi }
publlC int getRankO { return rank i }
publ ic String toString() { return displaYi } }
LISTAGEM E.14
$u;t.java
package blackjack.corei i mport java . util. Co ll ections; i mport java.utll.listi import java.ut il.Arraysi publiC final class Suit {
Ildefine publl c publl c public public
estaticamente todos os valores static final Suit OIAMONOS - new st atic final Suit HEARTS • new static final Suit SPAOES • new static final Suit (LUBS • new
private sta tic final Suit [] VALUES
=
válidos de Su it Suit ( (c har)4 ) i Suit ( (char)3 )i Suite (char)6 ) ; Suite (char)5 )i {OIAMONDS. HEARTS . SPAOES . CLUBS }i
II fornece uma lista n1l0 modificáve l para fazer l aço public st atic fi nal List SUI TS • Collect i ons.unmodifiable Li st( Arrays.asList( VALUES ) )i
II variável de instância para conter o valor de exibiçã o private final char displaYi
II
não permite inst ancia ção por objetos ex ternos private Suit e char disp lay ) { lh i s. display • displ ay; }
II retorna o valor de Suit publ ic String toStringO {
Listagens do código do jogo vi nte-e-um
LISTAGEM E,14
6 19
$ult.java (continuação)
return String.valueOf( display }; } }
blackjack.core.threaded bl ack j ack. core. threaded contém um objeto Bl ackjackOeal er que coloca osjogadores em suas próprias lin has de execução (Listagem E. IS). LISTAGEM E.15
ThreadedBl ackjac kDea 1er.j ava
package blackjack . core.threaded; import blackja ck. co re.*; i mport java.utll .ArrayLi st; import java.uti 1. Iterator ; public cl as s Threaded BlackjackDealer extends BlackjackOealer ( publiC ThreadedBlackjackOealer( String name , Hand hand, Oeckpile cards ) ( supere name , hand, cards ); }
protected PlayerSta te getWaitingState() ( return new OealerWaiting(); }
protected PlayerState getCollectingBetsState() ( return new DealerCol l ectingBets() ; }
private class DealerCollectingBets implements PlayerState ( publi c void handChanged{) { II impos sí ve l no estado de aposta }
public void handPl ayable() { II impossíve l no estado de aposta }
public void handBl ackjack() { II impossíve l no estado de aposta }
publ ic void handBusted() { II impossível no estado de aposta
I 620
Apêndice E
LISTAGEM E.15
ThreadedBl ackjackDea 1er .java (cont inuoçõo)
}
public void execute( fina l Oea ler dealer } { H( !betting_p1ayers. isEmptyO } { final Player player ~ (Player) betting_players.get( O }i betting players.remove( player }; Runnable runnable = new Runnable(} ( publi c void run() I player.play( dealer }; }
}; Thread threaded = new Thread( runnable }i threaded.start(}i } else I setCurrentState( getDealingState() )i getCurrentState().execute( dealer )i II faz a transição e executa }
} }
private class DealerWaiting implements PlayerState ( publi c void handChanged() { II impossfvel no estado de espera }
publ i C void handPlayable() { II impossfvel no estado de espera }
publiC void handBlackjack() { II impossfvel no estado de espera }
publiC void handBusted() I II impossfvel no estado de espera }
public void execute ( final Dealer d ) I H( !waiting_players.isEmptyO ) { final Player p (Player) waiting_players.get( O )i waiting_players . remove( p ); Runnable r z new Runnable() I public void runO { p.play( d )i K
}
}; Thread t • new Thread( r )i t.startO i } else {
Listagen s do código do jogo vinte-e-um
LISTAGEM E.15
621
ThreadedBl ackjackDea 1er .java (cont inuoção) setCurrentState( getPlayingState() exposeHand () ; getCurrentState().execu te( d ); II faz a transição e executa
l;
} } }
}
blackjack.exe blackjack.exe contém os executáveis para GU I MVC, GU I PAC, UI CU e si mu lador (I iSlagens E. 16 a E. 19).
LISTAGEM E.16
Bl ackjackCLI.java
package bl ackjack.exe; i mpo rt blackjack.core .*: impo rt blackjack.players.*; import blackjack.ui.*; public class Bl ackjackClI 1 publ1c static vo1d maln( String [] args ) 1 Oeckpile ca rds ·new Oeckpile() : for( int i .. O; i < 4; i ++ ) { cards.shuffl eO: Deck deck • new Deck(): deck.addToStack( cards l ; cards. shuf fle{): }
Hand dealer_hand • new Hand(); BlackjackDealer dealer .. new BlackjackDealer{ "Oealer" , dealer_hand. cards " );Bank human_bank .. new Bank{ 1000 ); Hand human_hand • new Hand(l: Player player .. new Coomandl inePlayer( "Human", human_hand. human_bankl; dealer . addllstener{ Consol e.INSTANCE ) ; player.a ddl istener{ Consol e.INSTANCE l; dea l er.addP layer( pl ayer ); do
I
I 622
Apêndice E
LISTAGEM E.16
BlackjackCLI.java (continuação)
dealer.newGame( ); } while ( playAga i n() ); Console .INSTANCLprintMessage( "Thank you for playing !" ); J
private sta t ic boolean playAgain() ( Console. INSTMCLprintMessage( "Wou ld you like to play again? [Y] es [Nlo" ) ; String response -Co nsole. IN STANCLreadlnput( "invalid" ); if( response.equalsIgnoreCase( "y" ) ) { return true; J
return fa 1se; J
J
LISTAGEM E.17
BlackjackMVC .java
package blackjack.exe ; impo rt import impo rt import import import
blackjack.core .*; blackjack.core . th readed .*; blackjack.u i.mvc .*; javax.swing.* : java . awt .*; java.awt.event.*;
public class 8lackjackMVC extends JFrame ( public static void main( St r ing [] args ) ( JFrame frame = new 81ackjackMVC() ; frame.ge t ContentPa ne(). set8ackg round( FOREST GREEN )i frame.setSize( 580 , 480 ); frame.show(): J
private BlackjackDealer dealer; priv ate GUIPl ayer human: private JPanel players - new Jpanel( new Gridlayout( O, 1 ) }; private static fina l Color FOREST GREEN = new Color( 35 , 142, 35 ,; public Bl ackjackMVC() {
Listagen s do código do jogo vinte-e-um
LISTAGEM E.17
BlackjackMVC .java (continuação)
setUpO ;
WindowAdapter wa • new WindowAdapter() ( public void windowClosing( WindowEvent e ) I System.exit ( O ); } };
addWindowListener( wa }; }
II precisa ser protegid o se houve r subclasse private PlayerView getPlayerView( Player player } { PlayerView view • new PlayerView( player }; view.setBackground( FOREST_GREEN }; return view; }
II precisa ser protegido se houver subclasse private void setUp{) { BlackjackOealer dealer • getOealer(}; PlayerView vI • getPlayerView( dea l er }; GUIPlayer human • getHuman(}; PlayerView v2 • getPlayerView( human ): PlayerView (] views • I vI, v2 }: addPlayers{ views }: dealer.addPl ayer( human }; addOptionView( human, dealer }: }
II precisa ser protegido se houver subclasse private void addPlayers( PlayerV iew [] pview ) { players.setBackground( FOREST_GREEN }: for( int i • O: i < pview . length: i ++ } { players.add( pview [i] }; }
getContentPane().add( players, Borderlayout.CENTER }: }
private void addOpt ionView( GUIPlayer human , BlackjackOealer dealer ) I OptionView ov • new OptionView( human, dealer ): ov.setBackground( FOREST_GREEN }: getContentPane().add( ov, BorderLayout.SOUTH };
623
I 624
Apêndi ce E
LISTAGEM E.17
Bl ackjackMVC .java (continuação)
}
private 81ackjackDealer getDea1er() ( if( dea1er == null ) ( Hand dea1er_hand = new Hand(); Deckpi 1e cards ,. getCards() ; dea1er" new ThreadedBlackjackOea1er( "Deale r ", dea1er_hand, cards) ; }
return dea1er; }
private GUIP1ayer getHuman( ) ( if( human a. nu11 ) ( Hand human_hand new Hand() ; 8ank bank ,. new Bank( 1000 ) ; human" new GUIPl ayer( "Huma n", human_hand , bank ); 3
}
return human; }
pr;vate Oeckpi1e getCards() { Oeckpi1e cards ,. new Oeckpile(} ; for( int i ,. O; i < 4; i ++ ) { cards.shuff1e{} ; Deck deck • new VOeck(); deck.addToStack( cards ); cards.shuffleO; }
return cards ; }
}
LISTAGEM E.18
BlackjackPAC.java
package b1ackjack.exe ; i mport bl ackjack.core.*; i mport bl ackjack. ui. pac. *; • •; import Javax . swlng. . t .• ; i mpo rt Java.aw l. mpor t ·jdva.awt.even t . • ; •
pub1ic cl ass B1ackjackPAC extends J Frame {
Listagen s do código do jogo vi nte-e-um
LISTAGEM E.18 8lackjackPAC .java (continuação) publ iC static void main( String [] args ) { JFrame frame • new Bl ackj ackPAC(). frame .getContentPane(}.setBackgraund{ FOREST_GREEN )i frame.setSize{ 580. 480 ). frame.shaw(); J
private VPlayerFactory factary = new VPlayerFactorY()i private JPanel players • new Jpanel( new GridLayout( O. I ) ) i private static final Colar FOREST GREEN
=
new Co l or( 35 . 142 . 35 );
public Bl ackjackPAC() { se tup() ; WindowAdapter wa • new WindowAdapter() { pub l i c voi d windowC l os i ng( WindowEvent e ) { System.exit( O )i J J;
addWindowListener( wa ,;
J
II preci sa ser proteg ido se houver subc l asse private void se tup() { VBlackjackDealer dealer • factory.getOealer()i GUIPlayer huma n
=
factory.getHuman() ;
dealer.ad dPl ayer( human }; players.add( dealer.view() ); players.add( human.view() ) ; getContentPane().add( pl ayers . 8orderLayout.CENTER )j J J
LISTAGEM E.19 Bla ckjackSim .java pa ckage bl ackja ck . exe; import blackjack.co re.*; import blackjack.ui.* ; impo rt blackjack.players.*;
625
Apêndi ce E
LISTAGEM E.19
8lackj ackSim .java
publ i c class BlackjackSim { public static void main( String [] args } I Console , INSTANCE.printMessage( "How many t imes should the simula tor pl ay?" ): St ring response • Conso l e.INSTANCE . read Input( "invalid" ): 1nt loops • Integer.parselnt( response ) : Oeckpile cards • new Deckpile() : for( int i • O: i < 4: i ++ ) { ca rd s . shuffl e () : Deck deck • new Deck(): deck.addToStack( cards ) : cards.shuffl eO:
I
II
cri a uma banca Hand dea le r hand • new Hand() : BlackjackOealer dealer" new Bl ackjackDealer( -Oeal er". dealer_hand . cards ) :
II
cria um OneHitPl aye r Bank one_bank • new Bank( 1000 }: Hand one_hand • new Hand( ) ; Pl ayer oplayer • new OneHHPlayer( "OneHH ", one_hand , one bank );
II
cr ia um SmartPlayer Bank smart_bank " new Bank( 1000 ) : Hand smart_hand • new Hand(): Pl ayer smplayer · new SmartPlayer( "Smart" , smart_ha nd. smart_bank ):
II
cria um SafePlayer Bank safe_ban k new Bank( 1000 }: Hand safe_hand = new Hand() ; Pl ayer spl ayer • new SafeP l ayer( "Safe" , safe_hand , safe bank ) i t
II
cria um FlipPlaye r Bank flip_bank • new Bank( 1000 ) : Hand flip_hand • new Hand() ; Playe r fplayer = new FlipPlayer( MFlip". flip_hand. fl i p_bank )i
II
cria um jogador intel igente Bank kn_bank • new Bank( 1000 ); Hand kn_hand " new Hand();
Listagens do código do jogo vi nte-e-um
627
LISTAGEM E.19 8lackjackSim .java (continuação) Pl ayer knpl ayer ,. new Know ledgeablePlayer( "Knowledgeable", kn_hand. kn_bank );
/1 cri a um jogador "õtlmo" Bank opt_bank ,. new Bank( 1000 ) ; Hand opt_hand • new Hand(); Player optp l ayer ,. new OptlmalPl ayer( "Opti mal " , opt_hand. opt_bank );
1/ reúne todos os jogadores dealer.addlistener( Console.INSTANCE ) ; oplayer.addlistener( Console . INSTANCE ) ; dealer . addPlayer( opTayer )i splayer.addlistener ( Conso l e. INSTANCE ) õ dealer.addPlayer( splayer ) i smplayer . addllstener( Console.INSTANCE )õ dealer . addPlayer( smplayer )i fplayer .add listener( Console.INSTANCE ) õ dealer.addPl ayer( fplayer ) ; knplayer.addlistener( Console.INSTANCE ); deal er.addPl ayer ( knplayer ); op tplayer.addli stener( Console . INS TANCE ); dealer.addPlayer( optplayer li int counter • O; while( counter < loops ) { dealer . newGame( ) õ cou nter ++; J J
J
blackjack.players bl ackjack. pI ayers contém os vários jogadores que fo ram criados no texto e cm alguns dos exercícios (listagens E.20 a E.26). LISTAGEM E.20 COrmland Li neP l aye r . j ava pa ckage bl ackj ack.pl ayers õ import blackjack.core.* õ import blackjack .ui.*õ
Apêndi ce E
LISTAGEM E.20
COl11TlandL i nePl aye r . java
publ i c class Command LinePlayer extends BettingPlayer ( private private pri vate private private private private private private private private
final fi na 1 fi na 1 fi nal fi nal fi na 1 fi na 1 fi na 1 final final fina 1
static statlc static static static static static static static static static
String String String Str; ng Str; ng String Stri ng String String String String
HIT " "H" : STANO ~ "S" : PLAY _MSG = "[H] H ou [5] tay " ; BET_MSG = ·Pl ace Bet:(l O] [50] or [100]"; 00- MSG • "Oouble Oown? [i] es [N]o " ., BEl_lO = "10" : BEl_50" "50": BEl_IOO = "100" : NO = "N": YES = "Y" : OEFAULT" "i nva li d":
publ ic CommandLinePlayer( String name , Hand hand , Bank bank ) ( supere name , hand, bank ):
I protected boolean hit( Oealer dealer ) ( while( true ) ( Console. INSTANCE.printMessage( PLAY_MSG ) : String response • Console.INSTANCE. readlnput( OEFAULT ); if( response.equa l sIgnoreCase( HI T ) ) I return true: I el se if( response.equalsIgnoreCase( STANO ) ) I return false: I II se chegarmos até aqui . faz laço até obtermos uma entrada II significativa I
I protected boolean doubleOown ( Oealer dea l er ) ( while( true ) ( Console. INSTANCE.printMessage( OO_MSG ) ; Stri ng response • Conso l e.INSTANCE.readlnput( OEFAUlT ): if( response.equals IgnoreCase( NO ) ) ( return false; lelse if( response.equalsIgnoreCase( YES ) ) { return true;
I
II se chegarmos até aqui , faz l aço até obtermos uma entrada significativa I I
Listagens do código do jogo vinte-e-um
LISTAGEM E.20
Colt1t1andLi nePl ayer. j ava (cont inuoção)
protected void betO ( whl1e( true ) { Co nso le . INSTANCE.pri ntMessage( BET_MSG )j String response = Console . INSTANCE . readIn put( DEFAULT )j if( re sponse.equa l s( BET_I0 } ) I getBank() . place lOBet()j return j J
if( response . equals( BET_50 ) ) I getBank() . place50Bet() : returnj J
if( response .equal s ( BET_I00 ) } ( getBank().placelOOBet(); returnj J
II se cheganmos até aqui. faz laço até obtenmos uma entrada II s ignif ica t iva J J
J
LISTAGEM E.21
Fl ipPlayer. java
package blackjack.players : import blackjack.core.*; pub l ic class FlipPlayer extends BettingPlayer { private boolean hit • false; private boolean should_hit_once
~
false ;
publ ic Fl i pPlayer( Stri ng name. Hand hand. Bank bank ) { supere name, hand , bank };
J public boolean hit( Dea ler dealer ) { if( should_hit_once && !hit ) { hit .. true; return true j J
629
Apêndice E
LISTAGEM E.21 Fll pPl ayer .java (continuação) return false : }
publlC voi d resetO I super. reset O; hit • false ; should hit once •
- -
}
public void bet() I getBank{) .place l OBetO ; }
publi c boolean doubleDown( Deale r dealer ) { ret urn false: } }
LISTAGEM E.22 KnowledgeablePlayer.java package bl ackja ck.players: import blackjack. core .*: publ i c cl ass Knowl edgeablePlayer extends BettingPl ayer I public KnowledgeablePlayer( String name, Hand hand , Bank bank) I supere name, hand, bank ) ; }
pub l ic boolean doubleDown( Deale r dealer ) { int total · getHandO . total O ; 1f( total ·· 10 I I total ,.'"' 11 ) { return true: }
ret urn false: }
pub l iC boolean hit( Dealer dealer ) I int total. getHandO.total0: Ca rd ca rd = dealer. getUpCard( ) : l/nunca di stribu i mais cartas , independente de qual, se total> 15 1f( total> 15 ) I
Listagens do código do jogo vi nte-e-um
LISTAGEM E.22
Knowl edgeab 1ePl ayer .java (continuação)
return false;
I l/sempre distribui mais cartas para 11 e menos 1f( total c- 11 ) { return true; I l/ isso deixa 11, 12, 13 , 14 l/baseia a decisão na banca 1f(
card .getRank() .getRankO return true;
>
7 ) {
I return false:
I pub l ic voi d bet() { getBank().place lOBet();
I I
LISTAGEM E.23
OneHitPlayer.java
package blackjack.players; import blackjack . core.*: pub l i c class OneHit Player extends Bett1ngPlayer { pr1vate boolean has_hit • false : publ iC OneHitPlayer( String name , Hand hand, Bank bank ) { supere name , hand, bank ): I pub l ic boole an hit ( Dea ler dea ler ) { 1f( !has_hit ) { has_h it - true; return t rue ;
I return fa lse ;
63 1
I 632
Apê ndi ce E
LISTAGEM E.23
OneHitP1ayer .java (continuação)
}
public void reset() I super.reset(): has hit .. false: }
public void bet() { getBank() .placelOBet(): }
publiC boolean doubleOown( Oeale r dealer ) { return false; } }
LISTAGEM E.24
Opt ima lPlayer.java
package bl ackjack . players : import blackjack .co re.*: public class Opt imalPlayer extends BettingPlayer I publi C OptimalPlayer( St ring name, Hand hand , Bank bank ) I super( name , hand, bank ): }
public boo lean doubleOown( Oealer dealer ) { i nt total .. getHand() . tota 1 () : Card ca rd = dealer.getUpCard() : if( total ·· 11 ) { return true: } H( total .... 10 ) {
if( card . getRank().getRank() != Rank.TEN.getRank() && card .getRankO )" Rank .ACE ) { return true: }
return fa l se: }
if l tot.1 •• 9 } I H( card.getRankO .... Rank. TWO
II
card.getRank() .... Rank.THREE
II
Listagens do código do jogo vinte-e-um
LISTAGEM E.24
Opt imal Player . java (conmt inuoção) card.getRankO == Rank.FOUR II card .getRank() •• Rank.FIVE I I card .getRank{) •• Rank.SIX ) { return true;
}
return fal se ; }
return false; }
publi C boolean hit( Oealer dealer ) { ; nt total .. getHand O . tota 1 () ; Card ca rd • dealer.getUpCard() ; H( total
>.
17 ) {
return false; } H{ total ·" 16 ) { H{ card.getRankO == Rank..SEVEN
II II
card .getRank() •• Rank.EIGHT card .getRa nk {) •• Rank..N INE ) I return true; } else I return false; }
} H{ total·· 13
II
total·· 14 II total ... 15 ) { if( card.getRank{) •• Rank.TWO I I card.getRankO == Rank . THREE II card.getRank.{) .... Rank .FOUR II card.getRank{) ,,~ Rank . FIVE I I card.getRank.{) == Rank . SIX ) { return false; } else { return true; }
}
if( total .~ 12 ) { H( card.getRankO == Rank..FOUR II card .getRank() •• Rank.FI VE II card.getRank{) •• Rank.S IX ) { return false; } else {
633
I 634
Apêndice E
LISTAGEM E.24
Opt imal Player .java (conmt inuoção) return true:
I I return truei
I publ ic voi d betO { getBank().p l ace l OBet() :
I I
LISTAGEM E.25
SafePlaye r.java
package blackjack.p l ayers i i mport bl ackja ck. co re.*: publiC cla ss SafePlayer extends BettingPlayer { publ ic Sa feP layer( String name , Hand hand, Bank bank ) ( super( name , hand. bank )i
I publi C boolean hit ( Oealer dealer ) ( return falsei
I publiC boolean doubleOown( Dealer dealer ) { return fa lse i
I public void bet() { getBank() . placelOBetO i
I I
LISTAGEM E.26
SmartP l ayer.java
pa ckage bl ackjack.players; import blackjack. core.*i public cla ss Sma rtPlayer extends Bett i ngPlayer {
Listagen s do código do jogo vinte-e-um
LISTAGEM E.26
SmartPlayer.java (continuação)
publiC SmartPlayer( St ring name, Hand hand , Bank bank ) ( super( name , hand, bank ); J
publiC boolean hit( Oealer dealer ) ( if( getHand().total() > 11) I return fal se ; J
return true ; J
publiC void bet() I getBank() .placelOBet(); J
publ ic boolean doubleOown( Dealer dealer ) { return false; J J
blackjack.ui blackjack.ui contém cód igo da UI comum (Listagem E.27). LISTAGEM E.27
Co nso l e.java
package blackjack.ul ; import import import import
blackjack.core.*j java . io.BufferedReader; java.io. lnputStreamReader j java.io.IOException:
pub li C cl ass Console imp lements PlayerListener {
II console singleton public final static Console IN STANCE
=
new Console() :
private BufferedReader in = new BufferedReader( new InputS treamReader( System.in ) ); publiC void printMessage( String message ) ( Sys tem.out.print ln( message );
635
I 636 LISTAGEM
Apê ndi ce E
E.27
Console .java (continuação)
}
publiC String readlnput( St r ing default_input ) ( String response; try ( return i n.read Line(); I cat ch (IOException ioe) I return default_input; } }
public void pl ayerChanged( Playe r pl ayer ) { printMess age( player.toString() , ; }
public vo i d playerBusted( Player player ) I printMessage( player.toStringO + "BUSTEOl" , ; }
pub l ic void playerBlackjack( Pl ayer player ) I pr i ntMessage ( player.toStringO + "BLACKJACK!" ); }
publiC void playerStanding( Pl ayer player )1 printHessage( pl ayer . toStringO + "STANOING • }; }
publi C void playerWon( Pl ayer pl ayer ) I printMessage( pl ayer . toStringO + "WINNER!" ); }
publi C void playerLost( Pl ayer pl ayer ) I printMessage( player.toStringO + "LOSER!" ,; }
publiC void playerStandoff( Player player ) I pr; ntMessage ( pl ayer. toS t ri ng () + "STANDOFF" ); }
l/privado para ev i tar instanciação private ConsoleO {} }
blackjack.ui.mvc blackja ck. ui .mvc contém o código mvc (listagens E.28 a E.34).
Listagens do código do jogo vinte-e-um
LISTAGEM E.28
Ca rdVi ew.java
pa ckage bl ackja ck . ui .mvc ; import blackj ack .core .*; import javax.swing.*; import java . awt.*; publi c cla ss CardView extends Jlabel ( pr i vate Imagelcon icon ; publiC CardView( VCard card ) ( ge t Image( ca rd.get lmage() )j set Icon( i con ); setBackg round( Co lor.white ); setOpaque( true )i
I pri vate void getlmage ( St ri ng name ) ( java.net.URl url • this.getC l ass() . ge tResource( name l i icon • new Imagel con( url ) i
I I
LISTAGEM E.29
GUIPlayer. j ava
package blackj ack . ui.mvc ; import blackjack.core.*; publ ic cla ss GU IPlayer extends Bett ingPlayer { private Dealer dealer; pu bl iC GU IPlayer ( String name , Hand hand , Bank bank) ( s upere name, hand , bank );
I pub l ic boolean hit ( Dea ler dealer ) { return truei
I public voi d betO I I I não faz nada , is t o não será chamado II em vez disso . o jogador humano press iona um bo t ão da GUI
637
I 638
A pêndi ce E
LISTAGEM E.29
GUIP1ayer.java (continuação)
}
II II II
esses métodos de aposta serão chamados pelo controlador da GUI para cada um: faz a aposta correta, muda o estado, penmite que a banca saiba que o j ogador tenminou de apostar publ ic void placelOBetO I getBank().placelOBet(); setCurrentState( getWaitingState() ); dealer.doneBetting( th is );
}
publiC void place50Bet() ( getBank() .place50Bet(); setCurrentState( getWaitingState() ); dealer . doneBetting( this ); }
pub l iC void placelOOBet() ( getBank() . pl ace l OOBet() ; setCurrentState( getWaitingState() ) ; dealer.doneBetting( this ); }
II II II
a aposta em dobro é um pouco diferente , pois o jogador precisa responder aos eventos de Hand quando uma carta é adicionada na máo de modo que configura o estado como Ooubli ngOown e depois o executa protected boolean doubleDown( Dealer d ) I setCurrentState( getDoublingDownState() ); getCurrentState().execute( dealer ) ; return true;
}
II II
takeCard será chamado pe l o contro l ador da GUI quando o jogador decidir receber mais cartas public void takeCard() { dealer.hit( th is ) ; }
II II II
stand se rá chamado pel o controlador da GUl quando o jogador optar por parar. quando a parada mudar de estado. deixa o mundo saber. e depois diz á banca public void standO { setCurrentSta te ( getS tandi ngS tate () ); not ifyStanding() ; getCurrentState().execute( dealer );
Listagens do código do jogo vinte-e-um
LISTAGEM E.29
GUIP1ayer.java (continuação)
}
II II
você precisa sObrepor play para que ele armazene a banca para uso posterior public void play( Dealer dealer ) I this.dealer • dealerj super.p l ay( dea l er ); }
II
o seguinte trata dos estados protected PlayerState getPlayingState() I return new Playing(); }
protected PlayerState getBettingState() I return new Betting(); }
private class Playing implements PlayerState I publiC void handPlayable() { II não faz nada }
publiC void handBlackjack() I setCurrentState( getBl ackjackState() ); notifyBlackjack(); getCurrentState().execute( dealer ) ; }
publiC void handBusted() I setCurrentState( getBustedState() ); not ifyBus ted () ; getCurrentState().execute( dealer ); }
public void handCh anged() I notifyChanged(); }
publiC void exec ute( Dealer dealer ) I II não faz nada aqui, as ações virão da GUI, Que é II externa ao estado, mas quando eventos vie rem, certifica- se de II força r a transição de estado imediatamente }
639
I 640
Apêndice E
LISTAGEM E.29
GUIP1ayer.java (continuação)
}
private class Betting implement s Pl ayerState I public void handChanged{) { II impossfvel no estado de estou ro }
public void handPlayable() { II impos sfvel no estado de estouro }
public void handBlackjack() { II impossfvel no estado de estouro }
public void handBusted() I II impossfvel no estado de estouro }
publ1c vold execute( Oea l er dealer ) I II não faz nada aqui, as ações virão da GUI, que é II exte r na ao estado, pois nenhum evento aparece como parte da II aposta, o estado precisará ser mudado externamente para este estado } }
}
LISTAGEM E.30
OptionView.java
package blackjack . ui.mvc: import blackjack . core.*; • •: import Javax.swlng. . import Java . aw t . • : •
pub li c class Op tionVi ew extends JPanel { pub 1i c publi c publ ic publi c publi c pub l ic pub l ic publi c
static static static static static static statlc static
fi nal fi na l final final fi nal fi na l final final
String String String String String St r ing String String
NEW GAME QUIT HIT STAND BEl- 10 BEl 50 BEl 100 OOUBlE OOWN
"new": • "quit" : - "hit"; • "stand ": • "BETlO" : •
• • •
~BET50":
-BETlOO M; Mdd'" •
private JButton bet 10 • new Jbutton( " $10" ); pr; vate JButton bet 50 • new Jbutton( "$50" ): private JButton bet 100 • new Jbutton( "$1 00" l: -
Listagens do código do jogo vinte-e-um
LISTAGEM E.30 private priva te private private private pr ivate private
Opt i onVi ew .java (continuação)
JButton deal • new Jbutton( NNew Game- ); JButton quit • new Jbutton( "Quit- ); JButton hit • new Jbutton( "Hit" ) ; JButton stand = new Jbutton( ·Stand N ) : JButton ddown z new Jbutton( -Double Down· ); BlackjackDealer dealer: GUIPlayer player:
private stat1c final Color FOREST_GREEN
=
new Co lor( 35 , 142, 35 ) :
publi C OptionView( GUIPlayer player , BlackjackDealer dea l er ) I super( new BorderLayout() ): this.player • player: this.dea l er • dealer; attachController( makeContro l ler() ); buildGUI ();
I publiC void attachController( OptionViewControl ler controller )1 deal.addAction l istener( control le r ) : quit.addActionlistener( controller ) ; hit . addActionListener( contro ll er ) ; stand.addActionlistener( controller ): bet_lO.addActionListener( controller ): bet_50.addActionlistener( controller ) ; bet_lOO . addActionListener( controller ): ddown.addActionlistener( controller }:
I public void enableDoubleDown( boolean enable ) I ddown . setEnabled( enable };
I publi C vo id enableBettingContro l s( boolean enable ) I bet_l O. se tEnabled( enable ); bet_50 . setEnabled( enable ) : bet_lOO.setEnabled( enab l e );
I public void enablePlayerControls( boolean enabl e ) ( hit . setEnab l ed( enable ); sta nd. setEnabled( enable l ;
I public vo1d enableGameControls( boo l ean enabl e ) I
641
Apêndice E
LISTAGEM E.30
OptionView. j ava (con tinuação)
dea l. setEnab led( enable}; quit .se tEnab led( enable ); J
protected Op t ionV iewCont roller makeController(} { return new Opt i onV i ewControl ler( player, dealer, this }; J
priva te vo1d buildGUI() I JPanel betting_con trols • new JPane l () ; JPa nel game_control s = new Jpanel() ; add ( betting_controls. Border layout.NORTH ); add( game_controls. Borderlayout . SOUTH ); betting_contro l s .se tBackground{ FOREST_GREEN }; game control s .setBackground( FOREST GREEN ); ddown. setAc ti onCommand( DOUBlE_DOWN ) ; dea l .setActi onComma nd( NEW_GAME ) i quit. setActionComma nd( QUIT } i hit .se tActionComma nd( HIT ) ; sta nd .setAct i onCommand( STAND ) ; bet_lO.setAct i onCommand ( BET_lO l ; bet_50.setActionCommand( BET_50 l; bet_lOO .se tAct ionCommand( BET_100 }; betting_control s.add ( bet_lO l ; bett i ng_control s.add ( bet_50 l ; betti ng_controls . add ( bet_IOO l ; game_contro l s . add( ddown }; game_control s.add( hit l ; game_contro l s . add( st and }; game_contro l s .add( deal ) ; game_controls.add( quit }; enableBettingContro l s( fal se ); enablePlayerControls( false }; enableDoubleDown( false }; J
J
LISTAGEM E.31
Opt i onVi ewContro l l er . j ava
package blackjack.ui.mvc; import blackjack. core.· ; impo rt java. awt.e ven t .· ;
Listagens do código do jogo vinte-e-um
LISTAGEM E.31
643
Dpt 1onVi ewContro 11 er .java (cont inuoção)
publiC class OptionViewController implements Actionllstener , Pl aye rlistener { prlvate GUIPlayer mode1i private QptionView view: private BlackjackDealer dealer; public OptionViewContro l 1er( GU IPlayer model , BlackjackOealer dealer , OptionView view ) ( thls.model • model i model.addlistener{ this ) i this.dealer • dealer ; thls.view • view; view.enablePlayerControls( false ) i J
public void actionPe rformed( ActionEvent event ) { if( event.getActionCommand() . equals( OptionView .QUIT ) ) ( System.exit( O )i ) else if( event.getActionCommand().equals( OptionView . HIT ) ) ( view.enableDoubleOown( false ) i model.takeCa rd( ); } else if( event . getActionCommand().equals( OptionView.STAND ) ) { view.enableOoubleOown( false }; mode l. standOi } else if ( event.getActionCommand().equal s( OptionView . NEW_GAME ) ) { view.enableOoubleDown( false )i view.enableGameControls( false }; view.enablePlayerControls( false )i view.enableBettingControls( true )i dealer.newGame(); I else i f{ event.getActionCommand().equals( OptionView.BET_lO ) ) { vlew . enableBettingControls( false }; view.enablePlayerControls( t rue )i view.enableDoubleDown( true ) ; model.placelOBet()i ) else if( event.getActionCommand().equals( OptionVi ew.BET_50 l ) { view.enableBettingControl s( false li vlew . enab lePlayerContro ls( true ); view .enableDoubleOown( true )i model.place50Bet() ; } else if( event.getActionCommand() . equals( OptionView.BET_lOO ) ) { view.enabl eBettingContro l s( false )i view.enab l ePlayerControls( t rue ); view.enableOoubleOown( true ): model.placelOOBetO i
I 644 LISTAGEM E.31
Apêndice E
OptionV i ewController.java (continuação)
} else if( event.getActionCommand().equals( OptionView .OOUBLE_OOWN ) ) { view.enableBettingControls( false }; view .enablePlayerControl s( false ); view.enableOoubleOown( false ); view .enableGameControls( true ); model.doubleOown( dealer ); I
I public vo i d pl ayerChanged( Player player } {} publiC void playerBusted( Player player ) { view.enablePl ayerControls( false ) ; view.enableOoubleOown( false ) ; view.enableGameControls( true ) ;
I pub l iC void playerBlackjack( Player player ) { view.enablePlayerCont rols( false }; view.enableOoubleOown( fal se ) ; view.enableGameControls( true ) ;
I publiC void playerStanding( Player player ) ( view .enablePlayerControl s( false ) ; view.enableGameControls( true ); I public void playerWon( Playe r player ) ( view.enablePlayerControls( false ); view.enableGameControls( true );
I publiC void playerLost( Player player } ( view.enablePlaye rControls( false ); view.enableOoubleOown( false }; view.enableGameControls( true );
I public void playerStandoff( Player player ) ( view.enablePlayerContro ls( fal se ); view.enableGameControls( true }; I
I
Listagen s do código do jogo vinte-e-um
LISTAGEM E.32
PlayerView.java
package bl ackjack . ui .mvc: import import import import import
blackjack.core .*: . . : Javax.swlng. javax.swing.border.*: . t. j Java.aw java.util.lterator: ~
~
public class PlayerView extends JPanel implements PlayerL1stener {] private JPanel cards • new JPanel (new FlowLayout( FlowLayout.LEFT ) ): private T;tledBorder border: publi c Pl ayerView( Player player ) ( super( new Borde r LayoutO ): bUildUI( player )i player.addListener( this );
I public void playerChanged( Player pl ayer ) ( border.setTille( player . getName() ) : ca rds.removeAl 1(): Hand hand • player.gelHand() : Iteralor i • hand.getCards(); while( 1.hasNext() ) ( VCard vcard • (Vcard) i.next() : JLabel card : new CardView( vcard ) ; cards.add( card ):
I reval idateO; repa;ntO i
I publi c vo i d playerBusted( Player player ) ( border.setTitle( player.getNameO + " BUSTED!" l; cards . repaint();
I pub l ic void playerBlackjack( Player pl ayer )( border.setTille( player.getName() + ~ BLACkJACk!~ }; ca rds. repaint( ) i
I public void playerSlanding( Player player ) ( border.setTitle( player.getNameO + • STANOING " ) ; card s. repaint():
645
I 646 LISTAGEM E.32
Apê ndice E
Pl ayerVi ew .java (c on tinuação)
}
public voi d playerWon( Player playe r ) I bo rder.se tT itle( playe r.getNameO + " WINNER!" )i ca rd s.repa int( )i }
publ ic vold pl ayerLost( Player playe r ) ( border.setTitle( player.getName() + " LOSER!" li cards .repain t() ; }
pu bli c vo i d pl ayerStandoff( Player player ) I border.setTitle( player.getNameO + " STANDOFF!" ) i cards .repaint(): }
pri vate vo id buildU I( Player pl aye r ) ( add ( cards . Borderlayou t.NORTH l i bo rde r • new TitledBo rder ( player .getName () l: ca rds.set6order( borde r ); ca rds.setBackground( new Color( 35. 142 . 35 ) li border.setTit leCo lor ( Color.black li } }
LISTAGEM E.33
VCard.java
pack age blackjack.ul.mvc : import blackjack . core. *j public cl ass VCard extends Ca rd I private String imagei publi c Yca rd( Suit suit , Rank rank, super( suit. rank ) i th i s. image • image i }
publiC St ring getlmage() { if( is Fa ceUp() ) ( return image i
St ring lIl1dge ) I
Listagens do código do jogo vinte-e-um
LISTAGEM E.33
VCard.java (continuação)
} else { return "/ b1ackj ack/ ui / bi tmaps/ empty _pi 1e. xbm" ; } }
}
LISTAGEM E.34
VDeck .java
package blackjack.ui .mvc ; import blackjack.core.*j import java.util . lterator; pub li C class VDeck extends Deck { protected void bu ildCardsO { // Isso ê horrível, mas é melhor do que os laços e estruturas cond i cionais alternativas Ca rd () deck • new Card [52]; setOeck( deck ); deck [O] • new Vcard( Suit.HEARTS, Rank.TWO, "/ blackjack/ ui / bitmaps/ h2" l; deck [1] • new Vcard( Suit. HEARTS , Rank.THREE, · / blackjack/ ul / bitmaps/ h3" ,; deck [2] • new Vcard( Suit.HEARTS, Rank.FOUR , "/b1ackj aCk/u i /bi tmaps/h4" ,; deck [3] • new Vcard( Suit.HEARTS , Rank.FIVE, "/ b1ackj ac k/u i /b i tmaps/h5" ) : deck [4 ) = new Vcard( Suit.HEARTS , Rank .SIX . "/b 1ac kj ac k/u 1/bi tmaps/h6" ): deck [5] • new Vca rd( Suit.HEARTS, Rank.SEVEN, "/bl ackjack/ui/bitmaps/h7" ); de ck [6] ~ new Vcard( Suit.HEARTS . Rank.EIGHT , ~ /black jack/ ui /bi tmaps/h8" ): deck [7] • new Vcard( Su it.H EARTS. Rank.NINE, "/ blackjack/ ui / bitmaps/ h9" }; deck [8 ] • new Vcard( Suit .HEARTS , Rank.TEN, "/ blackjack/ ui/ bitmaps/ hIO" ): deck (9] • new Vcard( Su it . HEARTS, Rank . JACK , "/ blackjack/ ui / bitmaps/ hll· ); deck [10] • new Vcard( Suit . HEARTS, Rank.QUEEN , · / blackjack/ ui / bitmaps/ hI2" );
647
I 648 LISTAGEM E.34
Apêndice E
VOeck.java (continuação)
deck (11 ] • new Vcard( "j blackjackj u1 j b1tmapsj h13" ); deck [1 2] • new Vca rd( "j blackjackj ui j bitmapsj hl " ); deck (13] " new Vcard( "j bl ackjack j ui j bi tmaps j d2" ); deck [14] " new Vcard( "/ bl ackjack j ui /Mtmaps j d3" ) ; deck [1 5] • new Vcard( "/b1ackj ackju i /bi tmaps/d4" ) ; deck [16) • new Vcard ( "/b 1ac kj ac k/u i /bi tmaps/d5" ) ; deck (1 7) " new Vca rd( "/ b1ackj ackju i /b i tmaps/d6" ) ; deck [1 8J • new Vcard( "/bla ckjack/ui/bitmaps/d7" ); deck [19] " new Vcard( "j blackjack/ ui / bitmaps/ d8" ); deck [20] • new Vcard( -j blackjack/ ui j bitmaps / d9" ); deck [21J " new Vcard( "j blackjackj ui j bitmapsj dlO" ); deck [22] " new Vcard( "/ blackjackj ui j bitmapsj dl1" ); deck [23] • new Vcard( "j bl ackjack j ui j bi tmaps j dl 2" ) ; deck [24] • new Vcard( "/ blackjackj ui / bi tmaps j d13" ) ; deck [2 5] • new Vca rd( "/blackjackjui/bltmaps/dl" ) ; de ck [26) " new Vca rd( "/bl ackjack/ui/bitmaps/s2" ); deck (27) " new Vcard( "/bl ackja ckjui / bitmaps/s3" ); deck [2 8] • new Vcard( "/blackjack/ui/bltmaps/ s4" ); deck [29] " new Vcard( "j blackjackj ui / bltmaps/ s5" ); deck [30] • new Vcard( -j blackjack/ ui j bitmapsj s6" ); deck [31] = new Vcard( "j blackjackj ui j bitmapsj s7 " ); deck [32] " new Vcard( "j blackjackj ui j bitmapsj s8" ); deck (3 3] • new Vcard(
Suit.HEARTS . Rank.KING . Su it.H EARTS . Rank.ACE . Sult.DIAMONDS . Rank . TWO . Suit . DIAMONDS . Rank.THREE. Suit.OIAMONDS. Rank.FOUR . Suit.OIAMONDS , Ra nk. FIVE , Sui t.OIAMONDS. Ra nk. SIX. Suit. OI AMONDS . Rank.SEVEN. Suit.DIAMONDS , Rank.E IGHT, Suit .O IAMONOS . Rank.N INE . Suit.OIAMONOS. Rank . TEN. Suit.DI AMONOS. Rank.JACK. Su it.DIAMONOS. Rank .QU EEN. Su it.OIAMONDS . Rank.KING, Suit.OIAMONDS. Rank.ACE . Suit.SPADES . Rank.TWO . Suit.SPADES . Rank.THREE. Suit.SPAOES . Rank.FOUR. Suit.SPADES . Rank.FIVE. Suit.SPAOES. Rank.SIX . Suit.SPADES. Rank.SEVEN . Suit.SPAOES. Rank.EIGHT, Suit.SPAOES. Rank.NINE ,
Listagens do código do jogo vi nte-e-um
LISTAGEM E.34
VOeck.java
-j blackjackj ul j bitmapsjs9" ); deck [34] • new Vcard( Mjblackjackj ui j bitmapsj slO" ); deck [35] = new Vcard( -j blackjackj ui j bitmapsjs ll" ); deck [36] • new Veard( "/ blackjackj ui j bitmapsj sI2" ); deek [37J • new Veard( "/ b1ackj ackju1/ bl tmap sjs 13" ); deek [38J • new Veard( "/ b1aekj aekj ui / bi tmap s/s 1" ); deek [39J • new Veard( "/bl aekjaekjui/bitmaps/c2" ) ; deek [40) • new Veard( "/bl aekjaek/ ul / bitmaps/c3" ); deek [4 1] • new Vcard( "/ blaekjaek/ ui / bitmaps/c4" ,; deck [42] • new Vcard( Mjblaekja ek/ui j bi t ma ps/c 5" )i dec k [43] • new Vcard( -j blackjackj ui j bitmapsjc6" ,; deck [44] • new Vcard( -j blackjackj ui j bitmapsjc7" deck [45] • new Vcard( -/ b1ackjack j ui / bi t maps/c8" ); deck [46J • new Veard( j blackjackjui j bi tmaps je9" ); dee k [47] • new Veard( "/ b1ackj aek j ui / bi tmaps je 10" ) ; deek [48J • new Veard( "/b 1aekj ae kj ui / bi tmap s/ c 11" ); deek [49] • new Vcard( "/bl aekjaek j ui/bitm apsje12" deek [50) = new Veard( "/b 1ackj ae k/ ui / bitmaps/ c 13" ) ; de ek [51} • new Veard( ~/blaekjaek/ui/bitmaps/cl" };
'i
$uit.SPADES. Rank.TEN. Suit .SPAOES , Rank.JACK. Suit .SPAOES. Rank.QUEEN. Suit.SPAOES . Rank.KING. Suit.SPAOES . Rank.ACE. Suit.CLUBS . Rank . TWO , Suit.CLUBS . Rank . THREE . Suit.ClU8S. Rank.FOUR. Suit.ClUBS . Rank.FIVE , Suit.ClUBS . Rank.S IX. $uit.ClUBS. Rank.SEVEN. Suit . ClUBS. Rank . EIGHT. Suit.CLUBS. Rank . NINE.
H
Suit.CLUBS . Rank . TEN , Suit.CLUBS . Rank.JACK.
'i
Suit .CLUBS . Rank.QUEEN . Suit .CLUBS . Rank . KING . Suit.ClU8S. Rank.ACE.
I I
blackjack.ui.pac b1ack j ae k. ui . pac contém o código pac (listagens E.35 a E,42).
649
Apêndice E
Disp layable.java package blackjack . ui . pac: import javax.swing .JComponent: publiC interface Oisplayable I public JComponent view() : }
Listagem E.36
GUIPlayer.java
package blackjack.ui.pac: import i mport import i mport
blackjack.core.*; javax.swing.*: java.awt.*: java.awt.event.*:
publiC cla ss GUIPlayer extends VBettingPlayer impl ements Displayable ( private BlackjackDealer deale r; private JPanel view: pUblic GUIP layer( Str ing name , VHand hand, Bank bank . VblackjackDealer _ dea le r ) I super( name. hand, bank ): this .dealer • dealer ; }
public boo lean h1t( Dealer dealer ) I return true; }
public void bet() I II não faz nada, isto não será chamado II em vez disso, o jogador humano pressiona um botão da GUI }
II esses métodos de aposta se rão chamados pelo contro lado r da GUI II para cada um: faz a aposta correta, muda o estado. permite que a II banca sa iba que o jogador terminou de apostar public void placelOBet() { getBank().pl acelOBet(): setCurrentState( ge tWai tingState() ) : dealer.done8etting( th i s ) :
Listagens do código do jogo vinte-e-um
LISTAGEM E.36
651
GUIP1ayer.java (continuação)
}
public void place50Bet () { getBank().place50Bet(); setCurrentState( getWaitingState() ); dealer.doneBetting( this ): }
public vo1d placelOOBet() { getBank().place IOOBet() : setCurrent State( getWaitingState() ) ; dealer . doneBetting( this ): }
II II II
a aposta em dobro é um pouco diferente, pois o jogador pre cisa responder aos eventos de Hand quando uma carta é adicionada na mão de modo que configura o estado como DoublingDown e depois o executa protected boolean doubleOown( Dealer d ) { se tCurrentState( getOoublingOownState() ) ; getCurrentState().execute( dealer ) : return true : }
II II
takeCard serâ chamado pelo controlador da GU I quando o jogador decidir receber mais cartas public void takeCard() { dealer.hit( th is ); }
II II II
stand será chamado pelo controlador da GU I quando o jogador optar por parar, quando a parada mudar de estado, deixa o mu ndo saber, e depois diz â ba nca publi c void standO { setCurrentState( getStandingSta te() ); notifyStand i ng(): getCurrentState() . execute( dealer l : }
pub l ic JComponent view() { if( view ,,= null ) { view = new Jpanel( new Borderlayout() ): JComponent pv " super.view(): GU IView cv • new GUIView(); addListener( cv l; view.add( pv, BorderLayout. CENTER ):
I 652
Apê ndi ce E
LISTAGEM E.36
GUIP1ayer.java (continuação)
view.add( cv, Bo rderlayout.SOUTH );
I return view;
I
II
o seguinte trata dos estados protected PlayerState getPlayingState() ( return new Playing()j
I protected PlayerState getBettingState() I return new Betting() ;
I private class Playing implements PlayerState { publiC void handPlayableO I II nlio faz nada
I publiC void handBl ackjack() ( setCurrentState( getBlackjackStateO ); notifyBlackjack(): getCurrentState().execute( dealer )j
I public void handBusted() ( setCurrentState( getBustedState() ) ; notifyBustedO: getCurrentState().execute( dealer l i
I public vo i d handChanged{) { not i fyChanged () ;
I publ i c void executeI Oea l er dealer ) ( II nlio faz nada aqui, as ações virão da GUI , que é II externa ao estado, mas quando eventos vierem, certifica-se de II forçar a transição de estado imediatamente
I I private class Betting implements Pl ayerState I public void handChanged() { II impossfvel no estado de estouro
Listagens do código do jogo vinte-e-um
LISTAGEM E.36
653
GUI P1aye r.java (con t inuação)
}
publ ic vo l d handPlayable() { II impossfvel no estado de estouro }
pub l ic void handBlackjack() { II impossfvel no estado de es t ouro }
pu bl lc void handBusted{) { II impossfvel no estado de estouro }
pub l ic void exec ut e( Dealer dealer ) { II n~o faz nada aqui, as ações virão da GU I. que é II externa ao estado , pois nenhum evento aparece como parte da II aposta, o estado preci sa rá ser mudado externamente para este estado } }
private class GU IView extends JPanel implement s Playe rl is t ener , Act io nl is tener
I private pri vate prlvate pri vate pri vate pr i vate pr i vate private
JButlon JButton JButton JBut ton JButt on JButt on JButton JBu t ton
pr ivate pr ivate pr ivat e private private private private private
fi nal fi na l final f inal final final final fi na 1
bet 10 bet 50 bet- 100 dea l • quit • hit • stand • ddown •
String String String Str ing St ring St ring String St ring
• new Jbutton ( "$10· }: • new Jbu tton( "$50· ) ; • new Jbutton( "$1 00· }; new Jbutton { NNew Game" }; new Jbutton{ ·Quit" }: new Jbutt on( "Hit" }: new Jbutton ( ·Stand" ) : new Jbutton ( "Doub le Down" ) :
NEW- GAME • "new" : QUIT • "quit" ; HIT • "hit"; STAND • "stand": BET- 10 - "BETlO"·• BET 50 • "BET50" ; BET 100 • "BETlOO" ; O DOWN • "ODown " .•
private final Color FORES T GREEN = new Color( 35 . 142 . 35 ); publ iC GUIView() ( supe r e new Borde rLayoutO ); GU IPlaye r.thi s.add l is tener ( th is ) ; bui 1dGUI (); }
I 654
A pêndi ce E
LISTAGEM E.36
GUIP1ayer.java (continuação)
private void buildGUI() I JPane l bett i ng_controls • new Jpanel(): JPane l game_control s • new JPanel(): add( betting_controls . Borderl ayout.NORTH ): add( game_controls. Borderlayout.SOUTH ): betting_controls.setBackground( FOREST_GREEN ) ; game_control s.setBackground( FOREST_GREEN ): deal.setActionCommand( NEW_GAME ) : deal.addActionliste ner( this ); qu i t.se t ActionCommand( QUIT ) : qu i t.addActionliste ner( this ) : hit.setActionCommand( HI T ) ; hit . addActionlistener( this ) : stand.setActionCommand( STAND ): stand.addActionlistener( this ): bet_lO.setActionCommand( BET_IO ) : bet_IO . addActionlistener( this ); bet_SO. setActionCommand( BET_50 ): bet_50 .addActionlistener( this ); bet_ IOO.setActionCommand( BET_IOO ): bet_IOO.addActionlistener( this ): ddown. setActionCommand( D_OOWN ): ddown.addActi onli stener( this ) : betting_control s.add( bet_IO ); betting_contro l s.add( bet_50 ); betting_controls.add( bet_IOO ) ; game_control s.add{ ddown ): game_control s.add{ hit ) : game_contro l s.add{ stand ); game_contro l s.add{ deal ) : game_contro l s.add( quit ) : enableBettingControls( false ) ; enablePlayerControls( fa l se ) : enabl eDoubleOown( false }:
I private void enableBettingControls( boolean enable ) I bet_lO. setEnabled( enable }; bet_50 .setEnabled( enable ); bet_lOO.setEnabled( enab l e ): I private void enablePlayerControls( boo l ean enable ) {
Listagens do código do jogo vinte-e-um
LISTAGEM E.36
GUIP1ayer.java (continuação)
hit.setEnabled( enabl e li stand .setEnabl ed( enable )i }
private void enableGameControls( boolean enab l e ) ( deal.setEnabled( enabl e )i quit.setEnabled( enable ); }
private void enableDoubleDown( boolean enable ) { ddown,setEnabled( enable ); }
public void actionPerformed( ActionEvent eve nt ) { if( event.getActionCommand(),equal s( QUIT ) l { System .exit( O ,; lelse if( event.getActionCommand().equals( HIT ) ) { enabl eDoubleDown( false ); takeCard O i )else if( event.getActionCommand().equals( STANO ) ) { enableOoubleOown( false l i standO i )e lse i f ( event.getActionCommand().equals( NEW_GAME ) ) { enabl eOoubleOown( false ); enab l eGameControls( fa lse ); enablePlayerControls( false )i enableBettingControls( true )i dealer,newGame(); lelse if( event .getActionCommand(),equals( BET_IO ) ) { enableOoubleDown( true )i enableBetti ngControls( false )i enablePlayerCont rols( true ); pl acelOBet () i lelse if( event.getActionCommand() .equals( BET 50 } ) { enableOoubleDown( true ) i enableBettingControls( false }i enablePl ayerControls( true ) i placeSOBet() i )else if( event.getActionCommand().equals( BET 100 ) ) { enableOoubleOown( true ); enableBettingControl s( false l i enabl ePlayerControls( true ); placelOOBetO; }else if( event.getActionCommand().equals( O OOWN ) ) I enab l ePlayerControls( false lj
655
I 656
Apêndi ce E
LISTAGEM E.36
GUIP1ayer.java (continuação) enabl eDoubleDown( false ) ; doubl eDown( dealer );
I I public void playerChanged( Player pl ayer ) {} publiC void playerBusted( Pl ayer player ) ( enablePlayerControls( false ) ; enableGameControls( true ) ;
I public void playerB l ackjack( Pl ayer player ) { enabl eDoubleDown( false ) ; enablePl ayerControls( fa l se ) ; enableGameContrOl S( true };
I publiC void playerStanding( Player player ) ( enabl ePlayerCont rols( fa l se l ; enableGameControls( true l ;
I publiC void playerWon( Player player ) ( enablePlayerControl s( false ); enableGameControls( true ,; I public void playerLost( Player player ) { enableDoubleDown( false ) ; enablePl ayerControls( false ) ; enableGameControls( true , ;
I publiC void playerStandoff{ Player pl ayer ) { enabl ePlaye rControls( false }; enabl eGameContro l s{ true };
I I I
Listagen s do código do jogo vi nte-e-um
657
LISTAGEM E.37 VBettlngPlayer.java package bl ackjack . ui . pac; import lmport import import
blackjack.core .*; java.awt.*; javax.swlng.*; javax.swing . border.*;
public abstract class VBettingPlayer extends BettingPlayer implements - Oisplayable { private BettingView view; public VBettingPlayer( String name , VHand hand, Bank bank ) { supere name, hand. bank ) ;
I publ iC JComponent view() { H( view ... null ) { vlew • new BettingView( (VHand)getHandO ); addListener( view );
I return view;
I
II Note que tudo que essa classe faz
é recuperar o modo de vlsualizaç!o de
Hand. ad i ciona esse modo II em si mesmo e atualiza a borda , conforme for necessário. Note o que essa classe não faz: II atualiza as cartas quando elas mudam. 00 ponto de vista deste modo de visualização. a Ilatuallzaçao da carta acontece automaticamente, pois VHand atualiza sua exibição nos Ilbastidores private class BettingView extends JPanel implements Pl ayerListener { private TitledBorder border; publiC BettingVlew( VHand hand ) ( supere new FlowLayout( Fl owLayout.LEFT ) ); buildGUI( hand.view() );
I publiC void playerChanged( Player pl ayer ) { String name • VBettingPlayer.this.getName();
Apêndice E
LISTAGEM E.37 VBett 1ngPl ayer .java (continuação) border.setTit l e( name ): repalntO: }
pub l iC void player6usted( Player player ) ( String name • VBettingPlayer.this.getHame(): border.setTitle( name + " BUSTEO!" )i repaint()i }
pub l iC void playerBlackjack( Playe r player ) { String name • VBettingPlayer.this.getName()i border.setTitle( name + " BLACKJACK!" )i repa i ntO: }
publiC void pl ayerStanding( Player player ) ( String name • V6ettingPlayer . this.getName() ; border. setTitle( name + " STANDING ~ ): repa in tO ; }
publ ic void playerWon( Pl ayer pl ayer ) ( String name • VBettingPlayer. t his .ge tHame() i border.setTit l e( name + " WINNER! " ): repaintO; }
pub l ic void playerLost( Player player ) ( String name • VBettingPlaye r.this.getName() : border.setTitle( name + " LOSER !" ); repaintO i }
publiC void playerStando ff( Player player ) ( St ring name • VBettingPlayer.this.getName()i border.setTitle( name + " STANDQFF !" ) i repaint() ; }
pri vate void buildGUI( JComponent hand )( border· new TitledBorder( V6ettingPlayer.thi s.getNameO ); setBorder ( border ) ; setBackg round ( new Col or( 35 , 142 , 35 »i border . se tT lt l eCo l or( Co l or.bl ack ) ;
Listagen s do código do jogo vinte-e-um
LISTAGEM E.37
659
VBettlngPlayer.java (continuação)
add( hand ); } } }
LISTAGEM E.3S
VBlackjackDealer.java
package blackjack.ui.pac ; import import import import import
blackjack.core.*; blackjack.core.threaded.*; javax .swing.*; javax.swing.border.*; java.awt.*;
public class VBlackjackDealer extends ThreadedBlackjackDealer implements Displayable { private OealerView view; public VBlackjackDealer( String name, VHand hand, Deckpile cards ) { supere name . hand, cards ) ; }
publi c JComponent view() { if(v1ew •• null ) ( v1ew • new OealerView( (VHand)getHand() ); addlistener( view ); }
return view; }
II
Note que tudo que essa classe faz é recuperar o modo de v1sualizaçao de Hand, adiciona esse modo II em si mesmo e atualiza a borda conforme for necessário .Note o que essa classe não faz: II atua l iza as cart as quando elas mudam. 00 ponto de vista desse modo de visualização. II a atualizaçáo da ca rta acontece automaticamente, pois VHand atualiza sua exibição II nos ba st idores private class OealerView extends JPane l impl ements Playerlistener { private TitledBorder border ;
I 660
Apêndice E
LISTAGEM E.3S
VBlackjackOea l er .java (continua ção)
publiC Oea l erView( VHand hand ) I supe r e new Fl owlayout( Fl owlayout.lEFT ) ); Stri ng name • VBlackjackOealer.this.getName() ; border = new Ti tledBorder( name ); setBo rder ( bo rder ) ; se tBackgrou nd ( new Col or( 35 . 142. 35 ) ) i border.setTit leColor( Co l or . black ) ; add( hand.view() ) i repaintOi J
publiC voi d playerChanged( Player player ) { St ring name • VBlackjackDealer . this.getName()i border.setTitle( name ); repalntO i J
publ iC void pl ayerBust ed( Playe r player ) { String name • VBla ckjackOea l er.th is .getName() ; border.setT itle( name +" BUSTED! ") i repa intO i
J pub l iC vo i d playerB l ackjack( Player player ) I Stri ng name • VBlackjackDealer . this.getName() ; border.setTitle( name + " BlACKJACK !" )i repa intO; J
pu bl ic void playerStand i ng( Player pl ayer ) ( String name • VBlackjac kDeale r. this . getName ()i border.setTit le ( name + " STANDING " ); repaintOi J
publ ic void pl ayerWon( Playe r playe r ) { String name • VBla ckjackOea ler.this .getName() i border. setT itle( name + " WINNER !" ) ; repaintO i
J publ ic vo i d pl aye rLost( Playe r player ) ( String name • VBlackjackDealer . this.getName(): border.setTit le( name + " LOSER!- )i repaintOi
Listagens do código do jogo vinte-e-um
LISTAGEM E.3S
VBlackjackOea l er .java (continuação)
I publ i c void playerStandoff( Player player } I Stri ng name z VBlackjackOealer . this.getName(); border . setTit l e( name + MSTANOOFF!M }; repa intO;
I I I
LISTAGEM E.39
VCa rd. java
package blackjack.ui.pac; i mport bla ckj ack . core.* ; • • •; i mpor t Ja vax.swlng. l. mpor t ·Java.aw t . • ; publ i c cla ss VCa rd extends Card implements Displayab l e { private Stri ng image ; private CardView view ; publ ic IJcard( Suit suit, Ran k rank . String image ) ( supere sui t, rank l ; th i s. image • image ; view = new CardlJiew( getImageO );
I publ iC void set Fa ceUp( boolean up ) I super. se tFaceUp( up ); view. changed();
I public JComponent view() I return view;
I pri vate String get Image( ) ( i f( i sFaceUp() ) ( return image ; ) else { return "/ blackj ack/ ui / bi tmaps/ empty pile.xbm M ;
661
I 662
Apêndice E
LISTAGEM E.39
VCard.java (continuação)
} }
private class CardV iew extends JLabel { pub li c CardView( St ring image ) { setImage( image ) ; setBackground( Color.white ); setOpaque( true ); }
publie void changed(){ set Image( getlmage() ); }
private void se tImage( String image ) { java . net.URL url .. th is .getC la ss().getResouree( image ); Image l eon ieon .. new ImageIeon ( url ) ; set lcon( ieon ); }
} }
LISTAGEM E.40
VDeck .java
package blackjack . ui.pae ; import blaekjaek.eore.*; import java . uti l .Iterator; pub li c class VOeck extends Oeck { protected void bu ildCards () {
II Isso
é ho rrível. mas é me lhor do que os l aços e as estrut uras
condicionai s alternativas Card [] deck" new Card[52] ; setDeck( deck ) ; deck (O] .. new Vcard( Su it.HEARTS. Rank. TWO . -/ blackjac k/ ui / bitmaps/ h2" ): deck (1] z new VCard(Suit.HEARTS. Rank. THREE . -/ blackjack/ ui / bitmaps/ h3" ); deck [2] .. new Vcard( Suit. HEARTS. Rank . FOUR.
Listagens do código do jogo vinte-e-um
LISTAGEM E.40
VOeck.java (continuação)
- j b1ackjackj u; j bi tmaps j h4" ); deck (3) • new Vcard( Suit.HEARTS, Rank.FIVE, - j blackjack j ui j bitmaps j h5" ); deck [4) = new Vcard( Sult.HEARTS. Rank . SIX, - j blackjackj ui j bitmaps j h6" ); deek [5] • new Veard( Suit.HEARTS . Rank.SEVEN, / bl aekjaek j ui / bi tmaps j h7" ) ; deek [6] • new Vcard( Suit.HEARTS, Rank.EIGHT. "/b 1aekj aekju 1/bl tmaps/h8" ) ; deck [7] • new Veard( Sult . HEARTS. Rank.NINE, "/ b1aekj aekju i /bi tmaps/h9" ); deek [8) • new Vcard( Suit.HEARTS . Rank.TEN. "/b 1ae kj ae kju i /bltmaps/hIO" ) i deek [9] • new Vea rd( Suit.HEARTS , Ran k.JACK , "/b 1ae kjae k/u i /bitmaps/h 11" ) i deck [10] - new Veard( Suit.HEARTS. Rank.QUEEN, /blaek jaekjui/bi tmaps/hI2" ); deek [11) .. new Veard( Sult .HEARTS . Rank.KING. "j bl aekja ek/ui j bitmaps/ h13" ) i deek (12 ) .. new Veard( Suit . HEARTS, Rank.ACE, - j blackjackj ui j bitmaps j hI " ); deek (13) • new Veard( Suit.OIAMONDS, Rank. TWO, "j blackjaekj ui j bitmaps j d2" )i deek [14) .. new Veard( Suit.OIAMONOS . Rank.THREE . - / b 1aekjaek j ui / bi tmaps j d3 " ); deek [15) .. new Veard( Suit.OIAMONDS, Rank.FOUR, j blaekjack j ui j bi tmaps j d4" ) i deek [16) .. new Veard( Suit.OIAMONDS . Rank .FI VE , "/b 1aekjaekjui /bitma ps jd5" ) i deek [17) • new Veard( Suit . DIAMONDS , Rank.SIX, "/b 1aekj ae kju i /bi tmapsjd6" ); deek [18) .. new Vca rd( Suit.DIAMONDS , Rank . SEVEN . "/b 1ae kj ae kj ui /b i tmaps/d7" ); deck [19] = new Veard( Suit.DIAMONDS , Rank . EIGHT, "/b la ckjaek/ui/bitmaps/d8" ) i deck [20] • new Veard( Suit.DIAMONDS . Rank . NINE. "/blaekjaekjui/bi tmaps/d9" ); deek [21) - new Vcard( Sult.D IAMONDS. Rank. TEN, "j blaekjaek/ ui j bitmaps / dl0" ) i deck (22] .. new Veard( Suit . OIAMONOS . Rank . JACK . - j blackjackj ui j bitmaps j dll" ); deek (23) .. new Veard( Suit.OIAMONOS. Rank.QUEEN. "j blackjack j ui j bitmaps j d1 2" ); deek (24) .. new Veard( Suit.OIAMONDS.Rank . KING, .. / bl aekjaek j ui / bi tmaps j d13 " ); H
H
H
663
I 664 LISTAGEM E.40
Apêndice E
VOeck.java (con tinuação)
deck [25] • new Vcard( Suit.OIAMONOS . Rank.ACE. -j blackjackj ul j bltmapsj dl " l; deck [26] • new Vcard( Suit.SPAOES. Rank.TWO . "j blackjackj ui j bilmapsj s2" l; deck (27) ~ new Vcard( Suit.SPAOES . Rank . THREE. "j blackjackjui j bitmapsjs3 " ); deck (28) · new Vcard( Suit .SPADES. Rank .FOUR , • / blackjackjui/bi tmapsjs4" ) ; deck [29] • new VCard(S ui t.SPADES. Rank. FIVE. "/b1ackj ack ju i /bi tmaps/sS" l ; deck [30] = new Vca rd( Suit .SPADES. Rank .SIX . "/b 1ac kj ac k/ ui /bi tmaps/s 6" ); deck [31] • new Vca rd( Suit.SPADES . Rank.SEVEN. "/bl ackjackjui/bitmaps/s7" ); deck [32] • new Vcard( Suit.SPADES . Rank.EIGHT. "/b 1ac kjac k/u i /b i tmaps/s8" ); deck [33] • new Vcard( Suit.SPADES , Rank . NINE. "jblackjack/ui/bitmaps/s9" ); deck [34] • new Vcard( Suit.SPADES, Rank.TEN. "j blackjack/ ui j bi tmaps/s IO" l; deck (35] • new Vcard( Suit.SPAOES , Rank .JACK, -j blackjackj ul j bHmapsjs ll" l; deck [36] • new Vca rd ( Suit.SPAOES . Rank.QUEEN. "/ blackjackj ui j bitmapsj sI2" l; deck [37] z new Vcard( Suit.SPADES . Rank . KING. "j blackjackj ui j bitmapsj s 13" ) ; deck (38) • new Vcard( Suit.SPADES . Rank .ACE . "/blackjackjuijbi tmapsjsl" l; deck [39) • new Vcard( Suit.CLUBS. Rank. TWO. "/b1ackj ac kju i / bi tmaps/c2" l ; deck [40] • new Vca rd( Suit.CLUBS . Rank.THREE . "/b 1ac kj ac k/u i /b i tmaps/ c3" l; deck [41] • new Vcard( Suit.CLUBS , Rank.FOUR . "/bl ackjack j ui / bitmaps/c4" l; deck [42] • new Vcard( Suit.CLUBS , Ran k.FI VE . "/b 1ac kjack/u i / bi tmaps/cS" ) ; deck [43J • new Vcard( Suit.CLUBS , Rank .SIX . "j blackjackjui / bitmaps/c6" l; deck [44] • new Vcard( Suit . CLUBS , Rank.SEVEN . "j blackjack/ui / bitmaps /c 7" ); deck (45] = new Vcard( Suit.CLUBS . Rank . EIGHT. -j blackjack/ ui / bi tmaps/ c8" ,; deck [46) • new Vcard( Suit.CLUBS . Rank.NINE, "j blackjack/ ui j bitmaps/c9" l; deck [47) • new Vcard( Suit.CLUBS. Rank. TEN.
Listagen s do código do jogo vinte-e-um
665
VOeck.java (continuação) -j blackjack/ ul j bitmapsj clO N ); deck (48] • new Vcard( Suit.CLUBS, Rank.JACK, Mjblackjackj ui j bitmapsj cll N ); deck [49] = new Vcard( Suit.CLUBS. Rank . QUEEN, Nj blackjackj ui j bitmapsj cl2" ) ; deck [50] • new Vcard( Sult.CLUBS . Rank.KING, "/blackjackjui j bitmapsj c13" ); deck [51J • new Vcard( Suit.CLUBS, Rank.ACE . "/blackjackjul/bitmaps/cl" );
LISTAGEM E.40
I I
LISTAGEM E.41
VHand.java
package blackjack.ui.pac; import import import i mpo rt
bla ckjack.core.* ; java.awt.*; javax . swi ng.*; java.uti 1. Iterator;
public cl ass VHand extends Hand implements Oisplayabl e I private HandV iew view
=
new HandView();
public JComponent view() I return view;
I
II você precisa sobrepor addCard e reconfigurar para que quando a mão mudar. a II alteraç!o se propague no modo de visualização public vold addCard( Card card ) { super.add Ca rd( card ) ; view.changed() ;
I pub l ic void reset() { super. reset O i view . changedO i
I private class HandView extends JPanel I pub l ic HandView() I super e new Flowlayout( Fl owlayout . lEFT ) };
I 666
Apêndi ce E
LISTAGEM E.41
VHand.java (continuação)
setBackground( new Color( 35 . 142 , 35 ) );
I pub1i c void changed() I relllOveA 11 () ; Iterator i • getCards() ; whi1e( i.hasNext() ) I VCard card • ( Vcard) i. next(); add( card.viewO ) ;
I revalidate();
I I I
LISTAGEM E.42
VPl ayerFactory .java
package bl ackjack.ui.pac; impo rt b1ackjack.core.*; public class VPlayerFactory I private VBlackjackDea l er dea1er ; private GUIP l ayer human; private Deckpile pile; publiC VBlackjackOealer getDealer() I II cria e retorna apenas um if( dealer •• nu l l ) { VHand dea l er_hand = getHand() ; Oeckpile cards • getCards ()j dea1er · new VBlackjackOealer( "Deale r ". dea l er_hand . cards )i
I ret urn deale r ;
I pub l iC GUIP1ayer getHuman() { II cria e retorna apenas um if( human •• null ) I VHand human_ha nd • getHand(); Bank bank • new Bank( 1000 ) ; human • new GUI Player( -Human". human_hand. bank. getOea l er() );
I
Listagens do código do jogo vinte-e-um
LISTAGEM E.42
VPlayerFactory.java (continuação)
return human : }
publlC Oeckpile getCardsO I II cria e retorna apenas um if( pile •• nul1 ) I pi le · new Oeckpile(); for( in t i • O: i < 4 : i ++ ) { pile.shuffleO: Oeck deck • new VOeck(lj deck.addToStack( pile l; pile.shuffleO j } }
return pile: }
private VHand getHand() I return new VHand(): }
}
667
índice Remissivo Símbolos • (asterisco), agr egnções de o bjeto, 186
A Abstração,26-27 exem plo de jogo de cartas, 56
exemplo de, 26-29 imp lementação eficie nte de, 41 -47 regras para, 28-29 situações apropriadas para, 29 Abst ração, a ltern ativas, 93 Abstracl Datll Ty pc. Veja ADT
Acesso privado, 25
protegido, 25 púb lico, 25
níveis de, 25 Assesso res, 11 classes, 575 ACM S peciallntc rcst G ro up 0 0 Computer- Hum a " lnleraction (S IGC HI), 290 Ada ptado r es, 242 de classe, 244-245 de objeto, 244-245 Agentes, o bjetos. 23 1 Agregações, UM L, 585 Ambiente
configuração, SDK, 555-556 variáveis, PATH , 556 Ana lisadores documentos XML, padrão de projeto Abslracl Faclol)' e, 266
exemplo de código, 266-267 Aná lise
recursos, 587 Veja também AOO
Análise de caso de uso, 199 casos de uso resultantes, 204-205 combinando casos de uso, ?03-204 definindo a seqUência de eventos, 205-207 diagramas, 207 de atividade, 2 12 de colaboraçãO. 2 11 de interação, 209 de seqUência, 2 10-211 dicas para escrever, 207 divid indo casos de uso, 203-204 entrada do usuârio e, 200 identificando atores, 200-201 jogo de cartas vinte-c-um aposta, 414-417 GU I, 433-435 regras, 384-388 listando casos de uso, 201-203 lógica booleana da, 476 Ancestrais, 88 Aninhando in struções ir/cise, 337 AOO (A ná lise O rientada a O bj eto) modelo de caso de uso, 199 casos de uso resultantes, 204 combinando casos, 203-204 definindo seqUência de eventos, 205-207 diagramas de atividade, 2 12 diagramas de caso de uso, 207-208 diagramas de colaboração, 211 diagramas de interação, 209-210 diagramas de seqUência, 210-2 11 dividindo a lista de casos, 203 dividindo casos, 203 gerando lista preliminar, 20 1-203 identificando atores, 200-201
670
Aprenda Programação Orientada a Objetos em 21 Dias
modelo de domínio, diagramas de 3t ividade, 2 12 objelivo, 352 panorama, 197-198 protólipos,2 14 sistema, 198
APls (interraces de programa aplicativo), 24 Argum entos conversão, 137 ferramentas de terceiros, adaptadores, 244-245 Veja wmbém parâmetros, 133- 134
Arquivo
Product.java, 573-574 SimpleHelloWorldjava,564
Arquivos, criando arquivos .jar, 562-563 Associações (obj elos), UML, 584 Asteriscos (.), associações d e objeto, 185 Atividades, diagrama de estados. 390-391 Atores, 200 análise de caso de uso, 352-353 jogo de canas viOle-c-um, 353 modelando, 207
Atributos, 8 especialização, 87-89 herança, 77-78 recursivos, 84-85 sobrepostos, 79-83 controle de acesso, 83 visibi lidade, UML, 58 1-582 Atuali .....ando doc umen tação, 339 interfaces, 35 projeto de UI desacoplada e, 290-291 bibliotecas de terceiros, padrão de projeto Abstracl FaCial)', 264 Autotransições, dia gr a ma s de estado, 390-391
B Bibliotecas ferram entas de terceiros
adaptadores, 244-245 arualizando, 264 Swillg, 293 Blackjack.cxc, intcrraces com O us uário, 621-627 Blackjack.java, 376-377 BlackjackDealer.j ava, 374-375
c C++, recursos, 588 Cabeçalhos (m étodos e cla sses), dOCulllcntllção, 338-339 Camada controladora (MVC), 301 combinando com camada de modelo, 302-303 implementando, 301-302 Camada de modelo (MVC), 294 implementando, 295-296 reunindo com camada de controle, 302-303 Camada de modo d e vis ua lizaçi\o (MVC), 297 Ca pacidade de conexi\o, 9 1-92 im plementando, 297-300 Cartões C RC (Class Rcs pons ibility Co llaboratio n),224-225 aplicando, 225-226 exemplo, 226-229 GU ls, jogo de cartas vinte-e-um , 437 limitações, 230-23 1 Casos de teste, 316 nomeando, 322 teste de caixa branca, 316 teste de caixa preta, 3 16 Casos de uso aposta, 414-416 atares, 352 jogo de cartas vinle-e-um, 353, 356-360 Banca efellla jogo, 387-388 Deal Cards, 384-387
Indice Remissivo
Cená rios (a nálise de easo de uso), 205 Cha madas de método, 8 Chaves ({ }) no código-fonte, 564 Chaves, especifica ndo, 51 C lasse Bank Gogo de cartas vinte-e-um) exemplo de cód igo, 159 implementação, 421 4 22 projetando, 417 C lasse BettingPlayer, implementação, 423-424 Blackjack Oealer. implementação, 426-427 BlackjackG UI, método setUpO, 448-449 CardV iew, implementando, 443 CountedObjecl, exemplo de código, 65 Oealer DoubleKey construtores, 52 exemplo de código, 50-5 1 exemplo de código, 61 FlipPlayer, implementando, 475-476 GUIPlayer, implementando, 445-448, 465 Hand, receptor para, 395-399 HumanPlayer, im plementação, 424-426 OneHit l)layer. implementando. 476 Option View, implementando, 445 Opt ionViewController, implementando, 445 Player herança, refazendo a hierarquia, 419-420 interface PlayerListener, 403-404 interface Pl ayerState, 399-403 PlayerView, implementando, 444-445 progenitora, 77-79 exem plo de cód igo de herança, 99-100 palavra-chave super, 84 SafePlayer adicionando na GU I, 472-473 criando, 471-472 Smart Player, implementando, 477 Stack, herança exemplo de código da solução, 118- 119 exemplo de problenta, 11 7- 118 exposição do problema, 118 State, jogo de cartas vinte-e-um, 393
671
I
VBettingPlayer, implementando, 462-463 VB lackjackDealer, implementando, 464-465 VCard, implementando, 44 1-442, 460-46 1 VDeck, implementando, 442 VHand, implementando, 46 1-462 Classes abstratas exemplo de código, 102-103, 126-128 métodos, definindo, 104 requisitos do exemplo, 105 UML,582 notação, 182 C la sses a nôllimas, criando, 324 C lasses filh as, 77-78 especiali zação, 87-88 exemp lo de código de herança, 100-10 I novos métodos, 84 palavra-chave super, 84 polimorfismo, exemplo de código, 147-148 requisitos do exemplo, 99-10 1 Classes fo lha, 89 Classes intern as, 577-579 anôn imas, 577-579 Classes-raiz, 89 anónimas, 577-579 Classes, 8 abstratas exemplo de código, 102- 103 notação UML, 181 - 182 requisitos do exemplo, 105- 106 AOT (Abstract Data Type), TAO, implementação eficiente de, 47 agregações, UM L, 585 ancestral , 88 anónimas, 577-579 criando, 324-325 assessores, 575 associações de objelo, UML, 584 atributos, 8 Bank, 416-4 17 implementação, 42 1 BaseLog, exemplo de cód igo, 127-128 BettingPlayer, implementação, 423-424
672
Aprenda Programação Orientada a Objetos em 21 Dias
Blackjack diagrama de atualiz.1çllo, 420-42 1 método setUp( ), 448-449 BlackjackDealer, im plementação, 426-427 cabeçalhos, documentação e, 338-339 CardV iew, implcmcnlando, 443 cartões CRC (C lass Responsibility Collaboration), 224-225
aplicando, 225-226 exemplo, 226-227 limitações, 230-23 [ compilando, 56 1-562 composiçi'lo, 76
UML,585 com uns pacote blackjack.core, 592-619, 618-619 considerações de projeto, lestes de unidade e,321 -322 convenções de atribuição de nomes, 338 CountedObject, exemplo de código, 65 criando, estado de jogo e, 393, 395 Dealer, implementação, 424-425 diagramas de colaboração, UML, 586 de seqUência, UML, 586 DoubleKey, construtores, 52 exemp lo de código, 9- 10 Bank, 159-1 60 BankAccount, 111-1 12 CheckingAccount, 114- 11 5 OverdraftAccount, 116 Savi ngsAccount, 112-113 TimeMaturityAccount, I 13-114 exem plo de cOnla código para, 55 requi sitos, 5343 exemplo de jogo de cartas, código para, 57-62 exemplos de código, DoubleKey.java, 50 filhas, 77 especialização, 87 exemplo de código de herança, 100-101 novos métodos, 84
palavra-chave super, 84 polimorfismo (exemplo de código), 147- 148
requisitos do exemplo, 100 FlipPlayer, implementando, 475 fo lha, 89
GUIPlayer, implementando, 445-448, 465 Hand, receptor para, 395 -398 Hashtablc, canvcrsllo c, 157 herança, principias básicos, 77-78 hierarquia de herança, 77
Hum anP layer, implementação, 424-425 instanciação, evitando mü lt ipla, 268 internas, 577-579 Java, 564-565, 57 1-572 criando, 572-573 jogo de cartas vinte-c-um , 360-364 criando jogadores não-humanos, 471-472 implementação, 365-380 modelo, 365 métodos construtores, 574-575 de con figuração, 575 de obtenção, 575 modelando, 188 documentando código, 179 notação de seleção UM L, 181 notação UML avançada, 181 notação UML, 179-1 80 re lacionamentos de agregação, 186-187 relacionamentos de associação, 184-1 85 relacionamentos de composição, 187-188 relacionamentos de dependência, 183- 184 relacionamentos de generalização, 188-189 relacionamentos, 183 modificadores, 575 mult iplicidade, UML, 584-585 numero de responsabilidades (problemas de projeto), 229-230 objctos, 7-8 OneHitPlayer, implementando, 476 OptionV iew, implemen tando, 445