# 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

```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
}
```