NOTE

6.4 模板方法

1. 定义 适用于某个算法整体步骤固定,某些部分易变化.可以把变化的部分抽象出来.如servlet的doGet()和doPost()方法 2. uml 3. Java 3.1. client 3.2. 固定步骤 4. Golang 4.1. 第一种 4.1.1. client 4.1.2. 模板方法

Software Architecture & Engineering创建于 更新于 historical

这是历史学习笔记,可能存在过时或不完整的理解。

1. 定义

适用于某个算法整体步骤固定,某些部分易变化.可以把变化的部分抽象出来.如servlet的doGet()和doPost()方法

2. uml

PlantUML 图表

3. Java

3.1. client

public class Client
{
    public static void main(String[] args)
    {
        BankTemplateMethod method = new BankTemplateMethod()
        {
            @Override
            public void transact()
            {
                System.out.println("我要存钱!");
            }
        };

        method.process();


        BankTemplateMethod method2 = new BankTemplateMethod()
        {
            @Override
            public void transact()
            {
                System.out.println("我要理财!");
            }
        };

        method2.process();


    }
}

3.2. 固定步骤

public abstract class BankTemplateMethod
{
    private void takeNumber()
    {
        System.out.println("取号排队");
    }

    public abstract void transact();//钩子方法,放到具体的子类中实现

    private void evaluate()
    {
        System.out.println("反馈评分");
    }

    //固定的步骤
    public final void process()
    {
        this.takeNumber();

        this.transact();

        this.evaluate();
    }


}

4. Golang

4.1. 第一种

4.1.1. client

func main() {
	var downloader 模板方法.IDownloader
	downloader = 模板方法.NewFTPDownloader()
	downloader.Download("https://www.baidu.com/")

	downloader = 模板方法.NewHTTPDownloader()
	downloader.Download("https://www.baidu.com/")
}

4.1.2. 模板方法

/*下载器*/
type IDownloader interface {
	Download(url string)
}

/*模板方法内部的步骤*/
type ISteps interface {
	getContentFromUrl()
	saveContentToDisk()
}

type template struct {
	ISteps //模板方法的内部步骤接口
	url    string
}

func newTemplate(iSteps ISteps) *template {
	return &template{ISteps: iSteps}
}

/*模板方法固定的步骤*/
func (t *template) Download(url string) {
	t.url = url
	fmt.Print("准备下载\n")
	t.ISteps.getContentFromUrl()
	t.ISteps.saveContentToDisk()
	fmt.Print("下载完成\n")
	fmt.Println()
}

4.1.3. 具体的模板方法子类

/*FTP下载器实现template接口*/
type FTPDownloader struct {
	*template
}

func NewFTPDownloader() *FTPDownloader {
	downloader := &FTPDownloader{}
	downloader.template = newTemplate(downloader)
	return downloader
}

func (F *FTPDownloader) getContentFromUrl() {
	fmt.Printf("FTP从URL[%s]获取数据\n", F.url)
}

func (F *FTPDownloader) saveContentToDisk() {
	fmt.Printf("FTP把数据存放到磁盘\n")
}



/*HTTP下载器实现template接口*/
type HTTPDownloader struct {
	*template
}

func NewHTTPDownloader() *HTTPDownloader {
	downloader := &HTTPDownloader{}
	downloader.template = newTemplate(downloader)
	return downloader
}

func (H *HTTPDownloader) getContentFromUrl() {
	fmt.Printf("HTTP从URL[%s]获取数据\n", H.url)
}

func (H *HTTPDownloader) saveContentToDisk() {
	fmt.Printf("HTTP把数据存放到磁盘\n")
}

4.2. 第二种

package 模板方法

import (
	"fmt"
	"testing"
)

// 接口
type IGetInfo interface {
	GetName() string
	GetAge() int
}

//抽象实现类
type BaseGetInfo struct {
}

func (b *BaseGetInfo) GetName() string {
	return "BaseGetInfo-GetName"
}

func (b *BaseGetInfo) GetAge() int {
	return 0

}

//具体类1
type GetInfo1 struct {
	*BaseGetInfo
}

func (a *GetInfo1) GetAge() int {
	return 1

}

//具体类2
type GetInfo2 struct {
	*BaseGetInfo
}

func (a *GetInfo2) GetName() string {
	return "GetInfo2-GetName"
}

//模板方法
func TemplateMethod(handler IGetInfo) {
	fmt.Println("1111")
	fmt.Println(handler.GetName(),
		handler.GetAge())
	fmt.Println("2222")
}

func TestHandle(t *testing.T) {

	TemplateMethod(&GetInfo1{})
	TemplateMethod(&GetInfo2{})

}

5. 实例

模板方法+简单工厂+策略模式

  • pb.go
package pb

type SwitchRoomReq struct {

}

type SwitchRoomRsp struct {

}
  • abstract_template.go
package template

