Goでデザインパターン (Bridgeパターン)

Goデザインパターン

Bridgeパターンとは

構造的デザインパターンの1つ。巨大なクラスや密接に関連したクラスの集まりを、抽象(機能)部分と実装部分の2つの階層に分離し、それぞれを独立して開発できるようにしたパターン。

例としてアプリケーション開発の場合を考えてみる。まずアプリケーションで提供する機能を抽象クラスで定義し、それらを各プラットフォームごとに実装する。この時のクラス図は以下のようになる。

ここで新たに機能を追加するため抽象クラスRefinedAppを追加する。この時追加した機能で先に実装したLinuxApp, IPhoneAppのメソッドを利用したい場合、そのままでは利用できないため、新たにLinuxApp, IPhoneAppと同じ機能を実装したクラスLinuxApp’, IPhoneApp’を追加する。この時のクラス図は以下のようになる。

提供機能と各プラットフォーム毎の実装のように、独立した次元のものを継承によって拡張してこうとする場合、上記の例でみたように非常大きなコストがかかることになる。そこでコストの大きいクラスの継承をやめて、オブジェクトの委譲によってこの問題を解決するのがBridgeパターンである。

Bridgeパターンを利用することで下記のようなメリットを得ることができる。

  • プラットフォームに依存しないクラスやアプリを作成できる。
    (クラスやアプリを抽象化層に、プラットフォーム依存の部分を実装部分に分離できる)
  • 抽象化層と実装が互いに独立しているため、それぞれ個別に開発・新規追加が可能 (開放閉鎖の原則)
  • 高レベルロジックを抽象化層に、低レベルロジックを実装層に分離できる (単一責任の原則)
  • クライアント側は抽象化層のみを利用するため、クライアントコードが実装に依存しない。

クラス図

  • Abstraction (抽象化層)
    機能を定義するクラス。Implementationに宣言されているAPIを利用した機能を提供する。高レベルの制御ロジックを提供し、低レベルの作業はImplementationを実装したオブジェクトに任せている。
    ここで定義するメソッドは単に実装と同じメソッドを並べたものでも構わないが、一般的にはそれらを利用したさらに複雑な処理を定義する。
  • Implementation (実装)
    全ての具象実装に共通のインターフェースを宣言する。抽象化層はここで宣言されたインターフェースを介して具象実装とやり取りをする。
  • Concrete Implementations (具象実装)
    実装で宣言されたインターフェースを実装する。
  • Refined Abstraction (改善した抽象化層)
    抽象化層に対して新たな機能を追加したもの。

実装

Abstraction

package main

import (
	"fmt"
)

// Abstraction
type Application struct {
	os OS
}

// 初期化時に具象実装を受け取る
func NewApplication(os OS) *Application {
	return &Application{os: os}
}

func (a *Application) TakeScreenShot() {
	a.os.ScreenShot()
}

func (a *Application) Analize() {
	n := a.os.GetOS()
	m := a.os.AnalizeMemory()
	v := a.os.AnalizeVolume()
	fmt.Printf("OS: %s\nMemory: %v\nVolume: %v\n", n, m, v)
}

func (a *Application) Terminate(appName string) {
	a.os.Terminate(appName)
}

func (a *Application) Delete(filePath string) {
	a.os.Delete(filePath)
}

Implementation

package main

// Implementation
type OS interface {
	GetOS() string
	ScreenShot()
	AnalizeMemory() map[string]int
	AnalizeVolume() map[string]int
	Terminate(appName string)
	Delete(filePath string)
}

Concrete Implementations

package main

import (
	"fmt"
)

// Concrete Implementations
type Windows struct {}

func NewWindows() *Windows {
	return &Windows{}
}

func (w *Windows) GetOS() string {
	return "Windows"
}

func (w *Windows) ScreenShot() {
	fmt.Println("[Windows] take screen shot")
}

func (w *Windows) AnalizeMemory() map[string]int {
	return map[string]int{"app1": 20, "app2": 30, "app3": 50}
}

func (w *Windows) AnalizeVolume() map[string]int {
	return map[string]int{"file1": 50, "file2": 10, "file3": 15}
}

func (w *Windows) Terminate(appName string) {
	fmt.Printf("[Windows] terminate %s\n", appName)
}

func (w *Windows) Delete(filePath string) {
	fmt.Printf("[Windows] delete %s\n", filePath)
}
package main

import (
	"fmt"
)

// Concrete Implementation
type Mac struct {}

func NewMac() *Mac {
	return &Mac{}
}

func (m *Mac) GetOS() string {
	return "Mac"
}

func (m *Mac) ScreenShot() {
	fmt.Println("[Mac] take screen shot")
}

func (m *Mac) AnalizeMemory() map[string]int {
	return map[string]int{"app1": 90, "app2": 3, "app3": 7}
}

func (m *Mac) AnalizeVolume() map[string]int {
	return map[string]int{"file1": 10, "file2": 70, "file3": 20}
}

func (m *Mac) Terminate(appName string) {
	fmt.Printf("[Mac] terminate %s\n", appName)
}

func (m *Mac) Delete(filePath string) {
	fmt.Printf("[Mac] delete %s\n", filePath)
}

Refined Abstraction

package main

// Refined Abstraction
type AdvancedApplication struct {
	Application
}

func NewAdvancedApplication(os OS) *AdvancedApplication {
	return &AdvancedApplication{
		Application: *NewApplication(os),
	}
}

func (a *AdvancedApplication) ReleaseMemory() {
	m := a.os.AnalizeMemory()
	for k := range m {
		a.os.Terminate(k)
	}
}

動作確認

package main

import (
	"fmt"
)

func getOS(name string) OS {
	if name == "windows" {
		return NewWindows()
	}
	if name == "mac" {
		return NewMac()
	}
	return nil
}

func main() {
	ol := []string{"windows", "mac"}
	for _, o := range ol {
		os := getOS(o)

		fmt.Printf("\n<< %s - Application >>\n", o)
		app := NewApplication(os)
		app.Analize()

		fmt.Printf("\n<< %s - AdvancedApplication >>\n", o)
		ad := NewAdvancedApplication(os)
		ad.Analize()
		ad.ReleaseMemory()
	}
}
>> go run .

<< windows - Application >>
OS: Windows
Memory: map[app1:20 app2:30 app3:50]
Volume: map[file1:50 file2:10 file3:15]

<< windows - AdvancedApplication >>
OS: Windows
Memory: map[app1:20 app2:30 app3:50]
Volume: map[file1:50 file2:10 file3:15]
[Windows] terminate app1
[Windows] terminate app2
[Windows] terminate app3

<< mac - Application >>
OS: Mac
Memory: map[app1:90 app2:3 app3:7]
Volume: map[file1:10 file2:70 file3:20]

<< mac - AdvancedApplication >>
OS: Mac
Memory: map[app1:90 app2:3 app3:7]
Volume: map[file1:10 file2:70 file3:20]
[Mac] terminate app2
[Mac] terminate app3
[Mac] terminate app1

コメント

タイトルとURLをコピーしました