An Interest In:
Web News this Week
- April 24, 2024
- April 23, 2024
- April 22, 2024
- April 21, 2024
- April 20, 2024
- April 19, 2024
- April 18, 2024
Anotaes Captulo 3: Functions
As funes permaneceram durante a evoluo da programao;
O que torna uma funo fcil de ler e entender?
Pequenas!
- A primeira regra para funes que elas devem ser pequenas. A segunda que precisam ser mais espertas do que isso.
- Funes devem ter no mximo 20 linhas;
- Kent Beck escreveu um programa (Java/Swing) que ele chamava de Sparkle e cada funo desse programa tinha duas, ou trs, ou quatro linhas e essa deve ser o tamanho das nossas funes.
Blocos e Indentao
- Os blocos dentro de
if, else, while
e outros devem ter apenas uma linha. Possivelmente uma chamada de funo. - O nvel de indentao de uma funo deve ser de, no mximo, um ou dois nveis.
- Facilita a leitura de compreenso das funes.
Faa uma Coisa
- AS FUNES DEVEM FAZER UMA COISA. DEVEM FAZ-LA BEM. DEVEM FAZER APENAS ELA.
- Uma forma de saber se uma funo faz mais de uma coisa se voc pode extrair outra funo dela a partir de seu nome que no seja apenas uma reformulao de sua implementao.
Sees Dentro de Funes
- Se temos sees, como declaraes, inicializaes, um sinal de est fazendo mais de uma coisa;
- No d para dividir em sees as funes que fazem apenas uma coisa.
Um Nvel de Abstrao por Funo
- Vrios nveis dentro de uma funo sempre geram confuso.
- Leitores podem no conseguir dizer se uma expresso determinada um conceito essencial ou um mero detalhe.
Ler o Cdigo de Cima para Baixo: Regra Decrescente
- Queremos que o cdigo seja lido de cima para baixo, como uma narrativa.
- Regra Decrescente: Cada funo seja seguida pelas outras no prximo nvel de modo que possamos ler o programa descendo um nvel de cada vez conforme percorremos a lista de funes.
- Acaba sendo muito difcil para programadores aprenderem a seguir essa regra e criar funes que fiquem em apenas um nvel.
- muito importante aprender esse truque, pois ele o segredo para manter funes curtas e garantir que faam apenas uma coisa.
- Fazer com que a leitura do cdigo possa ser feita de cima para baixo como uma srie de pargrafos TO uma tcnica eficiente para manter o nvel consistente.
Estrutura Switch
- difcil criar uma estrutura
switch
pequena; - Tambm difcil criar uma que faa apenas uma coisa.
- Por padro, os
switch
sempre fazem muitas coisas. - Mas podemos nos certificar se cada um est em uma classe de baixo nvel e nunca repetido, usando o polimorfismo.
- A seguinte funo mostra apenas uma das operaes que podem depender do tipo de funcionrio:
public Money calculatePay(Employee e) throws InvalidEmployeeType { switch (e.type) { case COMMISSIONED: return calculateCommissionedPay(e); case HOURLY: return calculateHourlyPay(e); case SALARIED: return calculateSalariedPay(e); default: throw new InvalidEmployeeType(e.type); }}
- Essa funo acima tem vrios problemas:
- Primeiro: Ela grande, e quando adicionarmos novos tipos de funcionrios ela crescer mais ainda;
- Segundo: Ela faz mais de uma coisa;
- Terceiro: Ela viola o Princpio da Responsabilidade nica (SRP - SOLID) por haver mais de um motivo para alter-la;
- Quarto: Ela viola o Princpio de Aberto-Fechado (OCP - SOLID), porque ela precisa ser modificada sempre que novos tipos forem adicionados;
- E possivelmente o pior problema a quantidade ilimitada de outras funes que tero a mesma estrutura, por exemplo:
isPayday(Employee e, Date date),
Ou
deliverPay(Employee e, Money pay),
- A soluo nesse caso inserir uma estrutura switch no fundo de uma ABSTRACT FACTORY:
- Assim, a factory usar o
switch
para criar instncias apropriadas derivadas de Employee; - As funes:
calculatePay
,isPayday
edeliverPay
, sero enviadas de forma polifrmica atravs da interfaceEmployee
.
- Assim, a factory usar o
- A regra geral para estruturas
switch
que so aceitveis se aparecerem apenas uma vez para a criao de objetos polimrficos, e se estiverem escondidas atrs de uma relao de herana de modo que o resto do sistema no possa enxerg-la. Mas cada caso um caso e podem haver casos de no respeitar todas essas regras. - Assim temos como soluo o seguinte cdigo:
public abstract class Employee { public abstract boolean isPayday(); public abstract Money calculatePay(); public abstract void deliverPay(Money pay);}-----------------public interface EmployeeFactory { public Employee makeEmployee(EmployeeRecord r) throws InvalidEmployeeType;}-----------------public class EmployeeFactoryImpl implements EmployeeFactory { public Employee makeEmployee(EmployeeRecord r) throws InvalidEmployeeType { switch (r.type) { case COMMISSIONED: return new CommissionedEmployee(r) ; case HOURLY: return new HourlyEmployee(r); case SALARIED: return new SalariedEmploye(r); default: throw new InvalidEmployeeType(r.type); } }}
Use Nomes Descritivos
- muito importante ter bons nomes;
- Lembre-se do princpio de Ward: Voc sabe que est criando um cdigo limpo quando cada rotina que voc l como voc esperava;
- Metade do esforo para satisfazer esse princpio escolher bons nomes para funes pequenas que fazem apenas uma coisa.
- Quanto menor e mais centralizada a funo, mais fcil pensar em um nome descritivo.
- No tenha medo de criar nomes extensos, pois eles so melhores do que um pequeno e enigmtico. Um nome longo e descritivo melhor do que um comentrio extenso e descritivo.
- Experimente diversos nomes at encontrar um que seja bem descritivo.
- Seja consistente nos nomes. Use as mesmas frases, substantivos e verbos nos nomes de funes de seu mdulo.
- Exemplos:
includeSetup-AndTeardownPages
,includeSetupPages
,includeSuiteSetupPage
, eincludeSetupPage
.
Parmetros de Funes
- A quantidade ideal de parmetros para uma funo zero. Depois vem um, seguido de dois. Sempre que possvel devem-se evitar trs parmetros. Para mais de trs deve-se ter um motivo muito especial, mesmo assim no devem ser usados.
- Parmetros so complicados. Eles requerem bastante conceito.
- Os parmetros so mais difceis ainda a partir de um ponto de vista de testes:
- Imagina a dificuldade de escrever todos os casos de testes para se certificar de que todas as vrias combinaes de parmetros funcionem adequadamente.;
- Se no houver parmetros, essa tarefa simples;
- Se houver um, no to difcil assim;
- Com dois, a situao fica um pouco desafiadora. Com mais de dois, pode ser desencorajador testar cada combinao de valores apropriados.
- Os parmetros de sada so mais difceis de entender do que os de entrada.
- Por fim, um parmetro de entrada a melhor coisa depois de zero parmetro!
Formas Mondicas (Um parmetro) Comuns
- Duas razes para se passar um nico parmetro a uma funo:
- Voc pode estar fazendo uma pergunta sobre aquele parmetro, exemplo:
boolean fileExists(MyFile)
. - Ou voc pode trabalhar parmetro, transformando-o em outra coisa e retornando-o, exemplo:
InputStream fileOpen(MyFile)
transforma aString
do nome de um arquivo em um valor retornado por InputStream. - Outro uso menos comum para uma funo de evento, neste caso h um parmetro de entrada, mas nenhum de sada. Cuidado ao usar essa abordagem!
- Voc pode estar fazendo uma pergunta sobre aquele parmetro, exemplo:
- Se uma funo vai transformar seu parmetro de entrada, a alterao deve aparecer como o valor retornado. Por exemplo:
StringBuffer transform(StringBuffer in)
melhor do que:
void transform(StringBuffer out)
Parmetros Lgicos
- Esses parmetros so feios.
- Passar um booleano para uma funo certamente uma prtica horrvel, pois ele complica imediatamente a assinatura do mtodo, mostrando explicitamente que a funo faz mais de uma coisa.
- Ela faz uma coisa se o valor for verdadeiro, e outra se for falso!
Funes Dades (Dois parmetros)
- Uma funo com um parmetro mais difcil de entender do que com um.
- Com dois parmetros, preciso aprender a ignorar um dos parmetros, porm o local que ignoramos justamente onde os bugs se escondero.
- Casos em dois parmetros so necessrios:
- Por exemplo, uma classe com eixos cartesianos, como por exemplo,
Point p = new Point(0, 0)
, preciso ter os dois parmetros; - Nesse caso os dois parmetros so componentes de um nico valor.
- Por exemplo, uma classe com eixos cartesianos, como por exemplo,
- Mesmo funes bvias como
assertEquals(expected, actual)
, so problemticas! - Quantas vezes j colocou
actual
on deveria serexpected
? - Os dois parmetros no possuem uma ordem pr-determinada natural;
- A ordem
expected
,actual
uma conveno que requer prtica para assimil-la. - Funes com dois parmetros no so ruins e vamos us-las!
- Mas devemos tentar converter essas funes em funes de um parmetro, usando outro mtodo ou variveis de classe ou ainda outra classe que recebe o parmetro no construtor.
Trades (Trs parmetros)
- So consideravelmente mais difceis de entender do que as com dois parmetros;
- Pense bastante antes de criar uma trade!
- O processo de ordenao, pausa e ignorao apresenta mais do que o dobro de dificuldade.
Objetos como parmetro
- Quando uma funo parece precisar de mais de dois parmetros, provvel que alguns desses parmetros devam ser agrupados em uma classe prpria. Por exemplo:
Circle makeCircle(double x, double y, double radius);Circle makeCircle(Point center, double radius);
- Criar objetos para reduzir o nmero de parmetros pode parecer trapaa, mas no .
Listas como parmetro
- Quando queremos passar um nmero varivel de parmetros para uma funo, como por exemplo:
String.format("%s worked %.2f hours.", name, hours);
- Se os parmetros forem todos tratados da mesma forma, eles sero equivalentes a um nico parmetro do tipo
List
. - Por isso, o
String.format
uma funo com dois parmetros:
public String format(String format, Object... args)
Verbos e palavras-chave
- Escolher bons nomes para uma funo pode ajudar muito a explicar a inteno da funo e a ordem e a inteno dos parmetros.
- No caso de funo com um parmetro, a funo e o parmetro devem formar um par verbo/substantivo muito bom. Por exemplo,
write(name)
, qualquer que seja essa coisa name est sendo write (escrito). Um nome ainda melhor poderia serwriteField(name)
, que indica que o name um campo. - Por exemplo:
assertEquals
pode ser melhor escrito comoassertExpectedEqualsActual(expected, actual)
, assim diminui o problema de ter que lembrar a ordem dos parmetros.
Evite Efeitos Colaterais
- Efeitos colaterais so mentiras.
- Se a funo promete fazer apenas uma coisa, mas tambm faz outras coisas escondidas. Vamos ter efeitos indesejveis. Por exemplo:
public class UserValidator { private Cryptographer cryptographer; public boolean checkPassword(String userName, String password) { User user = UserGateway.findByName(userName); if (user != User.NULL) { String codedPhrase = user.getPhraseEncodedByPassword(); String phrase = cryptographer.decrypt(codedPhrase, password); if ("Valid Password".equals(phrase)) { Session.initialize(); return true; } } return false; }}
Essa funo de verificar a senha, tem um efeito colateral, que a chamada do Session.initialize()
, o nome checkPassword
indica que verifica a senha, mas no indica que tambm inicializa a sesso. Assim, podemos correr o risco de apagar os dados da sesso existente quando ele decidir autenticar o usurio.
- Assim, temos um efeito colateral de acoplamento. No caso o
checkPassword
s pode ser chamada quando realmente formos inicializar a sesso, do contrrio dados sero perdidos. - Se realmente queremos manter o acoplamento dessa forma, deveramos deixar explcito no nome da funo, como por exemplo,
checkPasswordAndInitializeSession
.
Parmetros de Sada
- Quando precisamos reler a assinatura da funo para entender o que acontece com o parmetro de entrada, temos um problema, e isso deve ser evitado!
- De modo geral, devemos evitar parmetros de sada. Caso a funo precise alterar o estado de algo, mude o estado do objeto que a pertence.
Separao comando-consulta
- As funes devem fazer ou responder algo, mas no ambos. Ou alterar o estado de um objeto ou retorna informaes sobre ele.
- Fazer as duas tarefas costuma gerar confuso. Por exemplo:
public boolean set(String attribute, String value);
Pode levar a instrues estranhas como:
if (set("username", "unclebob"))...
E fica um caos a interpretao, o que significa esse trecho de cdigo, estamos perguntando se o atributo username recebeu o valor unclebob? Ou se username obteve xito ao receber o valor unclebob?
A inteno neste cdigo acima ter o set
como um adjetivo, assim deveramos ler se o atributo username
anteriormente recebeu o valor unclebob
, porm no fica bem claro, para isso devemos usar o nome melhor como setAndCheckIfExists
, mesmo assim ainda tinhamos um cdigo estranho:
if (attributeExists("username")) {setAttribute("username", "unclebob");...}
Prefira excees a retorno de cdigos de erro
- Fazer funes retornarem cdigos de erros uma leve violao da separao comando-consulta, pois os comandos so usados como expresses de comparao em estruturas
if
:
if (deletePage(page) == E_OK)
- Retornar cdigo de erro se torna um problema para quem chama a funo, j que ele vai ter que lidar com o erro e possivelmente criar estruturas aninhadas, deixando o cdigo muito ruim.
- Mas se usarmos excees, o cdigo de tratamento de erro pode ficar separado do cdigo e ser simplificado, por exemplo:
try { deletePage(page); registry.deleteReference(page.name); configKeys.deleteKey(page.name.makeKey());}catch (Exception e) { logger.log(e.getMessage());}
- Extraia os blocos try/catch
- Esses blocos no tem o direito de serem feios;
- Eles confundem a estrutura do cdigo e misturam o tratamento de erro com o processamento normal do cdigo;
- melhor colocar esses blocos em suas prprias funes:
public void delete(Page page) { try { deletePageAndAllReferences(page); } catch (Exception e) { logError(e); }}private void deletePageAndAllReferences(Page page) throws Exception { deletePage(page); registry.deleteReference(page.name); configKeys.deleteKey(page.name.makeKey());}private void logError(Exception e) { logger.log(e.getMessage());}
Assim, a funo delete
s faz o tratamento de erro, a funo deletePageAndAllReferences
s trata de processos que excluem toda pgina, e o log apenas adicionar a mensagem do erro no console.
Tratamento de erro uma coisa s
- Tratamento de erro uma coisa s, portanto uma funo que trata erros no deve fazer mais nada!
- Assim, a instruo
try
deve ser a primeira instruo da funo e nada mais antes dela. - Assim, podemos evitar o uso de classe de erros como por exemplo
Error.java
, sendo um Enum como vrios erros e que tudo dependeria dessa classe.
Evite repetio
- Repetio um problema.
- Sempre ser necessrio modificar mais de um lugar quando o algoritmo mudar.
- E nisso podemos omitir erros gerando bugs.
- A duplicao pode ser a raiz de todo o mal no software.
- Muitos princpios e prticas tm sido criadas com a finalidade de controlar ou eliminar a repetio de cdigo.
Programao estruturada
- Programao estruturada de Edsger Dijkstra: Cada funo e bloco dentro de uma funo deve ter uma entrada e uma sada.
- Apenas em funes maiores tais regras proporcionam benefcios significativos.
- Se mantivermos funes pequenas, as vrias instrues
return, break, continue
no traro problemas.Mas instrues comogoto
s devem existir em grandes funes e devemos evit-las.
Como escrever funes como essa?
- Talvez no seja possvel aplicar todas as regras vistas at aqui de incio.
- Nas funes, elas comeam longas e complexas, com muitos nveis de indentaes e loops aninhados, muitos parmetros, nomes ruins e aleatrios, duplicao de cdigo.
- Porm depois organizamos, refinamos o cdigo, dividindo em funes, trocamos os os nomes, removemos a duplicao, e no fim devemos ter uma funo que respeite as regras vistas aqui.
Concluso
- As funes so os verbos e as classes os substantivos.
- Essa uma verdade muito antiga.
- A arte de programar , e sempre foi, a arte do projeto de linguagem (linguagem literal, narrativa).
- Seguindo as regras deste captulo, suas funes sero curtas, bem nomeadas e bem organizadas.
- Mas jamais se esquea de que seu objetivo verdadeiro contar a histria do sistema.
Original Link: https://dev.to/jonilsonds9/anotacoes-capitulo-3-functions-12gm
Dev To
An online community for sharing and discovering great ideas, having debates, and making friendsMore About this Source Visit Dev To