0% found this document useful (0 votes)
262 views36 pages

S.O.L.I.D. Software Development, One Step at A Time

This document summarizes an article about achieving low coupling, high cohesion, and strong encapsulation in software development using the SOLID principles. It discusses how tightly coupled code can make software development difficult, like a game of Jenga, by causing unexpected bugs when changes are made. The SOLID principles, when properly applied, help create loosely coupled and cohesive code modules that are reusable and resilient to changes. Specifically, the principles of low coupling, high cohesion, and encapsulation are explained in the context of object-oriented programming.

Uploaded by

junkyard
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
262 views36 pages

S.O.L.I.D. Software Development, One Step at A Time

This document summarizes an article about achieving low coupling, high cohesion, and strong encapsulation in software development using the SOLID principles. It discusses how tightly coupled code can make software development difficult, like a game of Jenga, by causing unexpected bugs when changes are made. The SOLID principles, when properly applied, help create loosely coupled and cohesive code modules that are reusable and resilient to changes. Specifically, the principles of low coupling, high cohesion, and encapsulation are explained in the context of object-oriented programming.

Uploaded by

junkyard
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 36

S.O.L.I.D.

SoftwareDevelopment,OneStepataTime
ByDerickBailey

DerickBailey
DerickBaileyisaDeveloperAdvocateforKendoUI,adeveloper,speaker,trainer,screencasterandmuch
more.Hesbeenslingingcodesincethelate80sanddoingitprofessionallysincethemid90s.Thesedays,
DerickspendshistimeprimarilywritingJavaScriptwithbackendlanguagesofalltypes,includingRuby,
NodeJS,PHP,.NETandanythingelsehecangethishandson.
DerickblogsatLosTechies.com,producesscreencastsatWatchMeCode.net,tweetsas@derickbaileyand
providessupportandassistanceforJavaScript,BackboneJS,MarionetteJS,andmuchmorearoundthe
Web.

Thisarticlewaspublishedin:

Thisarticlewasfiledunder:

Mostprofessionalsoftwaredevelopersunderstandtheacademicdefinitionsofcoupling,cohesion,and
encapsulation.However,manydevelopersdonotunderstandhowtoachievethebenefitsoflowcoupling,
highcohesionandstrongencapsulation,asoutlinedinthisarticle.Fortunately,othershavecreatedstepping
stonesthatleadtothesegoals,
resultinginsoftwarethatiseasiertoread,easiertounderstandandeasiertochange.Inthisarticleseries,I
willdefinethreeoftheprimaryobjectorientedprinciplesandshowhowtoreachthemthroughthefive
S.O.L.I.D.designprinciples.
HaveyoueverplayedJenga?Itsthatgameofwoodenblocksthatarestackedontopofeachotherinrows
ofthree.InJengayoutrytopushorpullablockoutofthestackandplaceitontopofthestackwithout
knockingthestackover.Theplayerthatcausesthestacktofallloses.
HaveyoueverthoughtyouwereplayingagameofJengawhenyouwerewritingordebuggingsoftware?
Forexample,youmayneedtochangeonefieldononescreen.Youstudythestackofcode,youlookfor
thatlittlespaceoflightpeakingbetweentheclasses,andyoumakethechangeintheoneplacethatyou
thoughtwouldbesafe.Unfortunately,youdidntrealizethatthecodeyouwerechangingwasreferencedin
severalcriticalprocessesthroughsomestrangelevelsofindirection.Theresultingcrashofthesoftware
stackhasleftyoutheloser,cleaningupthemesswithyourbossbreathingdownyourneckaboutthe
customerbeingupsetabouttheirlostdata,andHowmanytimeshaveyoubeenthere?Icantevenbegin
tocounthowoftenitshappenedtome.
SoftwaredevelopmentdoesnothavetobelikeagameofJenga.
Thereisgoodnews,though:softwaredevelopmentdoesnothavetobelikeagameofJenga.Infact,
softwaredevelopmentshouldnotbelikeanygamewheretherearewinnersandlosers.Whatyouwant,
instead,isasustainablepaceofsoftwaredevelopmentwhereeveryonewins.Youwanttoensurethatyou
dontoverworkthedevelopers,thatyoudontpressuremanagerstosayjustgetitdone,andthecustomer
getsthesoftwaretheywantinatimeframetheyagreeto.

ASustainablePace
Whentryingtosetasustainablepaceinanyendeavor,youfirstneedtounderstandhowfaryouneedtogo.
Youalsoneedtoknowhowfastyouneedtogetthere.Forexample,ifyouwanttoruna50meterdash,you
shouldrunasfastasyoupossiblycanandthenpushyourselftotrytorunfaster.However,ifyouwantto
runan800meterraceyoushouldsetasomewhatslowerpace.Whenyoustarttalkingaboutsignificant
distanceslikeamarathon,thepacebecomessignificantlyslower.Forsuchadistance,youwanttoseta
pacethatyoucanmaintainthroughouttherace.
Insoftwaredevelopment,youcanthinkofthepaceyouneedtorunasthetimelineoftheprojectcombined
withtheexpectedfeaturesandfunctionality.
Insoftwaredevelopment,youcanthinkofthepaceyouneedtorunasthetimelineoftheprojectcombined
withtheexpectedfeaturesandfunctionality.Forshortertimeframes,youneedtorunfaster.However,you
alsohavetoconsiderhowmuchfunctionalityyoucanreasonablyaddtoyoursystem,givenashort
timeframe.Ifyouonlyneedafewfeaturesandyouneedtogetitdonequickly,youmayneedtosprint
towardthegoal.Ifyourcustomerexpectsyoutocramtoomanyfeaturesintoashorttimeframe,youhavea

higherlikelihoodofburningout.Imaginetryingtosprintforthedurationofamarathon,orevena1600
meterrace.Theprobabilityofsustainingthatpaceforthatdistanceisgoingtoapproachzeroasyou
continuemovingforward.Youneedtoworkwithyourmanagement,yourteam,andyourcustomerstoset
theexpectationsofhowfastyoucanrunforagivenperiod.
Assumingthatyouhaveareasonablenumberoffeaturesforagiventimeframe,younowhavetosetthe
paceforfeaturedevelopmentinyourdailyactivities.Objectorientedsoftwaredevelopmenthasvarious
principles,patternsandpracticesthathelpyouachievethesustainablepaceyouneed.Theprinciples
includecouplingtheextenttowhichthepartsofthesystemrelyoneachothercohesiontheextenttowhich
thepartsofasystemworktogetherforasinglepurposeandencapsulationtheextenttowhichthedetails
ofimplementationarehidden.Builtontopoftheseprinciplesarevariousdesignandimplementation
patternssuchasstrategy,command,bridge,etc.Whenyoucombinetheseprinciples,patternsand
practicestheywillhelpyoutocreatesystemsthatarewellfactored,easytounderstand,andeasyto
change.

TheObjectOrientedPrinciples
Ideally,youwanttoensurethatyoursystemshavelowcouplingandhighcohesion.Thesetwoprinciples
helpyoutocreatethebuildingblocksofasoftwaresystem.Youalsowanttoensurethatyouhaveself
containedbuildingblocksthatis,theyarewellencapsulated.Youdontwanttheconcernsofonebuilding
blockleakingintootherblocks.Ifyoucreatebuildingblocksthathavethecorrectsizeandshape,youcan
putthemtogetherinmeaningfulwaystosolveyourproblems.
Oftenitseemsthatdevelopersonlydiscusstheseprinciplesinacademicsettings.Mostuniversitieswith
degreesthatcoversoftwaredevelopmentprovideatleastacursoryintroductiontothem.However,many
softwaredevelopersseemtomisstheircorrectusage,causingmoreproblemsthantheysolve.Indeed,
developerscanveryeasilymisapplytheseprinciplesinthewrongplaceatthewrongtime.Toavoidthis
situation,youneedtounderstandhowcoupling,cohesion,andencapsulationcorrectlyplayintodeveloping
softwaresolutions.

LowCoupling
Couplinginsoftwaredevelopmentisdefinedasthedegreetowhichamodule,class,orotherconstruct,is
tieddirectlytoothers.Forexample,thedegreeofcouplingbetweentwoclassescanbeseenashow
dependentoneclassisontheother.Ifaclassisverytightlycoupled(or,hashighcoupling)tooneormore
classes,thenyoumustusealloftheclassesthatarecoupledwhenyouwanttouseonlyoneofthem.
Youcanreducecouplingbydefiningstandardconnectionsorinterfacesbetweentwopiecesofasystem.
Forexample,akeyandalockhaveadefinedinterfacebetweenthem.Thekeyhasacertainpatternof
ridgesandvalleys,andthelockhasacertainpatternofpinsandsprings.Whenyouplacetherightkeyin
thelock,itpushesthepinsintoapositionthatallowsthemechanismtolockorunlock.Ifyouplacethe
wrongkeyintothelock,thepinswillnotmoveintothecorrectpositionandthemechanismwontmove.
Insoftwaredevelopment,developersalsoworkwithstandardconnectionsandinterfacedefinitions.Object
orientedlanguagessuchasC++,C#,Java,andVisualBasichavesomeconstructsthatallowyoutodefine
thoseinterfacesimplicitlyandexplicitly.Whetheritsaclasspublicmethodsandproperties,anabstractbase
class,anexplicitinterfaceorotherformofabstraction,theseconstructsallowyoutodefinecommon

interactionpointsbetweenpartsofyoursystem.Withoutabstractiontodecouplethesecommonpointsof
interaction,youareleftwithpiecesofthesystemthatmustknowabouteachotherdirectly.Basically,this
meansyouarestuckwithakeythatisweldeddirectlytothepinsofalock,preventingyoufromremoving
thekey,andcompromisingthesecurityofthatlock.
ImaginethatyouareworkingwiththestructureinFigure1.Thesoftwareworksfine,atthemoment,and
youcanfixbugswhenyouneedto.Thenyourbosshandsyouanewrequirementandyourealizethatthe
modulehighlightedinredcanhandlemostoftherequirement.Youwouldliketoreusethatmodulebutyou
dontneedanyofthesurroundingmodulesforthisnewfeature.Whenyoutrytopulloutthemoduleinred,
though,youquicklyrealizethatyoullhavetobringseveralmoremoduleswithitduetothehighcoupling
betweenthismoduleandtheonessurroundingit.

Figure1:Atightlycoupledsystem.

