InterfaceSummary接口小结
InterfaceSummary
接口小结
继承
注意: 其实这个和接口没多大关系,结构体实现,为了和实现接口做对比,但是还是要放在这里
场景: 所有的程序员都有格子衫,有一个叫mike的普通程序员也是如此。但是有个牛逼的程序员mao
,他还会骑 自行 车,也就是运动员的运动。
实现代码
# nest.go文件
package nest
import "fmt"
// 所有程序员的特质: 格子衫
type CommonProgrammer struct {
Name string
}
// 所有程序员都有的特质: 格子衫
func (p *CommonProgrammer) commonHave() {
fmt.Println(p.Name, "有格子衫")
}
// 一个叫maomao的程序员还会骑车
type MaoProgrammer struct {
CommonProgrammer
Biking string
}
func (m *MaoProgrammer) PrintSkill() {
fmt.Println(m.Name, "还会", m.Biking)
}
func TestMain() {
// 先初始化一个普通程序员
mike := CommonProgrammer{}
mike.Name = "mike"
mike.commonHave()
// 特殊技能程序员
maomao := MaoProgrammer{}
maomao.Name = "mao"
maomao.Biking = "骑车"
maomao.commonHave()
maomao.PrintSkill()
}
测试
# nest_test.go文件
package nest
import "testing"
func TestTestMain(t *testing.T) {
TestMain()
}
# 输出
mike 有格子衫
mao 有格子衫
mao 还会 骑车
实现接口
对于以上的自行车运动,这样看起来并不好,假如有其他分类,看着不是很规范,我们又不想破坏程序员继承关系,又想拓展其他方面,这时候我们就需要接口了
代码实现
package real
import "fmt"
// 声明运动员接口
type Sporter interface {
Biking()
}
// 所有程序员的特质: 格子衫
type CommonProgrammer struct {
Name string
}
func NewCommonProgrammer(n string) CommonProgrammer {
return CommonProgrammer{Name:n}
}
// 所有程序员都有的特质: 格子衫
func (p *CommonProgrammer) commonHave() {
fmt.Println(p.Name, "有格子衫")
}
// 一个叫maomao的程序员还会骑车
type MaoProgrammer struct {
CommonProgrammer
}
func NewMaoProgrammer(n string) MaoProgrammer {
return MaoProgrammer{CommonProgrammer{Name:n}}
}
func (m *MaoProgrammer) Biking() {
fmt.Println(m.Name, "还会骑自行车")
}
func TestIntesrface(S Sporter) {
fmt.Println("我是主要测试")
S.Biking()
fmt.Println("这里证明了结构体实现了接口的所有方法,就是这个接口")
}
func TestMain() {
// 先初始化一个普通程序员
mike := NewCommonProgrammer("mike")
mike.commonHave()
// 特殊技能程序员
maomao := NewMaoProgrammer("mao")
maomao.commonHave()
var i Sporter
// 注意这里的取地址
i = &maomao
TestIntesrface(i)
}
测试
package real
import "testing"
func TestTestMain(t *testing.T) {
TestMain()
}
输出
mike 有格子衫
mao 有格子衫
我是主要测试
mao 还会骑自行车
这里证明了结构体实现了接口的所有方法,就是这个接口
小结
-
接口和继承解决的问题不同
继承在于解决代码的复用性和可维护性
接口价值在于设计好规范
-
接口比继承更加灵活
接口满足
like-a
,继承需要是is-a
-
接口在一定程度上实现代码解耦
附加
责任链模式(设计模式,这里可以更加理解接口,我很喜欢这个大佬的实现,因为这个代码可以无限拓展,结 合了链表)
package main
import (
"fmt"
)
// Context Context
type Context struct {
}
// Handler 处理
type Handler interface {
// 自身的业务
Do(c *Context) error
// 设置下一个对象
SetNext(h Handler) Handler
// 执行
Run(c *Context) error
}
// Next 抽象出来的 可被合成复用的结构体
type Next struct {
// 下一个对象
nextHandler Handler
}
// SetNext 实现好的 可被复用的SetNext方法
// 返回值是下一个对象 方便写成链式代码优雅
// 例如 nullHandler.SetNext(argumentsHandler).SetNext(signHandler)
func (n *Next) SetNext(h Handler) Handler {
n.nextHandler = h
return h
}
// Run 执行
func (n *Next) Run(c *Context) (err error) {
// 由于go无继承的概念 这里无法执行当前handler的Do
// n.Do(c)
if n.nextHandler != nil {
// 合成复用下的变种
// 执行下一个handler的Do
if err = (n.nextHandler).Do(c); err != nil {
return
}
// 执行下一个handler的Run
return (n.nextHandler).Run(c)
}
return
}
// NullHandler 空Handler
// 由于go无继承的概念 作为链式调用的第一个载体 设置实际的下一个对象
type NullHandler struct {
// 合成复用Next的`nextHandler`成员属性、`SetNext`成员方法、`Run`成员方法
Next
}
// Do 空Handler的Do
func (h *NullHandler) Do(c *Context) (err error) {
// 空Handler 这里什么也不做 只是载体 do nothing...
return
}
// ArgumentsHandler 校验参数的handler
type ArgumentsHandler struct {
// 合成复用Next
Next
}
// Do 校验参数的逻辑
func (h *ArgumentsHandler) Do(c *Context) (err error) {
fmt.Println("任务0")
return
}
// AddressInfoHandler 地址信息handler
type AddressInfoHandler struct {
// 合成复用Next
Next
}
// Do 校验参数的逻辑
func (h *AddressInfoHandler) Do(c *Context) (err error) {
fmt.Println( "任务1...")
return
}
func main() {
// 初始化空handler
nullHandler := &NullHandler{}
// 链式调用 代码是不是很优雅
// 很明显的链 逻辑关系一览无余
nullHandler.SetNext(&ArgumentsHandler{}).
SetNext(&AddressInfoHandler{})
//无限扩展代码...
// 开始执行业务
nullHandler.Run(&Context{})
// 成功
fmt.Println("Success")
return
}
输出
任务0
任务1...
Success