Object calisthenics (PyConPL 2016)

Preview:

Citation preview

ObjectCalisthenics9stepstobetterOOcode

Agenda

Learnhowtomakeourcodemore:

readablereusabletestablemaintainable

Raiseyouhandifyouknowoneofthefollowing:

DRYKISSSOLIDYAGNIZenofPython

Calisthenics

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

"Calisthenicsareexercisesconsistingofavarietyofgrossmotormovements;often

rhythmicalandgenerallywithoutequipmentorapparatus."

Wikipedia

WrittenforJava

Whybother?

Codeisreadmorethanit'swritten

Rule#1

Onlyonelevelofindentationpermethod

classBoard(object):def__init__(self,data):#Level0self.buf=""foriinrange(10):#Level1forjinrange(10):#Level2self.buf+=data[i][j]

classBoard(object):def__init__(self,data):self.buf=""self.collect_rows(data)defcollect_rows(self,data):foriinrange(10):self.collect_row(data[i])defcollect_row(self,row):forjinrange(10):self.buf+=row[j]

Benefits

SingleresponsibilityBetternamingShortermethodsReusablemethods

Rule#2

Donotuseelsekeyword

ifoptions.getCategories()isNone:...eliflen(options.getCategories())==1:...elifSPECIAL_CATEGORYinoptions.getCategories():...elifoptions.getCategories()andoptions.getQuery():...elifoptions.getContentType():...

deflogin(self,request):ifrequest.user.is_authenticated():returnredirect("homepage")else:messages.add_message(request,messages.INFO,'Badcredentials')returnredirect("login")

deflogin(self,request):ifrequest.user.is_authenticated():returnredirect("homepage")

messages.add_message(request,messages.INFO,'Badcredentials')returnredirect("login")

Extractcode

Defaultvalue

Polymorphism

Strategypattern

Statepattern

BenefitsAvoidscodeduplicationLowercomplexityReadability

Rule#3

Wrapprimitivetypesifithasbehaviour

ValueObjectinDDD

classValidator(object):defcheck_date(self,year,month,day):pass

#10thofDecemberor12thofOctober?validator=Validator()validator.check_date(2016,10,12)

classValidator(object):defcheck_date(year:Year,month:Month,day:Day)->bool:pass

#Functioncallleavesnodoubt.validator.check_date(Year(2016),Month(10),Day(12))

BenefitsEncapsulationTypehintingAttractssimilarbehaviour

Rule#4

Onlyonedotperline

OK:Fluentinterface

classPoem(object):def__init__(self,content):self.content=content

defindent(self,spaces):self.content=""*spaces+self.contentreturnself

defsuffix(self,content):self.content=self.content+"-"+contentreturnself

Poem("RoadNotTravelled").indent(4)\.suffix("RobertFrost").content

NotOK:getterchain

classCartService(object):defget_token(self):token=self.get_service('auth')\.auth_user('user','password')\.get_result()\.get_token()

returntoken

#1.WhatifNoneisreturnedinsteadofobject?#2.Howaboutexceptionshandling?

classLocation(object):def__init__(self):self.current=Piece()

classPiece(object):def__init__(self):self.representation=""

classBoard(object):defboard_representation(self,board):buf=''forfieldinboard:buf+=field.current.representation

returnbuf

classLocation(object):def__init__(self):self.current=Piece()defadd_to(self,buffer):returnself.current.add_to(buffer)

classPiece(object):def__init__(self):self.representation=""defadd_to(self,buffer):returnbuffer+self.representation

classBoard(object):defboard_representation(self,board):buf=''forfieldinboard:buf=field.add_to(buf)

returnbuf

BenefitsEncapsulationDemeter'slawOpen/ClosedPrinciple

Rule#5

Donotabbreviate

Whyabbreviate?

Toomanyresponsibilities

Nametoolong?

Split&extract

Duplicatedcode?

Refactor!

BenefitsClearintentionsIndicateunderlyingproblems

Rule#6

Keepyourclassessmall

Whatissmallclass?15-20linespermethod50linesperclass10classespermodule

BenefitsSingleResponsibilitySmallermodules

Rule#7

Nomorethan2instancevariableperclass

Classshouldhandlesinglevariablestate

Insomecasesitmightbetwovariables

classCartService(object):def__init__(self):self.logger=Logger()self.cart=CartCollection()self.translationService=TranslationService()self.authService=AuthService()self.userService=UserService()

BenefitsHighcohesionEncapsulationFewerdependencies

Rule#8

Firstclasscollections

collectionsmodule

BenefitsSingleResponsibility

Rule#9

Donotusesetters/getters

Accessorsarefine

Don'tmakedecisionsoutsideofclass

Letclassdoit'sjob

Tell,don'task

classGame(object):def__init__(self):self.score=0

defset_score(self,score):self.score=score

defget_score(self):returnself.score

#UsageENEMY_DESTROYED_SCORE=10game=Game()game.set_score(game.get_score()+ENEMY_DESTROYED_SCORE)

classGame(object):def__init__(self):self.score=0defadd_score(self,score):self.score+=score

#UsageENEMY_DESTROYED_SCORE=10game=Game()game.add_score(ENEMY_DESTROYED_SCORE)

BenefitsOpen/ClosedPrinciple

Catch'emall!

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

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

Drawyourownconculsions

Customizetheserules

Finalthoughts

Thesearenotbestpractices

Thesearejustguidelines

Usewithcaution!

Questions?

Thankyou!

Recommended