Nowimagineaclassthathaszerocoupling.Thatis,theclassdependsonnothingelseandnothingelse
dependsonit.Whatbenefitdoesthatoffer?Forone,youcanusethatclassanywhereyouwantwithout
havingtoworryaboutdependenciescomingalongwithit.However,youessentiallyhaveauselessclass.
Withzerocouplingintheclass,youwontbeabletogetanyinformationintooroutofit.Trytocreateaclass
in.NETthatdoesnotrelyonanythingnotaninteger,notastring,notaConsoleorApplicationstatic
referencenoteventheimpliedobjectinheritanceofeveryconstructin.NET.Goaheadtryitseehow
usefulthatisinyoursystem.
Couplingisnotinherentlyevil.Ifyoudonthavesomeamountofcoupling,yoursoftwarewillnotdoanything
foryou.
Couplingisnotinherentlyevil.Ifyoudonthavesomeamountofcoupling,yoursoftwarewillnotdoanything
foryou.Youneedwellknownpointsofinteractionbetweenthepiecesofyoursystem.Withoutthem,you
cannotcreateasystemofpartsthatcanworktogether,soyoushouldnotstrivetoeliminatecoupling
entirely.Rather,yourgoalinsoftwaredevelopmentshouldbetoattainthecorrectlevelofcouplingwhile
creatingasystemthatisfunctional,understandable(readablebyhumans),andmaintainable.

HighCohesion
Cohesionistheextenttowhichtwoormorepartsofasystemarerelatedandhowtheyworktogetherto
createsomethingmorevaluablethantheindividualparts.Thinkoftheoldadage,Thewholeisgreaterthan

thesumoftheparts.
Peopleseekhighcohesioninsportsteams,forexample.Theywanttohaveateamofbasketballplayers
thatknowhowandwhentopasstheball,andhowandwhentoscore.Everyoneexpectstheindividual
playerstoplaytogetherasateamtoincreasethechancesoftheteamwinningthegame.Companiesalso
seekcohesionintheirprojectteamsatwork.Theyputdevelopersanduserinterfacedesignerstogether
withbusinessanalystsanddatabaseadministrators,alongwithotherrolesandresponsibilities.Theintentof
creatingteamsofcrossfunctionalskillsetsistousethestrengthsofeachteammembertocounterthe
weaknessesofothers.Youlikelyalsolookforcohesioninthetechnologyyouareusingandthesoftware
thatyouarewriting.Youprobablywantadatabasesystemthatconnectseasilytoyourprogramming
languageofchoice.Youalsowantauserinterfacetechnologythatmakesiteasytowireupthebusiness
logicanddataaccess.Cohesionisallaround.Youonlyneedtorecognizeitforwhatitis.
Insoftwaresystems,developerstalkabouthighlevelconcernsandlowlevelimplementationdetails.This
scaleofconcerncanhelpyouunderstandthemanyperspectivesofcohesionwithinyoursoftware.Howwell
dothelinesofcodeinamethodorfunctionworktogethertocreateasenseofpurpose?Howwelldothe
methodsandpropertiesofaclassworktogethertodefineaclassanditspurpose?Howwelldotheclasses
fittogethertocreatemodules?Howwelldothemodulesworktogethertocreatethelargerarchitectureof
thesystem?Understandingtheperspectivethatyouaredealingwithatanygiventimewillhelpyou
understandtheextenttowhichthepiecesarecohesive.
Understandingtheperspectivethatyouaredealingwithatanygiventimewillhelpyouunderstandthe
extenttowhichthepiecesarecohesive.
ExaminethepuzzlepictureofmysoninFigure2.Ifyouseparatealloftheindividualpieces,whatdoyou
have?Youhaveaseriesofpiecesthatprovideverylimitedvalueontheirown.Theintrinsicvalueofan
individualpieceisonlythatitcanbecombinedwithotherpieces.Imnotinterestedinplayingwithasingle
piece,though.Iwanttohaveenoughpiecestocompletethepuzzleinquestion.Iwantahighlycohesive
systemofpieces.

Figure2:Acohesivesetofparts.

Acompletepuzzlehasmuchmorevaluethanthealloftheindividualpieces.Knowingthatthepuzzlepieces
shouldcreateapictureofmysonalsoprovidesahigherlevelofvaluetomemorevaluethananyother
randompuzzle.Apuzzlewhereallofthepiecesareblack,orapuzzlethatshowsapictureofafield,willnot
inspirethefeelingsofloveinmethewayapictureofmysonwill.Thisdesiretocompletethepictureofmy
sonprovidesmotivationtonotonlyputthepuzzletogether,buttoputtherightpiecesintherightplaces.
Ifcohesivesystemssoftware,puzzles,orotherwisehavemultiplepartscomingtogethertocreatemore
valuethanthenindividualparts,itstandstoreasonthatyoucannotcreateahighlycohesivesystemoutof
large,allinonepieces.Forexample,asoftwaresystemcannotbecohesiveifitismadeupofexcessively
largegodclasses.Thesetypesofclassestendtohavetoomanyconcernswithinthemtocreateany
cohesionoutsideofthemselves.Asingleclassthatdoesalloftheactionsinthefollowingbulletlisthasfar
toomanyconcernstobecohesivewithanythingelse.
Loaddatafromafileordatabase
Processthedataintoastructure
Displaythedatatotheuser
Obtaininputfromtheuseronwhattodowiththedata
Performtheactionsrequestedbytheuser
Persistthechangesbacktothefileordatabase

Theprocesseslistedhereareverycommon.Manysoftwaresystemsusesomeformofthisbasicworkflow.
However,includingalloftheseprocessesinasingleclasswouldmakeitdifficulttocreateacohesive
system.Youmightseecohesionbetweenhighlevelprocessesbutyoulosetheabilitytocreatecohesionat
lowerlevelssuchasmethodsandclasses.
Tocreateamorecohesivesystemfromthehigherandlowerlevelperspectivesinthisexample,youcan
breakoutthevariousneedsintoseparateclasses.Youmightseparatetheuserinterfaceneeds,thedata
loadingandsavingneeds,andtheprocessingofthedata.Havingthesesmallerparts,eachwiththeirown
valueintheoverallprocess,givesyouamuchmorecohesivesystemfromthevariousperspectives.

Encapsulation
Mostdevelopersdefineencapsulationasinformationhiding.However,thisdefinitionoftenleadstoan
incompleteunderstandingandimplementationofencapsulation.Itseemsthatmanypeopleseetheword
informationandthinkdataorproperties.Basedonthistheytrytomakepublicpropertiesthatwraparound
privatefields,ormakepropertiesprivateinsteadofpublic.Thisperspective,unfortunately,missesoutona
tremendousopportunitythatencapsulationprovides:tohidenotjustdata,butprocessandlogicaswell.
Strongencapsulationisevidencedbytheabilityforadevelopertouseagivenclassormodulebyits
interface,alone.Thedeveloperdoesnot,andshouldnot,needtoknowtheimplementationspecificsofthe
classormodule.Figure3representsawellencapsulatedprocesswherethecallingobjects,representedby
blueboxes,donothavetoknowabouttheimplementationdetailoftheredbox.Theredboxshouldbefree
tochangeitsimplementationwithoutfearofbreakingthecodethatiscallingit,solongasthepublic
interfaceorthesemanticsofthatinterfacedonotchange.

Figure3:Encapsulation:hiddendataandprocessimplementation.

Encapsulationhelpsreduceduplicationofdataandprocessesinyoursystem.Whetheryouhaveabusiness
process,asinglepointofcommondata,oratechnicalorinfrastructureprocess,youshouldhaveoneand
onlyoneimplementationtorepresenttheiteminquestion.
Ifyouplantouseacircularsawtocutapieceofwood,youprobablydontcareabouthowthemotorinthat
sawworks.Youonlycarethatwhenyoupullthetrigger,thebladespinsatarapidrateandallowsyouto
easilycutthewood.

Insituationswhereyouneedtouseaprocessinmorethanonelocation,properencapsulationcombined
withlowcouplingwillhelptoensurethatyouhaveapartthatcancreatecohesioninthesystem.For
example,ifyouplantouseacircularsawtocutapieceofwood,youprobablydontcareabouthowthe
motorinthatsawworks.Youonlycarethatwhenyoupullthetrigger,thebladespinsatarapidrateand
allowsyoutoeasilycutthewood.Thesawhasencapsulatedtheprocessofcausingthebladetoturn
throughasimple,publicinterfacethehandlewiththetrigger.Additionally,thesawitselfcontainsotherforms
ofencapsulationsuchastheconnectionpointsbetweenthesawbladeandthemotor.Thisallowsyouto
replacethesawbladewithouthavingtoreconstructthemotor,thetriggermechanism,oranyotherpartof
thesaw.

ObjectOrientedPrinciplesinOurDaytoDayJobs
Developershearabouttheseandotherprinciplesofobjectorienteddevelopmentfairlyregularlyduringtheir
professionalcareer.Theserealworlddiscussionsoftencenteraroundhowtheprincipleswouldbeniceto
achieve,though,relegatingthemtotherealmsoftheivorytoweracademic.Whenitcomestotheeveryday
workofsoftwaredevelopment,itseemsthatmostdeveloperseitherdontunderstandhowtogettothese
principlesordontthinkitspossibleinareasonabletimeframe.However,theseprinciplesarenotjustfor
theacademics.Developersshouldapplythemtotheirdevelopmentefforts.Thequestionshouldchange
fromCanyouapplytheprinciples,here?toHowdoyoucorrectlyapplytheprinciples,here?

S.O.L.I.D.SteppingStones
Whenyoustartaskingthequestionofhow,itsalittlelikelookingatamarathonraceandwonderinghow
youendupatthefinishline.Obviously,foramarathonyouarriveatthefinishlinebyrunningonestepata
time.Softwaredevelopmentletsyoumoveonestepatatimetowardyourobjectorientedgoals,aswell.
Thestepsarecomposedofadditionalprinciplesandimplementationgoals,suchasthoseoutlinedinthe
SOLIDacronym:
S:SingleResponsibilityPrinciple(SRP)
O:OpenClosedPrinciple(OCP)
L:LiskovSubstitutionPrinciple(LSP)
I:InterfaceSegregationPrinciple(ISP)
D:DependencyInversionPrinciple(DIP)
OriginallycompiledbyRobertC.Martininthe1990s,theseprinciplesprovideaclearpathwayformoving
fromtightlycoupledcodewithpoorcohesionandlittleencapsulationtothedesiredresultsoflooselycoupled
code,operatingverycohesivelyandencapsulatingtherealneedsofthebusinessappropriately.
TheSingleResponsibilityPrinciplesaysthatclasses,modules,etc.,shouldhaveoneandonlyonereasonto
change.Thishelpstodrivecohesionintoasystemandcanbeusedasameasureofcouplingaswell.
TheOpenClosedPrincipleindicateshowasystemcanbeextendedbymodifyingthebehaviorofindividual
classesormodules,withouthavingtomodifytheclassormoduleitself.Thishelpsyoucreatewell
encapsulated,highlycohesivesystems.
TheLiskovSubstitutionPrinciplealsohelpswithencapsulationandcohesion.Thisprinciplesaysthatyou
shouldnotviolatetheintentorsemanticsoftheabstractionthatyouareinheritingfromorimplementing.

