programe voltado a interface nao a implementacao
TRANSCRIPT
-
7/23/2019 Programe Voltado a Interface Nao a Implementacao
1/7
C
Tpicos de Orientao a Objetos
Um bom design de soware 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 sur-
gem, 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.
.. P,
Ao trabalhar com colees, escolher a implementao certa para cada caso uma
tarefa difcil. Cada uma delas, como ArrayList, LinkedListou HashSet, me-
lhor para resolver determinas categorias de problemas. Pode ser muito arriscado
ArqDesignSoftware_BOOK indb 47ArqDesignSoftware_BOOK.indb 47 12/11/2011 14:48:3612/11/2011 14:48:36
-
7/23/2019 Programe Voltado a Interface Nao a Implementacao
2/7
48
Captulo 3. Tpicos de Orientao a Objetos
escrever todo o cdigo da aplicao dependente de uma deciso antecipada. Ape-
sar 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 ArrayListcom os nomes:
public classFuncionarioDao {
publicArrayList buscaPorTurno(Turno turno) { ... }
}
E um cdigo que precisa saber se determinado funcionrio esteve presente,
efetuando uma busca simples na lista devolvida:
FuncionarioDao dao = newFuncionarioDao();
ArrayList nomes = dao.buscaPorTurno(Turno.NOITE);
booleanpresente = nomes.contains(Anton Tcheckov);
Mas a busca com containsem um ArrayList, em termos computacionais,
bastante custosa. Poderamos utilizar outras alternativas de coleo, trocando o
retorno de ArrayListpara HashSet, por exemplo, pois sua operao contains
muito mais eficiente computacionalmente, usando internamente uma tabela
de espalhamento (hash table). Para poucos funcionrios, a diferena imper-
ceptvel, porm, medida que a lista aumenta, a diferena de desempenho entre
um ArrayListe umHashSetse 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 comoArrayListquebra, 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 detrabalhoso, tarefas do tipo search/replaceso um forte sinal de cdigo ruim.
H esse acoplamento sinttico com a assinatura do mtodo, que consegui-
mos resolver olhando os erros do compilador. Mas sempre h tambm informa-
es 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 Listpermite 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
ArqDesignSoftware_BOOK indb 48ArqDesignSoftware_BOOK.indb 48 12/11/2011 14:48:3612/11/2011 14:48:36
-
7/23/2019 Programe Voltado a Interface Nao a Implementacao
3/7
3.1. Programe voltado interface, no implementao
49
execuo, da um motivo da importncia de testes que garantam o comporta-
mento esperado.
O grande erro do mtodo buscaPorTurnoda classe FuncionarioDao foi
atrelar todos os usurios do mtodo a uma implementao especfica de Collec-
tion. Desta forma, alterar a implementao torna-se sempre muito mais custoso,
caracterizando o alto acoplamentoque 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 classFuncionarioDao {
publicCollection 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 interfacesJava 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 de-
pender 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 classFuncionarioDao {
public voidgravaEmLote(ArrayList funcionarios) { ... }
}
Receber precisamente ArrayList como argumento tem pouca utilidade; ra-
ramente necessitamos que uma coleo seja to especfica assim. Receber aqui um
Listprovavelmente baste para o nosso mtodo, e permite que o cdigo invocador
passe outras colees como argumento. Podemos ir alm e receber Collection
ArqDesignSoftware_BOOK indb 49ArqDesignSoftware_BOOK.indb 49 12/11/2011 14:48:3612/11/2011 14:48:36
-
7/23/2019 Programe Voltado a Interface Nao a Implementacao
4/7
50
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 quanti-dade 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 me-
nos chances de alteraes em sua estrutura, com outra menos estvelpode ser
perigoso.2Considere 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 desenvolve-
dor provavelmente mais instvelque a interface List, j que as foras que a im-
ArqDesignSoftware_BOOK indb 50ArqDesignSoftware_BOOK.indb 50 12/11/2011 14:48:3712/11/2011 14:48:37
-
7/23/2019 Programe Voltado a Interface Nao a Implementacao
5/7
3.1. Programe voltado interface, no implementao
51
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
Sessiondo Hibernate quanto a EntityManagerda JPA devolvem List nos
mtodos que envolvem listas de resultados. Ao analisar a fundo as implemen-
taes atuais de Sessione 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 inter-faces. 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 deListe 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
Iterableou 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 classImportadoraDeDados {
public voidcarrega(FileInputStream stream) { ... }
}
Desta forma, no possvel passar qualquer tipo de InputStream para a
ImportadoraDeDados. Adotar esta limitao depende do cdigo dentro do m-
todo carrega. Ao utilizar algum mtodo especfico de FileInputStreamqueno 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.3Classes abstratas possuem a vantagem
de se poder adicionar-lhes um novo mtodo no abstrato, sem quebrar o cdigo
ArqDesignSoftware_BOOK indb 51ArqDesignSoftware_BOOK.indb 51 12/11/2011 14:48:3812/11/2011 14:48:38
-
7/23/2019 Programe Voltado a Interface Nao a Implementacao
6/7
52
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 ocdigo est sempre desacoplado de qualquer outro que as utilize. Isso muda com
os polmicos extension methodsdo Java 8, permitindo escrever uma implemen-
tao 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 depen-
dem 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 ge-
nricos (ByteArrayInputStream).
AJava Database Conectivity(JDBC) outra API firmemente fundada no uso
de interfaces. O pacote java.sqlpossui 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 im-
plementao sem modificar todo o restante do cdigo. Isso ocorre graas ao de-
sacoplamento 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 Connectionde acordo com os
parmetros passados como argumentos ao mtodo getConnectione 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 exage-
rado de reflection para invocar mtodos dependendo de algumas condies pode
ser muitas vezes substitudo por interfaces. Assim, a deciso de qual mtodo in-
vocar deixada para a invocao virtual de mtodo que o polimorfismo promove,
ArqDesignSoftware_BOOK indb 52ArqDesignSoftware_BOOK.indb 52 12/11/2011 14:48:3812/11/2011 14:48:38
-
7/23/2019 Programe Voltado a Interface Nao a Implementacao
7/7
3.2. Componha comportamentos
53
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 ifsencadeados; 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,3abordado por meio de outros
exemplos no seminal Dependency Inversion Principle, de Bob Martin.7
.. C
Um cdigo com poucas possibilidades de fluxos lgicos (branchesde execuo), ou
seja, poucos caminhos de execuo, mais fcil de entender e manter. O exemplo
a seguir mostra um processo de pagamento:
public voidprocessa(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 m-
todo, 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 ex-
trao de quatro mtodos, uma soluo que simplifica o cdigo atual, mas no
aumenta sua coeso.