Você está na página 1de 7

Captulo 3

Tpicos de Orientao a Objetos


Um bom design de software visa a uma arquitetura flexvel que permita futuras
alteraes, facilite a produo de cdigo organizado e legvel, maximizando seu
reaproveitamento. Todo o paradigma da orientao a objetos, seus princpios e
boas prticas procuram trazer esses benefcios para o design.
Ao pensar no sistema como um todo, outras questes de mais alto nvel surgem, em especial aquelas que tratam da forma como os objetos se relacionam
e sua organizao dentro e entre sistemas. Ao relacionar dois objetos distintos,
deve-se levar em conta as boas prticas que sero discutidas nesse captulo, como
a diminuio do acoplamento entre objetos.

3.1. Programe voltado interface, no


implementao
Ao trabalhar com colees, escolher a implementao certa para cada caso uma
tarefa difcil. Cada uma delas, como ArrayList, LinkedList ou HashSet, melhor para resolver determinas categorias de problemas. Pode ser muito arriscado

ArqDesignSoftware_BOOK.indb 47

12/11/2011 14:48:36

Captulo 3. Tpicos de Orientao a Objetos

escrever todo o cdigo da aplicao dependente de uma deciso antecipada. Apesar disso, grande parte dos desenvolvedores opta por sempre utilizar ArrayList
desde o incio sem critrio algum.
Considere um DAO de funcionrios que pode listar o nome de todos que
trabalham em determinado turno, devolvendo um ArrayList com os nomes:
public class FuncionarioDao {
public ArrayList<String> buscaPorTurno(Turno turno) { ... }
}

E um cdigo que precisa saber se determinado funcionrio esteve presente,


efetuando uma busca simples na lista devolvida:
FuncionarioDao dao = new FuncionarioDao();
ArrayList<String> nomes = dao.buscaPorTurno(Turno.NOITE);
boolean presente = nomes.contains(Anton Tcheckov);

Mas a busca com contains em um ArrayList , em termos computacionais,


bastante custosa. Poderamos utilizar outras alternativas de coleo, trocando o
retorno de ArrayList para HashSet, por exemplo, pois sua operao contains
muito mais eficiente computacionalmente, usando internamente uma tabela
de espalhamento (hash table). Para poucos funcionrios, a diferena imperceptvel, porm, medida que a lista aumenta, a diferena de desempenho entre
um ArrayList e um HashSet se torna mais clara, e at mesmo um gargalo de
performance.
O problema em realizar uma mudana como esta, de implementao, que
todo cdigo que usava o retorno do mtodo como ArrayList quebra, mesmo que
s usssemos mtodos que tambm existem definidos em HashSet. Seria preciso
alterar todos os lugares que dependem de alguma forma desse mtodo. Alm de
trabalhoso, tarefas do tipo search/replace so um forte sinal de cdigo ruim.
H esse acoplamento sinttico com a assinatura do mtodo, que conseguimos resolver olhando os erros do compilador. Mas sempre h tambm informaes semnticas implcitas na utilizao desse mtodo, e que no so expostos
atravs da assinatura. Um exemplo de acoplamento semntico est em depender
da informao de que uma List permite dados duplicados, enquanto um Set
garante unicidade dos elementos. Como problemas no acoplamento sinttico
so encontrados em tempo de compilao, os semnticos somente o so em
48
ArqDesignSoftware_BOOK.indb 48

12/11/2011 14:48:36

3.1. Programe voltado interface, no implementao

execuo, da um motivo da importncia de testes que garantam o comportamento esperado.


O grande erro do mtodo buscaPorTurno da classe FuncionarioDao foi
atrelar todos os usurios do mtodo a uma implementao especfica de Collection. Desta forma, alterar a implementao torna-se sempre muito mais custoso,
caracterizando o alto acoplamento que tanto se procura evitar.
Para minimizar esse problema, possvel usar um tipo de retorno de mtodo
mais genrico, que contemple diversas implementaes possveis, fazendo com que
os usurios do mtodo no dependam em nada de uma implementao especfica.
A interface Collection uma boa candidata:
public class FuncionarioDao {
public Collection<String> buscaPorTurno(Turno turno) { ... }
}

Com o mtodo desta forma, podemos trocar a implementao retornada sem


