0
1

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 3 years have passed since last update.

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

Last updated at Posted at 2020-03-23

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

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

■ Prototype(プロトタイプ・パターン)

Prototypeパターン(プロトタイプ・パターン)とは、ソフトウェア開発で用いられる、生成に関するデザインパターンの1つである。生成されるオブジェクトの種別がプロトタイプ(典型)的なインスタンスであるときに使用され、このプロトタイプを複製して新しいオブジェクトを生成する。

このパターンは、以下の目的で用いられる。

  • Abstract Factory パターンでなされるように、クライアント・アプリケーションにおいてオブジェクトの生成者をサブクラスにすることを回避する
  • 標準的な方法(例えば'new')で新しいオブジェクトを作ることによる固有のコストが所与のアプリケーションにとって高すぎる時にそれを回避する

このパターンを実装するには、純粋仮想 (pure virtual method) の clone()メソッドを指定する抽象的(abstract)な基底クラスを宣言する。「多態性を持つコンストラクタ」の能力を必要とする全てのクラスは抽象的な基底クラスから自身を派生させ、clone()の操作を実装する。

UML class and sequence diagram

W3sDesign_Prototype_Design_Pattern_UML.jpg

UML class diagram

https---qiita-image-store.s3.ap-northeast-1.amazonaws.com-0-103595-3c90ab89-e715-587d-10eb-65490cb5be6c.png

(以上、ウィキペディア(Wikipedia)より引用)

□ 備忘録

Prototypeパターンは、クラスからインスタンスをつくるのではなく、インスタンスをコピーすることで、インスタンスから別のインスタンスをつくるというものです。
Prototypeパターンを活用するメリットについては、書籍「増補改訂版Java言語で学ぶデザインパターン入門」P.66に以下の記述があります。
(1) 種類が多すぎてクラスにまとめられない場合
(2) クラスからインスタンス作成が難しい場合
(3) フレームワークと生成するインスタンスを分けたい場合

まあ、大量に、インスタンスを生成するようなユースケースがあるならば、インスタンス生成に関わるオーバヘッド処理の削減につながるんですかねぇー

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

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

  • 文字列を、下線を引いて表示する
  • 文字列を、枠線で囲って表示する
$ go run Main.go 
"Hello World"
-------------

***************
* Hello World *
***************

///////////////
/ Hello World /
///////////////

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

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

  • ディレクトリ構成
.
├── Main.go
└── framework
    ├── manager.go
    ├── message_box_prototype.go
    ├── prototype.go
    └── underline_pen_prototype.go

(1) Prototype(原型)の役

Prototype役は、インスタンスをコピー(複製)して新しいインスタンスを作るためのインタフェースを定めます。
サンプルプログラムでは、Prototypeインタフェースが、この役を努めます。

framework/prototype.go
package prototype

// Prototype is interface
type Prototype interface {
	Use(s string)
	createClone() Prototype
}

(2) ConcretePrototype(具体的な原型)の役

ConcretePrototype役は、インスタンスをコピーして新しいインスタンスを作るメソッドを実際に実装します。
サンプルプログラムでは、UnderlinePen構造体やMessageBox構造体が、この役を努めます。

framework/underline_pen_prototype.go
package prototype

import (
	"fmt"
	"strings"
)

// UnderlinePen is struct
type UnderlinePen struct {
	ulchar string
}

// NewUnderlinePen func for initializing UnderlinePen
func NewUnderlinePen(ulchar string) *UnderlinePen {
	return &UnderlinePen{
		ulchar: ulchar,
	}
}

// Use func for confirming name
func (u *UnderlinePen) Use(s string) {
	length := len(s)
	line := strings.Repeat(u.ulchar, (length + 2))

	fmt.Printf("\"%s\"\n", s)
	fmt.Println(line)
	fmt.Println("")
}

func (u *UnderlinePen) createClone() Prototype {
	return NewUnderlinePen(u.ulchar)
}
framework/message_box_prototype.go
package prototype

import (
	"fmt"
	"strings"
)

// MessageBox is struct
type MessageBox struct {
	decochar string
}

// NewMessageBox func for initializing MessageBox
func NewMessageBox(decochar string) *MessageBox {
	return &MessageBox{
		decochar: decochar,
	}
}

// Use func for confirming name
func (m *MessageBox) Use(s string) {
	length := len(s)
	line := strings.Repeat(m.decochar, (length + 4))

	fmt.Println(line)
	fmt.Printf("%s %s %s\n", m.decochar, s, m.decochar)
	fmt.Println(line)
	fmt.Println("")
}

func (m *MessageBox) createClone() Prototype {
	return NewMessageBox(m.decochar)
}

(3) Client(利用者)の役

Client役は、インスタンスをコピーするメソッドを利用して、新しいインスタンスを作成します。
サンプルプログラムでは、Manager構造体やstartMain関数が、この役を努めます。

framework/manager.go
package prototype

// ManagerInterface is interface
type ManagerInterface interface {
	Register(name string, producter Prototype)
	Create(protoname string) Prototype
}

// Manager is struct
type Manager struct {
	showcase map[string]Prototype
}

// NewManager func for initializing Manager
func NewManager() *Manager {
	return &Manager{
		showcase: make(map[string]Prototype),
	}
}

// Register func for registering proto
func (m *Manager) Register(name string, proto Prototype) {
	m.showcase[name] = proto
}

// Create func for creating proto
func (m *Manager) Create(protoname string) Prototype {
	p := m.showcase[protoname]
	return p.createClone()
}
Main.go
package main

import (
	prototype "./framework"
)

func startMain(managerObject prototype.ManagerInterface) {
	upen := prototype.NewUnderlinePen("-")
	mbox := prototype.NewMessageBox("*")
	sbox := prototype.NewMessageBox("/")
	managerObject.Register("strong message", upen)
	managerObject.Register("warning box", mbox)
	managerObject.Register("slash box", sbox)

	p1 := managerObject.Create("strong message")
	p2 := managerObject.Create("warning box")
	p3 := managerObject.Create("slash box")
	p1.Use("Hello World")
	p2.Use("Hello World")
	p3.Use("Hello World")
}

func main() {
	startMain(prototype.NewManager())
}

■ 参考URL

0
1
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
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?