模板模式可以把一个流程抽象起来,一个流程中首先调用哪一个函数,后面再调用哪一个函数,基本的流程基本是固定的。只是每一步要怎么做跟具体类型有关系,这时候就很适合用模板模式了。
定义:一个抽象类公开定义了执行它的方法的方式/模板。 它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。 这种类型的设计模式属于行为型模式。
但是在GOLANG的实现中,需要用到这些知识:当一个子类通过直接在子类中添加父类类型名的方法组合,如果子类没有覆盖父类的方法,那么可以直接通过子类调用父类;如果子类覆盖了父类的方法,那么就会使用子类新定义的方法。
模板实现代码:
package templatemethod
import "fmt"
type Downloader interface {
Download(uri string)
}
type template struct {
implement
uri string
}
type implement interface {
download()
save()
}
func newTemplate(impl implement) *template {
return &template{
implement:impl,
}
}
func (t *template)Download(uri string) {
t.uri = uri
fmt.Print("prepare downloading\n")
t.implement.download()
t.implement.save()
fmt.Print("finish downloading\n")
}
func (t *template)save() {
fmt.Print("default save\n")
}
/*
这里 HTTPDownloader struct包含(封装)了*template, 因为*template实现了Download(uri string)方法,
在我们HTTPDownloader.Download(sting)的时候,其实调用的是*template实现的那一个,这里也是允许认为HTTPDownloader实现了
Download(uri string)方法的,所以可以将HTTPDownloader实例赋值给Downloader实例。就像下面的NewHTTPDownloader()函数直接
将HTTPDownloader实例赋值给Downloader实例那样子。如果我们在HTTPDownloader结构体中添加其他的东西导致HTTPDownloader.Download(sting)
这个调用失败,NewHTTPDownloader()这个函数就编译不通过了。
*/
type HTTPDownloader struct {
*template
}
func NewHTTPDownloader() Downloader {
downloader := &HTTPDownloader{}
template := newTemplate(downloader)
downloader.template = template
return downloader
}
func (d *HTTPDownloader)download() {
fmt.Printf("download %s via http\n", d.uri)
}
/*
这里如果我把下面的save函数注释掉,*HTTPDownloader.save()则会调用 *template 实现的save()方法。
上层的把底层的覆盖了,这个有点用处。
*/
func (*HTTPDownloader)save() {
fmt.Printf("http save\n")
}
type FTPDownloader struct {
*template
}
func NewFTPDownloader() Downloader {
downloader := &FTPDownloader{}
template := newTemplate(downloader)
downloader.template = template
return downloader
}
func (d *FTPDownloader)download() {
fmt.Printf("download %s via ftp\n", d.uri)
}
测试函数:
package templatemethod
import "testing"
func TestHTTPDownloader(t *testing.T) {
var downloader Downloader = NewHTTPDownloader()
downloader.Download("http://example.com/abc.zip")
}
func TestFTPDownloader(t *testing.T) {
var downloader Downloader = NewFTPDownloader()
downloader.Download("ftp://example.com/abc.zip")
}
结果:
=== RUN TestHTTPDownloader
prepare downloading
download http://example.com/abc.zip via http
http save //这里使用了http类型实现的save方法。
finish downloading
--- PASS: TestHTTPDownloader (0.00s)
=== RUN TestFTPDownloader
prepare downloading
download ftp://example.com/abc.zip via ftp
default save //ftp类没有save方法,直接调用默认的*template.save方法
finish downloading
--- PASS: TestFTPDownloader (0.00s)
PASS
Process finished with exit code 0