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
-
Um nível de identação por método
-
Não usar o "else"
-
Encapsular todos os tipos primitivos e strings
-
Encapsular suas coleções em uma classe
-
Um ponto por linha
-
Não abreviar
-
Manter suas entidades pequenas
-
Nenhuma classe com mais de 2 variáveis com instância
-
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,404