object calisthenics (phpconpl 2016)
TRANSCRIPT
ObjectCalisthenics9stepstobetterOOcode
Agenda
Learnhowtomakeourcodemore:
readablereusabletestablemaintainable
Calisthenics
Cal•is•then•ics-/ˌkaləsˈTHeniks/
"Calisthenicsareexercisesconsistingofavarietyofgrossmotormovements;often
rhythmicalandgenerallywithoutequipmentorapparatus."
Wikipedia
ObjectCalisthenics
JeffBay
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!