├── .gitattributes ├── LICENSE └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | *.md linguist-documentation=false 2 | *.md linguist-language=PHP 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Ryan McDermott 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Clean Code PHP 2 | 3 | ## Sumário 4 | 5 | 1. [Introdução](#introdução) 6 | 2. [Variáveis](#variáveis) 7 | * [Use variáveis pronunciaveis e com significado claro](#use-variáveis-pronunciaveis-e-com-significado-claro) 8 | * [Use o mesmo vocabulário para o mesmo tipo de variável](#use-o-mesmo-vocabulário-para-o-mesmo-tipo-de-variável) 9 | * [Use nomes pesquisáveis (parte 1)](#use-nomes-pesquisáveis-parte-1) 10 | * [Use nomes pesquisáveis (parte 2)](#use-nomes-pesquisáveis-parte-2) 11 | * [Use nomes explicativos](#use-nomes-explicativos) 12 | * [Evite mapa mental](#evite-mapa-mental) 13 | * [Não coloque contexto desnecessário](#não-coloque-contexto-desnecessário) 14 | * [Use argumentos padrão ao invés de condicionais](#use-argumentos-padrão-ao-invéis-de-condicionais) 15 | 3. [Funções](#funções) 16 | * [Parâmetros de funções (2 ou menos)](#parâmetros-de-funções-2-ou-menos) 17 | * [Funções devem fazer apenas uma coisa](#funções-devem-fazer-apenas-uma-coisa) 18 | * [Nome de função deve dizer o que ela faz](#nome-de-função-deve-dizer-o-que-ela-faz) 19 | * [Funções devem ter apenas um nível de abstração](#funções-devem-ter-apenas-um-nível-de-abstração) 20 | * [Não use flags como parâmetros](#não-use-flags-como-parâmetros) 21 | * [Evite efeito colateral](#evite-efeito-colateral) 22 | * [Não escreva funções globais](#não-escreva-funções-globais) 23 | * [Não use o padrão Singleton](#não-use-o-padrão-singleton) 24 | * [Encapsule condicionais](#encapsule-condicionais) 25 | * [Evite condicionais negativas](#evite-condicionais-negativas) 26 | * [Evite condicionais](#evite-condicionais) 27 | * [Evite verificação de tipo (parte 1)](#evite-verificação-de-tipo-parte-1) 28 | * [Evite verificação de tipo (parte 2)](#evite-verificação-de-tipo-parte-2) 29 | * [Remova código morto](#remova-código-morto) 30 | 4. [Objetos e estrutura de dados](#objetos-e-estrutura-de-dados) 31 | * [Use getters e setters](#use-getters-e-setters) 32 | * [Faça objetos terem membros private/protected](#faça-objetos-terem-membros-privateprotected) 33 | 5. [Classes](#classes) 34 | * [S: Princípio da Responsabilidade Única (SRP)](#princípio-da-responsabilidade-Única-srp) 35 | * [O: Princípio do Aberto/Fechado (OCP)](#princípio-do-abertofechado-ocp) 36 | * [L: Princício da Substituição de Liskov (LSP)](#princício-da-substituição-de-liskov-lsp) 37 | * [I: Princípio da Segregação de interface (ISP)](#princípio-da-segregação-de-interface-isp) 38 | * [D: Princípio da Injeção de dependências (DIP)](#princípio-da-injeção-de-dependências-dip) 39 | * [Use encadeamento de método](#use-encadeamento-de-método) 40 | * [Prefira composição do que herança](#prefira-composição-do-que-herança) 41 | 6. [Não repita você mesmo (DRY)](#não-repita-você-mesmo-dry) 42 | 7. [Traduções](#traduções) 43 | 44 | ## Introdução 45 | 46 | O livro Software engineering principles, de Robert C. Martin 47 | [*Clean Code*](https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882), 48 | adaptado para PHP. Isso não é um guia de estilo. É um guia para produção de software em PHP legível, 49 | reusável, e refatorável. 50 | 51 | Nem todos os princípios aqui contidos devem ser rigorosamente seguidos, e muito menos serão 52 | universalmente acordados. Estas são orientações e nada mais, mas são codificadas em muitos 53 | anos de experiência coletiva pelos autores do *Clean Code*. 54 | 55 | Inspirado em [clean-code-javascript](https://github.com/ryanmcdermott/clean-code-javascript) 56 | 57 | ## Variáveis 58 | 59 | ### Use variáveis pronunciaveis e com significado claro 60 | 61 | **Ruim:** 62 | 63 | ```php 64 | $ymdstr = $moment->format('y-m-d'); 65 | ``` 66 | 67 | **Bom:** 68 | 69 | ```php 70 | $currentDate = $moment->format('y-m-d'); 71 | ``` 72 | 73 | **[⬆ voltar para o topo](#sumário)** 74 | 75 | ### Use o mesmo vocabulário para o mesmo tipo de variável 76 | 77 | **Ruim:** 78 | 79 | ```php 80 | getUserInfo(); 81 | getUserData(); 82 | getUserRecord(); 83 | getUserProfile(); 84 | ``` 85 | 86 | **Bom:** 87 | 88 | ```php 89 | getUser(); 90 | ``` 91 | 92 | **[⬆ voltar para o topo](#sumário)** 93 | 94 | ### Use nomes pesquisáveis (parte 1) 95 | 96 | Nós vamos ler mais código do que escrever. É importante que o código que nós escrevemos seja 97 | legível e pesquisável. Por *não* nomear variáveis que sejam significativas para o entendimento 98 | do nosso programa, nós machucamos nossos leitores. 99 | Faça variáveis com nomes pesquisáveis. 100 | 101 | **Ruim:** 102 | 103 | ```php 104 | // What the heck is 448 for? 105 | $result = $serializer->serialize($data, 448); 106 | ``` 107 | 108 | **Bom:** 109 | 110 | ```php 111 | $json = $serializer->serialize($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); 112 | ``` 113 | 114 | ### Use nomes pesquisáveis (parte 2) 115 | 116 | **Ruim:** 117 | 118 | ```php 119 | // What the heck is 4 for? 120 | if ($user->access & 4) { 121 | // ... 122 | } 123 | ``` 124 | 125 | **Bom:** 126 | 127 | ```php 128 | class User 129 | { 130 | const ACCESS_READ = 1; 131 | const ACCESS_CREATE = 2; 132 | const ACCESS_UPDATE = 4; 133 | const ACCESS_DELETE = 8; 134 | } 135 | 136 | if ($user->access & User::ACCESS_UPDATE) { 137 | // do edit ... 138 | } 139 | ``` 140 | 141 | **[⬆ voltar para o topo](#sumário)** 142 | 143 | ### Use nomes explicativos 144 | 145 | **Ruim:** 146 | 147 | ```php 148 | $address = 'One Infinite Loop, Cupertino 95014'; 149 | $cityZipCodeRegex = '/^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/'; 150 | preg_match($cityZipCodeRegex, $address, $matches); 151 | 152 | saveCityZipCode($matches[1], $matches[2]); 153 | ``` 154 | 155 | **Nada mal:** 156 | 157 | É melhor, mas ainda depende do regex. 158 | 159 | ```php 160 | $address = 'One Infinite Loop, Cupertino 95014'; 161 | $cityZipCodeRegex = '/^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/'; 162 | preg_match($cityZipCodeRegex, $address, $matches); 163 | 164 | list(, $city, $zipCode) = $matches; 165 | saveCityZipCode($city, $zipCode); 166 | ``` 167 | 168 | **Bom:** 169 | 170 | Diminui a dependência do regex nomeando os subpadrões. 171 | 172 | ```php 173 | $address = 'One Infinite Loop, Cupertino 95014'; 174 | $cityZipCodeRegex = '/^[^,\\]+[,\\\s]+(?.+?)\s*(?\d{5})?$/'; 175 | preg_match($cityZipCodeRegex, $address, $matches); 176 | 177 | saveCityZipCode($matches['city'], $matches['zipCode']); 178 | ``` 179 | 180 | **[⬆ voltar para o topo](#sumário)** 181 | 182 | ### Evite mapa mental 183 | 184 | Não force o leitor do seu código a traduzir o que as variáveis significam. 185 | Explicito é melhor que implícito. 186 | 187 | **Ruim:** 188 | 189 | ```php 190 | $l = ['Austin', 'New York', 'San Francisco']; 191 | 192 | for ($i = 0; $i < count($l); $i++) { 193 | $li = $l[$i]; 194 | doStuff(); 195 | doSomeOtherStuff(); 196 | // ... 197 | // ... 198 | // ... 199 | // Wait, what is `$li` for again? 200 | dispatch($li); 201 | } 202 | ``` 203 | 204 | **Bom:** 205 | 206 | ```php 207 | $locations = ['Austin', 'New York', 'San Francisco']; 208 | 209 | foreach ($locations as $location) { 210 | doStuff(); 211 | doSomeOtherStuff(); 212 | // ... 213 | // ... 214 | // ... 215 | dispatch($location); 216 | } 217 | ``` 218 | 219 | **[⬆ voltar para o topo](#sumário)** 220 | 221 | ### Não coloque contexto desnecessário 222 | 223 | Se o nome de sua classe/objeto diz alguma coisa, não repita no nome 224 | de suas variáveis. 225 | 226 | **Ruim:** 227 | 228 | ```php 229 | class Car 230 | { 231 | public $carMake; 232 | public $carModel; 233 | public $carColor; 234 | 235 | //... 236 | } 237 | ``` 238 | 239 | **Bom:** 240 | 241 | ```php 242 | class Car 243 | { 244 | public $make; 245 | public $model; 246 | public $color; 247 | 248 | //... 249 | } 250 | ``` 251 | 252 | **[⬆ voltar para o topo](#sumário)** 253 | 254 | ### Use argumentos padrão ao invés de condicionais 255 | 256 | **Nada mal:** 257 | Isso não é nada mal porque `$breweryName` pode ser `NULL`. 258 | 259 | ```php 260 | function createMicrobrewery($breweryName = 'Hipster Brew Co.') 261 | { 262 |    // ... 263 | } 264 | ``` 265 | 266 | **Nada mal:** 267 | 268 | Isso pode ser entendido melhor do que a versão anterior, mas é melhor ter o controle do valor da sua variável. 269 | 270 | ```php 271 | function createMicrobrewery($name = null) 272 | { 273 |    $breweryName = $name ?: 'Hipster Brew Co.'; 274 | // ... 275 | } 276 | ``` 277 | 278 | **Bom:** 279 | 280 | Se você tiver suporte para PHP 7+, você pode usar [type hinting](http://php.net/manual/en/functions.arguments.php#functions.arguments.type-declaration) e ter certeza que 281 | `$breweryName` não será `NULL`. 282 | 283 | ```php 284 | function createMicrobrewery(string $breweryName = 'Hipster Brew Co.') 285 | { 286 |    // ... 287 | } 288 | ``` 289 | 290 | **[⬆ voltar para o topo](#sumário)** 291 | 292 | ## Funções 293 | 294 | ### Parâmetros de funções (2 ou menos) 295 | 296 | Limitando a quantidade de parâmetros em uma função é importante porque é possível testar 297 | a função de forma mais fácil. Tendo mais de três conduz em uma explosão combinacional 298 | onde você tem vários casos de teste de diferentes parâmetros separados. 299 | 300 | Zero parâmetros é o caso ideal. Um ou dois parâmetros é ok, e três ou mais deve ser evitado. 301 | Qualquer coisa mais deve ser consolidado. Frequentemente, se você tem mais de dois parâmetros 302 | quer dizer que sua função está tentando fazer coisas demais. Em casos onde não está, 303 | você deve usar um objeto higher-level para suprir os parâmetros. 304 | 305 | **Ruim:** 306 | 307 | ```php 308 | function createMenu($title, $body, $buttonText, $cancellable) 309 | { 310 | // ... 311 | } 312 | ``` 313 | 314 | **Bom:** 315 | 316 | ```php 317 | class MenuConfig 318 | { 319 | public $title; 320 | public $body; 321 | public $buttonText; 322 | public $cancellable = false; 323 | } 324 | 325 | $config = new MenuConfig(); 326 | $config->title = 'Foo'; 327 | $config->body = 'Bar'; 328 | $config->buttonText = 'Baz'; 329 | $config->cancellable = true; 330 | 331 | function createMenu(MenuConfig $config) 332 | { 333 | // ... 334 | } 335 | ``` 336 | 337 | **[⬆ voltar para o topo](#sumário)** 338 | 339 | ### Funções devem fazer apenas uma coisa 340 | 341 | Esse é de longe a regra mais importante na engenharia de software. Quando funções fazem 342 | mais do que uma coisa, elas são difíceis de utilizar, testar e racionalizar. Quando você 343 | pode isolar uma função para apenas uma coisa, elas podem ser refatoradas facilmente e seu 344 | código será lido com muito mais clareza. Se você não pegar nada desse guia, além disso, 345 | você vai está à frente de muitos desenvolvedores. 346 | 347 | **Ruim:** 348 | ```php 349 | function emailClients($clients) 350 | { 351 | foreach ($clients as $client) { 352 | $clientRecord = $db->find($client); 353 | if ($clientRecord->isActive()) { 354 | email($client); 355 | } 356 | } 357 | } 358 | ``` 359 | 360 | **Bom:** 361 | 362 | ```php 363 | function emailClients($clients) 364 | { 365 | $activeClients = activeClients($clients); 366 | array_walk($activeClients, 'email'); 367 | } 368 | 369 | function activeClients($clients) 370 | { 371 | return array_filter($clients, 'isClientActive'); 372 | } 373 | 374 | function isClientActive($client) 375 | { 376 | $clientRecord = $db->find($client); 377 | 378 | return $clientRecord->isActive(); 379 | } 380 | ``` 381 | 382 | **[⬆ voltar para o topo](#sumário)** 383 | 384 | ### Nome de função deve dizer o que ela faz 385 | 386 | **Ruim:** 387 | 388 | ```php 389 | class Email 390 | { 391 | //... 392 | 393 | public function handle() 394 | { 395 | mail($this->to, $this->subject, $this->body); 396 | } 397 | } 398 | 399 | $message = new Email(...); 400 | // What is this? A handle for the message? Are we writing to a file now? 401 | $message->handle(); 402 | ``` 403 | 404 | **Bom:** 405 | 406 | ```php 407 | class Email 408 | { 409 | //... 410 | 411 | public function send() 412 | { 413 | mail($this->to, $this->subject, $this->body); 414 | } 415 | } 416 | 417 | $message = new Email(...); 418 | // Clear and obvious 419 | $message->send(); 420 | ``` 421 | 422 | **[⬆ voltar para o topo](#sumário)** 423 | 424 | ### Funções devem ter apenas um nível de abstração 425 | 426 | Quando você possui mais de um nível de abstração, sua função deve estar fazendo 427 | coisas demais. Separar as funções leva a reusabilidade e testes de forma 428 | mais fácil. 429 | 430 | **Ruim:** 431 | 432 | ```php 433 | function parseBetterJSAlternative($code) 434 | { 435 | $regexes = [ 436 | // ... 437 | ]; 438 | 439 | $statements = explode(' ', $code); 440 | $tokens = []; 441 | foreach ($regexes as $regex) { 442 | foreach ($statements as $statement) { 443 | // ... 444 | } 445 | } 446 | 447 | $ast = []; 448 | foreach ($tokens as $token) { 449 | // lex... 450 | } 451 | 452 | foreach ($ast as $node) { 453 | // parse... 454 | } 455 | } 456 | ``` 457 | 458 | **Bad too:** 459 | 460 | Cuidamos de algumas funcionalidades, mas a função `parseBetterJSAlternative()` ainda é muito complexa e não testavel. 461 | 462 | ```php 463 | function tokenize($code) 464 | { 465 | $regexes = [ 466 | // ... 467 | ]; 468 | 469 | $statements = explode(' ', $code); 470 | $tokens = []; 471 | foreach ($regexes as $regex) { 472 | foreach ($statements as $statement) { 473 | $tokens[] = /* ... */; 474 | } 475 | } 476 | 477 | return $tokens; 478 | } 479 | 480 | function lexer($tokens) 481 | { 482 | $ast = []; 483 | foreach ($tokens as $token) { 484 | $ast[] = /* ... */; 485 | } 486 | 487 | return $ast; 488 | } 489 | 490 | function parseBetterJSAlternative($code) 491 | { 492 | $tokens = tokenize($code); 493 | $ast = lexer($tokens); 494 | foreach ($ast as $node) { 495 | // parse... 496 | } 497 | } 498 | ``` 499 | 500 | **Bom:** 501 | 502 | A melhor solução é retirar as dependências da função `parseBetterJSAlternative()`. 503 | 504 | ```php 505 | class Tokenizer 506 | { 507 | public function tokenize($code) 508 | { 509 | $regexes = [ 510 | // ... 511 | ]; 512 | 513 | $statements = explode(' ', $code); 514 | $tokens = []; 515 | foreach ($regexes as $regex) { 516 | foreach ($statements as $statement) { 517 | $tokens[] = /* ... */; 518 | } 519 | } 520 | 521 | return $tokens; 522 | } 523 | } 524 | 525 | class Lexer 526 | { 527 | public function lexify($tokens) 528 | { 529 | $ast = []; 530 | foreach ($tokens as $token) { 531 | $ast[] = /* ... */; 532 | } 533 | 534 | return $ast; 535 | } 536 | } 537 | 538 | class BetterJSAlternative 539 | { 540 | private $tokenizer; 541 | private $lexer; 542 | 543 | public function __construct(Tokenizer $tokenizer, Lexer $lexer) 544 | { 545 | $this->tokenizer = $tokenizer; 546 | $this->lexer = $lexer; 547 | } 548 | 549 | public function parse($code) 550 | { 551 | $tokens = $this->tokenizer->tokenize($code); 552 | $ast = $this->lexer->lexify($tokens); 553 | foreach ($ast as $node) { 554 | // parse... 555 | } 556 | } 557 | } 558 | ``` 559 | 560 | **[⬆ voltar para o topo](#sumário)** 561 | 562 | ### Não use flags como parâmetros 563 | 564 | Flags dizem para seu usuário que essa função faz mais de uma coisa. Funções devem 565 | fazer apenas uma coisa. Divida suas funções se elas estão seguindo diferentes 566 | diretórios baseado em um boleano. 567 | 568 | **Ruim:** 569 | 570 | ```php 571 | function createFile($name, $temp = false) 572 | { 573 | if ($temp) { 574 | touch('./temp/'.$name); 575 | } else { 576 | touch($name); 577 | } 578 | } 579 | ``` 580 | 581 | **Bom:** 582 | 583 | ```php 584 | function createFile($name) 585 | { 586 | touch($name); 587 | } 588 | 589 | function createTempFile($name) 590 | { 591 | touch('./temp/'.$name); 592 | } 593 | ``` 594 | 595 | **[⬆ voltar para o topo](#sumário)** 596 | 597 | ### Evite efeito colateral 598 | 599 | Uma função produz um efeito colateral se ela faz mais do que pegar um valor e retornar 600 | outro ou outros. Um efeito colateral pode ser, escrever em um arquivo, modificar 601 | uma variável global, ou acidentalmente mandar todo seu dinheiro pra um estranho. 602 | 603 | Agora, você pode ter efeitos colaterais em uma determinada situação. Como no exemplo 604 | anterior, você pode precisar escrever em um arquivo. O que você quer fazer é centralizar 605 | onde você está fazendo isso. Não tenha várias funções ou classes que escrevem em um 606 | arquivo particular. Tenha um serviço que faça isso. Um e apenas um. 607 | 608 | O ponto principal é evitar armadilhas comuns como compartilhar um estado entre objetos 609 | sem qualquer estrutura, usando dados mutáveis que podem ser escritos por qualquer coisa, 610 | e não centralizando onde os efeitos colaterais irão ocorrer. Se você pode fazer isso, 611 | você ficará mais feliz do que a maioria dos outros programadores. 612 | 613 | **Ruim:** 614 | 615 | ```php 616 | // Global variable referenced by following function. 617 | // If we had another function that used this name, now it'd be an array and it could break it. 618 | $name = 'Ryan McDermott'; 619 | 620 | function splitIntoFirstAndLastName() 621 | { 622 | global $name; 623 | 624 | $name = explode(' ', $name); 625 | } 626 | 627 | splitIntoFirstAndLastName(); 628 | 629 | var_dump($name); // ['Ryan', 'McDermott']; 630 | ``` 631 | 632 | **Bom:** 633 | 634 | ```php 635 | function splitIntoFirstAndLastName($name) 636 | { 637 | return explode(' ', $name); 638 | } 639 | 640 | $name = 'Ryan McDermott'; 641 | $newName = splitIntoFirstAndLastName($name); 642 | 643 | var_dump($name); // 'Ryan McDermott'; 644 | var_dump($newName); // ['Ryan', 'McDermott']; 645 | ``` 646 | 647 | **[⬆ voltar para o topo](#sumário)** 648 | 649 | ### Não escreva funções globais 650 | 651 | Criar funções globais é uma má prática em várias linguagens porque você pode colidir com outra 652 | biblioteca e não será claro para o usuário de sua API até que ele tenha uma exceção em 653 | produção. Vamos pensar em um exemplo: se você quiser um array de configuração. 654 | Você poderia escrever uma função global como `config()`, mas pode colidir com outra biblioteca 655 | que tentou fazer a mesma coisa. 656 | 657 | **Ruim:** 658 | 659 | ```php 660 | function config() 661 | { 662 | return [ 663 | 'foo' => 'bar', 664 | ] 665 | } 666 | ``` 667 | 668 | **Bom:** 669 | 670 | ```php 671 | class Configuration 672 | { 673 | private $configuration = []; 674 | 675 | public function __construct(array $configuration) 676 | { 677 | $this->configuration = $configuration; 678 | } 679 | 680 | public function get($key) 681 | { 682 | return isset($this->configuration[$key]) ? $this->configuration[$key] : null; 683 | } 684 | } 685 | ``` 686 | 687 | Carrega a configuração e crie uma instância da classe `Configuration` 688 | 689 | ```php 690 | $configuration = new Configuration([ 691 | 'foo' => 'bar', 692 | ]); 693 | ``` 694 | 695 | Agora você deve usar a instância de `Configuration` na sua aplicação. 696 | 697 | **[⬆ voltar para o topo](#sumário)** 698 | 699 | ### Não use o padrão Singleton 700 | 701 | Singleton é um [anti-padrão](https://en.wikipedia.org/wiki/Singleton_pattern). Interpretado de 702 | Brian Button: 703 | 1. Eles geralmente são usados como uma **instância global**, por que isso é tão ruim? Porque **você esconde as dependências** da aplicação no seu código, em vez de expor elas através de interfaces. Fazendo alguma coisa global pra evitar que o [código cheire mal](https://en.wikipedia.org/wiki/Code_smell). 704 | 2. Ele viola o [Princípio da Responsabilidade Única](#princípio-da-responsabilidade-Única-srp): pelo fato de **controlar sua prórpia criação e ciclo de vida**. 705 | 3. Eles inerentemente fazem com que o código seja muito [acoplado](https://en.wikipedia.org/wiki/Coupling_%28computer_programming%29). Isso faz com que eles resultem em falsos resultados em **testes** em muitos casos. 706 | 4. Eles carregam o estado por todo o tempo de vida da aplicação. Um bateria de testes **para simular uma situação específica os testes precisam ser ordenados** deixando o propósito dos testes unitários de lado. Por quê? Porque cada teste unitário deve ser independente um do outro. 707 | 708 | Também tem algumas ideias muitos boas do [Misko Hevery](http://misko.hevery.com/about/) falando sobre o [centro do problema](http://misko.hevery.com/2008/08/25/root-cause-of-singletons/). 709 | 710 | **Ruim:** 711 | 712 | ```php 713 | class DBConnection 714 | { 715 | private static $instance; 716 | 717 | private function __construct($dsn) 718 | { 719 | // ... 720 | } 721 | 722 | public static function getInstance() 723 | { 724 | if (self::$instance === null) { 725 | self::$instance = new self(); 726 | } 727 | 728 | return self::$instance; 729 | } 730 | 731 | // ... 732 | } 733 | 734 | $singleton = DBConnection::getInstance(); 735 | ``` 736 | 737 | **Bom:** 738 | 739 | ```php 740 | class DBConnection 741 | { 742 | public function __construct(array $dsn) 743 | { 744 | // ... 745 | } 746 | 747 | // ... 748 | } 749 | ``` 750 | 751 | Criar uma instância da classe `DBConnection` e configuar com [DSN](http://php.net/manual/en/pdo.construct.php#refsect1-pdo.construct-parameters). 752 | 753 | ```php 754 | $connection = new DBConnection($dsn); 755 | ``` 756 | 757 | E agora você deve usar a instância de `DBConnection` na sua aplicação. 758 | 759 | **[⬆ voltar para o topo](#sumário)** 760 | 761 | ### Encapsule condicionais 762 | 763 | **Ruim:** 764 | 765 | ```php 766 | if ($article->state === 'published') { 767 | // ... 768 | } 769 | ``` 770 | 771 | **Bom:** 772 | 773 | ```php 774 | if ($article->isPublished()) { 775 | // ... 776 | } 777 | ``` 778 | 779 | **[⬆ voltar para o topo](#sumário)** 780 | 781 | ### Evite condicionais negativas 782 | 783 | **Ruim:** 784 | 785 | ```php 786 | function isDOMNodeNotPresent($node) 787 | { 788 | // ... 789 | } 790 | 791 | if (!isDOMNodeNotPresent($node)) 792 | { 793 | // ... 794 | } 795 | ``` 796 | 797 | **Bom:** 798 | 799 | ```php 800 | function isDOMNodePresent($node) 801 | { 802 | // ... 803 | } 804 | 805 | if (isDOMNodePresent($node)) { 806 | // ... 807 | } 808 | ``` 809 | 810 | **[⬆ voltar para o topo](#sumário)** 811 | 812 | ### Evite condicionais 813 | 814 | Essa parece ser uma tarefa impossível. A primeira coisa que as pessoas dizem 815 | quando ouvem isso é "como eu poderia fazer qualquer coisa sem um `if`?" A 816 | resposta é que você pode usar polimorfismo para chegar no mesmo resultado em 817 | muitos casos. A segunda pergunta é, "Bom, isso é legal, mas por que eu faria 818 | isso?" A resposta é um outro conceito de código limpo que nós aprendemos: 819 | Uma função deve fazer apenas uma coisa. Quando você tem classes e funções com 820 | `if`, você está dizendo que sua função faz mais de uma coisa. Lembre-se, 821 | só uma coisa. 822 | 823 | **Ruim:** 824 | 825 | ```php 826 | class Airplane 827 | { 828 | // ... 829 | 830 | public function getCruisingAltitude() 831 | { 832 | switch ($this->type) { 833 | case '777': 834 | return $this->getMaxAltitude() - $this->getPassengerCount(); 835 | case 'Air Force One': 836 | return $this->getMaxAltitude(); 837 | case 'Cessna': 838 | return $this->getMaxAltitude() - $this->getFuelExpenditure(); 839 | } 840 | } 841 | } 842 | ``` 843 | 844 | **Bom:** 845 | 846 | ```php 847 | interface Airplane 848 | { 849 | // ... 850 | 851 | public function getCruisingAltitude(); 852 | } 853 | 854 | class Boeing777 implements Airplane 855 | { 856 | // ... 857 | 858 | public function getCruisingAltitude() 859 | { 860 | return $this->getMaxAltitude() - $this->getPassengerCount(); 861 | } 862 | } 863 | 864 | class AirForceOne implements Airplane 865 | { 866 | // ... 867 | 868 | public function getCruisingAltitude() 869 | { 870 | return $this->getMaxAltitude(); 871 | } 872 | } 873 | 874 | class Cessna implements Airplane 875 | { 876 | // ... 877 | 878 | public function getCruisingAltitude() 879 | { 880 | return $this->getMaxAltitude() - $this->getFuelExpenditure(); 881 | } 882 | } 883 | ``` 884 | 885 | **[⬆ voltar para o topo](#sumário)** 886 | 887 | ### Evite verificação de tipo (parte 1) 888 | 889 | PHP não é fortemente tipado, isso quer dizer, que suas funções podem receber 890 | qualquer tipo de argumento. Algumas vezes você é fisgado pela liberdade 891 | e isso se tornar tentador fazer verificação de tipo nas suas funções. 892 | Há várias maneiras de evitar fazer isso. A primeira é, APIs consistentes 893 | 894 | **Ruim:** 895 | 896 | ```php 897 | function travelToTexas($vehicle) 898 | { 899 | if ($vehicle instanceof Bicycle) { 900 | $vehicle->peddleTo(new Location('texas')); 901 | } elseif ($vehicle instanceof Car) { 902 | $vehicle->driveTo(new Location('texas')); 903 | } 904 | } 905 | ``` 906 | 907 | **Bom:** 908 | 909 | ```php 910 | function travelToTexas(Traveler $vehicle) 911 | { 912 | $vehicle->travelTo(new Location('texas')); 913 | } 914 | ``` 915 | 916 | **[⬆ voltar para o topo](#sumário)** 917 | 918 | ### Evite verificação de tipo (parte 2) 919 | 920 | Se você estiver trabalhando com tipos primitivos como strings, inteiros e arrays, 921 | e você usa PHP 7+ e você não pode usar polimorfismo, mas você ainda sente a necessidade 922 | de verificar os tipos, você deve considerar a [declaração de tipo](http://php.net/manual/en/functions.arguments.php#functions.arguments.type-declaration) 923 | ou modo estrito. Isso permite que você use tipos estáticos por padrão na sintáxe da 924 | linguagem PHP. O problema com verificação de tipo manual é que isso requer muito mais 925 | código escrito para ter os tipos validados, e você perde a legibilidade do código. 926 | Mantenha seu PHP limpo, escreva bons testes, e tenha boas revisões. Caso contrário, 927 | faça tudo como o modo estrito do PHP. 928 | 929 | **Ruim:** 930 | 931 | ```php 932 | function combine($val1, $val2) 933 | { 934 | if (!is_numeric($val1) || !is_numeric($val2)) { 935 | throw new \Exception('Must be of type Number'); 936 | } 937 | 938 | return $val1 + $val2; 939 | } 940 | ``` 941 | 942 | **Bom:** 943 | 944 | ```php 945 | function combine(int $val1, int $val2) 946 | { 947 | return $val1 + $val2; 948 | } 949 | ``` 950 | 951 | **[⬆ voltar para o topo](#sumário)** 952 | 953 | ### Remova código morto 954 | 955 | Código morto é tão ruim quanto duplicar o código. Não há motivos para manter 956 | ele no seu projeto. Se ele não está sendo chamado, livre-se dele! Ele ainda 957 | está seguro no histórico de versão quando você precisar dele. 958 | 959 | **Ruim:** 960 | 961 | ```php 962 | function oldRequestModule($url) 963 | { 964 | // ... 965 | } 966 | 967 | function newRequestModule($url) 968 | { 969 | // ... 970 | } 971 | 972 | $request = newRequestModule($requestUrl); 973 | inventoryTracker('apples', $request, 'www.inventory-awesome.io'); 974 | ``` 975 | 976 | **Bom:** 977 | 978 | ```php 979 | function requestModule($url) 980 | { 981 | // ... 982 | } 983 | 984 | $request = requestModule($requestUrl); 985 | inventoryTracker('apples', $request, 'www.inventory-awesome.io'); 986 | ``` 987 | 988 | **[⬆ voltar para o topo](#sumário)** 989 | 990 | 991 | ## Objetos e estrutura de dados 992 | 993 | ### Use getters e setters 994 | 995 | No PHP você pode usar as palavras chave `public`, `protected` e `private` nos 996 | métodos. Fazendo isso você pode controlar a mudança de propriedades em um objeto. 997 | 998 | * Quando você deseja fazer mais do que obter uma propriedade de objeto, não 999 | precisa procurar e alterar todos os acessadores em sua base de código. 1000 | * Facilita a adição de validação ao fazer um `set`. 1001 | * Encapsula a representação interna. 1002 | * Fácil de adicionar logs e manipular erros ao obter e editar propriedades. 1003 | * Herdando essa classe, você pode substituir a funcionalidade padrão. 1004 | 1005 | Além disso, isso faz parte do Princípio do Aberto / Fechado, a partir dos 1006 | princípios de design orientado a objetos. 1007 | 1008 | **Ruim:** 1009 | 1010 | ```php 1011 | class BankAccount 1012 | { 1013 | public $balance = 1000; 1014 | } 1015 | 1016 | $bankAccount = new BankAccount(); 1017 | 1018 | // Buy shoes... 1019 | $bankAccount->balance -= 100; 1020 | ``` 1021 | 1022 | **Bom:** 1023 | 1024 | ```php 1025 | class BankAccount 1026 | { 1027 | private $balance; 1028 | 1029 | public function __construct($balance = 1000) 1030 | { 1031 | $this->balance = $balance; 1032 | } 1033 | 1034 | public function withdrawBalance($amount) 1035 | { 1036 | if ($amount > $this->balance) { 1037 | throw new \Exception('Amount greater than available balance.'); 1038 | } 1039 | 1040 | $this->balance -= $amount; 1041 | } 1042 | 1043 | public function depositBalance($amount) 1044 | { 1045 | $this->balance += $amount; 1046 | } 1047 | 1048 | public function getBalance() 1049 | { 1050 | return $this->balance; 1051 | } 1052 | } 1053 | 1054 | $bankAccount = new BankAccount(); 1055 | 1056 | // Buy shoes... 1057 | $bankAccount->withdrawBalance($shoesPrice); 1058 | 1059 | // Get balance 1060 | $balance = $bankAccount->getBalance(); 1061 | ``` 1062 | 1063 | **[⬆ voltar para o topo](#sumário)** 1064 | 1065 | ### Faça objetos terem membros private/protected 1066 | 1067 | **Ruim:** 1068 | 1069 | ```php 1070 | class Employee 1071 | { 1072 | public $name; 1073 | 1074 | public function __construct($name) 1075 | { 1076 | $this->name = $name; 1077 | } 1078 | } 1079 | 1080 | $employee = new Employee('John Doe'); 1081 | echo 'Employee name: '.$employee->name; // Employee name: John Doe 1082 | ``` 1083 | 1084 | **Bom:** 1085 | 1086 | ```php 1087 | class Employee 1088 | { 1089 | private $name; 1090 | 1091 | public function __construct($name) 1092 | { 1093 | $this->name = $name; 1094 | } 1095 | 1096 | public function getName() 1097 | { 1098 | return $this->name; 1099 | } 1100 | } 1101 | 1102 | $employee = new Employee('John Doe'); 1103 | echo 'Employee name: '.$employee->getName(); // Employee name: John Doe 1104 | ``` 1105 | 1106 | **[⬆ voltar para o topo](#sumário)** 1107 | 1108 | ## Classes 1109 | 1110 | ### Princípio da Responsabilidade Única (SRP) 1111 | 1112 | De acordo com o *Clean Code*, "Nunca deve haver mais de um motivo para uma 1113 | classe mudar". É tentador empacotar uma classe com muitas funcionalidades, como 1114 | quando você só pode levar uma mala no seu voo. O problema disso é que sua 1115 | classe não será conceitualmente coesa e oferecerá muitos motivos para mudar. 1116 | Minimizar a quantidade de vezes que você precisa alterar uma classe é 1117 | importante. É importante porque, se houver muita funcionalidade em uma classe e 1118 | você modificar uma parte dela, pode ser difícil entender como isso afetará 1119 | outros módulos dependentes na sua base de código. 1120 | 1121 | **Ruim:** 1122 | 1123 | ```php 1124 | class UserSettings 1125 | { 1126 | private $user; 1127 | 1128 | public function __construct($user) 1129 | { 1130 | $this->user = $user; 1131 | } 1132 | 1133 | public function changeSettings($settings) 1134 | { 1135 | if ($this->verifyCredentials()) { 1136 | // ... 1137 | } 1138 | } 1139 | 1140 | private function verifyCredentials() 1141 | { 1142 | // ... 1143 | } 1144 | } 1145 | ``` 1146 | 1147 | **Bom:** 1148 | 1149 | ```php 1150 | class UserAuth 1151 | { 1152 | private $user; 1153 | 1154 | public function __construct($user) 1155 | { 1156 | $this->user = $user; 1157 | } 1158 | 1159 | public function verifyCredentials() 1160 | { 1161 | // ... 1162 | } 1163 | } 1164 | 1165 | class UserSettings 1166 | { 1167 | private $user; 1168 | private $auth; 1169 | 1170 | public function __construct($user) 1171 | { 1172 | $this->user = $user; 1173 | $this->auth = new UserAuth($user); 1174 | } 1175 | 1176 | public function changeSettings($settings) 1177 | { 1178 | if ($this->auth->verifyCredentials()) { 1179 | // ... 1180 | } 1181 | } 1182 | } 1183 | ``` 1184 | 1185 | **[⬆ voltar para o topo](#sumário)** 1186 | 1187 | ### Princípio do Aberto/Fechado (OCP) 1188 | 1189 | Como afirma Bertrand Meyer, "as entidades de software (classes, módulos, funções 1190 | etc.) devem estar abertas para extensão, mas fechadas para modificação". O que 1191 | isso significa? Esse princípio basicamente afirma que você deve permitir que 1192 | os usuários adicionem novas funcionalidades sem alterar o código existente. 1193 | 1194 | **Ruim:** 1195 | 1196 | ```php 1197 | abstract class Adapter 1198 | { 1199 | protected $name; 1200 | 1201 | public function getName() 1202 | { 1203 | return $this->name; 1204 | } 1205 | } 1206 | 1207 | class AjaxAdapter extends Adapter 1208 | { 1209 | public function __construct() 1210 | { 1211 | parent::__construct(); 1212 | 1213 | $this->name = 'ajaxAdapter'; 1214 | } 1215 | } 1216 | 1217 | class NodeAdapter extends Adapter 1218 | { 1219 | public function __construct() 1220 | { 1221 | parent::__construct(); 1222 | 1223 | $this->name = 'nodeAdapter'; 1224 | } 1225 | } 1226 | 1227 | class HttpRequester 1228 | { 1229 | private $adapter; 1230 | 1231 | public function __construct($adapter) 1232 | { 1233 | $this->adapter = $adapter; 1234 | } 1235 | 1236 | public function fetch($url) 1237 | { 1238 | $adapterName = $this->adapter->getName(); 1239 | 1240 | if ($adapterName === 'ajaxAdapter') { 1241 | return $this->makeAjaxCall($url); 1242 | } elseif ($adapterName === 'httpNodeAdapter') { 1243 | return $this->makeHttpCall($url); 1244 | } 1245 | } 1246 | 1247 | private function makeAjaxCall($url) 1248 | { 1249 | // request and return promise 1250 | } 1251 | 1252 | private function makeHttpCall($url) 1253 | { 1254 | // request and return promise 1255 | } 1256 | } 1257 | ``` 1258 | 1259 | **Bom:** 1260 | 1261 | ```php 1262 | interface Adapter 1263 | { 1264 | public function request($url); 1265 | } 1266 | 1267 | class AjaxAdapter implements Adapter 1268 | { 1269 | public function request($url) 1270 | { 1271 | // request and return promise 1272 | } 1273 | } 1274 | 1275 | class NodeAdapter implements Adapter 1276 | { 1277 | public function request($url) 1278 | { 1279 | // request and return promise 1280 | } 1281 | } 1282 | 1283 | class HttpRequester 1284 | { 1285 | private $adapter; 1286 | 1287 | public function __construct(Adapter $adapter) 1288 | { 1289 | $this->adapter = $adapter; 1290 | } 1291 | 1292 | public function fetch($url) 1293 | { 1294 | return $this->adapter->request($url); 1295 | } 1296 | } 1297 | ``` 1298 | 1299 | **[⬆ voltar para o topo](#sumário)** 1300 | 1301 | ### Princípio da Substituição de Liskov (LSP) 1302 | 1303 | Este é um termo assustador para um conceito muito simples. É formalmente 1304 | definido como "Se S é um subtipo de T, então objetos do tipo T podem ser 1305 | substituídos por objetos do tipo S (ou seja, objetos do tipo S podem 1306 | substituir objetos do tipo T) sem alterar nenhuma das propriedades desejáveis 1307 | desse programa. (correção, tarefa executada etc.) ". Essa é uma definição ainda 1308 | mais assustadora. 1309 | 1310 | A melhor explicação para isso é se você tiver uma classe pai e uma classe filho, 1311 | então a classe pai e a classe filho poderão ser usadas de forma intercambiável 1312 | sem obter resultados incorretos. Isso ainda pode ser confuso, então vamos dar 1313 | uma olhada no exemplo clássico do retângulo quadrado. Matematicamente, um 1314 | quadrado é um retângulo, mas se você o modelar usando o relacionamento "é-um" 1315 | por herança, você rapidamente terá problemas. 1316 | 1317 | **Ruim:** 1318 | 1319 | ```php 1320 | class Rectangle 1321 | { 1322 | protected $width = 0; 1323 | protected $height = 0; 1324 | 1325 | public function render($area) 1326 | { 1327 | // ... 1328 | } 1329 | 1330 | public function setWidth($width) 1331 | { 1332 | $this->width = $width; 1333 | } 1334 | 1335 | public function setHeight($height) 1336 | { 1337 | $this->height = $height; 1338 | } 1339 | 1340 | public function getArea() 1341 | { 1342 | return $this->width * $this->height; 1343 | } 1344 | } 1345 | 1346 | class Square extends Rectangle 1347 | { 1348 | public function setWidth($width) 1349 | { 1350 | $this->width = $this->height = $width; 1351 | } 1352 | 1353 | public function setHeight(height) 1354 | { 1355 | $this->width = $this->height = $height; 1356 | } 1357 | } 1358 | 1359 | function renderLargeRectangles($rectangles) 1360 | { 1361 | foreach ($rectangles as $rectangle) { 1362 | $rectangle->setWidth(4); 1363 | $rectangle->setHeight(5); 1364 | $area = $rectangle->getArea(); // BAD: Will return 25 for Square. Should be 20. 1365 | $rectangle->render($area); 1366 | } 1367 | } 1368 | 1369 | $rectangles = [new Rectangle(), new Rectangle(), new Square()]; 1370 | renderLargeRectangles($rectangles); 1371 | ``` 1372 | 1373 | **Bom:** 1374 | 1375 | ```php 1376 | abstract class Shape 1377 | { 1378 | protected $width = 0; 1379 | protected $height = 0; 1380 | 1381 | abstract public function getArea(); 1382 | 1383 | public function render($area) 1384 | { 1385 | // ... 1386 | } 1387 | } 1388 | 1389 | class Rectangle extends Shape 1390 | { 1391 | public function setWidth($width) 1392 | { 1393 | $this->width = $width; 1394 | } 1395 | 1396 | public function setHeight($height) 1397 | { 1398 | $this->height = $height; 1399 | } 1400 | 1401 | public function getArea() 1402 | { 1403 | return $this->width * $this->height; 1404 | } 1405 | } 1406 | 1407 | class Square extends Shape 1408 | { 1409 | private $length = 0; 1410 | 1411 | public function setLength($length) 1412 | { 1413 | $this->length = $length; 1414 | } 1415 | 1416 | public function getArea() 1417 | { 1418 | return pow($this->length, 2); 1419 | } 1420 | } 1421 | 1422 | function renderLargeRectangles($rectangles) 1423 | { 1424 | foreach ($rectangles as $rectangle) { 1425 | if ($rectangle instanceof Square) { 1426 | $rectangle->setLength(5); 1427 | } elseif ($rectangle instanceof Rectangle) { 1428 | $rectangle->setWidth(4); 1429 | $rectangle->setHeight(5); 1430 | } 1431 | 1432 | $area = $rectangle->getArea(); 1433 | $rectangle->render($area); 1434 | } 1435 | } 1436 | 1437 | $shapes = [new Rectangle(), new Rectangle(), new Square()]; 1438 | renderLargeRectangles($shapes); 1439 | ``` 1440 | 1441 | **[⬆ voltar para o topo](#sumário)** 1442 | 1443 | ### Princípio da Segregação de interface (ISP) 1444 | 1445 | O ISP declara que "os clientes não devem ser forçados a depender das interfaces 1446 | que não usam". 1447 | 1448 | Um bom exemplo para demonstrar esse princípio são classes que requerem objetos 1449 | de configurações grandes. Não é necessário exigir que os clientes configurem 1450 | grandes quantidades de opções, pois na maioria das vezes eles não precisam de 1451 | todas as configurações. Torná-los opcionais ajuda a evitar uma "interface gorda". 1452 | 1453 | **Ruim:** 1454 | 1455 | ```php 1456 | interface Employee 1457 | { 1458 | public function work(); 1459 | 1460 | public function eat(); 1461 | } 1462 | 1463 | class Human implements Employee 1464 | { 1465 | public function work() 1466 | { 1467 | // ....working 1468 | } 1469 | 1470 | public function eat() 1471 | { 1472 | // ...... eating in lunch break 1473 | } 1474 | } 1475 | 1476 | class Robot implements Employee 1477 | { 1478 | public function work() 1479 | { 1480 | //.... working much more 1481 | } 1482 | 1483 | public function eat() 1484 | { 1485 | //.... robot can't eat, but it must implement this method 1486 | } 1487 | } 1488 | ``` 1489 | 1490 | **Bom:** 1491 | 1492 | Nem todo trabalhador é um funcionário, mas todo funcionário é um trabalhador. 1493 | 1494 | ```php 1495 | interface Workable 1496 | { 1497 | public function work(); 1498 | } 1499 | 1500 | interface Feedable 1501 | { 1502 | public function eat(); 1503 | } 1504 | 1505 | interface Employee extends Feedable, Workable 1506 | { 1507 | } 1508 | 1509 | class Human implements Employee 1510 | { 1511 | public function work() 1512 | { 1513 | // ....working 1514 | } 1515 | 1516 | public function eat() 1517 | { 1518 | //.... eating in lunch break 1519 | } 1520 | } 1521 | 1522 | // robot can only work 1523 | class Robot implements Workable 1524 | { 1525 | public function work() 1526 | { 1527 | // ....working 1528 | } 1529 | } 1530 | ``` 1531 | 1532 | **[⬆ voltar para o topo](#sumário)** 1533 | 1534 | ### Princípio da Injeção de dependências (DIP) 1535 | 1536 | Este princípio afirma duas coisas essenciais: 1537 | 1. Os módulos de alto nível não devem depender dos módulos de baixo nível. 1538 | Ambos devem depender de abstrações. 1539 | 2. As abstrações não devem depender de detalhes. Os detalhes devem depender de 1540 | abstrações. 1541 | 1542 | Isso pode ser difícil de entender no começo, mas se você trabalhou com estruturas 1543 | PHP (como o Symfony), viu uma implementação desse princípio na forma de Injeção 1544 | de Dependência (DI). Embora não sejam conceitos idênticos, o DIP impede que os 1545 | módulos de alto nível conheçam os detalhes de seus módulos de baixo nível e os 1546 | configurem. Isso pode ser feito através do DI. Um grande benefício disso é que 1547 | reduz o acoplamento entre os módulos. O acoplamento é um padrão de 1548 | desenvolvimento muito ruim, pois dificulta a refatoração do seu código. 1549 | 1550 | **Ruim:** 1551 | 1552 | ```php 1553 | class Employee 1554 | { 1555 | public function work() 1556 | { 1557 | // ....working 1558 | } 1559 | } 1560 | 1561 | class Robot extends Employee 1562 | { 1563 | public function work() 1564 | { 1565 | //.... working much more 1566 | } 1567 | } 1568 | 1569 | class Manager 1570 | { 1571 | private $employee; 1572 | 1573 | public function __construct(Employee $employee) 1574 | { 1575 | $this->employee = $employee; 1576 | } 1577 | 1578 | public function manage() 1579 | { 1580 | $this->employee->work(); 1581 | } 1582 | } 1583 | ``` 1584 | 1585 | **Bom:** 1586 | 1587 | ```php 1588 | interface Employee 1589 | { 1590 | public function work(); 1591 | } 1592 | 1593 | class Human implements Employee 1594 | { 1595 | public function work() 1596 | { 1597 | // ....working 1598 | } 1599 | } 1600 | 1601 | class Robot implements Employee 1602 | { 1603 | public function work() 1604 | { 1605 | //.... working much more 1606 | } 1607 | } 1608 | 1609 | class Manager 1610 | { 1611 | private $employee; 1612 | 1613 | public function __construct(Employee $employee) 1614 | { 1615 | $this->employee = $employee; 1616 | } 1617 | 1618 | public function manage() 1619 | { 1620 | $this->employee->work(); 1621 | } 1622 | } 1623 | ``` 1624 | 1625 | **[⬆ voltar para o topo](#sumário)** 1626 | 1627 | ### Use encadeamento de método 1628 | 1629 | Esse padrão é muito útil e comumente usado em muitas bibliotecas, como PHPUnit 1630 | e Doctrine. Permite que seu código seja expressivo e menos detalhado. Por esse 1631 | motivo, use o encadeamento de métodos e veja como seu código ficará limpo. Em 1632 | suas funções de classe, basta usar `return $this` no final de cada função de 1633 | `set` definida, e você pode encadear outros métodos de classe nela. 1634 | 1635 | **Ruim:** 1636 | 1637 | ```php 1638 | class Car 1639 | { 1640 | private $make = 'Honda'; 1641 | private $model = 'Accord'; 1642 | private $color = 'white'; 1643 | 1644 | public function setMake($make) 1645 | { 1646 | $this->make = $make; 1647 | } 1648 | 1649 | public function setModel($model) 1650 | { 1651 | $this->model = $model; 1652 | } 1653 | 1654 | public function setColor($color) 1655 | { 1656 | $this->color = $color; 1657 | } 1658 | 1659 | public function dump() 1660 | { 1661 | var_dump($this->make, $this->model, $this->color); 1662 | } 1663 | } 1664 | 1665 | $car = new Car(); 1666 | $car->setColor('pink'); 1667 | $car->setMake('Ford'); 1668 | $car->setModel('F-150'); 1669 | $car->dump(); 1670 | ``` 1671 | 1672 | **Bom:** 1673 | 1674 | ```php 1675 | class Car 1676 | { 1677 | private $make = 'Honda'; 1678 | private $model = 'Accord'; 1679 | private $color = 'white'; 1680 | 1681 | public function setMake($make) 1682 | { 1683 | $this->make = $make; 1684 | 1685 | // NOTE: Returning this for chaining 1686 | return $this; 1687 | } 1688 | 1689 | public function setModel($model) 1690 | { 1691 | $this->model = $model; 1692 | 1693 | // NOTE: Returning this for chaining 1694 | return $this; 1695 | } 1696 | 1697 | public function setColor($color) 1698 | { 1699 | $this->color = $color; 1700 | 1701 | // NOTE: Returning this for chaining 1702 | return $this; 1703 | } 1704 | 1705 | public function dump() 1706 | { 1707 | var_dump($this->make, $this->model, $this->color); 1708 | } 1709 | } 1710 | 1711 | $car = (new Car()) 1712 | ->setColor('pink') 1713 | ->setMake('Ford') 1714 | ->setModel('F-150') 1715 | ->dump(); 1716 | ``` 1717 | 1718 | **[⬆ voltar para o topo](#sumário)** 1719 | 1720 | ### Prefira composição do que herança 1721 | 1722 | Como declarado de maneira famosa nos [*Padrões de Design](https://en.wikipedia.org/wiki/Design_Patterns) do *Gang of Four*, você deve preferir a composição do que a herança onde puder. 1723 | Existem muitas boas razões para usar a herança e muitas boas razões para usar a 1724 | composição. O ponto principal dessa máxima é que, se sua mente instintivamente 1725 | se dedicar à herança, tente pensar se a composição poderia modelar melhor o seu 1726 | problema. Em alguns casos, pode. 1727 | 1728 | Você deve estar se perguntando então: "quando devo usar a herança?" Depende do 1729 | seu problema em questão, mas esta é uma lista decente de quando a herança faz 1730 | mais sentido do que composição: 1731 | 1732 | 1. Sua herança representa um relacionamento "é-um" e não um relacionamento 1733 | "tem-um" (Humano-> Animal vs. Usuário-> Detalhes do Usuário). 1734 | 2. Você pode reutilizar o código das classes base (humanos podem se mover como 1735 | todos os animais). 1736 | 3. Você deseja fazer alterações globais nas classes derivadas alterando uma 1737 | classe base. (Altere o gasto calórico de todos os animais quando eles se 1738 | moverem). 1739 | 1740 | **Ruim:** 1741 | 1742 | ```php 1743 | class Employee 1744 | { 1745 | private $name; 1746 | private $email; 1747 | 1748 | public function __construct($name, $email) 1749 | { 1750 | $this->name = $name; 1751 | $this->email = $email; 1752 | } 1753 | 1754 | // ... 1755 | } 1756 | 1757 | // Bad because Employees "have" tax data. 1758 | // EmployeeTaxData is not a type of Employee 1759 | 1760 | class EmployeeTaxData extends Employee 1761 | { 1762 | private $ssn; 1763 | private $salary; 1764 | 1765 | public function __construct($name, $email, $ssn, $salary) 1766 | { 1767 | parent::__construct($name, $email); 1768 | 1769 | $this->ssn = $ssn; 1770 | $this->salary = $salary; 1771 | } 1772 | 1773 | // ... 1774 | } 1775 | ``` 1776 | 1777 | **Bom:** 1778 | 1779 | ```php 1780 | class EmployeeTaxData 1781 | { 1782 | private $ssn; 1783 | private $salary; 1784 | 1785 | public function __construct($ssn, $salary) 1786 | { 1787 | $this->ssn = $ssn; 1788 | $this->salary = $salary; 1789 | } 1790 | 1791 | // ... 1792 | } 1793 | 1794 | class Employee 1795 | { 1796 | private $name; 1797 | private $email; 1798 | private $taxData; 1799 | 1800 | public function __construct($name, $email) 1801 | { 1802 | $this->name = $name; 1803 | $this->email = $email; 1804 | } 1805 | 1806 | public function setTaxData($ssn, $salary) 1807 | { 1808 | $this->taxData = new EmployeeTaxData($ssn, $salary); 1809 | } 1810 | 1811 | // ... 1812 | } 1813 | ``` 1814 | 1815 | **[⬆ voltar para o topo](#sumário)** 1816 | 1817 | ## Não repita você mesmo (DRY) 1818 | 1819 | Tente observar o princípio [DRY](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself). 1820 | 1821 | Faça o seu melhor para evitar código duplicado. O código duplicado é ruim 1822 | porque significa que há mais de um lugar para alterar algo se você precisar 1823 | alterar alguma lógica. 1824 | 1825 | Imagine se você administra um restaurante e monitora seu inventário: todos os 1826 | seus tomates, cebolas, alho, temperos etc. Se você tiver várias listas em que 1827 | mantém isso, tudo precisará ser atualizado quando você servir um prato com 1828 | tomates neles. Se você tiver apenas uma lista, há apenas um lugar para atualizar! 1829 | 1830 | Muitas vezes, você tem código duplicado porque possui duas ou mais coisas 1831 | ligeiramente diferentes, que compartilham muito em comum, mas as diferenças 1832 | delas o forçam a ter duas ou mais funções separadas que fazem as mesmas coisas. 1833 | Remover código duplicado significa criar uma abstração que pode lidar com esse 1834 | conjunto de coisas diferentes com apenas uma função / módulo / classe. 1835 | 1836 | Conseguir a abstração correta é fundamental, é por isso que você deve seguir os 1837 | princípios do SOLID estabelecidos na seção [Classes](#classes). Abstrações 1838 | ruins podem ser piores que código duplicado, portanto, tenha cuidado! Dito 1839 | isto, se você pode fazer uma boa abstração, faça-o! Não se repita; caso 1840 | contrário, você estará atualizando vários lugares sempre que quiser alterar uma coisa. 1841 | 1842 | **Ruim:** 1843 | 1844 | ```php 1845 | function showDeveloperList($developers) 1846 | { 1847 | foreach ($developers as $developer) { 1848 | $expectedSalary = $developer->calculateExpectedSalary(); 1849 | $experience = $developer->getExperience(); 1850 | $githubLink = $developer->getGithubLink(); 1851 | $data = [ 1852 | $expectedSalary, 1853 | $experience, 1854 | $githubLink 1855 | ]; 1856 | 1857 | render($data); 1858 | } 1859 | } 1860 | 1861 | function showManagerList($managers) 1862 | { 1863 | foreach ($managers as $manager) { 1864 | $expectedSalary = $manager->calculateExpectedSalary(); 1865 | $experience = $manager->getExperience(); 1866 | $githubLink = $manager->getGithubLink(); 1867 | $data = [ 1868 | $expectedSalary, 1869 | $experience, 1870 | $githubLink 1871 | ]; 1872 | 1873 | render($data); 1874 | } 1875 | } 1876 | ``` 1877 | 1878 | **Bom:** 1879 | 1880 | ```php 1881 | function showList($employees) 1882 | { 1883 | foreach ($employees as $employee) { 1884 | $expectedSalary = $employee->calculateExpectedSalary(); 1885 | $experience = $employee->getExperience(); 1886 | $githubLink = $employee->getGithubLink(); 1887 | $data = [ 1888 | $expectedSalary, 1889 | $experience, 1890 | $githubLink 1891 | ]; 1892 | 1893 | render($data); 1894 | } 1895 | } 1896 | ``` 1897 | 1898 | **Muito bom:** 1899 | 1900 | É melhor usar uma versão compacta do código. 1901 | 1902 | ```php 1903 | function showList($employees) 1904 | { 1905 | foreach ($employees as $employee) { 1906 | render([ 1907 | $employee->calculateExpectedSalary(), 1908 | $employee->getExperience(), 1909 | $employee->getGithubLink() 1910 | ]); 1911 | } 1912 | } 1913 | ``` 1914 | 1915 | **[⬆ voltar para o topo](#sumário)** 1916 | 1917 | ## Traduções 1918 | 1919 | Também disponível em outros idiomas: 1920 | 1921 | * :cn: **Chinese:** 1922 | * [php-cpm/clean-code-php](https://github.com/php-cpm/clean-code-php) 1923 | * :ru: **Russian:** 1924 | * [peter-gribanov/clean-code-php](https://github.com/peter-gribanov/clean-code-php) 1925 | * :es: **Spanish:** 1926 | * [fikoborquez/clean-code-php](https://github.com/fikoborquez/clean-code-php) 1927 | * :thailand: **Thai:** 1928 | * [panuwizzle/clean-code-php](https://github.com/panuwizzle/clean-code-php) 1929 | * :fr: **French:** 1930 | * [errorname/clean-code-php](https://github.com/errorname/clean-code-php) 1931 | * :vietnam: **Vietnamese** 1932 | * [viethuongdev/clean-code-php](https://github.com/viethuongdev/clean-code-php) 1933 | * :kr: **Korean:** 1934 | * [yujineeee/clean-code-php](https://github.com/yujineeee/clean-code-php) 1935 | * :tr: **Turkish:** 1936 | * [anilozmen/clean-code-php](https://github.com/anilozmen/clean-code-php) 1937 | 1938 | **[⬆ voltar para o topo](#sumário)** 1939 | --------------------------------------------------------------------------------