TheInterfaceSegregationPrinciplehelpstomakeyoursystemeasytounderstandanduse.Itsaysthatyou
shouldnotforceaclienttodependonaninterface(API)thattheclientdoesnotneed.Thishelpsyou
developwellencapsulated,cohesivesetofparts.
TheDependencyInversionPrinciplehelpsyoutounderstandhowtocorrectlybindyoursystemtogether.It
tellsyoutohaveyourimplementationdetaildependonthehigherlevelpolicyabstractions,andnottheother
wayaround.Thishelpsyoutomovetowardasystemthatiscoupledcorrectly,anddirectlyinfluencesthat
systemsencapsulationandcohesion.
Throughouttherestofthisarticle,Iwillwalkthroughascenarioofcreatingasoftwaresystem.Youwillsee
howthefiveSOLIDprinciplescanhelpyoutoachievestrongencapsulation,highcohesion,andlow
coupling.Youwillseehowyoucanstartwitha50metergetitdonenowdash,andendwithalongterm
marathonofupdatestothesystemsfunctionality.

SettingthePace:A50MeterDash
Tohelpunderstandhowyoucanachievethegoalofanobjectorientedsystemthroughtheuseofthe
SOLIDprinciples,Illwalkyouthroughasimplescenario,asolution,andtheresultingexpectations.

Scenario:EmailanErrorLog
Onedayattheoffice,yourmanagerwalksintoyourcubeandlookslikehishairisonfire.Heinformsyou
thathismanager,theCTO,justgotoffthephonewithaveryiratecustomer.Apparently,oneofyour
companyshostedapplicationsisthrowingexceptionsandpreventingthecustomerfrombeingableto
completetheirwork.
TheCTOhasinformedyourmanagerthatheneedsimmediateknowledgeoftheexceptionsbeingthrown
fromthissystem,andpersonallywantstoseeanemailinhisinboxforeveryexceptionthrown,untilthe
systemisfixed.Yourmanager,worriedaboutkeepinghisjob,nowwantsyoutocreateaquickanddirty
applicationthatallowsanetworkoperationspersontosendthecontentsofalogfiletotheCTO.
Furthermore,thisthinghastobeoutthedoorandinthehandsofthenetworkoperationspersonbefore
lunchacoupleofhoursfromnow.Usingarunninganalogy,youarenowengagedina50meterdash.You
needtocrankthiscodeoutanddeliveritasquicklyaspossible.

Solution:SelectaFileandSendIt
Afewhoursafterthatconversationwithyourmanager,youhaveproducedaverysimplesystemthatallows
theusertoselectafileandsendthecontentsofthatfileviaemail(Figure4).

Figure4:Applicationstructuretosendfilecontents.

Theimplementationofthisapplicationisverycrudebyyourownstandards:youcodedtheentireapplication
intheformscode,didnoofficialtesting,anddidthebareminimumofexceptionhandling(Listing1).
However,yougotthejobdone.

NewExpectation:AllErrorsEmailed
Aweekafteryouwrotethatquickanddirtyemailsendingapplication,yourbossisbackinyourcubetotalk
aboutitagain.Thistime,heinformsyouthatyourapplicationwasasmashingsuccessandtheCTOhas
mandatedthatallsystemssenderrorlogemailstoaspecialemailaddressforcollectingthem.TheCTO
wantsyou,specifically,tohandlethissinceyouroriginalapplicationwasreceivedsowell.

Figure5:Aclasswithmanyreasonstochange.

ResettingthePace
Asyourfirstassignment,afterhearingaboutthisnewmandatefromtheCTO,youwanttofigureoutwhat
logfilesthenetworkoperationspersonnelwillneedtosend,andhowtheywanttofacilitatethis.Aftersome
discussionwiththeoperationsgrouplead,youhaveagreedtoaddtwonewaspectsoffunctionalityofthe
system:
1.TheoperationspeoplewantanAPItocodeagainstforsomeoftheirautomationscripts.
2.TheyneedtoparsethecontentsofanXMLfiletomakeitalittlemorehumanreadable.
Youhavealsonegotiatedaslightlybettertimeframewiththenetworkoperationspeoplethanyourmanager
gaveyoufortheoriginalapplication.Theyhaveagreedtoadeliverydateofcloseofbusiness,tomorrow.
Withthisnewdeadlineandthenewrequirementsinmind,youdecidetosettleinforaslightlylongerrace
thantheoriginal50meterdash.Thecodeyoustartedwithwassufficientatthetime,butnowyouneedto

enhanceandextendit.Youcouldconsiderthisa100orpossiblya400meterraceatthispoint.Thegood
newsisthatyouknowhowtosetyourpaceaccordingtothesituationyoufindyourselfin.

SingleResponsibilityPrinciple
TheSingleResponsibilityPrinciplesaysthataclassshouldhaveone,andonlyone,reasontochange.
Thismayseemcounterintuitiveatfirst.Wouldntitbeeasiertosaythataclassshouldonlyhaveone
reasontoexist?Actually,noonereasontoexistcouldveryeasilybetakentoanextremethatwouldcause
moreharmthangood.Ifyoutakeittothatextremeandbuildclassesthathaveonereasontoexist,youmay
endupwithonlyonemethodperclass.Thiswouldcausealargesprawlofclassesforeventhemostsimple
ofprocesses,causingthesystemtobedifficulttounderstandanddifficulttochange.
Whenthebusinessperceptionandcontexthaschanged,thenyouhaveareasontochangetheclass.
Thereasonthataclassshouldhaveonereasontochange,insteadofonereasontoexist,isthebusiness
contextinwhichyouarebuildingthesystem.Eveniftwoconceptsarelogicallydifferent,thebusiness
contextinwhichtheyareneededmaynecessitatethembecomingoneandthesame.Thekeypointof
decidingwhenaclassshouldchangeisnotbasedonapurelylogicalseparationofconcepts,butratherthe
businesssperceptionoftheconcept.Whenthebusinessperceptionandcontexthaschanged,thenyou
haveareasontochangetheclass.Tounderstandwhatresponsibilitiesasingleclassshouldhave,youneed
tofirstunderstandwhatconceptshouldbeencapsulatedbythatclassandwhereyouexpectthe
implementationdetailsofthatconcepttochange.
Consideranengineinacar,forexample.Doyoucareabouttheinnerworkingoftheengine?Doyoucare
thatyouhaveaspecificsizeofpiston,camshaft,fuelinjector,etc?Or,doyouonlycarethattheengine
operatesasexpectedwhenyougetinthecar?Theanswer,ofcourse,dependsentirelyonthecontextin
whichyouneedtousetheengine.
Ifyouareamechanicworkinginanautoshop,youprobablycareabouttheinnerworkingsoftheengine.
Youneedtoknowthespecificmodel,thevariouspartsizes,andotherspecificationsoftheengine.Ifyou
donthavethisinformationavailable,youlikelycannotservicetheengineappropriately.However,ifyouare
anaverageeverydaypersonthatonlyneedstransportationfrompointAtopointB,youwilllikelynotneed
thatlevelofinformation.Thenotionoftheindividualpistons,sparkplugs,pulleys,belts,etc.,isalmost
meaninglesstoyou.Youonlycarethatthecaryouaredrivinghasanengineandthatitperformscorrectly.
TheengineexampledrivesstraighttotheheartoftheSingleResponsibilityPrinciple.Thecontextsofdriving
thecarvs.servicingtheengineprovidetwodifferentnotionsofwhatshouldandshouldnotbeasingle
conceptareasonforchange.Inthecontextofservicingtheengine,everyindividualpartneedstobe
separate.Youneedtocodethemassingleclassesandensuretheyarealluptotheirindividual
specifications.Inthecontextofdrivingacar,though,theengineisasingleconceptthatdoesnotneedtobe
brokendownanyfurther.YouwouldlikelyhaveasingleclasscalledEngine,inthiscase.Ineithercase,the
contexthasdeterminedwhattheappropriateseparationofresponsibilitiesis.

SeparatingtheEmailApplication

Aftersomequickanalysisofyourexistingapplicationscode,youdecidethatthenewrequirementsare
reallytwodistinctpointsofchange.FollowingtheSingleResponsibilityPrinciple,thesetwopointsshowyou
whereyouneedtoseparatetheexistingcodeintomultipleclasses(Figure6).
Figure6:Twopointsofchangeforyourapplication.

AnewEmailSenderobjectwillprovidetheabilityforthenetworkoperationspersonneltohaveanAPIto
codeagainst.Additionally,separatingouttheformatreadingfromtheformisnecessarytoallowtheformor
theAPItoreadthefileformat.
TosimplifytheAPIthatthenetworkoperationspeopleneed,youdecidetoputthefilereadingcodeintothe
emailsender(Listing2).Thiswillprovideasimpleenoughinterfaceandletyougetthefunctionalityoutthe
doorinatimelymanner.
Intheinterestoftimeandnotneglectingyourotherresponsibilities,youdecidetogoaheadandcreatea
singleFormatReaderclasstohandlebothofthefileformats.Thiscodeonlyneedstoknowifthecontents
arevalidXML.AquickhacktoloadthecontentsintoanXmlDocumentshouldbesufficientforthissmall
application.
stringmessageBody;
try
{
XmlDocumentxmlDoc=newXmlDocument();
xmlDoc.LoadXml(fileContents);
messageBody=xmlDoc.
SelectSingleNode("//email/body")
.InnerText;
}
catch(Exception)
{
messageBody=fileContents;
}
returnmessageBody;

