5
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Golangで、デザインパターン「Adapter」を学ぶ

Last updated at Posted at 2020-03-23

GoFのデザインパターンを学習する素材として、書籍「増補改訂版Java言語で学ぶデザインパターン入門」が参考になるみたいですね。
取り上げられている実例は、JAVAベースのため、Pythonで同等のプラクティスに挑んだことがありました。
Qiita記事: "Pythonで、デザインパターン「Adapter」を学ぶ"

今回は、Pythonで実装した”Adapter”のサンプルアプリをGolangで実装し直してみました。

■ Adapter(アダプター・パターン)

Adapterパターン(アダプター・パターン)とは、GoF (Gang of Four; 4人のギャングたち) によって定義されたデザインパターンの1つである。Adapterパターンを用いると、既存のクラスに対して修正を加えることなく、インタフェースを変更することができる。Adapterパターンを実現するための手法として"継承を利用した手法"と"委譲を利用した手法"が存在する。

UML class diagram

W3sDesign_Adapter_Design_Pattern_UML.jpg

1. 継承を利用したAdapter

継承を利用したAdapterは、利用したいクラスのサブクラスを作成し、そのサブクラスに対して必要なインタフェースを実装することで実現される。
inheritence.png

2. 委譲を利用したAdapter

委譲を利用したAdapterは、利用したいクラスのインスタンスを生成し、そのインスタンスを他クラスから利用することで実現される。
delegation.png
(以上、ウィキペディア(Wikipedia)より引用)

■ Golangで、Pythonクラスを実装し直す際の備忘録

Golang では、Python等でお馴染みの、Inheritance(継承)ではなく、Composition(合成)のみが使われます。

具体的、以下の2点を気をつけて、サンプルアプリの再実装に取り組みました。

  • golangには、オブジェクトのクラス継承の概念が存在しないので、再利用性(Embedded)と多相性(Interface)の活用で対応する必要がある。
  • Pythonの場合だと、動的型付けのおかげて、引数/戻り値の型を意識することなくポリモーフィズムを実践できていたが、Golangの場合では、引数/戻り値の型を柔軟に扱えるようインタフェースを活用する必要がある。

□ 大変、お世話になったQiita記事

■ "Adapter"のサンプルプログラム

実際に、Adapterパターンを活用したサンプルプログラムを動かしてみて、次のような動作の様子を確認したいと思います。

  • 文字列を、カッコでくくって表示する
  • 文字列の前後に、*印をつけて表示する

なお、サンプルプログラムは、再利用性(Embedded)を活用したものと多相性(Interface)を活用したもの、それぞれ用意しました。サンプルプログラム動作の結果は、全く同じになります。

□ 再利用性(Embedded)を利用したサンプルプログラム

$ go run Adapter_1_Embedding/Main.go 
(Hello)
*Hello*

□ 多相性(Interface)を利用したサンプルプログラム

$ go run Adapter_2_Interface/Main.go 
(Hello)
*Hello*

■ サンプルプログラムの詳細

Gitリポジトリにも、同様のコードをアップしています。
https://github.com/ttsubo/study_of_design_pattern_with_golang/tree/master/Adapter

  • ディレクトリ構成
.
├── Adapter_1_Embedding
│   ├── Main.go
│   └── adapter
│       ├── banner.go
│       └── print_banner.go
└── Adapter_2_Interface
    ├── Main.go
    └── adapter
        ├── banner.go
        └── print_banner.go

□ 再利用性(Embedded)を利用したサンプルプログラム

(1) Target(対象)の役

Target役は、インスタンスの振る舞いに関わるインタフェースを定めます。
再利用性(Embedded)を利用したサンプルプログラムでは、この役は存在しません。

(2) Client(依頼者)の役

Target役のメソッドを使って、仕事をする役です。
サンプルプログラムでは、startMainメソッドが、この役を努めます。

Adapter_1_Embedding/Main.go
package main

import (
	"./adapter"
)

func startMain() {
	p := adapter.NewPrintBanner("Hello")
	p.PrintWeak()
	p.PrintString()
}

func main() {
	startMain()
}

(3) Adaptee(適合される側)の役

Adapter役のなかで実際に動作するメソッドを、ここで実装します。
サンプルプログラムでは、banner構造体が、この役を努めます。

Adapter_1_Embedding/banner.go
package adapter

import "fmt"

type banner struct {
	str string
}

func (b *banner) showWithParen() {
	fmt.Printf("(%s)\n", b.str)
}

func (b *banner) showWithAster() {
	fmt.Printf("*%s*\n", b.str)
}

(4) Adapter(適合する側)の役

Adapter役は、Target役のインタフェースを実装しているクラスです。
サンプルプログラムでは、PrintBanner構造体が、この役を努めます。

Adapter_1_Embedding/print_banner.go
package adapter

// PrintBanner is struct
type PrintBanner struct {
	*banner
}

// NewPrintBanner func for initializing PrintBanner
func NewPrintBanner(str string) *PrintBanner {
	return &PrintBanner{
		banner: &banner{str: str},
	}
}

// PrintWeak func for formatting with paren
func (p *PrintBanner) PrintWeak() {
	p.showWithParen()
}

// PrintString func for formatting with aster
func (p *PrintBanner) PrintString() {
	p.showWithAster()
}

□ 多相性(Interface)を利用したサンプルプログラム

(1) Target(対象)の役

Target役は、インスタンスの振る舞いに関わるインタフェースを定めます。
多相性(Interface)を利用したサンプルプログラムでも、この役は存在しません。

(2) Client(依頼者)の役

Target役のメソッドを使って、仕事をする役です。
サンプルプログラムでは、startMain関数が、この役を努めます。

Adapter_2_Interface/Main.go
package main

import (
	"./adapter"
)

func startMain() {
	p := adapter.NewPrintBanner("Hello")
	p.PrintWeak()
	p.PrintString()
}

func main() {
	startMain()
}

(3) Adaptee(適合される側)の役

Adapter役のなかで実際に動作するメソッドを、ここで実装します。
サンプルプログラムでは、bannerインタフェースに、Compositionを適用して、この役を努めます。

Adapter_2_Interface/banner.go
package adapter

import "fmt"

type banner interface {
	showWithParen()
	showWithAster()
}

func (p *PrintBanner) showWithParen() {
	fmt.Printf("(%s)\n", p.str)
}

func (p *PrintBanner) showWithAster() {
	fmt.Printf("*%s*\n", p.str)
}

(4) Adapter(適合する側)の役

Adapter役は、Target役のインタフェースを実装しているクラスです。
サンプルプログラムでは、PrintBanner構造体が、この役を努めます。

Adapter_2_Interface/print_banner.go
package adapter

// PrintBanner is struct
type PrintBanner struct {
	str    string
	banner banner
}

// NewPrintBanner func for initializing PrintBanner
func NewPrintBanner(str string) *PrintBanner {
	printBanner := &PrintBanner{
		str: str,
	}
	printBanner.banner = printBanner
	return printBanner
}

// PrintWeak func for formatting with paren
func (p *PrintBanner) PrintWeak() {
	p.banner.showWithParen()
}

// PrintString func for formatting with aster
func (p *PrintBanner) PrintString() {
	p.banner.showWithAster()
}

■ 参考URL

5
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?