Object Calisthenics

Tiago Gonçalves

Vizir Software Studio

O que é?

Em 2008, A ThoughtWorks lançou um livro com diversos artigos de desenvolvedores da empresa.

Um desses artigos foi o "Object Calisthenics" por Jeff Bay, um desenvolvedor Java.

O que é?

"Object Calisthenics" é apenas um exercício que promete ajudar a desenvolver os princípios de uma boa orientação à objetos.

O que é?

Esse exercício propõe que você desenvolva um programa pequeno com 1000 linhas de código utilizando rigorosamente 9 regras.

As regras

  1. Um nível de identação por método

  2. Não usar o "else"

  3. Encapsular todos os tipos primitivos e strings

  4. Encapsular suas coleções em uma classe

  5. Um ponto por linha

  6. Não abreviar

  7. Manter suas entidades pequenas

  8. Nenhuma classe com mais de 2 variáveis com instância

  9. Nenhum getter/setter/property

1. Um nível de identação por método

Propósito:

  • Aumentar a coesão do método, ou seja, fazer com que esse método tenha apenas uma responsabilidade;
  • Aumentar o reuso de código.

Se você precisa de mais um nível, você deve criar outro método e chama-lo

1. Um nível de identação por método

Exemplo

class Board {

  String board() {
    StringBuffer buf = new StringBuffer();

    for (int i = 0; i < 10; i++) {
      for (int j = 0; j < 10; j++) {
        buf.append(data[i][j]);
      }

      buf.append(“\n”);
    }

    return buf.toString();
  }
}
class Board {

  String board() {
    StringBuffer buf = new StringBuffer();
    collectRows(buf);
    return buf.toString();
  }
    
  void collectRows(StringBuffer buf) {
    for (int i = 0; i < 10; i++) {
      collectRow(buf, i);
    }
  }
  
  void collectRow(StringBuffer buf, int row) {
    for (int i = 0; i < 10; i++) {
      Buf.append(data[row][i]);
    }
    
    buf.append(“\n”);
  }
}

2. Não usar o "else"

Propósito:

  • Evitar if/else encadeados, que geralmente geram códigos duplicados;
  • Forçar o uso do polimorfismo para tratar casos complexos, deixando seu código mais fácil de ler e manter.

Ao invéz de usar o "else", saia do método caso entre no if.

 

2. Não usar o "else"

Exemplo

function isValid(user) {
  if (user.name.length > 5 && user.age >= 18) {
    return true
  } else {
    return false
  }
}
function isValid(user) {
  if (user.name.length <= 5) {
    return false
  }

  if (user.age < 18) {
    return false
  }

  return true
}

3. Encapsular todos os tipos primitivos e strings

Propósito:

  • Encapsular o estado e comportamento do seu dado no mesmo lugar.

Aponta para o bad smell "Primitive Obsession", que acontece quando você usa tipos de dados para representar idéias do domínio da aplicação.

3. Encapsular todos os tipos primitivos e strings

Exemplo

var regex = new RegExp('^[a-z ]+$', 'gi')

function Name(value) {
  this.value = value
}

Name.prototype.isValid = function() {
  return regex.test(this.value)
}

4. Encapsular suas coleções em uma classe

Propósito:

  • Encapsular o estado e comportamento do seu dado no mesmo lugar.

Toda coleção deve ser encapsulada em sua própria classe, junto com os seus comportamentos.

4. Encapsular suas coleções em uma classe

Exemplo

function Members(members) {
  this.members = members || []
}

Members.prototype.add(member) {
  this.members.push(member)
}

Members.prototype.isValid() {
  var isValid = true

  this.members.forEach(function(member) {
    if (!member.isValid()) {
      isValid = false
      return
    }
  })

  return isValid
}

5. Um ponto por linha

Propósito:

  • Diminuir acoplamento;
  • Mais fácil de manter;
  • Mais fácil de evoluir.

"lei de demeter": Você só poder acessar objetos ligados diretamente a você.

5. Um ponto por linha

Exemplo

Seller.prototype.sell = function(client, cart) {
  // Chamar o método getMoney() é uma violação dessa regra
  if (client.getWallet().getMoney() >= cart.getTotal()) {
  
  }
  
  // Ao invéz disso, deveríamos chamar algo do tipo:
  if (client.hasEnoughMoney(cart.getTotal())) {
  
  }
}

6. Não abreviar

Propósito:

  • Maior legibilidade de código.

Abreviações são confusas e podem esconder um problema de design da aplicação.

7. Manter suas entidades pequenas

Propósito:

  • Visualizar a classe sem precisar usar a barra de rolagem;
  • Ter classes mais coesas;

Nenhuma classe com mais de 50 linhas e nenhum pacote com mais de 10 arquivos.

8. Nenhuma classe com mais de 2 variáveis com instância

Propósito:

  • Ter classes mais coesas;

Quanto mais variáveis com instâncias, menor a coesão da classe.

Se você tiver mais que isso, quebre seu código em outras classes.

9. Nenhum getter/setter/property

Propósito:

  • Maior reuso de código;
  • Encapsular o estado e comportamento do seu dado no mesmo lugar.

Baseado no princípio, "Tell, don't ask".

9. Nenhum getter/setter/property

Exemplo

// Validate person

// WRONG
var regex = new RegExp('^[a-z ]+$', 'gi')
if (regex.test(person.getName())) {
  // ...
}


// OK
if (person.isValid()) {
  // ...
}

Obrigado!

Object Calisthenics (Portuguese)

By Tiago

Object Calisthenics (Portuguese)

Simple appresentation of this topic that is so famous in Brazil.

  • 2,405