Golangのデザインパターン
Factory Method
仕様:
お支払い方法はお客様を選ばせるように、市場の支払い方法を全部サポートしたいです。
問題:
各支払い方法が、ぞれぞれSDKやドキュメントあり、必要とされているパラメータはバラバラで統一化できません。
回答:
package factorymethod
import "fmt"
type Pay interface {
Pay(string) int
}
type PayReq struct {
OrderId string
}
type CreditPayReq struct {
PayReq
CardNum string
}
func (p *CreditPayReq) Pay() string {
fmt.Println(p.OrderId)
fmt.Println(p.CardNum)
...
// クレカ支払いロジック
...
return "クレカお支払い成功"
}
type XOPayReq struct {
PayReq
Uid int64
}
func (p *XOPayReq) Pay() string {
fmt.Println(p.OrderId)
fmt.Println(p.Uid)
...
// XO支払いロジック
...
return "XOPay支払い成功"
}
Builder Method
仕様:
ワークフローを定義したい、各ステップは分けることができ、順番通りに進められるようにしたい
問題:
コードを複雑化しそう、ステップ毎が必要とされているデータ式も違ってくる
回答:
package buildermethod
import "fmt"
type Builder interface {
Step1()
Step2()
Step3()
}
type Director struct {
builder Builder
}
func NewDirector(builder Builder) *Director {
return &Director{
builder: builder,
}
}
func (d *Director) Construct() {
d.builder.Step1()
d.builder.Step2()
d.builder.Step3()
}
type Builder struct {}
func (b *Builder) Step1() {
fmt.Println("Step1")
}
func (b *Builder) Step2() {
fmt.Println("Step2")
}
func (b *Builder) Step3() {
fmt.Println("Step3")
}
Singleton Method
仕様:
サーバーや全体的にアクセスできる変数を作りたい、ですが、管理し辛い点はを避けたい
問題:
グローバル変数はいつ変更されるのはおかしくない
回答:
package singletonmethod
import (
"sync"
)
type singleton struct {
Value int
}
type Singleton interface {
getValue() int
}
func (s singleton) getValue() int {
return s.Value
}
var (
instance *singleton
once sync.Once
)
func GetInstance(v int) Singleton {
once.Do(func() {
instance = &singleton{Value: v}
})
return instance
}
Adapter Method
仕様:
倉庫Aは人で確認して商品を運ぶ仕様に対し、
倉庫Bは完全AIで走っています。
ネットで買い物するユーザーに対して、運営側は倉庫Aから出たものか、それとも倉庫Bから出たものが心配することなくなりたい
問題:
倉庫Aと倉庫Bはそれぞれ実行方法違います
回答:
package adaptermethod
type SoukoA interface {
getItem(string) string
}
type SoukoB interface {
getItem(string) string
}
func NewSoukoB() M {
return &getItemFromB{}
}
type getItemFromB struct{}
func (*getItemFromB) getItem(name string) string {
// ロジック
return name
}
func NewSoukoA() Cm {
return &getItemFromA{}
}
type getItemFromA struct{}
func (*getItemFromA) getItem(name string) string {
// ロジック
return name
}
type SoukoAdapter interface {
getItem(string, string) string
}
func NewSoukoAdapter() SoukoAdapter {
return &NewSoukoAdapter{}
}
type getItemAdapter struct{}
func (*getItemAdapter) getItem(isType string, name string) string {
if isType == "A" {
return NewSoukoA().getItem(name)
}
return NewSoukoB().getItem(name)
}
Bridge Method
仕様:
現在のシステムにメッセージとメールの二つの機能APIが備えています、ビジネス拡大のため、新しいシステム1作って、こちらのメッセージとメールの機能APIを利用したい。
問題:
今後新しいシステムXに対しても、スムーズに対応できる。
回答:
package bridgemethod
import "fmt"
type SendMessage interface {
send(text, to string)
}
type sms struct{}
func NewSms() SendMessage {
return &sms{}
}
func (*sms) send(text, to string) {
fmt.Println(fmt.Sprintf("send %s to %s sms", text, to))
}
type email struct{}
func NewEmail() SendMessage {
return &email{}
}
func (*email) send(text, to string) {
fmt.Println(fmt.Sprintf("send %s to %s email", text, to))
}
type systemA struct {
method SendMessage
}
func NewSystemA(method SendMessage) *systemA {
return &systemA{
method: method,
}
}
func (m *systemA) SendMessage(text, to string) {
m.method.send(fmt.Sprintf("[System A] %s", text), to)
}
type systemB struct {
method SendMessage
}
func NewSystemB(method SendMessage) *systemB {
return &systemB{
method: method,
}
}
func (m *systemB) SendMessage(text, to string) {
m.method.send(fmt.Sprintf("[System B] %s", text), to)
}
Decorator Method
仕様:
うどん産業として、一番ベースの面とスープはラーメンできます、その上に、トッピングによって、最後の値段の管理したい。
回答:
package decoratormethod
type udon interface {
getPrice() int
}
type base struct {}
func (p *base) getPrice() int {
return 500
}
type tomatoTopping struct {
udon udon
}
func (c *tomatoTopping) getPrice() int {
udonPrice := c.udon.getPrice()
return udonPrice + 100
}
type tenpuraTopping struct {
udon udon
}
func (c *tenpuraTopping) getPrice() int {
udonPrice := c.udon.getPrice()
return udonPrice + 200
}
Facade Method
仕様:
Mysql、PostgreSql、Redisのライブラリをコントロールしたい
回答:
package facademethod
import "fmt"
type APIMysql interface {
TestMysql() string
}
func NewAPIMysql() APIA {
return &apiRunMysql{}
}
type apiRunMysql struct{}
func (*apiRunMysql) TestMysql() string {
return "Mysql test api running"
}
type APIPostgreSql interface {
TestPostgreSql() string
}
func NewAPIPostgreSql() APIB {
return &apiRunPostgreSql{}
}
type apiRunPostgreSql struct{}
func (*apiRunPostgreSql) TestPostgreSql() string {
return "PostgreSql test api running"
}
type APIDB interface {
Test() string
}
func NewAPIDB() API {
return &apiRun{
mysql: NewAPIMysql(),
postgresql: NewAPIPostgreSql(),
}
}
type apiRun struct {
mysql APIMysql
postgresql APIPostgreSql
}
func (a *apiRun) Test() string {
mysqlRet := a.mysql.TestMysql()
postgresqlRet := a.postgresql.TestPostgreSql()
return fmt.Sprintf("%s\n%s", mysqlRet, postgresqlRet)
}
Proxy Method
仕様:
ユーザーの権限によって、アクセスできるページを分けたい
回答:
package proxymethod
import "fmt"
type Subject interface {
Proxy() string
}
type Proxy struct {
real RealSubject
}
func (p Proxy) Proxy() string {
var res string
// 処理
p.real.Pre()
p.real.Real()
p.real.After()
return res
}
type RealSubject struct{}
func (RealSubject) Real() {
fmt.Print("real")
}
func (RealSubject) Pre() {
fmt.Print("pre:")
}
func (RealSubject) After() {
fmt.Print(":after")
}
Command Method
仕様:
スマト家電では、今日はテレビ購入した、明日はエアコンを購入するつもりですが、テレビのスイッチのベースしかないので、二つを影響しないように、リモコンは独自にしたいけど、形式はテレビと一緒にしたい。
回答:
package commandmethod
import "fmt"
type button struct {
command command
}
func (b *button) press() {
b.command.execute()
}
type command interface {
execute()
}
type onCommand struct {
device device
}
func (c *onCommand) execute() {
c.device.on()
}
type offCommand struct {
device device
}
func (c *offCommand) execute() {
c.device.off()
}
type device interface {
on()
off()
}
type tv struct{}
func (t *tv) on() {
fmt.Println("テレビON")
}
func (t *tv) off() {
fmt.Println("テレビOFF")
}
type airConditioner struct{}
func (t *airConditioner) on() {
fmt.Println("エアコンON")
}
func (t *airConditioner) off() {
fmt.Println("エアコンOFF")
}
Observer Method
仕様:
ステータス変更は関連しているクラスに通知を行いたい
回答:
package observer
import "fmt"
type Subject struct {
observers []Observer
content string
}
func NewSubject() *Subject {
return &Subject{
observers: make([]Observer, 0),
}
}
// 関連付け
func (s *Subject) AddObserver(o Observer) {
s.observers = append(s.observers, o)
}
func (s *Subject) UpdateContext(content string) {
s.content = content
s.notify()
}
type Observer interface {
Do(*Subject)
}
func (s *Subject) notify() {
for _, o := range s.observers {
o.Do(s)
}
}
type Reader struct {
name string
}
func NewReader(name string) *Reader {
return &Reader{
name: name,
}
}
func (r *Reader) Do(s *Subject) {
fmt.Println(r.name + " get " + s.content)
}
State Method
仕様:
とある状態になったら、何回やっても、変化を起こさないようにします。例えば、ドアの場合、開いたら、もう一回開けられない、また、壊したドアに対して、操作できないはず。
回答:
package state
import "fmt"
type state interface {
open(*door)
close(*door)
}
type door struct {
opened state
closed state
damaged state
currentState state
}
func (d *door) open() {
d.currentState.open(d)
}
func (d *door) close() {
d.currentState.close(d)
}
func (d *door) setState(s state) {
d.currentState = s
}
type opened struct{}
func (o *opened) open(d *door) {
fmt.Println("ドアすでに開く")
}
func (o *opened) close(d *door) {
fmt.Println("ドア閉じる")
}
type closed struct{}
func (c *closed) open(d *door) {
fmt.Println("ドア開く")
}
func (c *closed) close(d *door) {
fmt.Println("ドアすでに閉めている")
}
type damaged struct{}
func (a *damaged) open(d *door) {
fmt.Println("ドアが壊した、開けない")
}
func (a *damaged) close(d *door) {
fmt.Println("ドアが壊した、閉じれない")
}
Strategy Method
仕様:
旅行は、車で行くか、電車で行くか、飛行機で行くのかは3つの方法を選べるようになりたい。
問題:
車、電車、飛行機それぞれ独立しています。
回答:
package strategy
import "fmt"
type Travel struct {
name string
strategy Strategy
}
func NewTravel(name string, strategy Strategy) *Travel {
return &Travel{
name: name,
strategy: strategy,
}
}
func (p *Travel) traffic() {
p.strategy.traffic(p)
}
type Strategy interface {
traffic(*Travel)
}
type Fly struct{}
func (f *Fly) traffic(t *Travel) {
fmt.Println(t.name + " 飛行機で旅行")
}
type Train struct{}
func (r *Train) traffic(t *Travel) {
fmt.Println(t.name + " 電車で旅行")
}
type Drive struct{}
func (w *Drive) traffic(t *Travel) {
fmt.Println(t.name + " 車で旅行")
}
Visitor Method
仕様:
Structの構造を変えずに、新しい機能を追加したい
回答:
package visitormethod
import "fmt"
type Shape interface {
accept(visitor)
}
type square struct{}
func (s *square) accept(v visitor) {
v.visitForSquare(s)
}
type circle struct{}
func (c *circle) accept(v visitor) {
v.visitForCircle(c)
}
type visitor interface {
visitForSquare(*square)
visitForCircle(*circle)
}
type sideCalculator struct{}
func (a *sideCalculator) visitForSquare(s *square) {
fmt.Println("square side")
}
func (a *sideCalculator) visitForCircle(s *circle) {
fmt.Println("circle side")
}
type radiusCalculator struct{}
func (a *radiusCalculator) visitForSquare(s *square) {
fmt.Println("square radius")
}
func (a *radiusCalculator) visitForCircle(c *circle) {
fmt.Println("circle radius")
}