こんにちは、KOUKIです。
この記事では、デザインパターンの一つであるChain of Responsibilityパターンについて紹介します。
ちなみに、このパターンのMethod Chainについては、活用方法が思い浮かばなかったので、シチュエーションは書いてないです^^;
ただ、頭の体操にはなります!
<目次>
デザインパターンまとめ
Chain of Responsibilityパターンの適用
chain は「鎖」、responsibility は「責任」を意味します。故に、Chain of Responsibility パターンとは、「責任」を負ったものが、「鎖」状につながれた状態をイメージさせるパターンです。
Method Chainは、繋がれたインスタンスのメソッドを再起的に呼び出すことを可能にします。
と、言葉で説明してもわかりにくいと思うので、プログラムを実装しながら理解していきましょう^^
ロガーの定義
ロガーを例にサンプルコードを実装していきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
package main import "fmt" type Logger struct { Prefix string Level string Message string } func NewLogger(name, level, message string) *Logger { return &Logger{ Prefix: name, Level: level, Message: message, } } func (l *Logger) String() { fmt.Println(fmt.Sprintf( "%s: %s: %s", l.Prefix, l.Level, l.Message, )) } func main() { logger := NewLogger( "Test", "Normal", "Set Up Logger") logger.String() } |
1 2 |
$ go run main.go Test: Normal: Set Up Logger |
このお手製のロガーに、Method Chainで機能を追加していきます。
インターフェース
インターフェースを定義します。ここではまだ意味がわからなくても問題ありません。
1 2 3 4 |
type Modifier interface { Add(m Modifier, msg string) Handle() } |
つなぎ構造体
メソッドを繋げるための繋ぎ構造体を作成します。
1 2 3 4 5 6 7 8 |
type LoggerModifer struct { logger *Logger next Modifier } func NewLoggerModifer(logger *Logger) *LoggerModifer { return &LoggerModifer{logger: logger} } |
つなぎ構造体のAddメソッド
インターフェースを実装するために、Addメソッドを実装します。
1 2 3 4 5 6 7 8 |
func (lm *LoggerModifer) Add(m Modifier, msg string) { lm.logger.Message = msg if lm.next != nil { lm.next.Add(m, msg) } else { lm.next = m } } |
ここは、大切なポイントです。
Addは、オブジェクトを繋げるためのメソッドになります。
nextパラメータが空の場合は、パラメータとして渡した値(m)を格納します。このmは、Modifierインターフェースを満たすインスタンスです。
また、nextパラメータが空でない場合は、mを引数にAddメソッドを再帰的に呼び出して、同じ処理を繰り返します。
こうすることで、インスタンスを繋げているわけですね。
1 2 3 4 |
インスタンス1 L next - インスタンス2 L next - インスタンス3 L next - インスタンス4 |
また、nextの型はModifierインターフェース型であり、AddとHandlerをメソッドとして実装した構造体のインスタンスを渡すことができます。
豆知識ですが、他の構造体(SampleModifier)のパラメータにインターフェースを実装した構造体(LoggerModifier)を指定し、インスタンス化したものもnextに渡すことができます。
1 2 3 4 5 6 |
type LoggerModifer struct { <<<< Add/Handleを実装しているので渡せるnextに渡せる } type SampleModifier struct { <<<< これもnextに渡せる LoggerModifer <<<<< パラメータとして持っているから } |
つなぎ構造体のHandleメソッド
インターフェースを実装するために、Handleメソッドを実装します。
1 2 3 4 5 |
func (lm *LoggerModifer) Handle() { if lm.next != nil { lm.next.Handle() } } |
ここも大切なポイントです。
Handleメソッドは、連なった処理を実行するためのメソッドになります。
ロガーレベル構造体
ロガーレベルごとに構造体を作成しましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
type DebugModifier struct { LoggerModifer } func NewDebugModifier(l *Logger) *DebugModifier { return &DebugModifier{ LoggerModifer{ logger: l, }, } } type InfoModifier struct { LoggerModifer } func NewInfoModifier(l *Logger) *InfoModifier { return &InfoModifier{ LoggerModifer{ logger: l, }, } } type WarningModifier struct { LoggerModifer } func NewWarningModifier(l *Logger) *WarningModifier { return &WarningModifier{ LoggerModifer{ logger: l, }, } } type ErrorModifier struct { LoggerModifer } func NewErrorModifier(l *Logger) *ErrorModifier { return &ErrorModifier{ LoggerModifer{ logger: l, }, } } type CriticalModifier struct { LoggerModifer } func NewCriticalModifier(l *Logger) *CriticalModifier { return &CriticalModifier{ LoggerModifer{ logger: l, }, } } |
各構造体には、Add/Handlerメソッドを実装したLoggerModifer構造体を持ちます。
ロガーレベル構造体のHandleメソッド
ここでは、各インスタンスが行いたい処理を個別に実装します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
func (d *DebugModifier) Handle() { d.logger.Level = "DEBUG" d.logger.String() d.LoggerModifer.Handle() } func (i *InfoModifier) Handle() { i.logger.Level = "INFO" i.logger.String() i.LoggerModifer.Handle() } func (w *WarningModifier) Handle() { w.logger.Level = "WARNING" w.logger.String() w.LoggerModifer.Handle() } func (e *ErrorModifier) Handle() { e.logger.Level = "ERROR" e.logger.String() e.LoggerModifer.Handle() } func (c *CriticalModifier) Handle() { c.logger.Level = "CRITICAL" c.logger.String() c.LoggerModifer.Handle() } |
使ってみよう
ここまで実装したロガーを使ってみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
func main() { logger := NewLogger( "Test", "Normal", "Set Up Logger") logger.String() root := NewLoggerModifer(logger) root.Add(NewDebugModifier(logger), "This is Debug Logger") root.Add(NewInfoModifier(logger), "This is Info Logger") root.Add(NewWarningModifier(logger), "This is Warning Logger") root.Add(NewErrorModifier(logger), "This is Error Logger") root.Add(NewCriticalModifier(logger), "This is Critical Logger") root.Handle() } |
この様に、つなぎ構造体を作成してから、Addメソッドにロガーレベル構造体を渡していきます。そして、最後にHandleメソッドで処理を実行します。
1 2 3 4 5 6 7 |
$ go run main.go Test: Normal: Set Up Logger Test: DEBUG: This is Critical Logger Test: INFO: This is Critical Logger Test: WARNING: This is Critical Logger Test: ERROR: This is Critical Logger Test: CRITICAL: This is Critical Logger |
クリアロガー
Handleメソッドにより、ロガーメソッドの呼び出しが可能になりますが、逆に呼び出さないメソッドも実装可能です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
type NoLoggerModifier struct { LoggerModifer } func NewNoLoggerModifier(l *Logger) *NoLoggerModifier { return &NoLoggerModifier{ LoggerModifer{ logger: l, }, } } func (n *NoLoggerModifier) Handle() { // empty } |
Handleの中身を空にすることで、鎖を断ち切った感じですね。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
func main() { logger := NewLogger( "Test", "Normal", "Set Up Logger") logger.String() root := NewLoggerModifer(logger) // リセット root.Add(NewNoLoggerModifier(logger), "") root.Add(NewDebugModifier(logger), "This is Debug Logger") root.Add(NewInfoModifier(logger), "This is Info Logger") root.Add(NewWarningModifier(logger), "This is Warning Logger") root.Add(NewErrorModifier(logger), "This is Error Logger") root.Add(NewCriticalModifier(logger), "This is Critical Logger") root.Handle() } |
1 2 |
$ go run main.go Test: Normal: Set Up Logger |
OKですね。
まとめ
今回は、実用的というよりも頭の体操に近いコードサンプルでしたね。もしかしたらロガーを例にしたのが、悪かったのかもしれません^^;
InterfaceとStructをうまく組み合わせて、再帰的な処理を作り出すこの方法は結構面白いと思います。
しかし、実務への導入は検討する必要がありそうです。少し読みにくくなりそうですし、そもそも使うシチュエーションが思い浮かばないというか。。
どんな時にこのパターンは、使えるんでしょうね?
プログラミングのスキルが上がれば、わかってくるかもしれません。知識として覚えておこうと思います!
次回
次回は、ResponsibilityパターンのBroker Chainです。お楽しみに!
Go言語まとめ
ソースコード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 |
package main import "fmt" type Logger struct { Prefix string Level string Message string } func NewLogger(name, level, message string) *Logger { return &Logger{ Prefix: name, Level: level, Message: message, } } func (l *Logger) String() { fmt.Println(fmt.Sprintf( "%s: %s: %s", l.Prefix, l.Level, l.Message, )) } type Modifier interface { Add(m Modifier, msg string) Handle() } type LoggerModifer struct { logger *Logger next Modifier } func NewLoggerModifer(logger *Logger) *LoggerModifer { return &LoggerModifer{logger: logger} } func (lm *LoggerModifer) Add(m Modifier, msg string) { lm.logger.Message = msg if lm.next != nil { lm.next.Add(m, msg) } else { lm.next = m } } func (lm *LoggerModifer) Handle() { if lm.next != nil { lm.next.Handle() } } type DebugModifier struct { LoggerModifer } func NewDebugModifier(l *Logger) *DebugModifier { return &DebugModifier{ LoggerModifer{ logger: l, }, } } func (d *DebugModifier) Handle() { d.logger.Level = "DEBUG" d.logger.String() d.LoggerModifer.Handle() } type InfoModifier struct { LoggerModifer } func (i *InfoModifier) Handle() { i.logger.Level = "INFO" i.logger.String() i.LoggerModifer.Handle() } func NewInfoModifier(l *Logger) *InfoModifier { return &InfoModifier{ LoggerModifer{ logger: l, }, } } type WarningModifier struct { LoggerModifer } func (w *WarningModifier) Handle() { w.logger.Level = "WARNING" w.logger.String() w.LoggerModifer.Handle() } func NewWarningModifier(l *Logger) *WarningModifier { return &WarningModifier{ LoggerModifer{ logger: l, }, } } type ErrorModifier struct { LoggerModifer } func NewErrorModifier(l *Logger) *ErrorModifier { return &ErrorModifier{ LoggerModifer{ logger: l, }, } } func (e *ErrorModifier) Handle() { e.logger.Level = "ERROR" e.logger.String() e.LoggerModifer.Handle() } type CriticalModifier struct { LoggerModifer } func NewCriticalModifier(l *Logger) *CriticalModifier { return &CriticalModifier{ LoggerModifer{ logger: l, }, } } func (c *CriticalModifier) Handle() { c.logger.Level = "CRITICAL" c.logger.String() c.LoggerModifer.Handle() } type NoLoggerModifier struct { LoggerModifer } func NewNoLoggerModifier(l *Logger) *NoLoggerModifier { return &NoLoggerModifier{ LoggerModifer{ logger: l, }, } } func (n *NoLoggerModifier) Handle() { // empty } func main() { logger := NewLogger( "Test", "Normal", "Set Up Logger") logger.String() root := NewLoggerModifer(logger) // リセット // root.Add(NewNoLoggerModifier(logger), "") root.Add(NewDebugModifier(logger), "This is Debug Logger") root.Add(NewInfoModifier(logger), "This is Info Logger") root.Add(NewWarningModifier(logger), "This is Warning Logger") root.Add(NewErrorModifier(logger), "This is Error Logger") root.Add(NewCriticalModifier(logger), "This is Critical Logger") root.Handle() } |
コメントを残す
コメントを投稿するにはログインしてください。