Object calisthenics (PHPConPL 2016)

Post on 15-Apr-2017

98 views 2 download

Transcript of Object calisthenics (PHPConPL 2016)

ObjectCalisthenics9stepstobetterOOcode

Agenda

Learnhowtomakeourcodemore:

readablereusabletestablemaintainable

Calisthenics

Cal•is•then•ics-/ˌkaləsˈTHeniks/

"Calisthenicsareexercisesconsistingofavarietyofgrossmotormovements;often

rhythmicalandgenerallywithoutequipmentorapparatus."

Wikipedia

WrittenforJava

Whybother?

Codeisreadmorethanit'swritten

Rule#1

Onlyonelevelofindentationpermethod

classBoard{publicfunction__construct(array$data){$buf='';//0for($i=0;$i<10;$i++){//1for($j=0;$j<10;$j++){//2$buf.=$data[$i][$j]}}

return$buf;}}

classBoard{publicfunction__construct(array$data){$buf='';collectRows($buf);

return$buf;}

privatefunctioncollectRows($buf){for($i=0;$i<10;$i++){collectRow($buf,$i);}}

privatefunctioncollectRow($buf,$row){for($i=0;$i<10;$i++){$buf.=$data[$row][$i];}}}

Benefits

SingleresponsibilityBetternamingShortermethodsReusablemethods

Rule#2

Donotuseelsekeyword

if(...){...}elseif(...){...}elseif(...){...}elseif(...){...}elseif(...){...}elseif(...){...}else{...}

publicfunctionlogin($username,$password){if($this->userRepository->isValid($username,$password)){redirect("homepage");}else{addFlash("error","Badcredentials");redirect("login");}}

publicfunctionlogin($username,$password){if($this->userRepository->isValid($username,$password)){returnredirect("homepage");}

addFlash("error","Badcredentials");

returnredirect("login");}

Extractcode

Defaultvalue

Polymorphism

Strategypattern

Statepattern

BenefitsAvoidscodeduplicationLowercomplexityReadability

Rule#3

Wrapprimitivetypesifithasbehaviour

ValueObjectinDDD

publicfunctioncheckDate(int$year,int$month,int$day){...}

//10thofDecemberor12thofOctober?$validator->checkDate(2016,10,12);

publicfunctioncheckDate(Year$year,Month$month,Day$day){...}

$validator->checkDate(newYear(2016),newMonth(10),newDay(12));

BenefitsEncapsulationTypehintingAttractssimilarbehaviour

Rule#4

Onlyone->perline

OK:Fluentinterface

$validator->addFilter(newEmailFilter())->addFilter(newNotEmptyFilter());

NotOK:getterchain

$token=$this->getService(Service::AUTH)->authUser($user,$password)->getResult()->getToken();

//1.Whatifnonobjectisreturned?//2.Howaboutexceptionshandling?

classLocation{/**@varPiece*/publiccurrent;}

classPiece{/**@varstring*/publicrepresentation;}

classBoard{publicfunctionboardRepresentation(array$board){$buf='';

foreach($boardas$field){$buf.=substring($field->current->representation,0,1);}

return$buf;}}

classLocation{/**@varPiece*/private$current;

publicfunctionaddTo($buf){return$this->current->addTo($buf);}}classPiece{/**@varstring*/private$representation;

publicfunctioncharacter(){returnsubstring(representation,0,1);}

publicfunctionaddTo($buf){return$buf.$this->character();}}classBoard{publicfunctionboardRepresentation(array$board){$buf='';/**@varLocation$field*/foreach($boardas$field){$field->addTo($buf);}

return$buf;}

BenefitsEncapsulationDemeter'slawOpen/ClosedPrinciple

Rule#5

Donotabbreviate

Whyabbreviate?

Nametoolong?

Toomanyresponsibilities

Split&extract

Duplicatedcode?

Refactor!

BenefitsClearintentionsIndicateunderlyingproblems

Rule#6

Keepyourclassessmall

Whatissmallclass?15-20linespermethod50linesperclass10classespermodule

200linesperclass

10methodsperclass

15classespernamespace

BenefitsSingleResponsibilitySmallernamespaces

Rule#7

Nomorethan25instancevariableperclass

Classshouldhandlesinglevariablestate

Insomecasesitmightbetwovariables

classCartService{private$userService;private$logger;private$cart;private$translationService;private$entityManager;private$authService;

//...}

BenefitsHighcohesionEncapsulationFewerdependencies

Rule#8

Firstclasscollections

Doctrine'sArrayCollection

BenefitsSingleResponsibility

Rule#9

Donotusesetters/getters

Accessorsarefine

Don'tmakedecisionsoutsideofclass

Letclassdoit'sjob

Tell,don'task

classGame{/**@varint*/privatescore;

publicfunctionsetScore(score){$this->score=score;}publicfunctiongetScore(){return$this->score;}}

//Usage$game->setScore($game->getScore()+ENEMY_DESTROYED_SCORE);

classGame{/**@varint*/privatescore;

publicfunctionaddScore($delta){$this->score+=$delta;}}

//Usage$game->addScore(ENEMY_DESTROYED_SCORE);

BenefitsOpen/ClosedPrinciple

Catch'emall!1. Onlyonelevelofindentationpermethod,2. Donotuseelsekeyword,3. Wrapprimitivetypesifithasbehavior,4. Onlyonedotperline,5. Don’tabbreviate,6. Keepyourentitiessmall,7. Nomorethantwoinstancevariableperclass,8. FirstClassCollections,9. Donotuseaccessors10. ???11. PROFIT!

Homework

Createnewprojectupto1000lineslong

Applypresentedrulesasstrictlyaspossible

Drawconculsions

Customizetheserules

Finalthoughts

Thesearenotbestpractices

Thesearejustguidelines

Usewithcaution!

Thankyou!