ThelessontorememberinthisreleaseoftheapplicationisthattheSingleResponsibilityPrincipleisdriven
bytheneedsofthebusinesstoallowchange.Asinglereasontochangehelpsyouunderstandwhich
logicallyseparateconceptsshouldbegroupedtogetherbyconsideringthebusinessconceptandcontext,
insteadofthetechnicalconceptalone.

ExtensibilityandComingRequirements
AfewdaysafterdeliveringtheAPIsettoyournetworkoperationsdepartment,yourmanagerisbackinyour
cubewithgoodnewstheoperationspersonnellovewhatyouhavedoneforthem.TheAPIyoudelivered
wasverysimpleandtheywereabletogettheemailprocessupandrunninginnotimeatall.
Withthissuccessinmind,yourmanagerhasbeentryingtodrumupadditionalusesforyournew
application.Inthiseffort,hehasheardsomerumblingaboutneedingtosendlogmessagesfrommorethan

flatfilesorXMLfiles.Heexpectstheofficialrequestforfeaturestocomeinsoon,andwantsyoutogeta
headstartonbeingabletoextendtheapplicationinthismanner.
Giventheoperationsgroupcapabilitiestheywritesomecode,thoughitisusuallysomesortofscriptingyou
decidethattheyshouldbeabletoextendthesupportedfileformatswhenevertheyneedto.Afteraquick
discussionwiththeoperationspersonnel,theyagreeandappreciateyourconfidenceintheirabilities.From
thatdiscussionandthedirectionfromyourmanager,youdecidetomoveforwardontheabilitytoaddnew
fileformatsasneeded.

OpenClosedPrinciple
TheOpenClosedPrinciplesaysthataclassshouldbeopenforextension,butclosedformodification.In
otherwords,youshouldbeabletoeasilychangethebehavioroftheclassinquestionwithouthavingto
modifyit.
Thenexttimeyouareatahardwarestore,lookatthepowertools.Youwillnoticethatthereareawide
rangeofsawbladesthatcanattachtoasinglesaw.Onebladecomparedtoanothermaynotlookvery
differentatfirst,butacloserinspectionmayrevealsomesignificantdifferences.Somebladesare
constructedwithdifferentmetals,thenumberofteethoredgesmayvary,andthematerialthatisusedfor
theteethisoftendesignedforspecialpurposes.Nomatterwhatthedifference,though,ifyouarecomparing
twobladesthatattachtothesametypeofsaw,theywillhaveonethingincommon:howtheyattachtothe
sawtheinterfacebetweenthesawandtheblade.
Theindividualdifferencesofthebladesarewhatmakeeachtypeofbladeunique.Oneblademaycut
throughwoodextremelyquickly,butleavetheedgesrough.Anotherblademaycutwoodmoreslowlyand
leavetheedgessmooth.Stillothersmaybesuitedforcuttingmetalorothermaterials.Thewidevarietyof
blades,combinedwiththecommonmethodofattachingthemtothesaw,allowsyoutochangethebehavior
ofthesawwithouthavingtomodifythemechanicalportionofthesaw.
So,howdoyouallowaclasssbehaviortobemodifiedwithoutactuallymodifyingtheclass?Theansweris
surprisinglysimpleandthereareseveralmethodsfordoingthis.
Haveyoueverimplementedaninterfaceinaclassandthenpassedaninstanceofthatclassintoanother
object?PerhapsyouimplementedtheIPrincipalinterfaceforcustomsecurityneeds.Or,youmayhave
writtenyourowninterfacesuchastheclassicexampleofIAnimal,andimplementedaCatandaDogobject
fromthisinterface.Theubiquitousnatureofexplicitinterfacesin.NET,aswellasabstractbaseclasses,
delegates,andotherformsofabstraction,allprovidedifferentwaysofallowingcustombehaviortobe
suppliedtoexistingclassesandmodules.YoucanusedesignpatternssuchasStrategy,Template,State,
andotherstofacilitatethebehavioralchangesthroughtheuseofsuchabstractions.Therearestillother
patternsandabstractions,andothermethodsofinjectingbehaviorandalteringtheclassatruntime.
Chancesare,ifyouhavewrittenanapplicationthatrequiredevenasmallamountofflexibility,youhave
eitherprovidedacustombehaviorimplementationtoanexistingclass,orhavewrittenaclassthatrequired
acustombehaviortobesupplied.

RestructuringforOpenClosed

Giventheneedformultiple,unknownfiletypestobeparsed,youdecidetosupplyaninterfacethatcanbe
implementedbyanynumberofobjects,fromanynumberofthirdparties,includingthenetworkoperations
personnel.Inadditiontotheactualfileparsing,youwillneedtheinterfacetotellyouwhetherornotthe
specificimplementationcanhandlethecurrentfilecontents.Yourresultingapplicationstructurelooksmore
likeFigure7,withtheIFileFormatReaderinterfacedefinedasfollows:
Figure7:Enablingopen/closedviaIFileFormatReader.

publicinterfaceIFileFormatReader
{
boolCanHandle(stringfileContents);
stringGetMessageBody(stringfileContents);
}

Sinceyouknowthattherearemultiplefileformatsbeingreadnow,youalsodecidetomovetheexisting
codethatreadstheflatfileandXMLfileformatsintotwoseparateobjects.Theflatfilereadercanhandle
anynonbinarylogfile,soyoudecidethatthishandlerdoesnotneedtodetermineifitcanhandlethefile
contentssenttoit.Itonlyneedstosaythatitcanhandletheformat,andthensendtheoriginalcontentback
out.Yourewritetheimplementationoftheflatfileformatreaderasfollows:
classFlatFileFormatReader:IFileFormatReader
{
publicboolCanHandle(stringfileContents)
{
returntrue;
}

publicstringGetMessageBody(
stringfileContents)
{
returnfileContents;
}
}

TheXMLfileformatreaderwillcontainachecktoseeiftheXMLisvalid.TheGetMessageBodymethodwill
thenparsetheXMLforthecontent,asshowninListing3.
Next,youwanttointroducetheFileReaderServiceclass.ThiswillusethevariousIFileFormatReader
implementationsandiswherethebehavioralchangewilloccurwhenthevariousformatreadersare
supplied.
Tosupportanunknownnumberoffileformatreaders,youdecidetostorethelistofregisteredformat
readersinasimplecollection:

IList<IFileFormatReader>_formatReaders=new
List<IFileFormatReader>();

publicvoidRegisterFormatReader(
IFileFormatReaderfileFormatReader)
{
_formatReaders.Add(fileFormatReader);
}

TheRegisterFormatReadermethodallowsanycodethatcallstheFileReaderServiceAPItoregisteras
manyformatreadersastheyneed.Then,whenafileneedstobeparsed,acalltoaGetMessageBody
methodismade,passinginthecontentsofthefileasastring.Thismethodrunsthroughthelistof
registeredformatreadersandcheckstoseeifthecurrentonecanhandletheformat.Ifitcan,itcallsthe
GetMessageBodymethodofthereaderandreturnsthedata.
publicstringGetMessageBody(stringfileContents)
{
stringmessageBody=string.Empty;
foreach(IFileFormatReaderformatReader
in_formatReaders)
{
if(formatReader.CanHandle(fileContents))
{
messageBody=formatReader
.GetMessageBody(fileContents);
break;
}
}
returnmessageBody;
}

Atthispoint,ifthereisnoregisteredreaderthatcanhandlethefilecontents,anemptystringisreturned.
Yourealizethatyouneedtoaddadefaultfilereader.Theintentionistoensurethatalllogfilesarehandled,
regardlessofthecontent.Ifafilecantbehandledbyanyotherreader,youwillwanttoreturnallofthe
contentthroughtheflatfileformatreader.
ByaddingaseparateRegisterDefaultFileReadermethod,youcanensurethatonlyonedefaultexists.
Listing4showstheresultingGetMessageBodyimplementation.
Finally,youneedtoupdatetheusageoftheFormatReaderobjectinyourEmailSender.Youneedtoregister
boththeXMLfileformatreaderandtheflatfileformatreaderintheconstructoroftheemailsenderclass.
privatereadonlyFileReaderService
_fileReaderService=newFileReaderService();


publicEmailSender()
{
_fileReaderService.RegisterFormatReader(
newXmlFormatReader());
_fileReaderService.
RegisterDefaultFormatReader(
newFlatFileFormatReader());
}

HappyConsumersandMoreRequirements
AfewdaysafterreleasingthisversionoftheapplicationandAPI,youhearthattheoperationsgrouploves
yourIFileFormatReaderandtheextensibilityitbringstothetable.Theyhavesuccessfullyimplemented
severalformatreadersandareplanningonmore.
Ashorttimelater,anewrequestcomesinthatyouwerenotexpecting.Onesystemtheoperationsgroup
mustsupportlogsallofitserrorstoadatabase,notatextfile.Moreover,accordingtheoperations
personnel,theycannotwritecodethathitsthedatabaseinquestion.Apparently,thatsabovetheirpay
grade.Theyneedsomeoneonthedevelopmentstafftodoit,andareaskingforyourhelp.
ThemostchallengingpartofthisnewrequirementistheCTObeinginvolved,again.Duetothehighvisibility
ofthisprojectandthepotentialforlostrevenueiferrorsarenotproactivelycorrected,hewantsyour
applicationupdatedtosupportreadingfromthedatabase,immediately.Accordingtoyourmanager,when
theCTOsaysimmediatelyheusuallymeansbeforetheendoftheday.Itsonlyafewhoursbeforetheday
endsandyouvebeenrunningonverylittlesleepforthelastfewdays,butyouthinkyoucanbangouta
workingversionandgetittotheoperationsgroupintimetomaketheCTOhappy.

LiskovSubstitutionPrinciple
TheLiskovSubstitutionPrinciplesaysthatanobjectinheritingfromabaseclass,interface,orother
abstractionmustbesemanticallysubstitutablefortheoriginalabstraction.Eveniftheoriginalabstractionis
poorlynamed,theintentofthatabstractionshouldnotbechangedbythespecificimplementations.This
requiresasolidunderstandingofthecontextinwhichtheinterfacewasmeanttobeused.
Toillustratewhatasemanticviolationmaylooklikeincode,considerasquareandarectangle,asshownin
Figure8.Ifyouareconcernedwithcalculatingtheareaofaresultingrectangle,youwillneedaheight,a
widthandanareamethodthatreturnstheresultingcalculation.
Figure8:Semanticviolationsarenotalwayseasytosee.

