GoFのデザインパターンを学習する素材として、書籍「増補改訂版Java言語で学ぶデザインパターン入門」が参考になるみたいですね。
取り上げられている実例は、JAVAベースのため、Pythonで同等のプラクティスに挑んだことがありました。
Qiita記事: "Pythonで、デザインパターン「Facade」を学ぶ"
今回は、Pythonで実装した”Facade”のサンプルアプリをGolangで実装し直してみました。
■ Facade(ファサード・パターン)
Facadeパターンあるいは Façadeパターン(ファサード・パターン)とは、GoF(Gang of Four; 4人のギャングたち)によって定義された、コンピュータソフトウェアのデザインパターンの1つである。Facade(ファサード)とは「建物の正面」を意味する。異なるサブシステムを単純な操作だけを持ったFacadeクラスで結び、サブシステム間の独立性を高める事を目的とする。
UML class and sequence diagram
UML class diagram
■ "Facade"のサンプルプログラム
実際に、Facadeパターンを活用したサンプルプログラムを動かしてみて、次のような動作の様子を確認したいと思います。
$ go run Main.go
welcome1.html is created for hyuki@hyuki.com (Hiroshi Yuki)
welcome2.html is created for mamoru@hyuki.com (Mamoru Takahashi)
サンプルプログラムを起動すると、2つのhtmlファイルが生成されます。
おのおのを、Webブラウザを確認してみるとこんな感じになりました。
- welcome1.html
- welcome2.html
■ サンプルプログラムの詳細
Gitリポジトリにも、同様のコードをアップしています。
https://github.com/ttsubo/study_of_design_pattern_with_golang/tree/master/Facade
- ディレクトリ構成
.
├── Main.go
├── facade
│ ├── database.go
│ ├── html_writer.go
│ └── page_maker.go
├── maildata.json
├── welcome1.html
└── welcome2.html
(1) Facade(正面)の役
Facade
役は、システムを構成しているその他大勢の役の「シンプルな窓口」となります。Facade
役は、高レベルでシンプルなインタフェースをシステム外部に提供します。
サンプルプログラムでは、PageMaker
構造体が、この役を努めます。
package facade
import (
"fmt"
"os"
)
// PageMaker is struct
type PageMaker struct {
}
// Pagemaker is variable
var Pagemaker = &PageMaker{}
// MakeWelcomePage func for making welcome page
func (p *PageMaker) MakeWelcomePage(mailaddr, filename string) {
username := ""
database := &Database{}
prop := database.getProperties("maildata")
for _, person := range prop {
if person.Mail == mailaddr {
username = person.Name
}
}
file, _ := os.Create(filename)
writer := &HTMLWriter{file}
writer.title(fmt.Sprintf("Welcom to %s's page!", username))
writer.paragraph("We'll wait for your sending")
writer.mailto(mailaddr, username)
writer.close()
fmt.Printf("%s is created for %s (%s)\n", filename, mailaddr, username)
}
(2) システムを構成しているその他大勢の役
その他大勢の役は、それぞれの仕事を行いますが、Facade
役のことは意識しません。Facade
役から呼び出されて仕事を行いますが、その他大勢の役の方からFacade
役を呼び出すことはありません。
サンプルプログラムでは、Database
構造体と、HTMLWriter
構造体が、この役を努めます。
package facade
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
)
// Database is struct
type Database struct {
}
// Person is struct
type Person struct {
Name string `json:"name"`
Mail string `json:"mail"`
}
var maildata []Person
func (d *Database) getProperties(dbname string) []Person {
filename := dbname + ".json"
jsonString, err := ioutil.ReadFile(filename)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
if err = json.Unmarshal(jsonString, &maildata); err != nil {
fmt.Println(err)
os.Exit(1)
}
return maildata
}
[
{
"mail": "hyuki@hyuki.com",
"name": "Hiroshi Yuki"
},
{
"mail": "hanako@hyuki.com",
"name": "Hananko Sato"
},
{
"mail": "tomura@hyuki.com",
"name": "Tomura"
},
{
"mail": "mamoru@hyuki.com",
"name": "Mamoru Takahashi"
}
]
package facade
import (
"fmt"
"os"
)
// HTMLWriter is struct
type HTMLWriter struct {
writer *os.File
}
func (h *HTMLWriter) title(title string) {
h.writer.Write([]byte("<html>\n"))
h.writer.Write([]byte("<head>"))
h.writer.Write([]byte(fmt.Sprintf("<title>%s</title>", title)))
h.writer.Write([]byte("</head>\n"))
h.writer.Write([]byte("<body>\n"))
h.writer.Write([]byte(fmt.Sprintf("<h1>%s</h1>\n", title)))
}
func (h *HTMLWriter) paragraph(msg string) {
h.writer.Write([]byte(fmt.Sprintf("<p>%s</p>\n", msg)))
}
func (h *HTMLWriter) link(href, caption string) {
h.writer.Write([]byte(fmt.Sprintf("<a href=\"%s\">%s</a>", href, caption)))
}
func (h *HTMLWriter) mailto(mailaddr, username string) {
h.link(fmt.Sprintf("mailto:%s", mailaddr), username)
}
func (h *HTMLWriter) close() {
h.writer.Write([]byte("</body>\n"))
h.writer.Write([]byte("</html>\n"))
h.writer.Close()
}
(3) Client(依頼人)の役
Facadeパターンを利用する役です。
サンプルプログラムでは、startMain
関数が、この役を努めます。
package main
import (
"./facade"
)
func startMain() {
facade.Pagemaker.MakeWelcomePage("hyuki@hyuki.com", "welcome1.html")
facade.Pagemaker.MakeWelcomePage("mamoru@hyuki.com", "welcome2.html")
}
func main() {
startMain()
}