getArea()<\/strong>) to the Shape structs. <\/p>\n\n\n\nThere are many options to solve this problem<\/p>\n\n\n\n
First Option<\/strong><\/p>\n\n\n\nThe first option that comes to the mind is to add getArea()<\/strong> method in the shape<\/strong> interface and then each shape struct can implement the getArea() method. This seems trivial but there are some problems: <\/p>\n\n\n\nAs a maintainer of the library, you don’t want to alter a highly tested code of your library by adding additional behaviours.<\/li> There might be more request by the teams using your library for more behaviours such as getNumSides()<\/strong>, getMiddleCoordinates()<\/strong>. Then, in this case, you don’t want to keep modifying your library. But you do want other teams to extend your library without actual modification of the code much.<\/li><\/ul>\n\n\n\nSecond Option<\/strong><\/p>\n\n\n\nThe second option is that the team requesting the feature can write the logic for behaviour themselves. So based upon the shape struct type they like below code<\/p>\n\n\n\n
if shape.type == square {\n \/\/Calculate area for squre\n} elseif shape.type == circle {\n \/\/Calculate area of triangle \n} elseif shape.type == \"triangle\" {\n \/\/Calculate area of triangle\n} else {\n \/\/Raise error\n} <\/code><\/pre>\n\n\n\nAbove code is also problematic as you are not able to take the full advantage of interfaces and instead do an explicit type checking which is fragile. Second, getting the type at run time may have a performance impact or maybe even not possible in some languages.<\/p>\n\n\n\n
Third Option<\/strong><\/p>\n\n\n\nThe third option is to solve the above problem using the visitor pattern. We define a visitor interface like below<\/p>\n\n\n\n
type visitor interface {\n visitForSquare(square)\n visitForCircle(circle)\n visitForTriangle(triangle)\n}<\/code><\/pre>\n\n\n\nThe functions visitforSquare(square), visitForCircle(circle), visitForTriangle<\/strong>(triangle<\/strong>) allows us to add functionality to Square, Circle and Triangle respectively.<\/p>\n\n\n\nNow the question which comes to mind is why can’t we have a single method visit(shape)<\/strong> in the visitor interface. The reason we don’t have it because GO and also some other languages support method overloading. So a different method for each of the struct.<\/p>\n\n\n\nWe add an accept <\/strong>method to the shape interface with below signature and each of the shape struct needs to define this method.<\/p>\n\n\n\nfunc accept(v visitor)<\/code><\/pre>\n\n\n\nBut wait for a second, we just mentioned that we don’t want to modify our existing shape structs. But when using Visitor Pattern we do have to modify our shape structs but this modification will only be done once. In case adding any additional behaviour such as getNumSides()<\/strong>, getMiddleCoordinates() <\/strong>will use the same above accept(v visitor)<\/strong> function without any further change to the shape structs. Basically the shape structs just need to be modified once and all future request of additional behaviours will be handled using the same accept function. Let’s see how.<\/p>\n\n\n\nThe square struct will implement an accept method like below: <\/p>\n\n\n\n
func (obj *squre) accept(v visitor){\n v.visitForSquare(obj)\n}<\/code><\/pre>\n\n\n\nand similarly, circle and triangle will also define an accept function as above.<\/p>\n\n\n\n
Now the team requesting for the getArea()<\/strong> behaviour can simply define the concrete implementation of visitor interface and write the area calculation logic in that concrete implementation.<\/p>\n\n\n\nareaCalculator.go<\/strong><\/p>\n\n\n\ntype areaCalculator struct{\n area int\n}\n\nfunc (a *areaCalculator) visitForSquare(s *square){\n \/\/Calculate are for square\n}\nfunc (a *areaCalculator) visitForCircle(s *square){\n \/\/Calculate are for circle\n}\nfunc (a *areaCalculator) visitForTriangle(s *square){\n \/\/Calculate are for triangle\n}<\/code><\/pre>\n\n\n\nTo calculate the area of a square we first create an instance of the square they can simply call.<\/p>\n\n\n\n
sq := &square{}\nac := &areaCalculator{}\nsq.accept(ac)<\/code><\/pre>\n\n\n\nSimilarly, other team requesting for getMiddleCoordinates() <\/strong>behaviour can define another concrete implementation of the visitor interface similar to above.<\/p>\n\n\n\nmiddleCoordinates.go<\/strong><\/p>\n\n\n\ntype middleCoordinates struct {\n x int\n y int\n}\n\nfunc (a *middleCoordinates) visitForSquare(s *square) {\n \/\/Calculate middle point coordinates for square. After calculating the area assign in to the x and y instance variable.\n}\n\nfunc (a *middleCoordinates) visitForCircle(c *circle) {\n \/\/Calculate middle point coordinates for square. After calculating the area assign in to the x and y instance variable.\n}\n\nfunc (a *middleCoordinates) visitForTriangle(t *triangle) {\n \/\/Calculate middle point coordinates for square. After calculating the area assign in to the x and y instance variable.\n}<\/code><\/pre>\n\n\n\n<\/span>UML Diagram:<\/strong><\/span><\/h1>\n\n\n\n <\/figure>\n\n\n\nBelow is the corresponding mapping UML diagram with the practical example of shape struct and areaCalculator we gave above<\/p>\n\n\n\n <\/figure>\n\n\n\n<\/p>\n\n\n\n
<\/span>Mapping <\/strong><\/span><\/h1>\n\n\n\nThe below table represents the mapping from the UML diagram actors to actual implementation actors in “Example”<\/strong> below<\/p>\n\n\n\nelement<\/td> shape.go<\/td><\/tr> Concrete Element A<\/td> square.go<\/td><\/tr> Concrete Element B<\/td> circle.go<\/td><\/tr> Concrete Element C<\/td> rectangle.go<\/td><\/tr> Visitor<\/td> visitor.go<\/td><\/tr> Concrete Visitor 1<\/td> areaCalculator.go<\/td><\/tr> Concrete Visitor 2<\/td> middleCoordinates.go<\/td><\/tr> Client<\/td> main.go<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<\/p>\n\n\n\n
<\/span>Example<\/strong><\/span><\/h1>\n\n\n\nshape.go<\/strong><\/p>\n\n\n\npackage main\n\ntype shape interface {\n getType() string\n accept(visitor)\n}<\/code><\/pre>\n\n\n\nsquare.go<\/strong><\/p>\n\n\n\npackage main\n\ntype square struct {\n side int\n}\n\nfunc (s *square) accept(v visitor) {\n v.visitForSquare(s)\n}\n\nfunc (s *square) getType() string {\n return \"Square\"\n}<\/code><\/pre>\n\n\n\ncircle.go<\/strong><\/p>\n\n\n\npackage main\n\ntype circle struct {\n radius int\n}\n\nfunc (c *circle) accept(v visitor) {\n v.visitForCircle(c)\n}\n\nfunc (c *circle) getType() string {\n return \"Circle\"\n}<\/code><\/pre>\n\n\n\nrectangle.go<\/strong><\/p>\n\n\n\npackage main\n\ntype rectangle struct {\n l int\n b int\n}\n\nfunc (t *rectangle) accept(v visitor) {\n v.visitForrectangle(t)\n}\n\nfunc (t *rectangle) getType() string {\n return \"rectangle\"\n}<\/code><\/pre>\n\n\n\nvisitor.go<\/strong><\/p>\n\n\n\npackage main\n\ntype visitor interface {\n visitForSquare(*square)\n visitForCircle(*circle)\n visitForrectangle(*rectangle)\n}<\/code><\/pre>\n\n\n\nareaCalculator.go<\/strong><\/p>\n\n\n\npackage main\n\nimport (\n \"fmt\"\n)\n\ntype areaCalculator struct {\n area int\n}\n\nfunc (a *areaCalculator) visitForSquare(s *square) {\n \/\/Calculate area for square. After calculating the area assign in to the area instance variable\n fmt.Println(\"Calculating area for square\")\n}\n\nfunc (a *areaCalculator) visitForCircle(s *circle) {\n \/\/Calculate are for circle. After calculating the area assign in to the area instance variable\n fmt.Println(\"Calculating area for circle\")\n}\n\nfunc (a *areaCalculator) visitForrectangle(s *rectangle) {\n \/\/Calculate are for rectangle. After calculating the area assign in to the area instance variable\n fmt.Println(\"Calculating area for rectangle\")\n}<\/code><\/pre>\n\n\n\nmiddleCoordinates.go<\/strong><\/p>\n\n\n\npackage main\n\nimport \"fmt\"\n\ntype middleCoordinates struct {\n x int\n y int\n}\n\nfunc (a *middleCoordinates) visitForSquare(s *square) {\n \/\/Calculate middle point coordinates for square. After calculating the area assign in to the x and y instance variable.\n fmt.Println(\"Calculating middle point coordinates for square\")\n}\n\nfunc (a *middleCoordinates) visitForCircle(c *circle) {\n \/\/Calculate middle point coordinates for square. After calculating the area assign in to the x and y instance variable.\n fmt.Println(\"Calculating middle point coordinates for circle\")\n}\n\nfunc (a *middleCoordinates) visitForrectangle(t *rectangle) {\n \/\/Calculate middle point coordinates for square. After calculating the area assign in to the x and y instance variable.\n fmt.Println(\"Calculating middle point coordinates for rectangle\")\n}<\/code><\/pre>\n\n\n\nmain.go<\/strong><\/p>\n\n\n\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n square := &square{side: 2}\n circle := &circle{radius: 3}\n rectangle := &rectangle{l: 2, b: 3}\n \n areaCalculator := &areaCalculator{}\n square.accept(areaCalculator)\n circle.accept(areaCalculator)\n rectangle.accept(areaCalculator)\n \n fmt.Println()\n middleCoordinates := &middleCoordinates{}\n square.accept(middleCoordinates)\n circle.accept(middleCoordinates)\n rectangle.accept(middleCoordinates)\n}<\/code><\/pre>\n\n\n\nOutput:<\/strong><\/p>\n\n\n\nCalculating area for square\nCalculating area for circle\nCalculating area for rectangle\n\nCalculating middle point coordinates for square\nCalculating middle point coordinates for circle\nCalculating middle point coordinates for rectangle<\/code><\/pre>\n\n\n\n<\/p>\n\n\n\n
<\/span>Full Working Code:<\/strong><\/span><\/h1>\n\n\n\npackage main\n\nimport \"fmt\"\n\ntype shape interface {\n getType() string\n accept(visitor)\n}\n\ntype square struct {\n side int\n}\n\nfunc (s *square) accept(v visitor) {\n v.visitForSquare(s)\n}\n\nfunc (s *square) getType() string {\n return \"Square\"\n}\n\ntype circle struct {\n radius int\n}\n\nfunc (c *circle) accept(v visitor) {\n v.visitForCircle(c)\n}\n\nfunc (c *circle) getType() string {\n return \"Circle\"\n}\n\ntype rectangle struct {\n l int\n b int\n}\n\nfunc (t *rectangle) accept(v visitor) {\n v.visitForrectangle(t)\n}\n\nfunc (t *rectangle) getType() string {\n return \"rectangle\"\n}\n\ntype visitor interface {\n visitForSquare(*square)\n visitForCircle(*circle)\n visitForrectangle(*rectangle)\n}\n\ntype areaCalculator struct {\n area int\n}\n\nfunc (a *areaCalculator) visitForSquare(s *square) {\n \/\/Calculate area for square. After calculating the area assign in to the area instance variable\n fmt.Println(\"Calculating area for square\")\n}\n\nfunc (a *areaCalculator) visitForCircle(s *circle) {\n \/\/Calculate are for circle. After calculating the area assign in to the area instance variable\n fmt.Println(\"Calculating area for circle\")\n}\n\nfunc (a *areaCalculator) visitForrectangle(s *rectangle) {\n \/\/Calculate are for rectangle. After calculating the area assign in to the area instance variable\n fmt.Println(\"Calculating area for rectangle\")\n}\n\ntype middleCoordinates struct {\n x int\n y int\n}\n\nfunc (a *middleCoordinates) visitForSquare(s *square) {\n \/\/Calculate middle point coordinates for square. After calculating the area assign in to the x and y instance variable.\n fmt.Println(\"Calculating middle point coordinates for square\")\n}\n\nfunc (a *middleCoordinates) visitForCircle(c *circle) {\n \/\/Calculate middle point coordinates for square. After calculating the area assign in to the x and y instance variable.\n fmt.Println(\"Calculating middle point coordinates for circle\")\n}\n\nfunc (a *middleCoordinates) visitForrectangle(t *rectangle) {\n \/\/Calculate middle point coordinates for square. After calculating the area assign in to the x and y instance variable.\n fmt.Println(\"Calculating middle point coordinates for rectangle\")\n}\n\nfunc main() {\n square := &square{side: 2}\n circle := &circle{radius: 3}\n rectangle := &rectangle{l: 2, b: 3}\n areaCalculator := &areaCalculator{}\n square.accept(areaCalculator)\n circle.accept(areaCalculator)\n rectangle.accept(areaCalculator)\n \n fmt.Println()\n middleCoordinates := &middleCoordinates{}\n square.accept(middleCoordinates)\n circle.accept(middleCoordinates)\n rectangle.accept(middleCoordinates)\n}<\/code><\/pre>\n\n\n\nOutput:<\/strong><\/p>\n\n\n\nCalculating area for square\nCalculating area for circle\nCalculating area for rectangle\n\nCalculating middle point coordinates for square\nCalculating middle point coordinates for circle\nCalculating middle point coordinates for rectangle<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"Note: Interested in understanding how all other design patterns can be implemented in GO. Please see this full reference – All Design Patterns in Go (Golang) Table of Contents Introduction:UML Diagram:Mapping ExampleFull Working…<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"spay_email":"","footnotes":"","jetpack_publicize_message":"","jetpack_is_tweetstorm":false},"categories":[1],"tags":[26,3,94],"class_list":["post-730","post","type-post","status-publish","format-standard","hentry","category-tech","tag-design","tag-go","tag-visitor"],"yoast_head":"\n
Visitor Design Pattern in Go(Golang) - Welcome To Golang By Example<\/title>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\t \n\t \n\t \n