Livro: Clean Code by Robert C. Martin (2008)

Published: 2020-05-19, Updated: 2024-07-22

Livro: Clean Code by Robert C. Martin (2008)

Conteúdo

Summário

Recomendação de livros

Foreword by James O. Coplien

Small Things Matter, mainly when talking about programming

In software, 80% or more of what we do is quaintly called “maintenance”

Total Productive Maintenance (TPM) - 5S Principles

Introduction

1 - Clean Code #33

Our code should be matter-of-fact as opposed to speculative. It should contain only what is necessary. Our readers should perceive us to have been decisive #40

The Grand Redesign in the Sky

O Cenário do sistema que ficou impossível de manter, inventam de criar um novo do zero, separam o time em dois, o time que cria o novo sistema e o time que mantém o legado, agora dois sistemas tem que ser mantidos e quando o novo fica pronto, ele já tem que ser refatorado denovo.

This race can go on for a very long time. I’ve seen it take 10 years. And by the time it’s done, the original members of the tiger team are long gone, and the current members are demanding that the new system be redesigned because it’s such a mess. #36

The Total Cos of Owning a Mess

O Código ser ruim, é culpa sua, não é culpa do PO, do cliente, do prazo, etc. É seu trabalho proteger o código #37

"what if you were a doctor and had a patient who demanded that you stop all the silly hand-washing in preparation for surgery because it was taking too much time?"

A maioria das pessoas consegue dizer se um código é limpo ou não, mas saber fazer um código limpo é outra coisa, é como a pintura de quadros

Figure 1-1: Productivity vs. time when making bad code image

O mesmo código feito nas coxas é o que levanta e derruba a mesma empresa, traz a vida um produto rapidamente e o mata por bugs, dificuldade na sustentação e criação de novas features.

Two decades later I met one of the early employees of that company and asked him what had happened. The answer confirmed my fears. They had rushed the product to market and had made a huge mess in the code. As they added more and more features, the code got worse and worse until they simply could not manage it any longer. It was the bad code that brought the company down.

Every change they make to the code breaks two or three other parts of the code. No change is trivial #35

Faça o que precisa ser feito

When hand-washing was first recommended to physicians by Ignaz Semmelweis in 1847, it was rejected on the basis that doctors were too busy and wouldn’t have time to wash their hands between patient visits.

What is Clean Code?

We Are Authors

Indeed, many of the recommendations in this book are controversial. You will probably not agree with all of them. You might violently disagree with some of them. That’s fine. #44

2 - Meaningful Names

Use Intention-Revealing Names #49

Make Meaningful Distinctions #51

Use Searchable Names #53

Avoid Encodings

Avoid Mental Mapping #56

Readers shouldn’t have to mentally translate your names into other names they already know. #56 ...

In general programmers are pretty smart people. Smart people sometimes like to show off their smarts by demonstrating their mental juggling abilities

clarity is king

Professionals use their powers for good and write code that others can understand.

Method Names

Complex fulcrumPoint = Complex.fromRealNumber(23.0);

is generally better than

Complex fulcrumPoint = new Complex(23.0);

Pick One Word per Concept

Avoid using the same word for two purposes

Má ideia ter duas funções com mesmo nome que se comportam de formas diferentes, uma tem side-effect, a outra não.

int add(int a, int b){
  return a + b;
}

void add(int n) {
  this.numbersList.add(n);
}

Use Solution Domain Names

The name AccountVisitor means a great deal to a programmer who is familiar with the VISITOR pattern #58

Add Meaningful Context

List.of(1) vs of(1), of what?

Imagine that you have variables named firstName, lastName, street, houseNumber, city, state, and zipcode. Taken together it’s pretty clear that they form an address. But what if you just saw the state variable being used alone in a method? #59

github

Don’t Add Gratuitous Context #60

In an imaginary application called “Gas Station Deluxe,” it is a bad idea to prefix every class with GSD

3 - Functions #62

Small!

Blocks and Indenting

The indent level of a function should not be greater than one or two. This, of course, makes the functions easier to read and understand. #66

Do One Thing

FUNCTIONS SHOULD DO ONE THING. THEY SHOULD DO IT WELL. THEY SHOULD DO IT ONLY. #66

One Level of Abstraction per Function

