php para adultos: clean code e object calisthenics
Post on 27-Aug-2014
1.584 Views
Preview:
DESCRIPTION
TRANSCRIPT
GuilhermeBlanco
PH
P 4
Adults
"Vamos lá, Sucker! Mexa esse traseiro gordo!"
Fucker & Sucker: A Pair of Two Double Cops
GuilhermeBlanco
PH
P 4
Adults
Guilherme Blanco
@guilhermeblanco
github.com/guilhermeblanco
GuilhermeBlanco
PH
P 4
Adults
Instaclick
• ~300 empregados
• Mais de 67 milhões de usuários (somente em um website)
• 12000 requisições/segundo
• Nós usamos PHP!
GuilhermeBlanco
PH
P 4
Adults
Motivação
• Legibilidade
• Mantenabilidade
• Reusabilidade
• Testabilidade
GuilhermeBlanco
PH
P 4
Adults S T U P I D
ingleton
ight c
ouplin
g
ntestabil
ity
remature o
ptim
izatio
n
ndescrip
tiv
e n
amin
g
uplic
atio
n
GuilhermeBlanco
PH
P 4
Adults S O L I D
ingle r
esponsib
ilit
y
pen/c
lose p
rin
cip
le
iskov s
ubstit
utio
n
nterface s
egregatio
n
ependency in
versio
n
GuilhermeBlanco
PH
P 4
Adults
<?php!!interface Bird!{! public function setLocation($longitude, $latitude);!! public function setAltitude($altitude);!! public function draw();!}!
GuilhermeBlanco
PH
P 4
Adults
<?php!!class Penguim implements Bird!{! public function setAltitude($altitude)! {! // Do nothing! }!}!
GuilhermeBlanco
PH
P 4
Adults
<?php!!interface Bird!{! public function setLocation($longitude, $latitude);!! public function draw($altitude);!}!!interface FlightfulBird extends Bird!{!! public function setAltitude($altitude);!}!
GuilhermeBlanco
PH
P 4
Adults
<?php!!namespace Dating\UserBundle\Entity!{! class User! {! /**! * @var \Dating\UserBundle\Entity\Image! */!! protected $avatar;! }!}!!namespace Dating\MediaBundle\Entity!{! class Image! {! /**! * @var \Dating\UserBundle\Entity\User! */!! protected $owner;!! }!}
GuilhermeBlanco
PH
P 4
Adults
<?php!!namespace Dating\UserBundle\Entity!{! class User! {! /**! * @var AvatarInterface! */!! protected $avatar;! }!! interface AvatarInterface! {! // ...! }!}!!namespace Dating\MediaBundle\Entity!{! use Dating\UserBundle\Entity\AvatarInterface;!! class Image implements AvatarInterface! {! /**! * @var \Dating\UserBundle\Entity\User! */!! protected $owner;!! }!}!
GuilhermeBlanco
PH
P 4
Adults
<?php!!public function validateForm($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 $field => $messageList) {! foreach ($messageList as $message) {! if (strpos($message, "empty")) {! throw new Tss_FormException(! "The field {$field} cannot be empty!", ! 3, ! "javascript:history.back();"! );! } else { ! throw new Tss_FormException(! "{$message}", ! 3, ! "javascript:history.back();"! );! }! }! }! }!! return $input;!}
GuilhermeBlanco
PH
P 4
Adults
<?php!!public function validateForm($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 $field => $messageList) {! foreach ($messageList as $message) {! if (strpos($message, "empty")) {! throw new Tss_FormException(! "The field {$field} cannot be empty!", ! 3, ! "javascript:history.back();"! );! } else { ! throw new Tss_FormException(! "{$message}", ! 3, ! "javascript:history.back();"! );! }! }! }! }!! return $input;!}
0
Class prototype
12
34
GuilhermeBlanco
PH
P 4
Adults
<?php!!public function validateForm($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 => $messageList) {! foreach ($messageList as $message) {! if (strpos($message, "empty")) {! throw new Tss_FormException(! "The field {$field} cannot be empty!", ! 3, ! "javascript:history.back();"! );! } else { ! throw new Tss_FormException(! "{$message}", ! 3, ! "javascript:history.back();"! );! }! }! }!! return $input;!} !
GuilhermeBlanco
PH
P 4
Adults
<?php!!public function validateForm($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 => $messageList) {! foreach ($messageList as $message) {! if (strpos($message, "empty")) {! throw new Tss_FormException(! "The field {$field} cannot be empty!", ! 3, ! "javascript:history.back();"! );! } else { ! throw new Tss_FormException(! "{$message}", ! 3, ! "javascript:history.back();"! );! }! }! }!! return $input;!} !
01
23
GuilhermeBlanco
PH
P 4
Adults
<?php!!public function validateForm($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 => $messageList) {! foreach ($messageList as $message) {! $errorMessage = (strpos($message, "empty") === false)! ? "The field {$field} cannot be empty!"! : $message;!! throw new Tss_FormException(! $errorMessage, ! 3, ! "javascript:history.back();"! );! }! }!! return $input;!} !
GuilhermeBlanco
PH
P 4
Adults
<?php!!public function validateForm($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 => $messageList) {! foreach ($messageList as $message) {! $errorMessage = (strpos($message, "empty") === false)! ? "The field {$field} cannot be empty!"! : $message;!! throw new Tss_FormException(! $errorMessage, ! 3, ! "javascript:history.back();"! );! }! }!! return $input;!} !
01
2
GuilhermeBlanco
PH
P 4
Adults
<?php!!public function validateForm($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 => $messageList) {! $message = array_shift($messageList);! $errorMessage = (strpos($message, "empty") === false)! ? "The field {$field} cannot be empty!"! : $message;!! throw new Tss_FormException(! $errorMessage, ! 3, ! "javascript:history.back();"! );! }!! return $input;!} !
GuilhermeBlanco
PH
P 4
Adults
<?php!!public function validateForm($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 => $messageList) {! $message = array_shift($messageList);! $errorMessage = (strpos($message, "empty") === false)! ? "The field {$field} cannot be empty!"! : $message;!! throw new Tss_FormException(! $errorMessage, ! 3, ! "javascript:history.back();"! );! }!! return $input;!} !
01
Logical blocks
Variable interpolation
GuilhermeBlanco
PH
P 4
Adults
<?php!!public function validateForm($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 => $messageList) {! $message = array_shift($messageList);! $errorMessage = (strpos($message, "empty") === false)! ? sprintf("The field %s cannot be empty!", $field)! : $message;!! throw new Tss_FormException(! $errorMessage, ! 3, ! "javascript:history.back();"! );! }!! return $input;!} !
GuilhermeBlanco
PH
P 4
Adults
Benefícios
• Single Responsibility Principle ("S" de SOLID)
• Favorece reusabilidade
GuilhermeBlanco
PH
P 4
Adults
<?php!!public function createPost($request)!{! $entity = new Post();! $form = new MyForm($entity);! $form->bind($request);! if ($form->isValid()){! $repository = $this->getRepository('MyBundle:Post');! if (!$repository->exists($entity)) {! $repository->save($entity);! return $this->redirect('create_ok');! } else {! $error = "Post Title already exists";! return array('form' => $form, 'error' => $error);! }! } else {! $error = "Invalid fields";! return array('form' => $form, 'error' => $error);! } !}
Type-cast
Separe o código em blocos.
Considere como
parágrafos!
Espaçamento
GuilhermeBlanco
PH
P 4
Adults
<?php!!public function createPost(Request $request)!{! $repository = $this->getRepository('MyBundle:Post');! $entity = new Post();! $form = new MyForm($entity);!! $form->bind($request);!! if ($form->isValid()) {! if ( ! $repository->exists($entity)) {! $repository->save($entity);!! return $this->redirect('create_ok');! } else {! $error = "Post Title already exists";!! return array('form' => $form, 'error' => $error);! }! } else {! $error = "Invalid fields";! ! return array('form' => $form, 'error' => $error);! } !}!
GuilhermeBlanco
PH
P 4
Adults
<?php!!public function createPost(Request $request)!{! $repository = $this->getRepository('MyBundle:Post');! $entity = new Post();! $form = new MyForm($entity);!! $form->bind($request);!! if ($form->isValid()) {! if ( ! $repository->exists($entity)) {! $repository->save($entity);!! return $this->redirect('create_ok');! } else {! $error = "Post Title already exists";!! return array('form' => $form, 'error' => $error);! }! } else {! $error = "Invalid fields";! ! return array('form' => $form, 'error' => $error);! } !}!
GuilhermeBlanco
PH
P 4
Adults
<?php!!public function createPost(Request $request)!{! $repository = $this->getRepository('MyBundle:Post');! $entity = new Post();! $form = new MyForm($entity);!! $form->bind($request);!! if ($form->isValid()) {! if ( ! $repository->exists($entity)) {! $repository->save($entity);!! return $this->redirect('create_ok');! } else {! $error = "Post Title already exists";!! return array('form' => $form, 'error' => $error);! }! } else {! $error = "Invalid fields";! ! return array('form' => $form, 'error' => $error);! } !}!
GuilhermeBlanco
PH
P 4
Adults
<?php!!public function createPost(Request $request)!{! $repository = $this->getRepository('MyBundle:Post');! $entity = new Post();! $form = new MyForm($entity);!! $form->bind($request);!! if ($form->isValid()) {! if ( ! $repository->exists($entity)) {! $repository->save($entity);!! return $this->redirect('create_ok');! } else {! $error = "Post Title already exists";!! return array('form' => $form, 'error' => $error);! }! } else {! $error = "Invalid fields";! ! return array('form' => $form, 'error' => $error);! } !}!
GuilhermeBlanco
PH
P 4
Adults
<?php!!public function createPost(Request $request)!{! $repository = $this->getRepository('MyBundle:Post');! $entity = new Post();! $form = new MyForm($entity);!! $form->bind($request);!! if ($form->isValid()) {! if ( ! $repository->exists($entity)) {! $repository->save($entity);!! return $this->redirect('create_ok');! } else {! $error = "Post Title already exists";!! return array('form' => $form, 'error' => $error);! }! } else {! $error = "Invalid fields";! ! return array('form' => $form, 'error' => $error);! } !}!
GuilhermeBlanco
PH
P 4
Adults
<?php!!public function createPost(Request $request)!{! $repository = $this->getRepository('MyBundle:Post');! $entity = new Post();! $form = new MyForm($entity);!! $form->bind($request);!! if ($form->isValid()) {! if ( ! $repository->exists($entity)) {! $repository->save($entity);!! return $this->redirect('create_ok');! } else {! $error = "Post Title already exists";!! return array('form' => $form, 'error' => $error);! }! } else {! $error = "Invalid fields";! ! return array('form' => $form, 'error' => $error);! } !}!
GuilhermeBlanco
PH
P 4
Adults
<?php!!public function createPost(Request $request)!{! $repository = $this->getRepository('MyBundle:Post');! $entity = new Post();! $form = new MyForm($entity);!! $form->bind($request);!! if ( ! $form->isValid()) {! return array('form' => $form, 'error' => "Invalid fields");! }!! if ( ! $repository->exists($entity)) {! $repository->save($entity);!! return $this->redirect('create_ok');! } else {! $error = "Post Title already exists";!! return array('form' => $form, 'error' => $error);! }!}!
GuilhermeBlanco
PH
P 4
Adults
<?php!!public function createPost(Request $request)!{! $repository = $this->getRepository('MyBundle:Post');! $entity = new Post();! $form = new MyForm($entity);!! $form->bind($request);!! if ( ! $form->isValid()) {! return array('form' => $form, 'error' => "Invalid fields");! }!! if ( ! $repository->exists($entity)) {! $repository->save($entity);!! return $this->redirect('create_ok');! } else {! $error = "Post Title already exists";!! return array('form' => $form, 'error' => $error);! }!}!
GuilhermeBlanco
PH
P 4
Adults
<?php!!public function createPost(Request $request)!{! $repository = $this->getRepository('MyBundle:Post');! $entity = new Post();! $form = new MyForm($entity);!! $form->bind($request);!! if ( ! $form->isValid()) {! return array('form' => $form, 'error' => "Invalid fields");! }!! if ($repository->exists($entity)) {! return array('form' => $form, 'error' => "Post Title already exists");! }!! $repository->save($entity);!! return $this->redirect('create_ok');!}
GuilhermeBlanco
PH
P 4
Adults
<?php!!public function createPost(Request $request)!{! $repository = $this->getRepository('MyBundle:Post');! $entity = new Post();! $form = new MyForm($entity);!! $form->bind($request);!! if ( ! $form->isValid()) {! return array('form' => $form, 'error' => "Invalid fields");! }!! if ($repository->exists($entity)) {! return array('form' => $form, 'error' => "Post Title already exists");! }!! $repository->save($entity);!! return $this->redirect('create_ok');!}
GuilhermeBlanco
PH
P 4
Adults
Benefícios
• Evita duplicação de código
• Aumenta legibilidade
• Reduz cyclomatic complexity
GuilhermeBlanco
PH
P 4
Adults
O uso excessivo de objetos no PHP aumenta
significativamente o consumo de memória
GuilhermeBlanco
PH
P 4
Adults
<?php!!class Item !{! final public static function find($id)! {! if (is_string($id) && trim($id) != '') {! // do find ...! }!! throw new \InvalidArgumentException('$id must be a non-empty string');! }!! final public static function create($id, array $data)! {! if ( ! is_string($id)) {! throw new \InvalidArgumentException('$id must be a string');! }!! if (empty(trim($id))) {! throw new \InvalidArgumentException('$id must be a non-empty string');! }!! // do create ...! }!}
GuilhermeBlanco
PH
P 4
Adults
<?php!!class Item !{! final public static function find($id)! {! if ( ! is_string($id) || trim($id) === '') {! throw new \InvalidArgumentException('$id must be a non-empty string');! }!! // do find ...! }!! final public static function create($id, array $data)! {! if ( ! is_string($id) || trim($id) === '') {! throw new \InvalidArgumentException('$id must be a non-empty string');! }! ! // do create ...! }!}
GuilhermeBlanco
PH
P 4
Adults
<?php!!class Item !{! final public static function find($id)! {! if ( ! is_string($id) || trim($id) === '') {! throw new \InvalidArgumentException('$id must be a non-empty string');! }!! // do find ...! }!! final public static function create($id, array $data)! {! if ( ! is_string($id) || trim($id) === '') {! throw new \InvalidArgumentException('$id must be a non-empty string');! }! ! // do create ...! }!}
GuilhermeBlanco
PH
P 4
Adults
<?php!!final class Id!{! private $value;!! public function __construct($value)! {! if ( ! is_string($value) || trim($value) === '') {! throw new \InvalidArgumentException(! sprintf('%s must be a non-empty string', $value)! );! }!! $this->value = $value;! }!! public function getValue()! {! return $this->value;! }!}!
GuilhermeBlanco
PH
P 4
Adults
<?php!!class Item !{! final public static function find(Id $id)! {! // do find ...! }!! final public static function create(Id $id, array $data)! {! // do create ...! }!}
GuilhermeBlanco
PH
P 4
Adults
Benefícios
• Type hinting
• Encapsulamento de código
• Auxilia na prevenção de código duplicado
GuilhermeBlanco
PH
P 4
Adults
$this->manager->getConfig()->getSegment()->setName("foo");
Propriedades são difíceis de "mockar"
E se a chamada ao método anterior
retornasse NULL?
GuilhermeBlanco
PH
P 4
Adults
<?php!!final class NullObject!{!! public function __get($property)!! {!! return new self;!! }!!! public function __set($property, $value)!! {!! return new self;!! }!!! public function __call($method, array $arguments)!! {!! ! return new self;!! }!!! public function __callStatic($method, array $arguments)!! {!! return new self;!! }!!! public function __toString()!! {!! return 'null';!! }!}
GuilhermeBlanco
PH
P 4
Adults
Por que é ruim?
• Esconde um problema de encapsulamento
• Difícil de debugar e tratar exceções
• Código deve ser todo estruturado para utilizar NullObject
• Difícil de ler e entender
GuilhermeBlanco
PH
P 4
Adults
$filterChain ->addFilter(new Zend_Filter_Alpha())! ->addFilter(new Zend_Filter_StringToLower());
GuilhermeBlanco
PH
P 4
Adults
Benefícios
• Lei de Demeter
• Legibilidade
• Aumenta testabilidade (mais fácil "mockar")
• Facilidade para debugar
GuilhermeBlanco
PH
P 4
Adults
– Tim Bray (mencionando Phil Karlton)
“Há apenas 2 problemas difíceis na computação: invalidação de cache,
nomear coisas e errar por 1.”
Tit
le T
ext
GuilhermeBlanco
PH
P 4
Adults
Problema de duplicação de código!
Escrever o mesmo nome repetidamente
GuilhermeBlanco
PH
P 4
Adults
public function getPage($data) { ... }!!!!public function startProcess() { ... }!!!!$trx->process('site.login');
Obter de onde?
Que p. é essa? $extendedTranslator
fork, create, begin, open
renderHomePage()
GuilhermeBlanco
PH
P 4
Adults
Benefícios
• Melhora na comunicação e legibilidade
• Melhora mantenabilidade
• Bom indicador para problemas de encapsulamento e duplicação de código
GuilhermeBlanco
PH
P 4
Adults
Objetivo
• Máximo 200 linhas por classe (incluindo docblocks/documentação)
• 10 métodos por classe
• Até 20 linhas por método
• 15 classes por namespace (folder/pasta)
GuilhermeBlanco
PH
P 4
Adults
Benefícios
• Single Responsibility Principle
• Métodos claros e objetivos
• Melhor segregação de código
• Namespaces mais limpos
GuilhermeBlanco
PH
P 4
Adults class MyRegistrationService!
{! protected $userService;! protected $passwordService;! protected $logger;! protected $translator;! protected $entityManager;! protected $imageCropper;!! // ...!}
GuilhermeBlanco
PH
P 4
Adults class MyRegistrationService!
{! protected $userService;! protected $passwordService;! protected $logger;! protected $translator;! protected $entityManager;! protected $imageCropper;!! // ...!}
Interações com o banco de dados devemestar em $userService
GuilhermeBlanco
PH
P 4
Adults class MyRegistrationService!
{! protected $userService;! protected $passwordService;! protected $logger;! protected $translator;! protected $entityManager;! protected $imageCropper;!! // ...!}
Utilize um event systeme mova esta instância
para um listener
GuilhermeBlanco
PH
P 4
Adults
Benefícios
• Single Responsibility Principle
• Baixo acoplamento
• Melhor encapsulamento
• Melhora testabilidade
GuilhermeBlanco
PH
P 4
Adults
Qualquer classe que contenha um array não
deve conter outras propriedades
GuilhermeBlanco
PH
P 4
Adults
<?php!!class User!{! private $name;! // ...!! private $albumList;!!! public function getPublicAlbumList()!! {!! $filteredAlbumList = array();!!! foreach ($this->albumList as $album) {! if ($album->getPrivacy() === AlbumPrivacy::PUBLIC) {! $filteredAlbumList[] = $album;! }!! }!!! return $filteredAlbumList;!! }!! // ...!}!!// Exemplo:!$publicAlbumList = $user->getPublicAlbumList();
GuilhermeBlanco
PH
P 4
Adults
<?php!!class AlbumList extends Collection!{! public function getPublic()! {! $filteredAlbumList = array();!! foreach ($this->value as $album) {! if ($album->getPrivacy() === AlbumPrivacy::PUBLIC) {! $filteredAlbumList[] = $album;! }! }!! return $filteredAlbumList;! }!}!!class User!{! private $name;! private $albumList = new AlbumList();!! // ...!}!!// Exemplo:!$publicAlbumList = $user->getAlbumList()->getPublic();
GuilhermeBlanco
PH
P 4
Adults
<?php!!class AlbumList extends Collection!{! public function getPublic()! {! return new ArrayCollection(! array_filter(! $this->value,! function ($album)! {! return $album->isPublic();! }! )! );! }!}!!class User!{! private $name;! private $albumList = new AlbumList();!! // ...!}!!// Exemplo:!$publicAlbumList = $user->getAlbumList()->getPublic();
GuilhermeBlanco
PH
P 4
Adults
Benefícios
• Single Responsibility Principle
• Implementação de operações sobre coleções nas coleções
• Usar classes de SPL
• Facilidade em agrupar coleções sem se preocupar com o comportamento dos membros dela
• Filtragem, ordenação, mapeamento, combinação são bons exemplos de métodos
GuilhermeBlanco
PH
P 4
Adults
<?php!!class BankAccount !{! public $balance = 0;!! public function deposit($amount) ! {! $this->balance += $amount;! }!! public function withdraw($amount)! {! $this->balance -= $amount;! }!}!!// Exemplo:!$account = new BankAccount();!!$account->deposit(100.00);!!// ...!!$account->balance = 0;!!// ...!!$account->withdraw(10.00);!!echo $account->balance . PHP_EOL;
Balance pode seralterado sem que a
classe saiba, causandoerros inesperados
GuilhermeBlanco
PH
P 4
Adults
Benefícios
• Injeção de operações
• Encapsulamento de transformações
• Promove Open/Close Principle ("O" em SOLID)
GuilhermeBlanco
PH
P 4
Adults
Thanks! =)
@guilhermeblanco
github.com/guilhermeblanco
top related