receio de quebrar nenhum cdigo que esteja invocando buscaPorTurno, j que
ningum depende de uma implementao especfica. Usar interfaces Java um
grande benefcio nestes casos, pois ajuda a garantir que nenhum cdigo dependa
de uma implementao especfica, pois interfaces no carregam nenhum detalhe
de implementao.
Repare que possvel optar ainda por outras interfaces, como List (mais
especfica) e Iterable (menos especfica). A escolha da interface ideal vai depender do que voc quer permitir que o cdigo invocador possa utilizar e realizar
na referncia retornada. Quanto menos especfica, menor o acoplamento e mais
possibilidades de diferentes implementaes. Em contrapartida, o cdigo cliente
tem uma gama menor de mtodos que podem ser invocados.
Algo similar tambm ocorre para receber argumentos. Considere um mtodo
que grava diversos funcionrios em lote no nosso DAO:
public class FuncionarioDao {
public void gravaEmLote(ArrayList<Funcionario> funcionarios) { ... }
}

Receber precisamente ArrayList como argumento tem pouca utilidade; raramente necessitamos que uma coleo seja to especfica assim. Receber aqui um
List provavelmente baste para o nosso mtodo, e permite que o cdigo invocador
passe outras colees como argumento. Podemos ir alm e receber Collection
49
ArqDesignSoftware_BOOK.indb 49

12/11/2011 14:48:36

Captulo 3. Tpicos de Orientao a Objetos

ou, ainda, Iterable, caso nossa necessidade seja apenas percorrer os elementos. A
escolha de Iterable, neste caso, permitiria o maior desacoplamento possvel, mas
limitaria o uso dentro do mtodo; no seria possvel, por exemplo, acessar a quantidade de elementos que essa coleo possui, nem os elementos de maneira aleatria
atravs de um ndice. Devemos procurar um balano entre o desacoplamento e a
necessidade do nosso cdigo. Esta a ideia do Princpio de Segregao de Interfaces:
clientes no devem ser forados a depender de interfaces que no usam.1
O desenvolvedor deve ter em mente que acoplar uma classe, que possui menos chances de alteraes em sua estrutura, com outra menos estvel pode ser
perigoso.2 Considere a interface List, que possui muitas razes para no mudar,
afinal, ela implementada por vrias outras classes; se sofresse alteraes, todas
as classes que a implementam teriam que ser alteradas tambm. Consideramos,
ento, que ela altamente estvel, o que significa que ela raramente obrigar uma
mudana nas classes que a utilizam (Figura 3.1).

Figura 3.1 Interfaces so mais estveis por garantirem menores


mudanas com quebra de compatibilidade.

Uma implementao de lista, MeuProprioArrayList, feita pelo desenvolvedor provavelmente mais instvel que a interface List, j que as foras que a im50
ArqDesignSoftware_BOOK.indb 50

12/11/2011 14:48:37

3.1. Programe voltado interface, no implementao

pedem de mudar so fracas (no h outras classes utilizando essa implementao).


Ou seja, uma classe acoplada a essa implementao de lista eventualmente pode
ser obrigada a mudar por causa de alguma alterao em MeuProprioArrayList.
Os frameworks e bibliotecas consagrados sempre tiram proveito do uso
de interfaces, desacoplando-se o mximo possvel de implementaes. Tanto a
Session do Hibernate quanto a EntityManager da JPA devolvem List nos
mtodos que envolvem listas de resultados. Ao analisar a fundo as implementaes atuais de Session e EntityManager do Hibernate, elas no retornam
nem ArrayList, nem LinkedList, nem nenhuma coleo do java.util, e, sim,
implementaes de listas persistentes de pacotes do prprio Hibernate.
Isto possvel, novamente, pelo desacoplamento provido pelo uso das interfaces. Alm disso, o retorno das consultas com JPA e Hibernate so List, para
deixar claro ao usurio que a ordem importante. Manter a ordem de insero e
permitir acesso aleatrio so caractersticas do contrato de List e so importantes
para o resultado de consultas, pois podem definir uma ordenao (order by),
uma tima justificativa para optar por uma interface mais especfica, e no usar
Iterable ou Collection.
Nas principais APIs do Java, fundamental programar voltado interface.
Ao usar java.io, evitamos ao mximo nos referenciar a FileInputStream,
SocketInputStream, entre outras. O cdigo a seguir aceita apenas arquivos
como streams:
public class ImportadoraDeDados {
public void carrega(FileInputStream stream) { ... }
}

Desta forma, no possvel passar qualquer tipo de InputStream para a


ImportadoraDeDados. Adotar esta limitao depende do cdigo dentro do mtodo carrega. Ao utilizar algum mtodo especfico de FileInputStream que
no esteja definido em InputStream, no h o que fazer para desacoplar o c-

digo. Caso contrrio, esse mtodo poderia, e deveria, receber uma referncia a
InputStream, ficando mais flexvel e podendo receber os mais diferentes tipos de
streams, como argumento, que provavelmente no foram previamente imaginados.
Utilize sempre o tipo menos especfico possvel.
Repare que, muitas vezes, classes abstratas trabalham como interfaces, no
sentido conceitual de orientao a objetos.3 Classes abstratas possuem a vantagem
de se poder adicionar-lhes um novo mtodo no abstrato, sem quebrar o cdigo
51
ArqDesignSoftware_BOOK.indb 51