There are concepts in there that are at a very high level of abstraction, such as getHtml(); others that are at an intermediate level of abstraction, such as: String pagePathName = PathParser.render(pagePath) ; and still others that are remark-ably low level, such as: .append("\n").

Reading Code from Top to Bottom: The Stepdown Rule

The Stepdown Rule: We want every function to be followed by those at the next level of abstraction so that we can read the program, descending one level of abstraction at a time as we read down the list of functions #68

Switch Statements

My general rule for switch statements is that they can be tolerated if they appear only once, are used to create polymorphic objects, and are hidden behind an inheritance relationship so that the rest of the system can’t see them [G23].#70

Use Descriptive Names #70

Function Arguments #71

O autor é bem restritivo sobre a quantidade de argumentos, eu sou um pouco (apenas um pouco) mais flexível, eu sou do argumento que enquanto o tamanho da função for pequeno e os nomes forem bons dá pra manter funções de 1 a 4 argumentos sem sentir culpa e sem afetar consideravelmente a qualidade do código.

Common Monadic Forms (One argument function)

Good examples, use o padrão verbo(metodo)/nome(parametro)

Flag Arguments

Don't do that

Passing a boolean into a function is a truly terrible practice. It immediately complicates the signature of the method, loudly proclaiming that this function does more than one thing #72

Dyadic Functions (Dois Argumentos) #73

Triads (Três Argumentos)

Argument Objects

Circle makeCircle(double x, double y, double radius);
Circle makeCircle(Point center, double radius);

Have No Side Effects #75

Side Effect Example

public boolean checkPassword(String userName, String password) {
  User user = UserGateway.findByName(userName);
  if(user.password.equals(password)) {
    Session.initialize();
    return true;
  }
  return false;
}

Output Arguments #76

In general output arguments should be avoided. If your function must change the state of something, have it change the state of its owning object. #76

Command Query Separation

Functions should either change the state of an object, or it should return some information about that object. Doing both often leads to confusion. #76

Prefer Exceptions to Returning Error Codes #77

If you use exceptions instead of returned error codes, then the error processing code can be separated from the happy path code and can be simplified:

try {
  deletePage(page);
  registry.deleteReference(page.name);
  configKeys.deleteKey(page.name.makeKey());
}
catch (Exception e) {
  logger.log(e.getMessage());
}

Better than

if (deletePage(page) == E_OK) {
  if (registry.deleteReference(page.name) == E_OK) {
    if (configKeys.deleteKey(page.name.makeKey()) == E_OK){
      logger.log("page deleted");
    } else {
      logger.log("configKey not deleted");
    }
  } else {
    logger.log("deleteReference from registry failed");
  }
} else {
  logger.log("delete failed");
  return E_ERROR;
}

Extract Try/Catch Blocks #78

The Error.java Dependency Magnet

Returning error codes usually implies that there is some class or enum in which all the error codes are defined.

public enum Error {
  OK,
  INVALID,
  NO_SUCH,
  LOCKED,
  OUT_OF_RESOURCES,
  WAITING_FOR_EVENT;
}

Classes like this are a dependency magnet; many other classes must import and use them.

Don’t Repeat Yourself: Não duplique código #79

Dentro do método testableHtml é repetido 4x o seguinte código para os casos SetUp, SuiteSetUp , TearDown, and SuiteTearDown

WikiPage setup = PageCrawlerImpl.getInheritedPage("SetUp", wikiPage);
if (setup != null) {
  WikiPagePath setupPath = wikiPage.getPageCrawler().getFullPath(setup);
  String setupPathName = PathParser.render(setupPath);
  buffer.append("!include -setup .")
    .append(setupPathName)
    .append("\n");
}

Refatorado para

void include(String pageName, String arg) throws Exception {
  WikiPage inheritedPage = findInheritedPage(pageName);
  if (inheritedPage != null) {
    String pagePathName = getPathNameForPage(inheritedPage);
    buildIncludeDirective(pagePathName, arg);
  }
}

Structured Programming

Concordo com o ponto do goto e concordo em mitigar ao máximo o return, seguir na linha do Robert dizendo que basta manter a função pequena para mitigar os problemas.

Dijkstra said that every function, and every block within a function, should have one entry and one exit. Following these rules means that there should only be one return statement in a function, no break or continue statements in a loop, and never, ever, any goto statements. #79

