Design Patterns in Go - Medium

文章推薦指數: 80 %
投票人數:10人

I use this pattern in Go very often. And I think Golang is so suitable for this pattern. In this example, we will create a package, which could ... DecemberCallforSubmissionsSubmitRecentUpdatesNov2021Top10StoriesGetfullMediumaccessDesignPatternsinGoBoraKaşmerFollowFeb26·13minreadBenjaminLee.(FushimiInari-TaishaShrine)TodaywewilltalkabouthowtousedesignpatternsinGo.Isitreallynecessary?IsGosuitablefordesignpatterns?Let’simplementsomeDesignPatterns.“Adesignthatdoesn’ttakechangeintoaccountrisksmajorredesigninthefuture.”―ErichGamma,DesignPatterns:ElementsofReusableObject-OrientedSoftwareStrategyDesignPattern:TheStrategypatternbasicallyallowsanobjecttoselectandexecuteatruntimewithoutknowinghowtoimplementthebusinesslogicitwants,insteadofusingdifferenttypesofalgorithmsthatcanbeusedtoperformeachoperation.IusethispatterninGoveryoften.AndIthinkGolangissosuitableforthispattern.Inthisexample,wewillcreateapackage,whichcouldconnectdifferenttypesofDB.Andinthefuture,wecanaddanewtypeofDBConnectioneasily.Asseenbelow,Icreated“IDBConnection”interfaceandcommon“Connect()”methodforallinheritedstructs.IDBConnectionInterfaceAsseenbelow,IcreatedtheSqlConnectionstruct,andforimplementingtoIDBconnectinterface,Iwrotethe“Connection()”function.SqlConnectionAsseenbelow,IdidthesamethingforOracleConnection.OracleConnectionAsseenbelow,theDBConnectionstructisusedforcallingConnect()methodofthestruct,whichisimplementedfromtheIDBConnectioninterface.Asseenbelow,wecreatedSQLandOracleConnectionstruct.Bothofthemsetthe“db”propertyoftheDBConnectionstruct.AndDBConnectionstructcallsamemethod“DBConnect()”forOracleandSQLDBConnection.Examplemain()methodThisistheresultoftheabovecode:ResultStrategyDesignPatternFullCode:main.go:packagemainimport("fmt"typeIDBconnectioninterface{Connect()})typeSqlConnectionstruct{connectionStringstring}func(conSqlConnection)Connect(){fmt.Println(("Sql"+con.connectionString))}typeOracleConnectionstruct{connectionStringstring}func(conOracleConnection)Connect(){fmt.Println("Oracle"+con.connectionString)}typeDBConnectionstruct{dbIDBconnection}func(conDBConnection)DBConnect(){con.db.Connect()}funcmain(){sqlConnection:=SqlConnection{connectionString:"Connectionisconnected"}con:=DBConnection{db:sqlConnection}con.DBConnect()orcConnection:=OracleConnection{connectionString:"Connectionisconnected"}con2:=DBConnection{db:orcConnection}con2.DBConnect()}“Everyonethinksofchangingtheworld,butnoonethinksofchanginghimself.”―LeoTolstoyObserverDesignPattern:Theprincipleofthepatternistoconveythechangesthatmayoccurintheobjectsconnectedbyone-to-manyrelationshipstotheothers.Themainpurposeistonotifytherelationalgroupsaboutthechange.DesignPatterns/Observer:ThisistheobserverinterfaceandhastwoUpdate()andGetID()functions.customer/observer.go:packagecustomertypeObserverinterface{Update(string)GetID()string}DesignPatterns/Customer:WewillimplementallfunctionsoftheObserverinterfaceintheCustomerstruct.“Id”:propertyisthenameofthecustomer.Update()”:functiontakesproductnameasaparameterandnotifiesthecustomer’schangedproductwithemail.“GetID()”:Functionreturnstothecustomer’sId,inotherword’scustomer’semail.customer/customer.go:packagecustomerimport"fmt"typeCustomerstruct{Idstring}func(c*Customer)Update(productNamestring){fmt.Printf("%smüşterisine%sürünüiçinemailgönderiliyor.\n",c.Id,productName)}func(c*Customer)GetID()string{returnc.Id}DesignPatterns/ProductManage:Thisistheproductinterface.Wehavetoimplementallthebelowfunctionsintotheproductstruct.customer/productManage.go:packagecustomertypeProductManageinterface{Register(ObserverObserver)RegisterList(Observer[]Observer)Unregister(ObserverObserver)[]ObserverNotifyAll()}DesignPatterns/Product:Thisistheproductstruct.Allcustomersmonitorthisstruct.productstructobserverList:Listofnotifiedcustomers.name:ProductnameinStock:Thisisthestateofproductstockinfo.funcNewProduct(_namestring)*product{NewProduct():Thisfunctionreturnsanewproductwithanamebyreferance.”return&product{name:_name}”.ThisisthekindofconstructoroftheProduct.func(p*product)NotifyAll(){NotifyAll():ThisfunctionnotifiesallrelatedclientsintheobserverlistwhentheproductUpdate.We’llwalkthroughtheobserverListstepbystepandcalltheCustomerUpdate()functionin“for_,observer:=rangep.oberverList”Register():Wewilladdanewcustomertotheproduct’sobserverList.RegisterList():Wewilladdmorethanonecustomertotheproduct’sobserverList.RemoveFromList():WewillremovethespecificcustomerfromthecustomerListbyitsId.“ifremoveObserver.GetID()==observer.GetID()”ifwewillfindthecustomerfromthelistbyitsId.“returnappend(observerList[:index],observerList[index+1:]…)”WewillslicetheobserverListfrombeginningtofindingCustomerIndexandappendbeginningfromonemorefindingCustomerIndextotheend.SowewillremovetheselectedcustomerfromthelistbyitsId.RemoveFromListUnregister():WewillcallRemoveFromList()functioninthisfunctionandremovetheselectedCustomerfromtheObserverList.customer/product.go:packagecustomerimport("fmt""strings")typeproductstruct{oberverList[]ObservernamestringinStockbool}funcNewProduct(_namestring)*product{return&product{name:_name}}func(p*product)NotifyAll(){for_,observer:=rangep.oberverList{observer.Update(p.name)}}func(p*product)UpdateAvalabilitiy(){fmt.Printf("Product%sartıkstoklardadır\n",p.name)fmt.Printf(strings.Repeat("-",100))fmt.Printf("\n")p.inStock=truep.NotifyAll()}func(p*product)Register(oObserver){p.oberverList=append(p.oberverList,o)}func(p*product)RegisterList(olist[]Observer){for_,o:=rangeolist{p.oberverList=append(p.oberverList,o)}}funcRemoveFromList(observerList[]Observer,removeObserverObserver)[]Observer{forindex,observer:=rangeobserverList{ifremoveObserver.GetID()==observer.GetID(){returnappend(observerList[:index],observerList[index+1:]...)}}returnobserverList}func(p*product)Unregister(oObserver)[]Observer{p.oberverList=RemoveFromList(p.oberverList,o)returnp.oberverList}DesignPatterns/main:Thisthetestoftheobserverfunction.“NewProduct()”functioncalledfromcustomerstructandreturnnewPS5product.Weaddedtwocustomerstothisproduct’sobserverListbycalling“RegisterList()”function.FinallywecalledUpdateAvalabilitty()product’sfunction.AndNotifyAll()functioncalledinthisfunction.AndthelaststateofthePS5productnotifiedtoallcustomers,whichisintheobserverListofproducts.main.go:funcmain(){ps5:=cus.NewProduct("SonyPlaystation5")observer1:=&cus.Customer{Id:"[email protected]"}observer2:=&cus.Customer{Id:"[email protected]"}ps5.RegisterList([]cus.Observer{observer1,observer2})ps5.UpdateAvalabilitiy()}Thisistheresultoftheabovecode:PrototypeDesignPattern:Atthetimewedevelopsoftware,theremaybeobjectsthatarecostly.Costmeansruntimeandmemoryusage.Sotheseobjects’runtimeislongandkeepsalargeplaceinthememory.Aprototypetemplatehasbeenconsideredinordertousetheseobjectsmoreeffectivelyandefficiently.Thistemplateaimstoavoidthisproblembycloninganexistingobject.Therefore,wedonotgointothetroubleofconstantlycreatingnewobjects.Inotherwords,anobjectthatisthoughttobeusedlateriswantedtobeaprototypeandotherstobenefitfromit.Theprototypeobjectiscopied,andnowitisprocessedonthiscopy.Thus,theoriginalobjectisalsopreserved.Inthisexample,wewillcreatedifferenttypesoffightersforavideogame.Wecreateafighterinterface.Wewilluse“FighterType”asanEnum.Wekeepfightertypeonthisvariable.“Fighter”isthecoreInterface.WewillcreateallfighterstructsfromthisFighterinterface.Ithasthreefunctions:“GetId()”:WewillgettheuniqueIdoftheFighterstruct.“Fight()”:WewillprintFightersHit-Pointontheconsole.“Clone()”:WewillgetadeepcopyoftheFighterstructwiththisfunction.fighter/Fighter.go(1):packagefighterimport"fmt"typeFighterTypeintconst(SoldierTypeFighterType=1ElfType=2DwarfType=3)typeFighterinterface{GetId()FighterTypeFight()Clone()Fighter}Soldier:WewillcreateanewcharacterfromtheFighterinterface.Ithasfourproperties.Id:UniqueFighterTypeEnumId.GunHit:Fighters’Hit-Point.Life:It’sSoldier’slifepower.Mana:ItisSoldier’sstamina.“NewSoldier()”:Wecallthisfunctionfromthe“Clone()”function.WewillcreateanewSoldierstructbyusingthisfunction.“SolderType,gunHit,life,andmana”areparametersofthisfunction.ThistheconstructorofSoldier.“GetId()”:ThisfunctionreturnsusFighterTypeof“Soldier”struct.Actually,itis1.“Clone()”:ThisfunctionisusedforgettingDeepCopyof“Soldier”Struct,whichisinheritedfromtheFighterinterface.“Fight()”:ThisfunctionisshowingtheHitPointoftheSoldierStruct.fighter/Fighter.go(2):typeSoldierstruct{IdFighterTypeGunHitintLifeintManaint}funcNewSoldier(gunHit,life,manaint)Soldier{soldier:=Soldier{SoldierType,gunHit,life,mana}returnsoldier}func(sSoldier)GetId()FighterType{returns.Id}func(sSoldier)Clone()Fighter{returnNewSoldier(s.GunHit,s.Life,s.Mana)}func(sSoldier)Fight(){fmt.Println("SoldierGunHit-Points:",s.GunHit)}Elf:WewillcreateanewcharacterfromtheFighterinterfacelikeSoldierstruct.Ithasfourpropertiestoo.Id:UniqueFighterTypeEnumId.Itis2ArrowHit:Elf’sHit-Point.Life:It’sElf’slifepower.Mana:ItisElf’sstamina.“NewElf(),GetId(),Clone()andFight()”arethesamefunctionslikeSoldierstruct.fighter/Elf.go:packagefighterimport"fmt"typeElfstruct{IdFighterTypeArrowHitintLifeintManaint}funcNewElf(arrowHit,life,manaint)Elf{returnElf{ElfType,arrowHit,life,mana}}func(eElf)GetId()FighterType{returne.Id}func(eElf)Clone()Fighter{returnNewElf(e.ArrowHit,e.Life,e.Mana)}func(eElf)Fight(){fmt.Println("ElfArrowHit-Point:",e.ArrowHit)}Dwarf:WewillcreateanewcharacterfromtheFighterinterfacelikeSoldierstruct.AllthefunctionsandPropertiesarethesameasSoldierandElfStructs.fighter/Dwarf.go:packagefighterimport"fmt"typeDwarfstruct{IdFighterTypeAxePointintLifeintManaint}funcNewDwarf(arrowHit,life,manaint)Dwarf{returnDwarf{DwarfType,arrowHit,life,mana}}func(eDwarf)GetId()FighterType{returne.Id}func(eDwarf)Clone()Fighter{returnNewDwarf(e.AxePoint,e.Life,e.Mana)}func(eDwarf)Fight(){fmt.Println("DwarfAxeHit-Point:",e.AxePoint)}DesignPatterns/main:Firstly,wewillcreateacoupleofprototypeFightersforthemodelcharacters.Afterall,othercharacterswillClone()fromthesecharacters.SoRuntimeandMemoryUsagewillbelessbyusingthisPrototypePattern.dataHistoryList:This“[int]Fighter”isHashTable.KeyintegerandvalueFighterinterface.SowecanputSoldier,Elf,andDwarfcharactersareinit.WeputsoldieranddwarfFightersinthisdataHistoryList.funcloadCache():Main.go(1):vardataHistoryListmap[int]fig.FighterfuncloadCache(){soldier:=fig.NewSoldier(30,20,5)dwarf:=fig.NewDwarf(50,40,15)dataHistoryList=make(map[int]fig.Fighter)dataHistoryList[int(soldier.GetId())]=soldierdataHistoryList[int(dwarf.GetId())]=dwarf}funcmain():WewillcallloadCache()forfillingthedataHistoryList.“fighter:=dataHistoryList[int(fig.SoldierType)]”:WetrytogetSoldierfromdataHistoryList“f,ok:=fighter.(fig.Soldier)”:WetrytocastfighterInterfacetoSoldierstruct.“ifok{newFighter:=f.Clone().(fig.Soldier)”:Ifitisok,wewillClone()itfromtheSoldierstruct.AndwewillchangetheGunHitpropertyto45.Main.go(2):funcmain(){loadCache()fighter:=dataHistoryList[int(fig.SoldierType)]f,ok:=fighter.(fig.Soldier)ifok{newFighter:=f.Clone().(fig.Soldier)newFighter.GunHit=45newFighter.Fight()}WetrytogetElffromthedataHistoryList.IfthereisanElfstructinthislist,wecanClone()it,butifwedon’t,wewillcreateanewElfwiththe“NewElf()”function.AndaddittothedataHistoryList.Andfinally,wewillcallFight()functionofElfstruct.Main.go(3):SecondElffighteriscertainlyinthedataHistory,sowefindandClone()it.Andsetthenew“ArrowHit”propertyto35.Andfinally,callFight()functionofsecondElfstruct.Main.go(4):WewillcreateadwarfFighterasthelastfightercharacter.WetrytogetdwarffromthedataHistoryList.Ifthereisadwarfstructinthislist,wecanClone()it,butifwedon’t,wewillcreateanewdwarfwiththe“NewDwarf()”function.AndaddittothedataHistoryList.Andfinally,wewillcallFight()functionoftheDwarfstruct.Butifyouremember,weaddeddwarffighterinloadCache()functionbefore.SowewillfinddwarfFighterandClone()it.Main.go(5):main.go(Full):funcmain(){loadCache()fighter:=dataHistoryList[int(fig.SoldierType)]f,ok:=fighter.(fig.Soldier)ifok{newFighter:=f.Clone().(fig.Soldier)newFighter.GunHit=45newFighter.Fight()}elf:=dataHistoryList[int(fig.ElfType)]e,ok:=elf.(fig.Elf)ifok{newElf:=e.Clone()newElf.Fight()}else{elf:=fig.NewElf(15,30,3)dataHistoryList[int(elf.GetId())]=elfelf.Fight()}elf2:=dataHistoryList[int(fig.ElfType)]e2,ok:=elf2.(fig.Elf)ifok{newElf2:=e2.Clone().(fig.Elf)newElf2.ArrowHit=35newElf2.Fight()}dwarf:=dataHistoryList[int(fig.DwarfType)]d,ok:=dwarf.(fig.Dwarf)ifok{newDwarf:=d.Clone()newDwarf.Fight()}else{dwarf:=fig.NewDwarf(50,40,15)dataHistoryList[int(fig.DwarfType)]=dwarfdwarf.Fight()}}Thisistheresultoftheabovecode:“Lifeisnotreversiblelikevideogames,sothinktwicedoonce.”―BoraKaşmerMementoDesignPattern:Essentially,itisapatterndesignedtokeepthepreviousstate(s)ofanobjectandretrieveitwhendesired.Wecanalsothinkofobjectsasgainingtheabilitytoundoorredotheirinternalstate(InitialState).Inthismold,thereisacopyoftheobjectwhosestatusistobepreserved,exactlyoratleastkeepingtheareas(features)thataredesiredtobekept(Memento).Forthisexample,asseenbelow,wewillcreatemotorcyclesalesdata.WewillcreateMotorTypeasanEnum,andwewillkeeptheStateofServiceDatastructureforundo()andredo()functions.motorcycle/motorcycle.go(1):Asseenbelow,wewillcreateareferenceintcounterforkeepingtheindexofServiceDataintheHashTable(“varcounter*int”).OurHashTableisdataHistoryList.IntegerkeyandServiceDatavalue(“vardataHistoryListmap[int]ServiceData”).“UpdateDataChange()”functionisaddednewstateof“ServiceData”tothe“dataHistoryList”withcounterindex.IfdataHistoryListisnillwewillcreatenewonewith“make()”function.ForeveryServiceData,wewillincreasethecounter.AndsetthiscountertothedataHistoryListastheindex.Withthis,wekeepallstatesofServiceDatainthisdataHistoyListhashtable.Solater,wecanmovebackandforwardonthisdataHistoryListbychangingthiscounterindex.motorcycle/motorcycle.go(2):Asseenbelow,wecanreturnthepreviousstateofServiceDataondataHistoryListbyusing“Undo()”function.Onlywehavetosubtractonefromthecounterindex,andifitisbiggerthanzero,taketheServiceDatafromdataHistoryListbyusingthiscounterindex.ImageURL:y7eeund41gfpzdlbdvvi.gifWecanreturnthenextstateofServiceDataondataHistoryListbyusingthe“Redo()”function.Wehavetoincreaseonefromthecounterindex.IfitisnotbiggerthanthelengthofthedatahistoryHashTable,takesthenextServiceDatafromdataHistoryListbyusingthiscounterindex.motorcycle/motorcycle.go(3):motorcycle/motorcycle(Full).go:packagemotorcycleimport"time"typeMotorTypeintconst(KawasakiMotorType=iotaHondaSuzukiDucati)typeServiceDatastruct{IdintPersonalNamestringModelMotorTypePricefloat32Datetime.Time}varcounter*intvardataHistoryListmap[int]ServiceDatafuncUpdateDataChange(model*ServiceData){ifdataHistoryList==nil{dataHistoryList=make(map[int]ServiceData)counter=new(int)}*counter++model.Id=*counterdataHistoryList[*counter]=*model}funcUndo()ServiceData{index:=*counter-1ifindex>=1{*counter=indexreturndataHistoryList[index]}returndataHistoryList[*counter]}funcRedo()ServiceData{index:=*counter+1ifindex<=len(dataHistoryList){*counter=indexreturndataHistoryList[*counter]}returndataHistoryList[*counter]}main():Asseenbelow,wewillcreatethreeServiceDatastatesandaddthemtothe“dataHistoryList.”Andlater,whenwecallthe“Undo()”function,wewillgetthepreviousstateofServiceData.Actually,wewillmovefrom“MartinFowler”to=>“KentBack.”Whenwecallthe“Undo()”functionasecondtime,wewillgetthepreviousstateofServiceData,sowewillmovefrom“KentBack”to=>“BoraKasmer.”Whenwecallthe“Redo()”function,wewillmovetothenextstateofServiceData.Actually,wewillmovefrom“BoraKasmer”to=>“KentBack.”Whenwecallthe“Redo()”functionasecondtime,wewillmovetothenextstateofServiceData.Actually,wewillmovefrom“KentBack”to=>“MartinFowler.”.Sowewillcomethecurrentposition.SowiththisMementoDesignPattern,wecanmovenextandpreviousstateofServiceDatabykeepingallchangesinaHashTable.main.go:funcmain(){mainData:=motor.ServiceData{PersonalName:"BoraKasmer",Model:motor.Honda,Price:105000,Date:time.Now()}motor.UpdateDataChange(&mainData)fmt.Println(mainData)mainData=motor.ServiceData{PersonalName:"KentBack",Model:motor.Kawasaki,Price:170000,Date:time.Now()}motor.UpdateDataChange(&mainData)fmt.Println(mainData)mainData=motor.ServiceData{PersonalName:"MartinFowler",Model:motor.Ducati,Price:330000,Date:time.Now()}motor.UpdateDataChange(&mainData)fmt.Println(mainData)mainData=motor.Undo()fmt.Println("1GeriGit:",mainData)fmt.Println(strings.Repeat("-",100))mainData=motor.Undo()fmt.Println("1GeriGit:",mainData)fmt.Println(strings.Repeat("-",100))mainData=motor.Redo()fmt.Println("1İleriGit:",mainData)fmt.Println(strings.Repeat("-",100))mainData=motor.Redo()fmt.Println("1İleriGit:",mainData)}Thisistheresultoftheabovecode:“Thenumberonebenefitofinformationtechnologyisthatitempowerspeopletodowhattheywanttodo.Itletspeoplebecreative.Itletspeoplebeproductive.Itletspeoplelearnthingstheydidn’tthinktheycouldlearnbefore,andsoinasenseitisallaboutpotential.”―SteveBallmerConclusion:Inthisarticle,wetriedtounderstandhowtousesomeDesignPatternsonGoLang.GoLang’smainpurposeissimplicityandspeed.ButifwecanusetheinterfaceonGo,wecanbenefitfromsomeDesignPatterns’blessings.Wedon’thavetoresolveaproblemagainthathasbeensolvedbefore.SousingDesignPatternsinGoisuptoyou.ButifIwereyou,IgaveachancetoDesignPatternsinGo.IhopethisarticlehelpsyoutounderstandsomeDesignPatternsandusingtheminGo.“Ifyouhavereadsofar,firstofall,thankyouforyourpatienceandsupport.Iwelcomeallofyoutomyblogformore!”SourceCode:https://github.com/borakasmer/GoDesignPatternsSource:https://refactoring.guru/design-patterns/gohttps://golangbyexample.com/all-design-patterns-golang/https://golang.org/doc/TheStartupGetsmarteratbuildingyourthing.JoinTheStartup’s+747Kfollowers.4481DesignPatternsStrategyObservationPrototypeGo448 claps4481TheStartupGetsmarteratbuildingyourthing.FollowtojoinTheStartup’s+8millionmonthlyreaders&+747Kfollowers.WrittenbyBoraKaşmerFollowIhavebeencodingsince1993.Iamcomputerandcivilengineer.MicrosoftMVP.SeniorSoftwareArchitect.Ridemotorcycle.Gamer.Havetwodaughters.TheStartupGetsmarteratbuildingyourthing.FollowtojoinTheStartup’s+8millionmonthlyreaders&+747Kfollowers.MoreFromMediumWrite/ReadDatainto/fromExcelfileusingJava|CodeFactoryCodeFactoryCloudbot101 — CustomCommandsandVariables(PartOne)EthanMayinStreamlabsBlogConnectingtoCloudSQLwithCloudFunctionsusingCLISagadevanKounderIntegratingTwiliointoGravioforautomatedSMSnotificationsMaxSpringBoot — @ConfigurationProperties|CodeFactoryCodeFactoryLaunchingECDSAnodeonHOPRPC(AVADOi2)withinterfaceWeb3.0ToltekIfyouhaven‘theardofdescriptors,youdon‘tknowPythonFlorianRiegerLoadTestingFundamentalsJasonPhilip



請為這篇文章評分?