Object Calisthenics

Dorian Neto

  • Desenvolvedor desde 2011
  • Fundador do DojoCE
  • CTO na Prombox
  • ZCPE ID: ZEND030288

Sumário

  1. Questionamento e Motivações
  2. Conceito
  3. Estudo das regras

Você está satisfeito com a qualidade do seu código?

Motivações

  • Legibilidade
  • Manutenabilidade
  • Testabilidade
  • Reusabilidade

Calisthenics é um termo grego = Exercício

Apenas um nível de indentação por método

protected function destroyFile(Model $record)
{
    if (method_exists($record, 'getFileField')) {
        $field = $record->getFileField();
        $field = $record->$field;

        if (!empty($field)) {
            list($file, $extension) = explode('.', $field);
            $extension = '.' . $extension;

            $size = [
                '',
                '_150x150'
            ];

            foreach ($size as $item) {
                $file = $this->uploadPath . $file . $item . $extension;
                
                if (File::exists($file)) {
                    File::delete($file);
                }
            }
        }
    }
}

1

2

3

0

protected function destroyFile(Model $record)
{
    if (!method_exists($record, 'getFileField')) {
        return null;
    }

    $field = $record->getFileField();
    $field = $record->$field;

    if (!empty($field)) {
        list($file, $extension) = explode('.', $field);
        $extension = '.' . $extension;

        $size = [
            '',
            '_150x150'
        ];

        foreach ($size as $item) {
            $file = $this->uploadPath . $file . $item . $extension;
            
            if (File::exists($file)) {
                File::delete($file);
            }
        }
    }
}

1

2

0

protected function destroyFile(Model $record)
{
    if (!method_exists($record, 'getFileField')) {
        return null;
    }

    $field = $record->getFileField();
    $field = $record->$field;

    if (empty($field)) {
        return null;
    }

    list($file, $extension) = explode('.', $field);
    $extension = '.' . $extension;

    $size = [
        '',
        '_150x150'
    ];

    foreach ($size as $item) {
        $file = $this->uploadPath . $file . $item . $extension;
        
        if (File::exists($file)) {
            File::delete($file);
        }
    }
}

1

0

Não use o ELSE

public function authenticate(Request $request)
{
    $credentials = array(
        'email'    => $request->email,
        'password' => $request->senha
    );

    if (Auth::attempt($credentials)) {
        $notice = [
            'alert'   => 'success',
            'message' => trans('auth.success')
        ];

        return Redirect::intended(route('dashboard'))
            ->with('notice', $notice);
    } else {
        $notice = [
            'alert'   => 'danger',
            'message' => trans('auth.failed')
        ];

        return Redirect::route('login.index')
            ->with('notice', $notice);
    }
}
public function authenticate(Request $request)
{
    $credentials = array(
        'email'    => $request->email,
        'password' => $request->senha
    );

    $notice = [
        'alert'   => 'danger',
        'message' => trans('auth.failed')
    ];

    if (Auth::attempt($credentials)) {
        $notice['alert']   = 'success';
        $notice['message'] = trans('auth.success');

        return Redirect::intended(route('dashboard'))
            ->with('notice', $notice);
    }
    
    return Redirect::route('login.index')
        ->with('notice', $notice);
}

Um único operador de objeto (->) por linha





$this->group->member->setName("Fulano")->getConfig()->invite();




$this->group->member->setName("Fulano")->getConfig()->invite();







$this->group
    ->addFilter(new Foo())
    ->addFilter(new Bar());

Exceto para Interface fluente e Query Builder





$this->group->member->setName("Fulano")->getConfig()->invite();







$this->group
    ->addFilter(new Foo())
    ->addFilter(new Bar());



$this->group
    ->where('id', 34)
    ->select(['name'])
    ->get();

Exceto para Interface fluente e Query Builder

Não abrevie!

public function calcNum(Group $group) {...}
public function calcNum(Group $group) {...}




public function totalMembers(Group $group) {...}
public function addMemberAndInviteToGroup(Member $member) {...}
public function addMemberAndInviteToGroup(Member $member) {...}




public function addMember(Member $member) {...}
if ($rjx === true) {...}

???

if ($rjx === true) {...}




if ($request_ajax === true) {...}

Mantenha suas classes pequenas

  • Máximo de 200 linhas por classe (incluindo documentação) 
  • 10 métodos por classe
  • Até 20 linhas por método
  • 15 Classes por Namespaces (pasta)

Limite o número de atributos de instância numa classe (2 a 5)

class Group
{
    protected $provider;
    protected $member;
    protected $school;
    protected $helper;
    protected $view;
    protected $request;
}
class Group
{
    protected $provider;
    protected $member;
    protected $school;
}

Não use getters e setters

define('KILL_THE_ENEMY', 1);
define('KILL_THE_BOSS', 5);

class Game
{
    private $score = 0;

    public function setScore($score)
    {
        $this->score = $score;
    }

    public function getScore()
    {
        return $this->score;
    }
}

$player = new Game();
$player->setScore($player->getScore()+KILL_THE_BOSS);

Decisão está sendo tomada fora da classe

<?php
define('KILL_THE_ENEMY', 1);
define('KILL_THE_BOSS', 5);

class Game
{
    private $score = 0;

    public function addScore($score)
    {
        $this->score += $score;
    }
}

$player = new Game();
$player->addScore(KILL_THE_BOSS);
<?php
define('KILL_THE_ENEMY', 1);
define('KILL_THE_BOSS', 5);

class Game
{
    protected $score = 0;

    public function addScore($score)
    {
        $this->score += $score;
    }

    public function getScore()
    {
        return $this->score;
    }
}

$player = new Game();
$player->addScore(KILL_THE_ENEMY);
$player->addScore(KILL_THE_ENEMY);
$player->addScore(KILL_THE_BOSS);

var_dump($player->getScore());
// int(7)

Referências

  • http://pt.slideshare.net/guilhermeblanco/php-para-adultos-clean-code-e-object-calisthenics
  • http://williamdurand.fr/2013/06/03/object-calisthenics/
  • http://www.maawko.com/blog/carreira/object-calisthenics-regras-pra-um-codigo-melhor/
  • https://github.com/object-calisthenics/phpcs-calisthenics-rules
  • https://www.youtube.com/watch?v=u-w4eULRrr0

Seu código reflete o profissional que você é, então, zele para que ele, assim como você, cresça e se desenvolva.

Obrigado!

@doriansneto

dorianneto.com.br