How Do You Write Functions Like This?

Comece do jeito que for confortável de escrever, escreva todo o código necessário para fazer funcionar, faça os testes e depois refatore para respeitar as regras do clean code.

When I write functions, they come out long and complicated. They have lots of indenting and nested loops. They have long argument lists. The names are arbitrary, and there is duplicated code ... In the end, I wind up with functions that follow the rules I’ve laid down in this chapter. I don’t write them that way to start. I don’t think anyone could. #80

Conclusion

Pense nas classes e funções como uma forma de contar uma história onde cada camada (Classe, nome função e implementação da função) detalha mais um pouco daquela história e esse detalhe pode ser consultado apenas se a pessoa quiser saber esses detalhes, mas nem sempre precisa dele para entender a história.

Master programmers think of systems as stories to be told rather than programs to be written. #80

4 - Comments #84

The proper use of comments is to compensate for our failure to express ourself in code. #85 ... If our programming languages were expressive enough, or if we had the talent to subtly wield those languages to express our intent, we would not need comments very much—perhaps not at all.

Comments Do Not Make Up for Bad Code

Rather than spend your time writing the comments that explain the mess you’ve made, spend it cleaning that mess. #86

Explain Yourself in Code

// Check to see if the employee is eligible for full benefits

if ((employee.flags & HOURLY_FLAG) && (employee.age > 65))

Or this?

if (employee.isEligibleForFullBenefits())

Good Comments

Legal Comments

Sometimes our corporate coding standards force us to write certain comments for legal reasons.

// Copyright (C) 2003,2004,2005 by Object Mentor, Inc. All rights reserved.
// Released under the terms of the GNU General Public License version 2 or later.
package com.acme....

Informative Comments

// format matched kk:mm:ss EEE, MMM dd, yyyy
Pattern timeMatcher = Pattern.compile(
"\\d*:\\d*:\\d* \\w*, \\w* \\d*, \\d*");

Explanation of Intent

//This is our best attempt to get a race condition
//by creating large number of threads.
for (int i = 0; i < 25000; i++) {
  WidgetBuilderThread widgetBuilderThread =
  new WidgetBuilderThread(widgetBuilder, text, parent, failFlag);
  Thread thread = new Thread(widgetBuilderThread);
  thread.start();
}

Warning of Consequences

//SimpleDateFormat is not thread safe,
//so we need to create each instance independently.
SimpleDateFormat df = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z");
df.setTimeZone(TimeZone.getTimeZone("GMT"));

TODO Comments

Amplification

A comment may be used to amplify the importance of something that may otherwise seem inconsequential.

String listItemContent = match.group(3).trim();
// the trim is real important. It removes the starting
// spaces that could cause the item to be recognized
// as another list.

Javadocs in Public APIs

There is nothing quite so helpful and satisfying as a well-described public API #90

Bad Comments

Mumbling

Do a comment just because you feel you should or because the process requires it, is a hack. If you decide to write a comment, then spend the time necessary to make sure it is the best comment you can write.

Redundant Comments

// Utility method that returns when this.closed is true. Throws an exception
// if the timeout is reached.
public synchronized void waitForClose(final long timeoutMillis)
throws Exception {
  if(!closed){
    wait(timeoutMillis);
    if(!closed)
    throw new Exception("MockResponseSender could not be closed");
  }
}

or