publicclassRectangle
{
publicvirtualintHeight{get;set;}
publicvirtualintWidth{get;set;}

publicintArea()
{
returnHeight*Width;
}
}

Ingeometry,youknowthatallsquaresarerectangles.Youalsoknowthatnotallrectanglesaresquares.
Sinceasquareisarectangle,though,itseemsintuitivethatyoucouldcreatearectanglebaseclassand
havesquareinheritfromthat.Butwhathappenswhenyoutrytochangetheheightorwidthofasquare?
Theheightandwidthmustbethesameoryounolongerhaveasquare.Ifyoutrytoinheritfromrectangle
tocreateasquare,youendupchangingthesemanticsofheightandwidthtoaccountforthis.
publicclassSquare:Rectangle{
publicoverrideintHeight{
get{returnbase.Height;}
set{base.Height=value;
base.Width=value;
}
}
publicoverrideintWidth{
get{returnbase.Width;}
set{base.Width=value;
base.Height=value;
}
}
}

Whathappenswhenyouusearectanglebaseclassandasserttheareaofthatrectangle?Ifyouexpectthe
rectanglesareatobe20,youcansettherectanglesheightto5andwidthto4.Thiswillgiveyoutheresult
youexpect.
Rectanglerectangle=newRectangle();
rectangle.Height=4;
rectangle.Width=5;
AssertTheArea(rectangle);

privatevoidAssertTheArea(Rectanglerectangle)
{
intexpectedArea=20;
intactualArea=rectangle.Area();
Debug.Assert(expectedArea==actualArea);
}

WhatifyoudecidetopassasquareintotheAssertTheAreamethod,though?Themethodexpectstofindan
areaof20.Letstrytosetthesquaresheightto5.Youknowthatthiswillalsosetthesquareswidthto5.
Whenyoupassthatsquareintothemethod,whathappens?
Rectanglesquare=newSquare();
square.Height=5;
AssertTheArea(square);

privatevoidAssertTheArea(Rectanglerectangle)
{
intexpectedArea=20;
intactualArea=rectangle.Area();
Debug.Assert(expectedArea==actualArea);
}

Yougetthewrongresultbecause5x5is25,not20.Thatistoohigh,sonowtryaheightof4instead.You
knowthat4x4is16.Unfortunately,thatstoolow.Sothequestionis,howcanyouget20outofmultiplying
twointegers?Theansweris:youcant.
ThesquarerectangleissueillustratesaviolationoftheLiskovSubstitutionPrinciple.Youclearlyhavethe
wrongabstractiontorepresentbothasquareandarectangleforthisscenario.Thisisevidencedbythe
squareoverridingtheheightandwidthpropertiesoftherectangle,andchangingtheexpectedbehaviorofa
rectangle.
What,then,wouldacorrectabstractionbe?Inthiscase,youmaywanttouseasimpleShapeabstraction
andonlyprovideanAreamethod,asshowninListing5.Eachspecificimplementationsquareand
rectanglewouldthenprovidetheirowndataandimplementationforareaallowingyoutocreateadditional
shapessuchascircles,triangles,andothersthatyoudontyetneed.Bylimitingtheabstractiontoonlywhat
iscommonamongalloftheshapes,andensuringthatnoshapehasadifferentmeaningforareayoucan
helppreventLSPviolations.

AQuickandDirtyDatabaseReader
Afterfuellingupwithanotherenergydrinkandshakingoffthesleepyousodesperatelywant,youdiveinto
thecodeforthedatabasereader.Giventheshorttimeframe,youdecidetotakeashortcutandnot
introduceanewabstractionoranewmethodtotheAPI.Rather,youdecidetohardcodethebehaviorof
readingfromthedatabaseintotheapplication,facilitatedbytheuseofaspecialfileformatreader.Youknow
itsnotthebrightestmomentinyourcareer,butyoujustwanttogetitoutthedoorandgohomeforthe
night.Whatshouldhavebeenan800meterracehasnowbecomea50meterdashinyourmind.Listing6
showstheresult.
Youdelivertheworkingcodeontime,andmanagetomakeithomebeforefallingasleepwhiledriving.
Overall,youconsiderittobeasuccessfulday.
Thefollowingweek,youhearwordthatthenetworkoperationspersonnellikedtheabilitytoreadfroma
database.Infact,theylikeditsomuchthattheytoldanotherdepartmentaboutthisfeature.Whattheydidnt

know,though,wasthatthecodeyoudeliveredwaswrittenforoneveryspecificdatabaseanddidntactually
readtheconnectionstringfromafile.Youknewthatthesecurityguyswouldhaveyourheadifyoustored
therealconnectioninformationinaplaintextfile,soyouhardcodeditintothefilereaderservice.You
createdtheserver=contentofthefileasaplaceholdertoletyouknowthatyoushouldusethedatabase
connectionreader.
So,whenthenetworkoperationspersonnelgaveyourcodetotheotherdepartment,everyonestarted
wonderingwhytheotherdepartmentwasnowreadinglogfilesfromthenetworkoperationscenter.Alleyes
arenowlookingsquarelyatyou.

RevisitingtheDatabaseReader
Alloftheeyeslookingsquarelyatyouwerefromyourfriendsinthecompany,fortunately.Afterexplaining
thestressandsleeplessnessthatunderminedyourabilitytocodethatday,theyalllaughedandaskedwhen
youwouldhaveanewversionreadyforthem.Rememberingthatsprintingoutofthegateduringa
marathonraceislikelytocausethesameproblemsagain,youinformthemthatyoullneedadayortwoto
getthesituationsortedoutcorrectly.TheresnoimmediateneedorCTOputtingonthepressureatthis
point,soeveryoneagreestothegeneraltimelineandwaitspatientlywhileyouwork.
Afteraquickdiscussionwithsomecoworkers,yourealizethatyouhadchangedthesemanticsofthefile
formatreaderinterfaceandintroducedbehaviorthatwasincompatible.Afteralittlemorediscussion,you
endupwiththedesignrepresentedbyFigure9,andthechangeturnsouttobefairlysimple.
Figure9:RestructuringdatabasereadingtocorrecttheLSPviolation.

Byintroducingaseparatedatabasereaderservice,youcanremovethetypecheckingcodefromthefile
readerservice.
Byintroducingaseparatedatabasereaderservice,youcanremovethetypecheckingcodefromthefile
readerservice.Youcansetupthedatabasereadertoreadtherequiredconnectionstringfromthe
companystandardstorageforsensitivedata.Thatdecisionmakesthepeopleinnetworkoperations,
security,andtheotherdepartmentthatwantstousethecode,happy.
Next,youupdatetheUItoincludeaSendFromDatabasebuttonasshowninFigure10.Thisbuttoncalls
intothesameemailsenderobjectthatyouvebeenusingasthepublicAPI.However,theemailsendernow
hasaReadFromDatabasemethodalongwithaReadFromFilemethod.ThiskeepsthepublicAPI
centralizedwhilestillprovidingthefunctionalitythatthevariousdepartmentsneed.
Figure10:TheUIupdatedwiththeSendFromDatabasefunctionality.

publicclassEmailSender
{
publicvoidReadFile()
{/*...*/}

publicvoidSendEmail()
{/*...*/}

publicvoidReadDatabase()
{/*...*/}
}

Withthisnewlystructuredsysteminplace,youdeliverthesolutiontobothofthewaitingdepartments.Your
friendsarehappytohearthatyouvebeengettingmoresleepandthattheapplicationtheyvebeenwaiting
forisfinallydoneadayearlierthanpromised.

StillMoreUseforYourApplication
Shortlyafterdeliveringtheupdatedversionoftheapplicationwiththedatabasereadingcapabilities,another
departmentgetswindofitandtheywanttousetheAPI.Afteraquickconversationwiththemtofindoutif
yourapplicationiswhattheyreallyneed,youdelivertheworkingbits.Adaylater,oneofthedevelopersfrom
thatdepartmentstopsbyyourcubewithaconfusedlookonhisface.Aftersomequickchat,yourealizethat
hesconfusedbytheemailsenderobject.Itseemsthathedoesntunderstandwhytheresareadfrom
databaseandreadfromfilemethodonanobjectthatissupposedtosendemail.

InterfaceSegregationPrinciple
TheInterfaceSegregationPrinciplesaysthataclient(thecallingclassormodule)shouldnotbeforcedto
dependonaninterfacethatitdoesnotneed.Iftherearemultipleconcernsrepresentedbyaninterface,or
themethodsandpropertiesareunclear,thenitbecomesdifficulttoknowwhichmethodsshouldbecalled
when.Therefore,youshouldseparatetheinterfaceintologicalpieces,basedontheneedsofthe
consumers.
Toacertainextent,ISPcanbeconsideredasubset,ormorespecificformoftheSingleResponsibility
Principle.TheperspectiveshiftofISP,though,examinesthepublicAPIforagivenclassormodule.Looking
attheIHaveALotOfResponsibilitiesclassinFigure11,youcanseenotonlyasetofmethodsthatyou
shouldbreakintomultipleclassesforthesakeofSingleResponsibility,butaveryfatinterfacethatmay
confuseanyofthecallingclients.IfIwanttouseIHaveALotOfResponsibilities,doIneedtocalltheDo
methods?Ifso,doIneedtocallthembeforetheSomemethods?OrcanIjustcalltheSome
methods?Or???
Figure11:Aclasswithafatinterface.

Ratherthanforcingadevelopertoknowwhichmethodstheyshouldcalltofacilitatethefunctionality,you
shouldprovideaseparatedsetofinterfacesthatencapsulatetheprocessesinquestion,independently.This
helpstopreventconfusionandalsohelpstocutdownonsemanticcouplingtheideathatadeveloperhasto
knowthespecificimplementationoftheclasstouseitcorrectly.
ViolationsofISParenotjustfoundinsoftware,though.Mostprofessionalworkersinmodernsocietyhave
workedinanofficeatonetimeoranother.Dependingonthesizeoftheofficehowmanypeopleareworking
thereyouwilltypicallyfindoneormore,verylarge,allinonebusinessmachines.Thesemachinesprint,
copy,fax,scan&email,andotherfunctions.

