Skip to content

Commit

Permalink
add visitor
Browse files Browse the repository at this point in the history
  • Loading branch information
sioncojp committed Nov 10, 2021
1 parent 704d184 commit 72aa48e
Show file tree
Hide file tree
Showing 9 changed files with 160 additions and 0 deletions.
37 changes: 37 additions & 0 deletions visitor/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Visitor
アルゴリズムを操作対象のオブジェクトから分離できる動作設計パターン

複雑なオブジェクト構造のすべての要素(オブジェクトツリーなど)に対して操作を実行する必要がある場合

### メリット
- オープン/クローズド原則。クラスを変更せずに、異なるクラスのオブジェクトを操作できる新しい動作を導入できます。
- 単一責任の原則。同じ動作の複数のバージョンを同じクラスに移動できます。
- さまざまなオブジェクトを操作しているときに、いくつかの有用な情報を蓄積できます。オブジェクトツリーなどの複雑なオブジェクト構造をトラバースし、この構造の各オブジェクトにビジターを適用する場合に便利。

### デメリット
- クラスが要素階層に追加または要素階層から削除されるたびに、すべてのVisitorを更新する必要があります。
- 作業することになっている要素のプライベートフィールドとメソッドへの必要なアクセスを欠いている可能性があります。

### 他パターンとの関係性
- Commandより強力
- Compositeツリー全体に操作を実行できる
- Iteratorと一緒に使用して、複雑なデータ構造をトラバースし、その要素に対して何らかの操作を実行できる

### 例題
- 実際に構造体を変更せずに、構造体に動作を追加できる
- 次のlibをメンテナンスする場合
- 四角
- サークル
- 三角形
- 共通の形状インターフェースを実装している

- チームがgetAreaをshape構造体に追加するように要求
- これを解決するには3つ

1. getAreaメソッドをshapeインターフェイスに直接追加してから、各shape構造体に実装する
- コストが高い
- 誰かが別の動作を要求するたびに貴重なコードを壊す可能性が高い
2. 機能を要求するチームが自分で動作を実装できる
- プライベートコードに依存する可能性があるため、これが常に可能であるとは限らない
3. Visitorパターン
- visitorインターフェイスの具体的な実装を定義し、その具体的な実装に面積計算ロジックを記述するだけ
22 changes: 22 additions & 0 deletions visitor/areaCalculator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package visitor

import (
"fmt"
)

type areaCalculator struct {
area int
}

func (a *areaCalculator) visitForSquare(s *square) {
// Calculate area for square.
// Then assign in to the area instance variable.
fmt.Println("Calculating area for square")
}

func (a *areaCalculator) visitForCircle(s *circle) {
fmt.Println("Calculating area for circle")
}
func (a *areaCalculator) visitForRectangle(s *rectangle) {
fmt.Println("Calculating area for rectangle")
}
13 changes: 13 additions & 0 deletions visitor/circle.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package visitor

type circle struct {
radius int
}

func (c *circle) accept(v visitor) {
v.visitForCircle(c)
}

func (c *circle) getType() string {
return "Circle"
}
21 changes: 21 additions & 0 deletions visitor/middleCoordinates.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package visitor

import "fmt"

type middleCoordinates struct {
x int
y int
}

func (a *middleCoordinates) visitForSquare(s *square) {
// Calculate middle point coordinates for square.
// Then assign in to the x and y instance variable.
fmt.Println("Calculating middle point coordinates for square")
}

func (a *middleCoordinates) visitForCircle(c *circle) {
fmt.Println("Calculating middle point coordinates for circle")
}
func (a *middleCoordinates) visitForRectangle(t *rectangle) {
fmt.Println("Calculating middle point coordinates for rectangle")
}
14 changes: 14 additions & 0 deletions visitor/rectangle.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package visitor

type rectangle struct {
l int
b int
}

func (t *rectangle) accept(v visitor) {
v.visitForRectangle(t)
}

func (t *rectangle) getType() string {
return "rectangle"
}
6 changes: 6 additions & 0 deletions visitor/shape.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package visitor

type shape interface {
getType() string
accept(visitor)
}
13 changes: 13 additions & 0 deletions visitor/square.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package visitor

type square struct {
side int
}

func (s *square) accept(v visitor) {
v.visitForSquare(s)
}

func (s *square) getType() string {
return "Square"
}
27 changes: 27 additions & 0 deletions visitor/visitor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package visitor

import "fmt"

type visitor interface {
visitForSquare(*square)
visitForCircle(*circle)
visitForRectangle(*rectangle)
}

func main() {
square := &square{side: 2}
circle := &circle{radius: 3}
rectangle := &rectangle{l: 2, b: 3}

areaCalculator := &areaCalculator{}

square.accept(areaCalculator)
circle.accept(areaCalculator)
rectangle.accept(areaCalculator)

fmt.Println()
middleCoordinates := &middleCoordinates{}
square.accept(middleCoordinates)
circle.accept(middleCoordinates)
rectangle.accept(middleCoordinates)
}
7 changes: 7 additions & 0 deletions visitor/visitor_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package visitor

import "testing"

func TestVisitor(t *testing.T) {
main()
}

0 comments on commit 72aa48e

Please sign in to comment.