public abstract class ContainerBase implements Container {

...
  
/**
* The container event listeners for this Container.
*/
protected ArrayList listeners = new ArrayList();
  
...

or

Misleading Comments

Quando o comentário explica uma coisa e o código faz outra.

Mandated Comments #94

Comenta só porque é obrigado a comentar.

/**
*
* @param title The title of the CD
* @param author The author of the CD
* @param tracks The number of tracks on the CD
* @param durationInMinutes The duration of the CD in minutes
*/
public void addCD(String title, String author,
  int tracks, int durationInMinutes) {
...

Journal Commands / Attributions and By Lines

People add a comment to the start of a module every time they edit it, use git instead.

/*
 * Changes (from 11-Oct-2001)
 * --------------------------
 * 11-Oct-2001 : Re-organised the class and moved it to new package 
 *               com.jrefinery.date (DG); 
 * 05-Nov-2001 : Added a getDescription() method, and eliminated NotableDate
 *               class (DG);
 * 12-Nov-2001 : IBD requires setDescription() method, now that NotableDate
 *               class is gone (DG); Changed getPreviousDayOfWeek(),
 *               getFollowingDayOfWeek() and getNearestDayOfWeek() to correct
 *               bugs (DG);
......

or

/* Added by Rick */
public class Acme {
...

Noise / Scary Noise Comments

Só sujam o código, não agregam em nada

/**
* Default constructor.
*/
protected AnnualDateRule() {
}

Don’t Use a Comment When You Can Use a Function or a Variable

Before

// does the module from the global list <mod> depend on the
// subsystem we are part of?
if (smodule.getDependSubsystems().contains(subSysMod.getSubSystem()))

After

ArrayList moduleDependees = smodule.getDependSubsystems();
String ourSubSystem = subSysMod.getSubSystem();
if (moduleDependees.contains(ourSubSystem))

Commented-Out Code

InputStreamResponse response = new InputStreamResponse();
response.setBody(formatter.getResultStream(), formatter.getByteCount());
// InputStream resultsStream = formatter.getResultStream();
// StreamReader reader = new StreamReader(resultsStream);
// response.setContent(reader.read(formatter.getByteCount()));

5 - Formatting

Code formatting is about communication, and communication is the professional developer’s first order of business. #107

Vertical Formatting

Numa média de 200 linhas e no máximo 500 baseado nos projetos (junit, fitNesse, Time and Money). #108

Linhas brancas entre as seções do código ajuda a separar os conceitos, ex #109

package fitnesse.wikitext.widgets;

import java.util.regex.*;

public class BoldWidget extends ParentWidget {
public static final String REGEXP = "'''.+?'''";
private static final Pattern pattern = Pattern.compile("'''(.+?)'''",
  Pattern.MULTILINE + Pattern.DOTALL
);
  
public BoldWidget(ParentWidget parent, String text) throws Exception {
  super(parent);
  ....

Vertical Distance

Have you ever chased your tail through a class, hopping from one function to the next, trying to find out how the functions relate and operate, only to get lost in a rat’s nest of confusion? This is frustrating because you are trying to understand what the system does, but you are spending your time and mental energy on trying to locate and remember where the pieces are. #111

Horizontal Formatting

Team Rules

Cada dev tem seu gosto, por isso o time deve se juntar e definir a regra de formatação do projeto e usar sempre a mesma

The last thing we want to do is add more complexity to the source code by writing it in a jumble of different individual styles. #121

6 - Objects and Data Structures #124

Hiding implementation is not just a matter of putting a layer of functions between the variables... Rather it exposes abstract interfaces that allow its users to manipulate the essence of the data, without having to know its implementation #125

Listing 6-3: Os dados são literais, ainda será necessária outra classe para calcular o nível de combustível

public interface Vehicle {
  double getFuelTankCapacityInGallons();
  double getGallonsOfGasoline();
}

Listing 6-4: O dado é astraído, é universal, 0-100% é uma medida que serve para qualquer carro.

public interface Vehicle {
  double getPercentFuelRemaining();
}

Data Structure vs Data Transfer Object (ou Value Object)

O Robert defende que aqui que data structures tem que expor seus campos ao invés de usar accessors, mas dá alternativa de usar Data Transfer Objects (#131), então eu descarto a datastructure.

Data/Object Anti-Symmetry

O exemplo anterior do Veiculo é um objeto, porque ele esconde o dado por trás de abstração e expõe funções para operar esse dado, já a data structure simplesmente expõe o seu dado como é não customa ter funções de operação do dado #126.

Procedural code (code using data structures) makes it easy to add new functions without changing the existing data structures. OO code, on the other hand, makes it easy to add new classes without changing existing functions. #128

Mature programmers know that the idea that everything is an object is a myth. Sometimes you really do want simple data structures with procedures operating on them. #128

The Law Of Demeter

A Law of Demeter me parece fazer bastante sentido mas explicação do Robert para mim desvirtuou a regra da lei de Demeter então preferi seguir a lei original e não a interpretação do Robert.

Também existem exceções para essa lei, exemplo, java.util.stream.

Hybrids

São classes de datastructure que tem métodos de objects, evite isso.

Hiding Structure #131

Faça isso

BufferedOutputStream bos = ctxt.createScratchFileStream(classFileName);

Ao invés disso:

final String outputDir = ctxt.getOptions().getScratchDir().getAbsolutePath();
String outFile = outputDir + "/" + className.replace('.', '/') + ".class";
FileOutputStream fout = new FileOutputStream(outFile);
BufferedOutputStream bos = new BufferedOutputStream(fout);

Active Record (ou Entities da javax.persistence)

São DTOs que estão amarrados com o banco de dados de alguma forma, mexer neles, altera o dado no banco, exatamente como as Entities do JPA.

Segundo o Robert, misturar esse tipo de record com os Objects, criando métodos neles é uma má prática, eu acho que é OK, desde que essa Entity seja antes disso uma Entity do DDD e tenha o auto save do Hibernate desligado. #132

7 - Error Handling

Error handling is important, but if it obscures logic, it’s wrong.

Many code bases are completely dominated by error handling. When I say dominated, I don’t mean that error handling is all that they do. I mean that it is nearly impossible to see what the code does because of all of the scattered error handling. #134

Um método para tratar erro, outro para a lógica

Faça um método só para tratar o erro, não faça um método que trate erro e a lógica tudo junto.

We can write robust clean code if we see error handling as a separate concern, something that is viewable independently of our main logic #143

O listing 7 #135 dá um pouco de exemplo disso, cria o método sendShutdown e o tryToShutdown(), try significa que ele pode estourar exceção.

Eu prefiro fazer assim

/**
 * #or shutdownQuietly like Apache Commons io IOUtils.closeQuietly()
 */
void safeShutdown(){ 
  this.shutdown();
}

void shutdown() {
  throw new ShutdownException();
}

Use Exceptions Rather Than Return Codes

The problem with return error instead of throw exception is that they clutter the caller. The caller must check for errors immediately after the call #135.

Write Your Try-Catch-Finally Statement First

Try to write tests that force exceptions, and then add behavior to your handler to satisfy your tests. This will cause you to build the transaction scope of the try block first and will help you maintain the transaction nature of that scope. #137

Use Unchecked Exceptions

The debate is over. At the time, we thought that checked exceptions were a great idea; and yes, they can yield some benefit. However, it is clear now that they aren’t necessary for the production of robust software ... The price of checked exceptions is an Open/Closed Principle1 violation #137

Provide Context with Exceptions

Adicione o ID do registro que falhou, ou todo o contexto necessário no log de erro da exception.

Each exception that you throw should provide enough context to determine the source and location of an error. #138

Define Exception Classes in Terms of a Caller's Needs

Agrupe as exceptions de uma forma que vá trata-las não duplique código, que possa fazer isso:

LocalPort port = new LocalPort(12);
try {
  port.open();
} catch (PortDeviceFailure e) {
  reportError(e);
  logger.log(e.getMessage(), e);
}

Ao invés disso:

ACMEPort port = new ACMEPort(12);
try {
  port.open();
} catch (DeviceResponseException e) {
  reportPortError(e);
  logger.log("Device response exception", e);
} catch (ATM1212UnlockedException e) {
  reportPortError(e);
  logger.log("Unlock exception", e);
} catch (GMXError e) {
  reportPortError(e);
  logger.log("Device response exception");
} 

Define the normal flow

Evite quebrar o flow com exceptions que podem ser evitadas como no exemplo abaixo:

try {
  MealExpenses expenses = expenseReportDAO.getMeals(employee.getID());
  m_total += expenses.getTotal();
} catch(MealExpensesNotFound e) {
  m_total += getMealPerDiem();
}

Troque pelo dão retornando um MealExpenses do tipo MealPerDiem (default). SPECIAL CASE PATTERN [Fowler] #141

Don’t Return Null

Evite aumentar a complexidade do seu código nunca tendo objetos nulos nele, logo não precisará fazer nullchecks.

If you are tempted to return null from a method, consider throwing an exception or returning a SPECIAL CASE object instead. If you are calling a null-returning method from a third-party API, consider wrapping that method with a method that either throws an exception or returns a special case object. #141

List<Employee> employees = getEmployees();
for(Employee e : employees) {
  totalPay += e.getPay();
}

Ao invés de

List<Employee> employees = getEmployees();
if (employees != null) {
  for(Employee e : employees) {
    totalPay += e.getPay();
  }
}

Don't pass Null

Não passe nulo, fazer nullcheck dos parametros do método deixa o erro mais claro mas não resolve, apenas deixa o código mais complexo e no final das contas ainda vai dar problema em prod pois não será possível para o sistema tratar isso sozinho. #143

8 - Boundaries

In this chapter we look at practices and techniques to keep the boundaries (integration with other people's code) of our software clean. #144

When we use code that is out of our control, special care must be taken to protect our investment and make sure future change is not too costly. #151

It’s better to depend on something you control than on something you don’t control, lest it end up controlling you.

Using Third-Party Code

Não exponha detalhes de implementação, busque abstair para seus clientes seus detalhes de implementação, da classe Sensors que usa uma map para guardar, não exponha a map #145

public class Sensors {
  private Map sensors = new HashMap();
  public Sensor getById(String id) {
    return (Sensor) sensors.get(id);
  }
}

Exploring and Learning Boundaries / Learning Tests Are Better Than Free

Learning the third-party code is hard. Integrating the third-party code is hard too. #147

Para aprender a usar libraries, escreva testes que as usam e assim você vai aprender a usar e ao mesmo tempo documentar exemplos de uso.

When there are new releases of the third-party package, we run the learning tests to see whether there are behavioral differences #149

Using Code That Does Not Yet Exist

Defina a interface que você gostaria que tivesse, crie uma implementação de mock e quando tiver a implementação real, crie uma nova implementação para a interface. #150

image

9 - Unit Tests

Tests are as important to the health of a project as the production code is because tests preserve and enhance the flexibility, maintainability, and reusability of the production code. So

If you let the tests rot, then your code will rot too. #164


The Three Laws of TDD #153

Keeping Tests Clean

Não trate a qualidade do código dos testes como secundária ou cada vez mais vai ficar mais dificil de manter os testes, você vai desistir de fazer testes, deletar testes chatos de arrumar e ficar sem testes e então seu sistema vai começar a ter vários bugs pq não tem teste.

Clean Tests

Listing 9.1 - bad test example

Listing 9.2 - refactored sample

One Assert per Test

I think the single assert rule is a good guideline.7 I usually try to create a domain specific testing language that supports it, as in Listing 9-5. But I am not afraid to put more than one assert in a test. I think the best thing we can say is that the number of asserts in a test ought to be minimized. #162

F.I.R.S.T / FIRST

10 - Classes

Por que o código geralmente fica sujo?

As pessoas focam em resolver o problema e esquecem ou despriorizam deixar o código limpo.

Getting software to work and making software clean are two very different activities. #170

Class Organization #167

Encapsulation

Buscar deixar os recursos da classe isolados, encapsulados, não necessariamente sendo privados, protected pode ser o suficiente.

Eu gosto disso, de não ficar sempre colocando private, deixo como default, porque são menas letras na assinatura do método e ainda mantem o encapsulamento, o go trabalha assim.

Classes Should Be Small - SRP: Single Responsability Principle

Conte responsabilidades da classe ao invés de contar linhas

The name of a class should describe what responsibilities it fulfills. ... The more ambiguous the class name, the more likely it has too many responsibilities. #169

For example, class names including weasel words like Processor or Manager or Super often hint at unfortunate aggregation of responsibilities

We should also be able to write a brief description of the class in about 25 words, without using the words “if,” “and,” “or,” or “but.” How would we describe the SuperDashboard? “The SuperDashboard provides access to the component that last held the focus, and it also allows us to track the version and build numbers.” The first “and” is a hint that SuperDashboard has too many responsibilities.

Cohesion

The strategy of keeping functions small and keeping parameter lists short can sometimes lead to a proliferation of instance variables that are used by a subset of methods. When this happens, it almost always means that there is at least one other class trying to get out of the larger class #172

Organizing For Change - OCP: Open Closed Principle

Quando for criar novos comportamentos na clase, pense num design onde você consiga fazer isso criando classes novas ao invés de adicionar métodos na atual, exemplo da classe SQL. #179

DIP: Dependency Inversion Principle

Referência interfaces ao invés da classe concreta, assim você pode testar a classe e mocar suas dependências.

11 - Systems

Construa sistemas com arquiteturas simples, porém modularizadas, com as camadas separadas, conceitos separados, em resumo pratique DDD, Hexagonal Arc.

How would you build a city?

Cities also work because they have evolved appropriate levels of abstraction and modularity that make it possible for individuals and the “components” they manage to work effectively, even without understanding the big picture. #185

Separate Constructing a System from Using It

Em geral usar um framework para injeção de dependência como o Spring, Quarkus ou Dagger resolve o problema deste módulo, mesmo assim esteja ciente de separar o código de infraestrtura da app.Mesmo conceito da Hexagonal Arc. Separe o configurator do resto da app.

A powerful mechanism for separating construction from use is Dependency Injection (DI), the application of Inversion of Control (IoC) to dependency management #188.

Separe o código e lógica para subir a aplicação, injetar deps, etc. do código que vai simplesmente rodar.

The startup process of object construction and wiring is no exception. We should modularize this process separately from the normal runtime logic and we should make sure that we have a global, consistent strat- egy for resolving our major dependencies. #186

Separation of Main

Quando falando de criação da infraestrutura:

Mesmo conceito do configurator da Hexagonal Arc. a Main class ou main module constrói as dependencias e via DIP (Dependency Inversion Princle) ou IOC (Inversion Of Control) as passa para a app que vai rodar. A mesma regra funciona para o teste automatizado de componente.

Factories

As vezes a app precisa em runtime criar instâncias de classes que variam dependendo de regras, use interface de factories para abstrair essas criações, a implementação das factories pode ser provida pelo main module.

Scaling UP

It is a myth that we can get systems “right the first time.” #189

Software systems are unique compared to physical systems. Their architectures can grow incrementally, if we maintain the proper separation of concerns. #189

O Autor explica que separação de conceitos é que a lógica de domínio seja separada da tecnologia (framework, da infraestrutura, do dataprovider, do entrypoint) dá o exemplo do ejb que é altamente acoplado à lógica.

AOP / Proxies / AspectJ

O Autor sugere isolar a persistência que é um problema no EJB1 e EJB2 com AOP mas ele mesmo mostra que o EJB3 reslve isso com anotações que são como as do JPA. Ao meu ver AOP é uma feature interessante para resolver itens de segurança, talvez cache, error handling, etc.

This code is much cleaner than the original EJB2 code #197... The power of separating concerns through aspect-like approaches can’t be overstated, If you can write your application’s domain logic using POJOs, decoupled from any architecture concerns at the code level, then it is possible to truly test drive your architecture #198.

Test Drive the System Architecture

Se tratando de software você não precisa BDUFar

Building architects have to do BDUF because it is not feasible to make radical architectural changes to a large physical structure once construction is well underway. Although software has its own physics, it is economically feasible to make radical change, if the structure of the software separates its concerns effectively. #198

Use Standards Wisely, When They Add Demonstrable Value

I have seen teams become obsessed with various strongly hyped standards and lose focus on implementing value for their customers. #199

Systems Need Domain-Specific Languages

Faz referência a escrever o código usando linguagem ubiqua (DDD), trazer a comunicação do código para um nível ubiquo.

Domain-Specific Languages (DSLs) permit code to be written so that it reads like a structured form of prose that a domain expert might write. #199

12 - Emergence #202

Como desenvolver sistemas seguindo regras que facilitam que emerjam bons designs?

According to Kent, a design is “simple” if it follows these rules, The rules are given in order of importance:

Simple Design Rule 1: Runs All the Tests

Writing tests leads to better designs. #203

Direciona para usar coisas como IOC

Simple Design Rules 2–4: Refactoring

The fact that we have these tests eliminates the fear that cleaning up the code will break it!

This is also where we apply the final three rules of simple design:

Minimal Classes and Methods

High class and method counts are sometimes the result of pointless dogmatism. Consider, for example, a coding standard that insists on creating an interface for each and every class. Or consider developers who insist that fields and behavior must always be separated into data classes and behavior classes. Such dogma should be resisted and a more pragmatic approach adopted. #207

13 - Concurrency

Concurrent code is difficult to get right. Code that is simple to follow can become nightmarish when multiple threads and shared data get into the mix. If you are faced with writing concurrent code, you need to write clean code with rigor or else face subtle and infrequent failures. First and foremost, follow the Single Responsibility Principle. Break your system into POJOs that separate thread-aware code from thread-ignorant code. Make sure when you are testing your thread-aware code, you are only testing it and nothing else. This suggests that your thread-aware code should be small and focused. Know the possible sources of concurrency issues: multiple threads operating on shared data, or using a common resource pool. Boundary cases, such as shutting down cleanly or finishing the iteration of a loop, can be especially thorny. #221

Recommendations

Concurrency Defense Principles

Single Responsibility Principle

Keep your concurrency-related code separate from other code. #212

Corollary: Limit the Scope of Data

Reduza a quantidade de dados que são compartilhados por multiplas threads para idealmente zero ou um.

Take data encapsulation to heart; severely limit the access of any data that may be shared. #21

Corollary: Use Copies of Data

A good way to avoid shared data is to avoid sharing the data in the first place. In some situations it is possible to copy objects and treat them as read-only. In other cases it might be possible to copy objects, collect results from multiple threads in these copies and then merge the results in a single thread. #213

Corollary: Threads Should Be as Independent as Possible

Attempt to partition data into independent subsets than can be operated on by independent threads, possibly in different processors.

Testing threaded code #217

Write tests that have the potential to expose problems and then run them frequently, with different programatic configurations and system configurations and load. If tests ever fail, track down the failure. Don’t ignore a failure just because the tests pass on a subsequent run.

Make Your Threaded Code Tunable #219

Instrument Your Code to Try and Force Failures #221

Use jiggling strategies to ferret out errors.

14 - Sucessive Refinment: Args parser utility

Deixar o código limpo é muito mais fácil que limpar um código sujo que já estava lá há tempos

If you made a mess in a module in the morning, it is easy to clean it up in the afternoon #281

Este capitulo mostra como um código que começa simples (argument parser que parseia apenas boolan) ao necessitar ser evoluido (parsear string e int) demonstra que está com um design ruim onde a classe é sempre modificada e aumenta de tamanho de forma incrível, também vários ifs passam a ser feitos, então esse é o ponto onde você refatora aplicando um novo design que acomode a evolução de uma forma melhor.

Programming is a craft more than it is a science. To write clean code, you must first write dirty code and then clean it. #231

As vezes você precisa adicionar código duplicado para poder deletar código depois.

However, I couldn’t get rid of them just by deleting them because that would break the system. Instead, I added a new Map for the ArgumentMarshaler and then one by one changed the methods to use it instead of the three original maps. #252

Much of good software design is simply about partitioning—creating appropriate places to put different kinds of code. This separation of concerns makes the code much simpler to understand and maintain. #281

Programmers who satisfy themselves with merely working code are behaving unprofessionally #281

15 - JUnit Internals #282

Este capitulo pega uma classe do JUnit e mostra como o código dela poderia ser limpo um pouco mais aplicando os princípios elencados nas heurísticas apresentadas pelo Uncle Bob neste livro.

16 - Refactoring SerialDate

Antes de refatorar escreva testes até cobrir todo o código, pode usar o Clover para medir a cobertura #299

17 - Smells and Heuristics

The result is a rather long list of things that smell bad to me when I read code. #316

G5: Duplication

1 - The most obvious form of duplication is when you have clumps of identical code that look like some programmers went wild with the mouse, pasting the same code over and over again. These should be replaced with simple methods.

2 - A more subtle form is the switch/case or if/else chain that appears again and again in various modules, always testing for the same set of conditions. These should be replaced with polymorphism.

3 - Still more subtle are the modules that have similar algorithms, but that don’t share similar lines of code. This is still duplication and should be addressed by using the TEM- PLATE METHOD,4 or STRATEGY5 pattern.

G11: Inconsistency

If you do something a certain way, do all similar things in the same way.

G14: Feature Envy

The methods of a class should be interested in the variables and functions of the class they belong to, and not the variables and functions of other classes

G20: Function Names Should Say What They Do #328

--------------------- // ------------------

metadata={"id": 680, "createdAt": "2020-05-19 02:25:30.980806", "updatedAt": "2020-05-23 01:37:46.682837"}


Junit 5 Examples Ast Bookmarks

Comments