# Golang Large Function Refactoring ## Simple One ### FROM (❌) ``` if (isAlimSick) { var isAlimRequiredDoctor = isRequiredAssistanceFromHospital(isSick, alimPaitent); if(isAlimRequiredDoctor) { var findAllHospitals = GetHospitals(...); var suitableHospital = GetSuitableHospital(findAllHospitals, alimPaitent); return suitableHospital; } } ``` ### TO (✔) ``` if (isAlimNotSick) { return null; } // alim sick var isAlimRequiredDoctor = isRequiredAssistanceFromHospital(isSick, alimPaitent); if (!isAlimRequiredDoctor) { return null; } var findAllHospitals = GetHospitals(...); var suitableHospital = GetSuitableHospital(findAllHospitals, alimPaitent); return suitableHospital; ``` ## Comparing - [Version 1](https://pastebin.ubuntu.com/p/PyKrZknY5r/) - [Version 2](https://pastebin.ubuntu.com/p/vR6zhCnC5Z/) - [Version 3](https://pastebin.ubuntu.com/p/kZWPhzhJ5T/) ## Version 1 : Initial [Version 1](https://pastebin.ubuntu.com/p/PyKrZknY5r/) Current code is in level 4 nesting. Which is too complex in terms of unit testing. Our goal is to reduce that code branching or nesting to level 1, by following all the review guides from [Golang code review guides by Md. Alim Ul Karim](https://hackmd.io/@akarimevatix/golang-review-guides) ### Level 4 nesting indications ![Level 4 nesting indications](https://i.imgur.com/aEIBQtk.png) ```go= package main import ( "fmt" "strings" "github.com/jinzhu/copier" ) func makeFinalTimezone(timezoneNames []TimezoneName, wikiCsv []WikiTimezone) { var finalTimezone []FinalTimezone copier.Copy(&finalTimezone, &wikiCsv) for index, wiki := range wikiCsv { if wiki.TimezoneName == "-" { var foundInNamesByDatabaseName = searchInTimezoneNames(timezoneNames, wiki.DatabaseName) if foundInNamesByDatabaseName == "-" { fmt.Println("Not Found In Timezones for", wiki.DatabaseName) if strings.Contains(wiki.Notes, "Link to") { // Get value from Link To fmt.Println("Get Link to For wiki.DatabaseName", wiki.Notes) // split with Link To searchingFor := strings.TrimSpace(wiki.Notes[7:]) fmt.Println("Split searchingFor ::", searchingFor) var foundInNames = searchInTimezoneNames(timezoneNames, searchingFor) if foundInNames != "-" { finalTimezone[index].TimezoneName = foundInNames } else { foundInWiki := searchInWikiByLinkToNote(wikiCsv, searchingFor) finalTimezone[index].TimezoneName = foundInWiki } } } else { finalTimezone[index].TimezoneName = foundInNamesByDatabaseName } } } fmt.Printf("Field: %+v\n", finalTimezone) writeFinalCSV(finalTimezone) } ``` ## Version 2 : Improved [Version 2](https://pastebin.ubuntu.com/p/vR6zhCnC5Z/) ```go= package main import ( "fmt" "strings" "github.com/jinzhu/copier" "gitlab.com/evatix-go/localization/cmd/main/constants" ) type InvalidTimezoneInputParameters struct { hasInvalidTimezoneName bool timezoneNames *[]TimezoneName wikiCsv *[]WikiTimezone wiki *WikiTimezone finalTimezone *FinalTimezone finalTimezones *[]FinalTimezone index int } func makeFinalTimezone( timezoneNames *[]TimezoneName, wikiCsv *[]WikiTimezone, ) { var finalTimezones *[]FinalTimezone copier.Copy(&finalTimezones, wikiCsv) for index, wiki := range *wikiCsv { finalTimezone := (*finalTimezones)[index] invalidTimezoneInputParameters := InvalidTimezoneInputParameters{ hasInvalidTimezoneName: hasInvalidTimeZoneName(wiki.TimezoneName), timezoneNames: timezoneNames, wikiCsv: wikiCsv, wiki: &wiki, finalTimezone: &finalTimezone, finalTimezones: finalTimezones, index: index, } ProcessInvalidTimezoneName(&invalidTimezoneInputParameters) } fmt.Printf("Field: %+v\n", finalTimezones) writeFinalCSV(finalTimezones) } func ProcessInvalidTimezoneName( invalidTimezoneInputParameters *InvalidTimezoneInputParameters, ) { var foundInNamesByDatabaseName = searchInTimezoneNames( invalidTimezoneInputParameters.timezoneNames, invalidTimezoneInputParameters.wiki.DatabaseName) if hasValidTimeZoneName(foundInNamesByDatabaseName) { invalidTimezoneInputParameters.finalTimezone.TimezoneName = foundInNamesByDatabaseName } else { // invalid case , foundInNamesByDatabaseName == constants.InvalidEntryWithHyphen fmt.Println("Not Found In Timezones for", invalidTimezoneInputParameters.wiki.DatabaseName) populatedTimezone := GetPopulatedTimezoneNameInTimezone( invalidTimezoneInputParameters.timezoneNames, invalidTimezoneInputParameters.wikiCsv, invalidTimezoneInputParameters.wiki, invalidTimezoneInputParameters.finalTimezone) // here this step is redundant, just calling GetPopulatedTimezoneNameInTimezone // should be suffice, then it is doing some magic, // which requires future investigation on how it is set to the list again // any mistake in understanding could cause lots of hours. // As as result, to keep things simple, always get value and put the updated one // so that it make sense and also goes by the review process, not reassigning to the same variable again. finalTimezones := *invalidTimezoneInputParameters.finalTimezones finalTimezones[invalidTimezoneInputParameters.index] = *populatedTimezone } } // Finds Timezone name using the last part of // Links to keyword and then puts the right // timezone name in the FinalTimezone object and returns it. func GetPopulatedTimezoneNameInTimezone( timezoneNames *[]TimezoneName, wikiCsv *[]WikiTimezone, wiki *WikiTimezone, finalTimezone *FinalTimezone, ) *FinalTimezone { isNotContainsLinkTo := !strings.Contains(wiki.Notes, constants.LinksTo) if isNotContainsLinkTo { // the first rejection case return finalTimezone } // here we are expecting that constants.LinksTo exist // notes contains constants.LinksTo // Get value from Link To fmt.Println("Get Link to For wiki.DatabaseName", wiki.Notes) // split with Link To searchingFor := strings.TrimSpace(wiki.Notes[7:]) fmt.Println("Split searchingFor ::", searchingFor) foundInNames := searchInTimezoneNames(timezoneNames, searchingFor) hasTimezoneName := hasValidTimeZoneName(foundInNames) if hasTimezoneName { finalTimezone.TimezoneName = foundInNames return finalTimezone } foundInWiki := searchInWikiByLinkToNote(*wikiCsv, searchingFor) finalTimezone.TimezoneName = foundInWiki return finalTimezone } ``` ### Important Notes ```notes // here this step is redundant, just calling GetTimezoneNameFromLinksToReference // should be suffice, then it is doing some magic, // which requires future investigation on how it is set to the list again // any mistake in understanding could cause lots of hours. // As as result, to keep things simple, always get value and put the updated one // so that it make sense and also goes by the review process, not reassigning to the same variable again. ``` ## Version 3 : Final Improvements [Version 3](https://pastebin.ubuntu.com/p/kZWPhzhJ5T/) Use return value as best practice to apply all the review parameters. Here we have at most level 1 nesting. ```go= package main import ( "fmt" "strings" "github.com/jinzhu/copier" "gitlab.com/evatix-go/localization/cmd/main/constants" ) type InvalidTimezoneInputParameters struct { hasInvalidTimezoneName bool timezoneNames *[]TimezoneName wikiCsv *[]WikiTimezone wiki *WikiTimezone finalTimezones *[]FinalTimezone index int } func makeFinalTimezone( timezoneNames *[]TimezoneName, wikiCsv *[]WikiTimezone, ) { finalTimezones := getCopyFromWikiToFinalTimezones(wikiCsv) for index, wiki := range *wikiCsv { invalidTimezoneInputParameters := InvalidTimezoneInputParameters{ hasInvalidTimezoneName: hasInvalidTimeZoneName(wiki.TimezoneName), timezoneNames: timezoneNames, wikiCsv: wikiCsv, wiki: &wiki, finalTimezones: finalTimezones, index: index, } ProcessInvalidTimezoneName(&invalidTimezoneInputParameters) } fmt.Printf("Field: %+v\n", finalTimezones) writeFinalCSV(finalTimezones) } func ProcessInvalidTimezoneName( invalidTimezoneInputParameters *InvalidTimezoneInputParameters, ) { foundInNamesByDatabaseName := searchInTimezoneNames( invalidTimezoneInputParameters.timezoneNames, invalidTimezoneInputParameters.wiki.DatabaseName) index := invalidTimezoneInputParameters.index finalTimezones := *invalidTimezoneInputParameters.finalTimezones finalTimezone := finalTimezones[index] finalTimezone.TimezoneName = GetTimezoneFinalNameInCaseOfInvalid( invalidTimezoneInputParameters, foundInNamesByDatabaseName) // needs to be assigned back finalTimezones[index] = finalTimezone } func getCopyFromWikiToFinalTimezones( fromWikiCsv *[]WikiTimezone) *[]FinalTimezone { var toFinalTimezones []FinalTimezone wikiCopier := *fromWikiCsv // https://github.com/jinzhu/copier // it doesn't work with pointer to pointer directly. copier.Copy(&toFinalTimezones, &wikiCopier) return &toFinalTimezones } func GetTimezoneFinalNameInCaseOfInvalid( invalidTimezoneInputParameters *InvalidTimezoneInputParameters, foundInNamesByDatabaseName string, ) string { if hasValidTimeZoneName(foundInNamesByDatabaseName) { return foundInNamesByDatabaseName } // invalid case , foundInNamesByDatabaseName == constants.InvalidEntryWithHyphen fmt.Println("Not Found In Timezones for", invalidTimezoneInputParameters.wiki.DatabaseName) timeZoneFinalName := GetTimezoneNameFromLinksToReference( invalidTimezoneInputParameters.timezoneNames, invalidTimezoneInputParameters.wikiCsv, invalidTimezoneInputParameters.wiki) return timeZoneFinalName } // Finds Timezone name using the last part of // Links to keyword and then puts the right // timezone name in the FinalTimezone object and returns it. func GetTimezoneNameFromLinksToReference( timezoneNames *[]TimezoneName, wikiCsv *[]WikiTimezone, wiki *WikiTimezone, ) string { isNotContainsLinkTo := !strings.Contains(wiki.Notes, constants.LinksTo) if isNotContainsLinkTo { return constants.InvalidEntryWithHyphen } // here we are expecting that constants.LinksTo exist fmt.Println("Get Link to For wiki.DatabaseName", wiki.Notes) searchingFor := strings.TrimSpace(wiki.Notes[7:]) fmt.Println("Split searchingFor ::", searchingFor) foundInNames := searchInTimezoneNames(timezoneNames, searchingFor) hasTimezoneName := hasValidTimeZoneName(foundInNames) if hasTimezoneName { return foundInNames } foundInWiki := searchInWikiByLinkToNote(*wikiCsv, searchingFor) return foundInWiki } ```