Thinkbacktothelasttimeyouhadtouseoneoftheselarge,multifunctionmachines.Theytypicallyhave
controlpanelsthatinclude15to20buttons,anLCDdisplayofsomesort,andvariousotherformsofinput.
Thenumberoffunctionscombinedwiththenumberofinputoptionsoftencreatesaveryfrustratinguser
experience.Whatbuttonsorcontrolsdoyouneedtooperatethemachine?Howcanyouensurethatitis
goingtocreateaphotocopyofadocumentinsteadofscanningandemailingit?Doyouneedtoclearthe
currentsettings?Doyouneedtotypeinanumberofcopiesnow,orscanthedocumentfirstandthentellit
howmanycopiestoprint?Whataboutthebrightness,contrast,orpapersizeofyourcopy?Didthemachine
rememberthesettingsfromthelastuser,whowantedtoscanadocumentandemailittosomeone?The
numberofoptionsisoverwhelming.Theinstructionsaredifficulttofollowandyourenotalwayssurethatit
didwhatyouwant.Theworstoutcomeiswhenanerrormessagespopsupafterperformingwhatyou
believeisthecorrectsequenceofsteps.WhatdoesPCLOADLETTERmean,anyway?
Thelargenumberofcapabilitiesandoptionsthattheseallinonecopiersprovidemayoffersome
advantagestoanofficeenvironment.Theyprovidearelativelylowcost,highquality,documentcentricsetof
solutions.Unfortunately,theytypicallycomeatthecostofaconfusinginterface.(I,forone,havespenta
goodnumberofhourstryingtorememberhowtoscanandemailadocumenttomyselfvs.makinga
photocopyonthemachinesinmyoffice.)Ifmanufacturerswanttohavemachineswithsuchalargefeature
setprovidingvaluetosomanydifferentusers,theyshouldlookforwaystoseparatetheinterfaceintoeach
featuresothattheuserisnotleddownthewrongpath,orleddowntherightpathatthewrongtime.

SplittingUptheEmailSenderAPI
AfterthediscussionaboutyouremailsendersAPIbeingrolledupintoasingleclass,andtherealizationthat
noteveryoneneedstoreadfromadatabaseorafile,youdecidetoseparateouttheobjectsasshownin
Figure12.Whenyoudiveintothecodeagain,yourealizethatyoudonthavetomakemanychangesto
yoursystem.Thedatabasereaderserviceandthefilereaderservicearebothobjectsontheirown,already.
Therealchangethatyouneedtomakeistonotcallthemdirectlyfromtheemailsender.
Figure12:SeparatingthevariousserviceAPIsbyresponsibility.