import (
	"context"
	"fmt"
	"test/template/pb"
)

/*实现了IFetchDataService*/
type abstractFetchDataService struct {
	IFetchDataSteps //内部的处理步骤
	req             *pb.SwitchRoomReq
	rsp             *pb.SwitchRoomRsp
	basicInfo       interface{} //基础数据
	extraInfo       interface{} //额外数据
}

func (t *abstractFetchDataService) checkParam(ctx context.Context) error {
	fmt.Println("default checkParam")
	return nil
}

func (t *abstractFetchDataService) getBasicInfo(ctx context.Context) error {
	fmt.Println("default getBasicInfo")
	return nil

}

func (t *abstractFetchDataService) getExtraInfo(ctx context.Context) error {
	fmt.Println("default getExtraInfo")
	return nil

}

//newTemplate
func newTemplate(iSteps IFetchDataSteps) *abstractFetchDataService {
	return &abstractFetchDataService{IFetchDataSteps: iSteps}
}

/*模板方法固定的步骤*/
func (t *abstractFetchDataService) HandleRequest(ctx context.Context, req *pb.SwitchRoomReq, rsp *pb.SwitchRoomRsp) error {
	t.req = req
	t.rsp = rsp
	if err := t.IFetchDataSteps.checkParam(ctx); err != nil {
		return err
	}
	if err := t.IFetchDataSteps.getBasicInfo(ctx); err != nil {
		return err
	}
	if err := t.IFetchDataSteps.getExtraInfo(ctx); err != nil {
		return err
	}
	return nil
}
  • content_read_template_impl.go
package template

import (
	"context"
	"fmt"
)

const TypeContentReadService = 2

var contentReadServiceConstructorFunc fetchDataServiceConstructor = func() IFetchDataService {
	return NewFetchDataFromContentReadService()
}

func init() {
	GlobalServiceFactory.RegisterService(TypeContentReadService, contentReadServiceConstructorFunc)
}

/*content_read服务*/
type FetchDataFromContentReadService struct {
	*abstractFetchDataService
}

func (f *FetchDataFromContentReadService) checkParam(ctx context.Context) error {
	fmt.Println("checkParam")
	return f.abstractFetchDataService.checkParam(ctx)
}

func (f *FetchDataFromContentReadService) getBasicInfo(ctx context.Context) error {
	fmt.Println("getBasicInfo")
	return f.abstractFetchDataService.getBasicInfo(ctx)

}

//func (f *FetchDataFromContentReadService) getExtraInfo(ctx context.Context) error {
//	fmt.Println("getExtraInfo")
//	return f.abstractFetchDataService.getExtraInfo(ctx)
//
//}


//NewFetchDataFromContentReadService
func NewFetchDataFromContentReadService() *FetchDataFromContentReadService {
	service := &FetchDataFromContentReadService{}
	service.abstractFetchDataService = newTemplate(service)
	return service
}
  • factory.go
package template

import "fmt"

var GlobalServiceFactory = newServiceFactory()

//自定义func。用于创建service实例
type fetchDataServiceConstructor = func() IFetchDataService

//serviceFunc工厂
type serviceFactory struct {
	serviceMap map[int]fetchDataServiceConstructor
}

//newServiceFactory
func newServiceFactory() *serviceFactory {
	return &serviceFactory{serviceMap: make(map[int]fetchDataServiceConstructor)}
}

//根据service type获取service
func (s *serviceFactory) GetServiceByType(serviceType int) (IFetchDataService, error) {
	serviceConstructor, ok := s.serviceMap[serviceType]
	if ok {
		return serviceConstructor(), nil
	}
	err := fmt.Errorf("GetServiceByType: serviceType %v invalid", serviceType)
	return nil, err
}

//注册serviceType和serviceConstructor
func (s *serviceFactory) RegisterService(serviceType int, serviceConstructor fetchDataServiceConstructor) {
	s.serviceMap[serviceType] = serviceConstructor
}
  • itemplate.go
package template

import (
	"context"
	"test/template/pb"
)

/*对外提供获取数据列表的接口*/
type IFetchDataService interface {
	HandleRequest(ctx context.Context, req *pb.SwitchRoomReq, rsp *pb.SwitchRoomRsp) error
}

/*内部的处理步骤*/
type IFetchDataSteps interface {
	//检查参数
	checkParam(ctx context.Context) error
	//获取基础数据
	getBasicInfo(ctx context.Context) error
	//获取额外数据
	getExtraInfo(ctx context.Context) error
}
  • template_test.go
package template

import (
	"context"
	"testing"
)

func TestTemplate(t *testing.T) {
	service, _ := GlobalServiceFactory.GetServiceByType(TypeContentReadService)
	service.HandleRequest(context.Background(), nil, nil)
}
//输出
checkParam
default checkParam
getBasicInfo
default getBasicInfo
default getExtraInfo

6. 参考