go interface

开发项目时为了易于扩展和复用代码使用到了接口,对接口的几点思考做一下简单总结

接口和方法

接口中可以定义一些方法,当某个对象具备其中的某个或某些方法时,则就说这个对象实现了此接口,如
http_server.go

package main

import (
	"fmt"
	"net/http"
)

type Counter struct {
	n int
}

func (ctr *Counter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	ctr.n++
	fmt.Fprintf(w, "counter = %d\n", ctr.n)
}
func main() {
	ctr := new(Counter)
	http.Handle("/counter", ctr)
}

http.handle的第二个参数是接口类型,定义如下,定义了ServerHTTP函数指纹,而Counter对象实现了此方法,那么就可以说Counter实现了Handler接口类型,所以ctr可以作为http.Handle的参数

type Handler interface {
	ServeHTTP(ResponseWriter, *Request)
}

就这个case来说,对于http请求的处理,net/http包提供了许多方法,包括net/http已定义和上面自定义的方法。所以接口就像一副勾勒成型的简笔画,我们可以根据形体涂上合适的颜色。
在这里插入图片描述

使用接口的小例子

以下是部署服务的大概代码逻辑,一般部署服务都包含相同的几步,如初始化安装包、创建目录、初始化配置文件、脚本启动文件、启动服务,所以将这共有的部分,所以可以将这些方法指纹定义在一个接口中,然后具备这些相同方法的服务的对象(比如不同服务的参数对象)可以定义这些方法,进而这些对象也就实现了此接口;
方法DeployServerCommon通过Deploy接口实现这些公共的属性方法;


type serverAInstance common.ServerAInstance
type Deploy interface {
	Init(serverPort int32)
	InitPackage(agentAddr string) error
	RenderConf(agentAddr string) error
	RenderScirpt(agentAddr string) error
	CreateDir(agentAddr string) error
	Start(agentAddr string) error
}
type ClusterAlter interface {
	DeployServer(dp Deploy, cluster_name string) error
}
type ServerAConf struct {
	Port               int32  `json:"port"`
	BaseDir            string `json:"baseDir"`
	DeployDir          string `json:"deployDir"`
	PidDir             string `json:"pidDir"`
	DataDir            string `json:"dataDir"`
	LogFileDir         string `json:"logFileDir"`
	ConfDir            string `json:"confDir"`
	ScriptDir          string `json:"scriptDir"`
	PackageDir         string `json:"packageDir"`
	ServerControlFile  string `json:"serverControlFile"`
	ConfFile           string `json:"confFile"`
}

//生成部署目录相关的变量
func (r *ServerAConf) Init(port int32){
	r.DeployDir = "/test"
	r.Port = port
	r.BaseDir = fmt.Sprintf("%v/ServerA_%v", r.DeployDir, r.Port)
	r.PidDir = fmt.Sprintf("%v/var", r.BaseDir)
	r.DataDir = fmt.Sprintf("%v/data", r.BaseDir)
	r.LogFileDir = fmt.Sprintf("%v/log", r.BaseDir)
	r.ScriptDir = fmt.Sprintf("%v/script", r.DeployDir)
	r.ServerControlFile = fmt.Sprintf("%v/service.sh", r.ScriptDir)
	r.ConfFile = fmt.Sprintf("%v/conf.conf", r.ConfDir)
	return err
}

//渲染服务配置文件并调用agent生成配置文件
func (serverAConf *ServerAConf) RenderConf(agentAddr string) error {
	var (
		serverAConfText string
		err           error
		buf           bytes.Buffer
	)
	t, err := template.New("serverAConf").Parse(ServerAConfTemplate)
	if err != nil {
		common.Log.Warn("fail to parse ServerA conf template. error=[%v]", err)
		return err
	}
	err = t.Execute(&buf, serverAConf)
	serverAConfText = buf.String()
	if err != nil {
		common.Log.Warn("fail to execute ServerA conf template. error=[%v]", err)
		return err
	}
	// 创建配置文件
	cmd := fmt.Sprintf("echo '''%v''' >%v", serverAConfText, serverAConf.ConfFile)
	_, err = networker.SendShellExecMsgToServer(cmd, agentAddr)
	if err != nil {
		common.Log.Warn("fail to exec shell cmd. cmd=[%v] error=[%v]", cmd, err)
		return err
	}
	common.Log.Notice("serverA generate config file successfully.")
	return err
}

//调用agent创建 server启停脚本
func (serverAConf *ServerAConf) RenderScirpt(agentAddr string) ...
}

//创建目录如果不存在
func (config *ServerAConf) CreateDir(agentAddr string) error {
	...
}

// 初始化安装包
func (config *ServerAConf) InitPackage(agentAddr string) error {
	...
}

// 将部署服务中公共的的逻辑抽取到一个函数中
func DeployServerCommon(dp Deploy, serverPort int32, agentAddr string) error {
	var err error
	//初始化配置
	err = dp.Init(serverPort)
	if err != nil {
		common.Log.Warn("fail to init configure. err=[%v]", err)
		return err
	}
	// 下载安装包
	err = dp.InitPackage(agentAddr)
	if err != nil {
		common.Log.Warn("fail to init package. error=[%v]", err)
	}
	//创建相关目录
	err = dp.CreateDir(agentAddr)
	if err != nil {
		common.Log.Warn("fail to create dir. error=[%v]", err)
		return err
	}
	//生成配置文件
	err = dp.RenderConf(agentAddr)
	if err != nil {
		common.Log.Warn("fail to generate config. error=[%v]", err)
	}
	//生成脚本
	err = dp.RenderScirpt(agentAddr)
	if err != nil {
		common.Log.Warn("fail to generate server control script. error=[%v]", err)
	}
	//启动服务
	err = dp.Start(agentAddr)
	if err != nil {
		common.Log.Warn("fail to start server. error=[%v]", err)
		return err
	}
	return err
}

//部署单个ServerA服务
func (instance *ServerAInstance) DeployServer(dp Deploy, clusterName string) error {
	var (
		err error
	)
	serverPort := instance.Port
	agentAddr := fmt.Sprintf("%v:%v", instance.IP, instance.AgentPort)
	err = DeployServerCommon(dp, clusterName, serverPort, agentAddr)
	if err != nil {
		common.Log.Warn("fail to deploy server. serverAInstance=[%s:%d] error=[%v]",
			instance.IP, serverPort, err)
		return err
	}
	common.Log.Notice("deploy server successfully. edisInstance=[%s:%d]",
		instance.IP, serverPort)
	return err
}

通过调用上述方法部署服务serverA

func TestDeployServer(t *testing.T) {
	Convey("deploy serverA server success", t, func() {
		var (
			ca       ClusterAlter
			instance = new(ServerAInstance)
			serverAConf = new(ServerAConf)
		)
		instance.IP = "127.0.0.1"
		instance.Port = 6000
		instance.AgentPort = 8000
		ca = instance
		err := ca.DeployServer(serverAConf, "demo_1")
		So(err, ShouldBeNil)
	})
}

空接口

由于空接口没有定义任何方法指纹,所以任何类型都可以实现空接口,这也就是如果不确定参数类型可以使用interface{}的原因,不过当需要操作此类型时,需要先对interface{}进行类型断言

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值