TheresultingEmailSenderclassissignificantlysmaller.Additionally,youwereabletomakethemessage
bodyaparameteroftheSendEmailmethod.Thisallowsforanyclientoftheemailsendertoprovideany
messagebodytheyneed,regardlessofthesource.
publicclassEmailSender
{
publicvoidSendEmail(stringmessageBody)
{
SmtpClientclient=//new...
client.Credentials=//new...
MailAddressfrom=//new...
MailAddressto=//new...
MailMessagemsg=//new...
msg.Body=messageBody;
msg.Subject=//...
client.Send(msg);
}

Thefilereaderserviceanddatabasereaderserviceclassesneedednochangesthistimearound.You
simplymovedtheircallsintothecodebehindoftheformforyourapplication(Listing7).However,theother
departmentsthatareusingtheAPIneedtoknowthattheyareresponsibleforcallingthedatabasereader
andfilereader,directly.
Aftersomediscussionwiththeotherdepartments,therewasalittlebitofgrumblingabouthowtheythought
thenewAPIsetwasmoredifficulttouse.Theyagreedthatasmallsetofdocumentationonhowtousethe
APIwouldbesufficientfortheirneeds,though.Andhonestly,youfeelalittlemoresecureknowingthatyou
willhavesomedocumentationforthegrowingAPI.

TryingtoSettleinforaMarathon
Afterdeliveringthisversionofthesystem,includingthenewdocumentationthatyouwrotefortheAPIset,
youdecidetostepbackforaminuteandtakestockofyoursystem.Whatyouseeisrathersurprisingatthis
point.Youhaveagrowingnumberofclassesandalotoffunctionalitycomparedtotheoriginalstartingpoint
ofreadingaflattextfileandemailingit.Giventheattentionthatyouvereceivedforthisworkandthe
amountoffunctionalitythatyouhavebuiltin,youdecidetoaskyourmanagerforsomeofficialproject
support.Ratherthanworkingthiscodebaseonthesideofyourotherresponsibilities,youwouldliketohave
adedicatedprojectforthissystem.Thiswouldhelpprovidelongtermsupportforwhatisnowamission
criticalpartofthecompany.
Figure13:Adependency.

Yourmanager,havingreceivednothingbutpraiseforyourhardworkanddedicationfromeveryone
includingtheCTOhappilysaysyes.Hethenasksforatimelinetocompletethenextversion.Youlethim
knowthatyoullneedtothinkaboutthatforabit,butoffhand,youexpectthecodetochangewhennew
featuresareneeded,primarily.
Onyourwaybacktoyourdesk,anothercoworkerstopsyouandwantstodiscussthisproject.Heshearda
lotaboutwhatyouhavebeendoingandlikesthedirectionthatyouhavebeentakingthecode.Afterafew
minutesofdiscussion,yourcoworkerletsyouknowthathewantstousethisproject,butisntinterestedin
thecurrentformatreadersoremailsender.Hesmostlyinterestedinthegeneralprocessandwantstoknow
ifhecanreusevariouspartsofthesystemwithouthavingtobringallofyourspecificimplementationsalong.
Thecurrentobjectsandinterfacesprovidemostofwhatyourcoworkerwants.However,yourealizethatthe
bigpictureoftheprocessisstillhardcodedandtightlycoupledinthecurrentapplication.Yourcoworker
mentionsanideathatrevolvesaroundahigherlevelprocessprovidinganabstractionthatlowerlevel
detailscanimplement.Thispiquesyourinterestandyousitdownatyourdesk,readytotacklethisnext
challenge.

TheDependencyInversionPrinciple
TheDependencyInversionPrinciplehastwoparts:
1.Highlevelmodulesshouldnotdependonlowlevelmodules.Bothshoulddependonabstractions.

2.Abstractionsshouldnotdependupondetails.Detailsshoulddependuponabstractions.
Thinkbacktothelasttimeyouwantedtoturnonalamptohelplightanareaofaroom.Didyouhavetocut
aholeinthewall,digaroundforelectricalwires,stripthembare,andsolderthelampdirectlyintothewiring
ofthehouse?
Thinkbacktothelasttimeyouwantedtoturnonalamptohelplightanareaofaroom.Didyouhavetocut
aholeinthewall,digaroundforelectricalwires,stripthembare,andsolderthelampdirectlyintothewiring
ofthehouse?Ofcoursenot(atleast,Ihopenot!)Theelectricaloutletprovidesastandardinterfaceforsuch
anoccasion.Noone,inmostoftheindustrializedworld,wouldexpecttosolderalampdirectlyintothe
electricalwiringofthebuilding.Additionally,nooneexpectstoonlybeabletopluginalamp,toanoutlet.
Weexpecttopluginlamps,computers,televisions,vacuumsandotherdevices.Thestandard,120volt,60
hertzpoweroutlethasbecomeaubiquitouspartofsocietyintheUnitedStates.
Thesameprinciplealsoappliesinsoftwaredevelopment.Ratherthanworkingwithasetofclassesthatare
hardwired(tightlycoupled)toeachother,youwanttoworkwithastandardinterface.Furthermore,you
wanttoensurethatyoucanreplacetheimplementationwithoutviolatingtheexpectationsofthatinterface,
accordingtoLSP.So,ifyoureworkingwithaninterfaceandyouwanttobeabletoreplaceit,thenyouneed
toensurethatyouareonlyworkingwiththeinterfaceandneverwithaconcreteimplementation.Thatis,the
codethatreliesontheinterfaceshouldonlyeverknowabouttheinterface.Itshouldnotknowaboutanyof
thespecificclassesthatimplementtheinterface.

Policy,Detail,andAbstractionOwnership
AnotherwaytothinkaboutDIPistosaythatpolicy(highlevel)shouldnotdependondetail
(implementation),butdetailshoulddependonpolicy.Thehigherlevelpolicyshoulddefineanabstraction
thatitwillcalloutto,wheresomedetailimplementationexecutestherequestedaction.Thisperspectivecan
helptoillustratewhythisisthedependencyinversionprincipleandnotjustadependencyabstraction
principle.
Asanexampleofwhydetaildependingonpolicyisaninversionofthedependency,lookatthecodeyou
wroteintotheFormatReaderService.Theformatreaderserviceisthepolicy.Itdefineswhatthe
IFileFormatReaderinterfaceshoulddotheexpectedbehaviorofthosemethods.Thisallowsyoutobe
concernedwiththepolicyitself,bydefininghowtheformatreaderserviceworkswithoutregardforthe
implementationdetailoftheindividualformatreaders.Theformatreaders,then,aredependentonthe
abstractionprovidedbythereaderservice.Boththeserviceandindividualformatreaders,intheend,are
dependentontheabstractionoftheformatreaderinterface.

CorrectingCouplingbyInvertingDependencies
Youknowthatitsnotreasonableforaclasstohavezerodependenciestohavezerocoupling.Youwould
nothaveausablesetofclassesifyouhadzerocoupling.However,youalsoknowthatyouwanttoreduce
directcouplingwheneverpossible.Youwanttodecoupleyoursystemsothatyoucanchangeindividual
pieceswithouthavingtochangeanythingmorethantheindividualpiece.TheDependencyInversion
Principleisthekeytothisgoal.Bydependingonlyonanabstractionsuchasaninterfaceorbaseclass,you
cancorrectthecouplingofthevariouspartsofthesystem.Thisallowsyoutorecomposethesystemwith
differentimplementations.

Youwouldnothaveausablesetofclassesifyouhadzerocoupling.
Considerasetofclassesthatneedtobeinstantiatedintothecorrecthierarchysothatyoucangetthe
functionalityneeded.Itseasytohavethehighestlevelclasstheonethatyouwanttocallinstantiatethe
classatthenextleveldown,andhavethatclassinstantiateitsnextleveldown,andsoon.Figure14
representsastandardobjectgraphwherethehigherlevelobjectthepolicyisdependentonandcoupled
directlytothelowerlevelobjectthedetail.
Figure14:Policycoupledtodetail.

Thiscreatesthenecessaryhierarchybutcouplestheclassestogether,directly.Youwouldnotbeabletouse
FoowithoutbringingBaralongwithit.
Ifyouwanttodecoupletheseclasses,youcaneasilyintroduceaninterfaceforFootodependonandBarto
implement.Figure15illustratesasimpleIBarinterfacethatyoucancreatefromthepublicAPIoftheBar
class.
Figure15:Decouplingwithabstraction.

Inthisscenario,youcandecoupletheimplementationofBarfromtheuseofitinFoobyintroducingthe
interface.However,youveonlydecoupledtheimplementationbyseparatingtheinterfacefromit.You
haventinvertedthedependencystructureyetandyouhaventcorrectedallofthecouplingproblemsinthis
setup.
WhathappenswhenyouwanttochangeBarinthisscenario?Dependingonthechangeyouwanttomake,
youcouldhavearipplingeffectthatcausesyoutochangetheIBarinterface.FoodependsontheIBar
interface,soyoumustchangetheimplementationofFooaswell.Youmayhavedecoupledthe
implementationofBar,butyouhaveleftFoodependentonchangestoBar.Thatis,thePolicyisstill
dependentontheDetail.
IfyouwanttoinvertthedependencystructureandhavetheDetailbecomedependentonthePolicy,then
youmustfirstchangeyourperspective.Thedeveloperworkingwiththissystemmustunderstandthatyou
shouldnotmerelyabstracttheimplementationawayfromtheinterface.Yes,thisseparationisnecessary,
butitisnotsufficient.YoumustunderstandwhoownstheabstractionthePolicy,ortheDetail.
TheDependencyInversionPrinciplesaysthatDetailshouldbedependentonPolicy.Thismeansthatyou
shouldhavethePolicydefineandowntheabstractionthatthedetailimplements.IntheFoo>IBar>Bar
scenario,youneedtotreatIBaraspartofFooandnotjustawrapperaroundBar.Nothingmayhave
changedstructurally,buttheperspectiveofownershiphasshifted,asillustratedbyFigure16.
Figure16:Policyownstheabstraction.Detaildependsonpolicy.

IfFooownstheIBarabstraction,youcanplacethesetwoconstructsinapackagethatisindependentof
Bar.Youcanputthemintotheirownnamespace,theirownassembly,etc.Thiscangreatlyincreasethe
illustrationofwhatclassormoduleisdependentontheother.IfyouseethatAssemblyAcontainsFooand
IBar,andAssemblyBprovidestheimplementationofIBar,itiseasiertoseethatthedetailofBaris
dependentonthepolicydefinedbyFoo.

Whenyouhavethedependencystructureinvertedcorrectly,therippleeffectofchangingthepolicyand/or
detailisnowcorrectaswell.WhenyouchangetheimplementationofBar,youarenolongerseeingan
upwardrippleofchanges.ThisisduetoBarbeingrequiredtoconformtotheabstractionprovidedbyFoo
thedetailisnolongerdictatingchangestothepolicy.Then,whenyouchangetheneedsofFoo,causinga
changeintheIBarinterface,younowhavechangesthatrippledownthestructure.Barthedetailwillbe
forcedtochangebasedonthepolicychanging.

DecouplingandInvertingtheEmailSendingSystemDependencies
LookingthroughyourcodebaseyouseethattheIFileFormatReaderisalreadyaninstanceofDependency
Inversion.TheFormatReaderServiceclassownsthedefinitionoftheformatreaderinterface.Iftheneedsof
theformatreaderservicechanges,youwilllikelyseeripplesofchangedownintotheindividualformat
readers.However,ifanindividualfileformatreaderchanges,youwillnotlikelyseechangesrippleupinto
theformatreaderservice.Thismakesyouwonderwhereelseyoucaninvertthesystemsdependencies.
Thefirstthingyouwanttodoisdecouplethelogicofgettingthelogmessage,andsendingitasanemail,
fromtheform.Youdontmindthereferencestothetworeaderservicesandtheemailsender,buthaving
theexplicitknowledgeofwhattocallwhenisalittlequestionableinyourmind.Yourecognizethatthe
processisactuallyduplicatedintheform:onceforsendingfromafile,andonceforsendingfroma
database.Andthenyourememberalltheotherdepartmentsthatareusingthisaswell,andstarttowonder
justhowmuchduplicationoftheprocessreallyexists.Additionally,someofyourfriendshavebeentalking
aboutunittestingrecently.Theysaythatyoushouldensuretherealprocesslogicthatyouaretestingis
encapsulatedintoobjectsthatdonthavereferencestoexternalsystems.
Withallofthisinmind,youdecidetocreateanobjectcalledProcessingService.Afterafewminutesof
movingcodearoundtotryandconsolidatetheprocess,yourealizethatyoudontwanttheprocessing
servicetobecoupleddirectlytothedatabasereaderorfilereaderservices.Withanadditionalmomentof
thinking,yourecognizeapatternbetweenthetwo:theGetMessageBodymethod.Usingthismethodas
thebasis,youcreateanewinterfacedcalledIMessageInfoRetrieverandhaveboththedatabasereaderand
filereaderservicesimplementthat.
publicinterfaceIMessageInfoRetriever
{stringGetMessageBody();}

publicclassFileReaderService:
IMessageInfoRetriever
{
publicstringGetMessageBody(){/*...*/}
}

publicclassDatabaseReaderService:
IMessageInfoRetriever
{
publicstringGetMessageBody(){/*...*/}
}

Thisinterfaceallowsyoutoprovideanyimplementationyouneedtotheprocessingservice.Youthenset
youreyesontheemailservice,whichiscurrentlydirectlycoupledtotheprocessingservice.Asimple
IEmailServiceinterfacesolvesthat,though.Figure17showstheresultingstructure.
Figure17:Invertingthedependenciesoftheprocessingserviceandfilereaderservice.

Passingthemessageinforetrieverandemailserviceinterfacesintotheprocessingserviceensuresthatyou
haveaninstanceofwhateverclassimplementsthoseinterfaces,withouthavingtoknowaboutthespecific
instancetypes.YoucanseetheendresultofthisendeavorinListing8.

AnotherDay,AnotherDelivery
Youcoworkermeetsthedeliveryofthisversionwithgreatenthusiasm.Hesexcitedaboutthepossibilitiesof
usingtheprocessingserviceandprovidinghisownemailserviceimplementation,formatreaders,etc.It
seemsthatyouhavemadeyetanothersuccessfuldeliveryofthiseverevolvingsolutionandyougladly
acceptyourcoworkersthanks.
Headingbacktoyourdesk,yourealizehowhighyourconfidenceinthiscodebase,andyourabilityto
continueimprovingit,actuallyis.Thisleadsyoutowonderwhatthenextchangerequestwillbeandwhat
newprinciplesandpracticesyoucanassimilateintoyourskillset.Itisoflittleconcern,though.Whateverthe
task,forwhoevermakestherequest,youarecertainofyourabilitytomeettheirneeds.

SummaryofYourSOLIDRace
WhenyoufirstsetoutonthequesttosatisfyyourCTO,youhadnoideathattheresultingracewould
changesomanytimesorsorapidly.Youstartedatapacetowina50meterdash.Soonafter,youslowed
thepacedownfora400meterrace.Youthencontinuedtochangethepaceasyoucontinuedtoadd
featuresandfunctionality,realizingtheneedtorestructuretheapplicationforlongtermmaintenanceand
enhancements.Thejourney,nowatthepointwhereyouaresettledinforthemarathonofalongterm
project,hasbroughtyoufromasystemrepresentedbyFigure18,toasystemrepresentedbyFigure19.
Figure18:Theoriginalstructure.
Figure19:Thenewstructure.

Thedifferencebetweenthesetwostructuresisstaggeringandlookingatthemsidebysideforamoment,
youwonderwhatyouveactuallygainedwiththissprawlofobjectsandinterfaces.Youquicklydismissthe
senseofuncertainty,though,asyourememberthenumerousadvantages.

BenefitsoftheNewSystemStructure
Withasetofclassesthataresmallandeachprovidingavaluablefunction,youareabletoconstructalarger
systemthathasmorevaluethanjusttheindividualparts.ItslikeworkingwithaboxofLEGObuilding
blocks.Eachindividualblockmaybevaluable,buttheycancreatesomethingmuchmorevaluablewhen
stackedtogethercorrectly.
Thissystemisalsoeasilydissectible,andthevariouspartsareeasilyreplaceable.Youcanchangeoutthe
specificimplementationsoftheemailservice,themessageinforetrieval,andotherpartsofthesystem.You

canaccomplishallofthesechangeswithoutworryingaboutadverselyaffectingthepartsofthesystemthat
youarenottouching.
Theabilitytosegmentthesystem,bybothhorizontallayersandverticalslices,givesyoutheabilitytofocus
intoasinglepointinthesystem.
Theabilitytosegmentthesystem,bybothhorizontallayersandverticalslices,givesyoutheabilitytofocus
intoasinglepointinthesystem.Thisallowsyoutoassignvariouspartsofthesystemtodifferentteam
members,withmoreconfidenceofthesystemnotfallingapartlikeagameofJenga.

AchievingLowCoupling
Byabstractingmanyoftheimplementationneedsintovariousinterfacesandintroducingtheconceptsof
OCPandDIP,youcancreateasystemthathasverylowcoupling.Youcantakemanyoftheseindividual
piecesoutofthissystemwithlittletonospaghettimesstrailingafterthem.Separatingthevariousconcerns
intothevariousobjectimplementationsalsohelpstoensurethatyoucanchangethesystemsbehavioras
needed,withverylittlemodificationtotheoverallsystemjustupdatetheonepiecethatcontainsthe
behaviorinquestion.

AchievingHighCohesion
YoucangethighercohesionwithacombinationoflowcouplingandSRPyoucanstackalotofsmallpieces
togetherlikebuildingblockstocreatesomethinglargerandmorecomplex.Anyoftheseindividualpieces
maynotrepresentmuchfunctionalityorbehavior,butthen,anindividualpuzzlepieceisntmuchfuntouse
withouttheotherpieces.Onceseparated,DIPallowsyoutotiethevariousblocksbacktogetherby
dependingonanabstractionandallowingthatabstractiontobefulfilledwithdifferentimplementations.This
createsasystemthatismuchgreaterthanthemeresumofitsparts.

AchievingStrongEncapsulation
LSP,DIP,andSRPallworkhandinhandtocreateencapsulation.Youcanencapsulatethebehavioral
implementationsinmanyindividualobjects,preventingthemfromleakingintoeachother.Youcanensure
thatthedependenciesonthosebehaviorsareencapsulatedbehindanappropriateinterface.Atthesame
time,youdothenecessaryduediligencetoensurethatyouarentviolatinganyoftheindividual
abstractionssemanticsorpurpose,accordingtoLSP.Thishelpsensurethatyoucanproperlyreplacethe
implementationasneeded.

ANewUtilityBelt
Intheend,thesteppingstonesoftheSOLIDprinciplesoffernewinsightintoobjectorientedsoftware
development.Theprinciplesthatyouoncethoughtwerefortheacademicsarenowasetoftoolsthatyou
canreadilygrasp.

OnlineResources
InadditiontosomesimpleGooglesearches(oryoursearchengineofchoice),thereareanumberof
popularresourcesfortheSOLIDprinciples.BobMartinhasmadehisoriginalarticlesavailable,amongalist

ofothergreatprinciples.ThereareseveralresourcesavailablethroughtheLosTechies.comcommunity,as
well.
UncleBobsPrinciplesofOODhttp://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod
PablosTopicoftheMonthhttp://www.lostechies.com/blogs/chad_myers/archive/2008/03/07/pablostopic
ofthemonthmarchsolidprinciples.aspx
PablosSOLIDSoftwareDevelopmentebookhttp://www.lostechies.com/content/pablo_ebook.aspx

SingleResponsibility
Ifaclasshasmorethanoneresponsibility,thentheresponsibilitiesbecomecoupled.Changestoone
responsibilitymayimpairorinhibittheclassabilitytomeettheothers.Thiskindofcouplingleadstofragile
designsthatbreakinunexpectedwayswhenchanged.
RobertC.Martin

OpenClosed
Modulesthatconformtotheopen/closedprinciplehavetwoprimaryattributes:
TheyareOpenforExtension.Thismeansthatthebehaviorofthemodulecanbeextended.Thatis,youcan
makethemodulebehaveinnewanddifferentwaysastherequirementsoftheapplicationchange,orto
meettheneedsofnewapplications.
TheyareClosedforModification.Thesourcecodeofsuchamoduleisinviolate.Nooneisallowedtomake
sourcecodechangestoit.
RobertC.Martin

LiskovSubstitution
Ifforeachobjecto1oftypeSthereisanobjecto2oftypeTsuchthatforallprogramsPdefinedintermsof
T,thebehaviorofPisunchangedwheno1issubstitutedforo2thenSisasubtypeofT.
BarbaraLiskov

InterfaceSegregation
Thisprincipledealswiththedisadvantagesoffatinterfaces.Classesthathavefatinterfacesareclasses
whoseinterfacesarenotcohesive.Inotherwords,theinterfacesoftheclasscanbebrokenupintogroups
ofmemberfunctions.Eachgroupservesadifferentsetofclients.Thussomeclientsuseonegroupof
memberfunctions,andotherclientsusetheothergroups.
RobertMartin

DependencyInversion
Whatisitthatmakesadesignrigid,fragileandimmobile?Itistheinterdependenceofthemoduleswithin
thatdesign.Adesignisrigidifitcannotbeeasilychanged.Suchrigidityisduetothefactthatasingle
changetoheavilyinterdependentsoftwarebeginsacascadeofchangesindependentmodules.
RobertMartin

Books
BobMartinandhisson,Micah,havereleasedtwobooksthatdescribetheSOLIDprinciplesingreatdetail,
withindifferentcontexts.Youcanfindbothofthesebooksavailablethroughvariousbooksellers.
AgileSoftwareDevelopmentPrinciples,Patterns,andPracticesAgilePrinciples,Patterns,and
PracticesinC#

Listing1:Methodtosendemailfromfilecontents
privatevoidSend_Click(objectsender,EventArgse)
{
try
{
Output.Text=string.Empty;
FileInfofile=newFileInfo(_file);
StreamReaderrdr=file.OpenText();
stringmessageBody=rdr.ReadToEnd();
rdr.Dispose();

SmtpClientclient=newSmtpClient("some.mail.server.com");

client.Credentials=newNetworkCredential(
"someuser","somepassword");

MailAddressfrom=newMailAddress("[email protected]");

MailAddressto=newMailAddress("[email protected]");

MailMessagemsg=newMailMessage(from,to);
msg.Body=messageBody;
msg.Subject="Testmessage";
client.Send(msg);

Output.Text="Sentemailwithbody:"+
Environment.NewLine+messageBody;
}
catch(Exceptionex)

{
Output.Text=ex.ToString();
}
}

Listing2:TheEmailSenderobject
publicclassEmailSender
{
publicstringMessageBody{get;set;}
publicstringFileName{get;set;}

publicvoidReadFile()
{
FileInfofile=newFileInfo(FileName);
StreamReaderrdr=file.OpenText();
stringfileContents=rdr.ReadToEnd();
rdr.Dispose();

FormatReaderformatReader=newFormatReader();
MessageBody=
formatReader.GetMessageBody(fileContents);
}

publicvoidSendEmail()
{
SmtpClientclient=newSmtpClient("some.mail.server.com");

client.Credentials=newNetworkCredential(
"someuser","somepassword");

MailAddressfrom=newMailAddress("[email protected]");

MailAddressto=newMailAddress("[email protected]");

MailMessagemsg=newMailMessage(from,to);
msg.Body=messageBody;
msg.Subject="Testmessage";
client.Send(msg);
}
}

Listing3:ThenewXMLFileFormatReaderobject

publicclassXmlFileFormatReader:IFileFormatReader
{
XmlDocument_xmlDoc;

publicboolCanHandle(stringfileContents)
{
boolcanHandle;
try
{
_xmlDoc=newXmlDocument();
_xmlDoc.LoadXml(fileContents);
canHandle=true;
}
catch(Exception)
{
canHandle=false;
}
returncanHandle;
}

publicstringGetMessageBody(stringfileContents)
{
stringmessageBody=_xmlDoc.SelectSingleNode("//email/body")
.InnerText;
returnmessageBody;
}
}

Listing4:GetMessageBodywithdefaultfileformatreader
privateIFileFormatReader_defaultFormatReader;
publicvoidRegisterDefaultFormatReader(
IFileFormatReaderdefaultFormatReader)
{
_defaultFormatReader=defaultFormatReader;
}

publicstringGetMessageBody(stringfileContents)
{
stringmessageBody=string.Empty;
boolwasHandled=false;

foreach(IFileFormatReaderformatReaderin_formatReaders)
{
if(formatReader.CanHandle(fileContents))

{
messageBody=formatReader.GetMessageBody(fileContents);
wasHandled=true;
}
}

if(!wasHandled)
{
messageBody=_defaultFormatReader
.GetMessageBody(fileContents);
}

returnmessageBody;
}

Listing5:Acorrectabstractionforasquareandrectangle
publicinterfaceShape
{
intArea();
}

publicclassRectangle:Shape
{
publicvirtualintHeight{get;set;}
publicvirtualintWidth{get;set;}

publicintArea(){returnHeight*Width;}
}

publicclassSquare:Shape
{
publicintLengthOfSides{get;set;}

publicintArea(){returnLengthOfSides^2;}
}

publicclassDoStuff
{
publicvoidDoTheRectangleStuff()
{
Rectanglerectangle=newRectangle();
rectangle.Height=4;
rectangle.Width=5;
AssertTheArea(rectangle,20);

publicvoidDoTheSquareStuff()
{
Squaresquare=newSquare();
square.LengthOfSides=4;
AssertTheArea(square,16);
}

privatevoidAssertTheArea(Shapeshape,intexpectedArea)
{
intactualArea=shape.Area();
Debug.Assert(expectedArea==actualArea);
}
}

Listing6:ViolatingLSPwithspecifictypechecks
publicclassDatabaseConnectionReader:IFileFormatReader
{
publicboolCanHandle(stringfileContents)
{
boolcanHandle=false;
if(fileContents.StartsWith("server="))
canHandle=true;
returncanHandle;
}
publicstringGetMessageBody(stringfileContents)
{
thrownewNotImplementedException("Needtoreadfromthe
database!Notfromthisinterface!");
}
}

publicclassFileReaderService
{
//...
publicstringGetMessageBody(stringfileContents)
{
//...
foreach(IFileFormatReaderformatReaderin_formatReaders)
{
//...
if(formatReader.GetType()
.Equals(typeof(DatabaseConnectionReader)))

{
messageBody=//GETTHELOGINFOFROMADATABASE,HERE.
}
else
{
messageBody=formatReader.GetMessageBody(fileContents);
}
//...
}
//...
}
//...
}

Listing7:UsingThenewAPIsetinyourapplication
privatevoidSendFromFile_Click(objectsender,EventArgse)
{
try
{
Output.Text=string.Empty;

FileReaderServicefileReaderService=
newFileReaderService();

fileReaderService.RegisterFormatReader(
newXmlFormatReader());

fileReaderService.RegisterDefaultFormatReader(
newFlatFileFormatReader());

stringmessageBody=fileReaderService
.GetMessageBodyFromFile(_fileName);

EmailSenderemailSender=newEmailSender();
emailSender.SendEmail(messageBody);

Output.Text="Sentemailwithbody:"+
Environment.NewLine+messageBody;
}
catch(Exceptionex)
{
Output.Text=ex.ToString();
}
}


privatevoidSendFromDatabase_Click(objectsender,EventArgse)
{
try
{
Output.Text=string.Empty;

DatabaseReaderServicedatabaseReaderService=
newDatabaseReaderService();

stringmessageBody=databaseReaderService.GetMessageBody();

EmailSenderemailSender=newEmailSender();
emailSender.SendEmail(messageBody);

Output.Text="Sentemailwithbody:"+
Environment.NewLine+messageBody;
}
catch(Exceptionex)
{
Output.Text=ex.ToString();
}
}

Listing8:TheProcessingServiceclass
publicclassProcessingService
{
privatereadonlyIEmailSender_emailSender;
privatereadonlyIMessageInfoRetriever_messageInfoRetriever;

publicProcessingService(IEmailSenderemailSender,
IMessageInfoRetrievermessageInfoRetriever)
{
_emailSender=emailSender;
_messageInfoRetriever=messageInfoRetriever;
}

publicstringSendMessage()
{
stringmessageBody=_messageInfoRetriever.GetMessageBody();
_emailSender.SendEmail(messageBody);
return"SendEmailWithBody:"+messageBody;
}

Likewhatyoujustreadandwantmore?

You might also like