Apresentação ministrada em 27/09/2011 no Tribeca Pub em São Paulo.
Object Calisthenics são exercícios que podem ser praticados com o objetivo de melhor
Object Calisthenics
O que é Object Calisthenics?
‣ Object Calisthenics
Termo derivado do grego
“exercício”, sob o contexto
de ginástica.
Object Calisthenics
O que é Object Calisthenics?
‣ Jeff Bay em The ThoughtWorks Anthology[1]
cunhou o termo Object Calisthenics para a
computação, como o conjunto de exercícios para a
programação Orientada a Objetos.
[1] The ThoughtWorks Anthology: Essays on Software Technology and Innovation
Object Calisthenics
Regra 1: Somente um nível de indentação por método
‣ Somente um nível de indentação por método
Neologismo derivado da
palavra inglesa “indentatio”,
que significa “recuo”.
Object Calisthenics
Regra 1: Somente um nível de indentação por método
public function validaFormulario($filters='', $validators='', $options='')
{
$data = $_POST;
$input = new Zend_Filter_Input($filters, $validators, $data, $options);
$input->setDefaultEscapeFilter(new Zend_Filter_StringTrim());
if ($input->hasInvalid() || $input->hasMissing()) {
foreach ($input->getMessages() as $campo => $message) {
foreach ($message as $mensagem) {
if (strpos($mensagem, "empty")) {
throw new Tss_FormException(
"O campo {$campo} não pode ser vazio!",
3, 'javascript:history.back();'
);
} else {
throw new Tss_FormException(
"{$mensagem}", 3, 'javascript:history.back();'
);
}
}
}
}
return $input;
}
Object Calisthenics
Regra 1: Somente um nível de indentação por método
public function validaFormulario($filters='', $validators='', $options='')
{
$data = $_POST;
$input = new Zend_Filter_Input($filters, $validators, $data, $options);
$input->setDefaultEscapeFilter(new Zend_Filter_StringTrim());
0 ($input->hasInvalid() || $input->hasMissing()) { $message) {
if
1 ($message as $mensagem) {
foreach ($input->getMessages() as $campo =>
2 3
foreach
if (strpos($mensagem, "empty")) {
4
throw new Tss_FormException(
"O campo {$campo} não pode ser vazio!",
3, 'javascript:history.back();'
);
} else {
throw new Tss_FormException(
"{$mensagem}", 3, 'javascript:history.back();'
);
}
}
}
}
return $input;
}
Object Calisthenics
Regra 1: Somente um nível de indentação por método
public function validaFormulario($filters='', $validators='', $options='')
{
$data = $_POST;
$input = new Zend_Filter_Input($filters, $validators, $data, $options);
$input->setDefaultEscapeFilter(new Zend_Filter_StringTrim());
0 ($input->hasInvalid() || $input->hasMissing()) { $message) {
if
1 ($message as $mensagem) {
foreach ($input->getMessages() as $campo =>
2 3
foreach
if (strpos($mensagem, "empty")) {
4
throw new Tss_FormException(
"O campo {$campo} não pode ser vazio!",
3, 'javascript:history.back();'
);
} else {
throw new Tss_FormException(
"{$mensagem}", 3, 'javascript:history.back();'
);
}
}
}
}
return $input;
}
Object Calisthenics
Regra 1: Somente um nível de indentação por método
public function validaFormulario($filters = array(), $validators = array(), $options = null)
{
$data = $_POST;
$input = new Zend_Filter_Input($filters, $validators, $data, $options);
$input->setDefaultEscapeFilter(new Zend_Filter_StringTrim());
if ( ! ($input->hasInvalid() || $input->hasMissing())) {
return $input;
}
foreach ($input->getMessages() as $campo => $message) {
foreach ($message as $mensagem) {
if (strpos($mensagem, "empty")) {
throw new Tss_FormException(
"O campo {$campo} não pode ser vazio!",
3, 'javascript:history.back();'
);
} else {
throw new Tss_FormException(
"{$mensagem}", 3, 'javascript:history.back();'
);
}
}
}
}
Object Calisthenics
Regra 1: Somente um nível de indentação por método
public function validaFormulario($filters = array(), $validators = array(), $options = null)
{
$data = $_POST;
$input = new Zend_Filter_Input($filters, $validators, $data, $options);
$input->setDefaultEscapeFilter(new Zend_Filter_StringTrim());
if ( ! ($input->hasInvalid() || $input->hasMissing())) {
return $input;
}
0 foreach ($message as $mensagem) { => $message) {
foreach ($input->getMessages() as $campo
1 (strpos($mensagem, "empty")) {
if
2 3
throw new Tss_FormException(
"O campo {$campo} não pode ser vazio!",
3, 'javascript:history.back();'
);
} else {
throw new Tss_FormException(
"{$mensagem}", 3, 'javascript:history.back();'
);
}
}
}
}
Object Calisthenics
Regra 1: Somente um nível de indentação por método
public function validaFormulario($filters = array(), $validators = array(), $options = null)
{
$data = $_POST;
$input = new Zend_Filter_Input($filters, $validators, $data, $options);
$input->setDefaultEscapeFilter(new Zend_Filter_StringTrim());
if ( ! ($input->hasInvalid() || $input->hasMissing())) {
return $input;
}
foreach ($input->getMessages() as $campo => $message) {
foreach ($message as $mensagem) {
$errorMessage = (strpos($mensagem, "empty") === false)
? "O campo {$campo} não pode ser vazio!"
: "{$mensagem}";
throw new Tss_FormException(
$errorMessage, 3, 'javascript:history.back();'
);
}
}
}
Object Calisthenics
Regra 1: Somente um nível de indentação por método
public function validaFormulario($filters = array(), $validators = array(), $options = null)
{
$data = $_POST;
$input = new Zend_Filter_Input($filters, $validators, $data, $options);
$input->setDefaultEscapeFilter(new Zend_Filter_StringTrim());
if ( ! ($input->hasInvalid() || $input->hasMissing())) {
return $input;
}
0 foreach ($message as $mensagem) { => $message) {
foreach ($input->getMessages() as $campo
1
2
$errorMessage = (strpos($mensagem, "empty") === false)
? "O campo {$campo} não pode ser vazio!"
: "{$mensagem}";
throw new Tss_FormException(
$errorMessage, 3, 'javascript:history.back();'
);
}
}
}
Object Calisthenics
Regra 1: Somente um nível de indentação por método
public function validatePost($filters = array(), $validators = array(), $options = null)
{
$data = $_POST;
$input = new Zend_Filter_Input($filters, $validators, $data, $options);
$input->setDefaultEscapeFilter(new Zend_Filter_StringTrim());
if ( ! ($input->hasInvalid() || $input->hasMissing())) {
return $input;
}
foreach ($input->getMessages() as $field => $message) {
$messageKey = key($message);
$message = $message[$messageKey];
$errorMessage = (strpos($message, "empty") === false)
? "O campo {$field} não pode ser vazio!"
: "{$mensagem}";
throw new Tss_FormException(
$errorMessage, 3, 'javascript:history.back();'
);
}
}
Object Calisthenics
Regra 1: Somente um nível de indentação por método
public function validatePost($filters = array(), $validators = array(), $options = null)
{
$data = $_POST;
$input = new Zend_Filter_Input($filters, $validators, $data, $options);
$input->setDefaultEscapeFilter(new Zend_Filter_StringTrim());
if ( ! ($input->hasInvalid() || $input->hasMissing())) {
return $input;
}
foreach ($input->getMessages() as $field => $message) {
$messageKey = key($message);
$message = $message[$messageKey];
$errorMessage = (strpos($message, "empty") === false)
? "O campo {$field} não pode ser vazio!"
: "{$mensagem}";
throw new Tss_FormException(
$errorMessage, 3, 'javascript:history.back();'
);
}
}
Object Calisthenics
Regra 2: Não use a palavra-chave “else”
function login() {
$login
= $this->input->post('email', true);
$password
= $this->input->post('password', true);
$referencia = $this->input->post('referencia', true);
if ($this->clientes_model->login($login, $password)) {
redirect($referencia);
} else {
$this->session->set_flashdata('erro', 'Usuário ou senha inválidos.');
$this->session->set_flashdata('referencia', $referencia);
redirect('clientes');
}
}
Object Calisthenics
Regra 2: Não use a palavra-chave “else”
function login() {
$login
= $this->input->post('email', true);
$password
= $this->input->post('password', true);
$referencia = $this->input->post('referencia', true);
if ($this->clientes_model->login($login, $password)) {
redirect($referencia);
} else {
$this->session->set_flashdata('erro', 'Usuário ou senha inválidos.');
$this->session->set_flashdata('referencia', $referencia);
redirect('clientes');
}
}
Object Calisthenics
Regra 2: Não use a palavra-chave “else”
function login() {
$login
= $this->input->post('email', true);
$password
= $this->input->post('password', true);
$referencia = $this->input->post('referencia', true);
if ($this->clientes_model->login($login, $password)) {
redirect($referencia);
} else {
$this->session->set_flashdata('erro', 'Usuário ou senha inválidos.');
$this->session->set_flashdata('referencia', $referencia);
redirect('clientes');
}
}
Object Calisthenics
Regra 2: Não use a palavra-chave “else”
function login() {
$login
= $this->input->post('email', true);
$password
= $this->input->post('password', true);
$referencia = $this->input->post('referencia', true);
if ( ! ($this->clientes_model->login($login, $password))) {
$this->session->set_flashdata('erro', 'Usuário ou senha inválidos.');
$this->session->set_flashdata('referencia', $referencia);
$referencia = 'clientes';
}
redirect($referencia);
}
Object Calisthenics
Regra 3: Encapsule todos os tipos primitivos e strings
‣ Encapsule todos os tipos primitivos e strings
Object Calisthenics
Regra 3: Encapsule todos os tipos primitivos e strings
‣ Esta regra não pode ser completamente portada ao
PHP, pois a linguagem não possui boa performance com
um código completamente Orientado a Objetos
Object Calisthenics
Regra 3: Encapsule todos os tipos primitivos e strings
‣ Sugestivamente, se a variável de tipo primitivo possui
comportamento, ela deve ser encapsulada
Object Calisthenics
Regra 3: Encapsule todos os tipos primitivos e strings
class UIComponent
{
// ...
public function repaint($animate = true)
{
// ...
}
}
// ...
$component->repaint(false);
Object Calisthenics
Regra 3: Encapsule todos os tipos primitivos e strings
class UIComponent
{
// ...
public function repaint($animate = true)
{
// ...
}
}
// ...
$component->repaint(false);
Object Calisthenics
Regra 3: Encapsule todos os tipos primitivos e strings
class UIComponent
{
// ...
public function repaint(Animate $animate = null)
{
// ...
}
}
class Animate
{
public $animate = true;
public function __construct($animate)
{
$this->animate = $animate;
}
}
// ...
$component->repaint(new Animate(false));
Object Calisthenics
Regra 4: Somente um ponto por linha
‣ ...mas múltiplas chamadas aninhadas...
‣ tendem a expor um problema de encapsulamento
‣ dificultam o debug ou tratamento de exceção
‣ não simbolizam uma ação atômica
Object Calisthenics
Regra 4: Somente um ponto por linha
‣ Um chain de objetos diferentes, desde que a execução
só inclua getters e setters
$user->getLocationPoint()->getCountry()->getName();
Object Calisthenics
Regra 4: Somente um ponto por linha
‣ Um chain do próprio objeto através de uma interface
fluente
$filterChain->addFilter(new Zend_Filter_Alpha())
->addFilter(new Zend_Filter_StringToLower());
Object Calisthenics
Regra 5: Não abrevie
‣ Pense... por que não quer abreviar?
‣ Escrever o mesmo nome repetidamente?
Object Calisthenics
Regra 5: Não abrevie
‣ Pense... por que não quer abreviar?
‣ Escrever o mesmo nome repetidamente?
‣ Então seu método é reutilizado muitas vezes,
sinalizando duplicidade de código.
Object Calisthenics
Regra 5: Não abrevie
‣ Pense... por que não quer abreviar?
‣ Escrever o mesmo nome repetidamente?
‣ Então seu método é reutilizado muitas vezes,
sinalizando duplicidade de código.
‣ Nome do método muito longo?
Object Calisthenics
Regra 5: Não abrevie
‣ Pense... por que não quer abreviar?
‣ Escrever o mesmo nome repetidamente?
‣ Então seu método é reutilizado muitas vezes,
sinalizando duplicidade de código.
‣ Nome do método muito longo?
‣ Sinal de uma classe com múltiplas responsabilidades
ou a falta de uma classe auxiliar
Object Calisthenics
Regra 5: Não abrevie
‣ Pense... por que não quer abreviar?
‣ Escrever o mesmo nome repetidamente?
‣ Então seu método é reutilizado muitas vezes,
sinalizando duplicidade de código.
‣ Nome do método muito longo?
‣ Sinal de uma classe com múltiplas responsabilidades
ou a falta de uma classe auxiliar
Object Calisthenics
Regra 6: Mantenha suas entidades pequenas
‣ Adaptado ao PHP: 100 linhas por classe e não mais de
15 classes por pacote.
‣ A mudança se deve ao fato que não há regra quando à
documentação, que pode ocupar até 50% das linhas.
Object Calisthenics
Regra 7: Não crie classes com mais de duas variáveis de instância
‣ Não crie classes com mais de duas variáveis de instância
Object Calisthenics
Regra 7: Não crie classes com mais de duas variáveis de instância
‣ Objetivo:
‣ Baixa coesão
‣ Melhor encapsulamento
Object Calisthenics
Regra 7: Não crie classes com mais de duas variáveis de instância
‣ A regra original aponta 2 variáveis de instância
‣ Para o PHP, a sugestão são 5 variáveis de instância
Object Calisthenics
Regra 8: Use coleções de primeiro nível
‣ A regra é bem simples: Qualquer classe que contenha
uma coleção (array para o PHP), não deve conter
outras propriedades
Object Calisthenics
Regra 8: Use coleções de primeiro nível
‣ Objetivo:
‣ Comportamentos específicos tem um local adequado
‣ Filtragem, combinação, mapear, ...
Object Calisthenics
Regra 8: Use coleções de primeiro nível
‣ DoctrineCommonCollectionsArrayCollection
‣ Countable
‣ IteratorAggregate (herda Traversable)
‣ ArrayAccess
Object Calisthenics
Regra 9: Não crie métodos getter/setter para propriedades
‣ Não crie métodos getter/setter para propriedades
Object Calisthenics
Regra 9: Não crie métodos getter/setter para propriedades
‣ Não aplicável ao PHP devido à natureza da linguagem
Object Calisthenics
Regra 9: Não crie métodos getter/setter para propriedades
/**
* THIS CLASS WAS GENERATED BY THE DOCTRINE ORM. DO NOT EDIT THIS FILE.
*/
class ApplicationCoreDomainUserModelUserProxy
extends ApplicationCoreDomainUserModelUser
implements DoctrineORMProxyProxy
{
// ...
public function getId()
{
$this->__load();
return parent::getId();
}
public function setId($id)
{
$this->__load();
return parent::setId($id);
}
// ...
}