12/11/2011 14:48:38

Captulo 3. Tpicos de Orientao a Objetos

j existente. J com o uso de interfaces (aqui, pensando na palavra-chave do Java),


a adio de qualquer mtodo acarretar a quebra das classes que a implementam.
Como interfaces nunca definem nenhuma implementao, a vantagem que o
cdigo est sempre desacoplado de qualquer outro que as utilize. Isso muda com
os polmicos extension methods do Java 8, permitindo escrever uma implementao padro nas interfaces, possibilitando suas evolues, ao mesmo tempo que
minimiza a quebra de compatibilidade.
Os mtodos load(InputStream), da classe Properties, e fromXML
(InputStream), do XStream, so timos exemplos de cdigo que no dependem de implementao. Podem receber arquivos dos mais diferentes streams:
rede (SocketInputStream), upload HTTP (ServletInputStream), arquivos
(FileInputStream), de dentro de JARs (JarInputStream) e arrays de byte genricos (ByteArrayInputStream).
A Java Database Conectivity (JDBC) outra API firmemente fundada no uso
de interfaces. O pacote java.sql possui pouqussimas classes concretas. Sempre
que encontramos um cdigo trabalhando com conexes de banco de dados, vemos
referncias interface Connection, e nunca diretamente a MySQLConnection,
OracleConnection, PostGreSQLConnection, ou qualquer outra implementao
de um driver especfico, apesar de esta possibilidade existir.
Referindo-se sempre a Connection, deixamos a escolha da implementao
centralizada em um ou poucos locais. Desta forma, fica muito fcil trocar a implementao sem modificar todo o restante do cdigo. Isso ocorre graas ao desacoplamento provido pelo uso de interfaces.
No caso do JDBC, essa escolha por uma implementao est centralizada
na classe concreta DriverManager, que aqui age como uma factory. Ela decide
por instanciar uma implementao especfica de Connection de acordo com os
parmetros passados como argumentos ao mtodo getConnection e conforme
os possveis drivers previamente carregados.
Connection connection =
DriverManager.getConnection(jdbc:mysql://192.168.0.33/banco);

Pode ser fcil enxergar as vantagens do uso das interfaces, mas bem mais
difcil comear a utiliz-las extensivamente no seu prprio domnio. O uso exagerado de reflection para invocar mtodos dependendo de algumas condies pode
ser muitas vezes substitudo por interfaces. Assim, a deciso de qual mtodo invocar deixada para a invocao virtual de mtodo que o polimorfismo promove,
52
ArqDesignSoftware_BOOK.indb 52

12/11/2011 14:48:38

3.2. Componha comportamentos

diminuindo bastante a complexidade e aumentando a manutenibilidade, alm de


algum ganho de performance.4
Isto ainda mais gritante com o uso da instruo switch, ou mesmo em um
excessivo nmero de ifs encadeados; essa abordagem pode acoplar totalmente
seu modelo, tornando necessrias mudanas frequentes nele toda vez que uma
nova entidade for adicionada ao domnio.5
Programe voltado interface, no implementao outro dos prncipios
de orientao a objetos do livro Design Patterns,6,3 abordado por meio de outros
exemplos no seminal Dependency Inversion Principle, de Bob Martin.7

3.2. Componha comportamentos


Um cdigo com poucas possibilidades de fluxos lgicos (branches de execuo), ou
seja, poucos caminhos de execuo, mais fcil de entender e manter. O exemplo
a seguir mostra um processo de pagamento:
public void processa(Pagamento aPagar) {
if (aPagar.isServico() && aPagar.getValor() > 300) {
impostos.retem(aPagar.getValor() * TAXA_A_RETER);
} else if(aPagar.isProduto()) {
estoque.diminui(aPagar.getItem());
}
conta.executa(aPagar);
if (aPagar.desejaReceberConfirmacao()) {
emails.enviaConfirmacao(aPagar);
}
}

Apesar de simples, existem seis possibilidades diferentes de execuo do mtodo, alm de ele misturar diversos comportamentos que no possuem relao,
isto , responsabilidades diferentes para uma nica classe: impostos, estoques,
transferncias e e-mails.
Tal comportamento pode ser composto por diversas partes menores e, para
tanto, refatoraes pequenas podem ser executadas. A mais simples seria a extrao de quatro mtodos, uma soluo que simplifica o cdigo atual, mas no
aumenta sua coeso.
53
ArqDesignSoftware_BOOK.indb 53

12/11/2011 14:48:38