function_interface
normal function
在golang中,函数是一等公民,那就意味着函数可以做参数,返回值,甚至是struct成员, 来看下面的例子
//the entrance of program
func main(){
}
a := func(s string) error{
return errors.New("some error")
}
//function that reads the fields from the receiver
func (r receiver_type) methodName(params) {
//code here
}
//pointer receiver
func (r *receiver_type) methodName(params) {
//code here
}
//return function
func A() func(s stirng) string {
//some code
return func()stirng{
return ""
}
}
func printf(fmt string ,args ...interface{}) //args为slice
factory function
工厂函数用于创建自定义的结构体 便于使用者调用(使用者无需关心结构体有哪些字段)
func NewPerson(name string) *Person{
return &Person{Name: name}
}
通过工厂创建自定义结构体的方式可以让调用者不用太关注结构体内部的字段,给工厂函数传参就可以了
//下面的代码可以创建Person类型的指针变量p1
//工厂函数也可以用来创建一个接口
//可以隐藏内部具体的实现 让调用者只需关注接口的使用即可
p1:=NewPerson("scott")
example:
package main
import "fmt"
func main() {
e := New("File Not Found")
fmt.Println(e) //File Not Found
}
//工厂函数,返回一个error接口,其实具体实现是 *errorString
func New(text string) error {
return &errorString{text}
}
//结构体,内部一个字段s,存储错误信息
type errorString struct {
s string
}
//用于实现error接口
func (e *errorString)Error() string{
return e.s
}
interface
接口是一种约定
它是一个高度抽象的类型
不用和具体的实现细节绑定在一起
//struct
type Person struct{
Name string
Age uint
address Address
}
type Address struct {
Province string
City string
}
type Stringer interface {
String() string
}
//我们来实现Stringer这个接口
func (p Person) String() string{
return fmt.Sprintf("the name is %s, age is %d\n",p.Name, p.Age)
}
func (a Address) String() string{
return fmt.Sprintf("the province is %s, city is %s\n",a.Province, a.City)
}
//接着我们来定一个可以打印string的方法
//这个函数的优势是:它是面向接口编程的,只要实现了fmt.Stringer接口都可以打印字符串
//而不用管具体的实现
func printString(s fmt.Stringer) {
fmt.Println(s.String())
}
来体验一下接口的好处:
p:=Person{Name:"scott",Age:18}
printString(p) //the name is scott, age is 18
//因为Address实现了fmt.Stringer接口,所以printString方法无需做任何改变
//可以直接打印使用
//这就是面向接口的好处:只要定义和调用双方满足约定 就可以使用 而不用管具体实现
//面向接口也能更好的完成升级和重构 而不会有任何影响 因为接口约定没有变
a := Address{
Province:"GuangDong",
City:"Zhanjiang",
}
printString(a) //the province is GuangDong, city is Zhanjiang
tips
需要注意的是:如果一个接口定义了多个方法,那么我们必须实现这个接口的所有方法才算实现了这个接口
值接收者vs指针接收者
- 二者都可以调用方法
- 因为go语言编译器自动做了转换 所以值类型接收者和指针接收者是等价的;但是在接口的实现中值类型接收者和指针接收者又是不一样的
思考:上面的p(Person)实现了fmt.Stringer
接口, 那么其指针p是否也实现了fmt.Stringer
接口呢?
运行我们发现仍然可以正常打印:
所以我们得出以下结论:
以值类型接收者实现接口的时候,类型本身和该类型的指针类型都实现了该接口
接着我们改一下代码,把以值类型接收者实现接口改为以指针类型接收者实现接口
运行之后发现报错了:
意思是类型 Person 没有实现fmt.Stringer
接口,结论
以指针类型接收者实现接口的时候,只有指针类型实现了该接口
tips
可以看到指针类型比较万能,实现的接口类型都含有指针
struct vs interface
strcut是字段的组合。 interface是方法的组合。
//a combination of functions
type Bird interface{
fly() string
}
//define a new data struct
type Person struct{
firstName string
Age int
lastName string
}