Vous êtes sur la page 1sur 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("me@myserver.com");

MailAddressto=newMailAddress("your.cto@yourcompany.com");

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("me@myserver.com");

MailAddressto=newMailAddress("error.logs@yourcompany.com");

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?