从“技术焦虑”到“策略通关”:一位产品经理的CAIE认证备考心路
2025/12/26 20:02:18
Go接口的最大特点:一个类型不需要显式声明它实现了某个接口,只要它拥有接口要求的所有方法,就自动实现了该接口。
// 定义接口typeSpeakerinterface{Speak()string}// 定义类型typeDogstruct{namestring}// 为Dog实现Speak方法func(d Dog)Speak()string{return"Woof! I'm "+d.name}// 神奇的时刻:Dog自动实现了Speaker接口!// 不需要写:type Dog implements Speaker当你把Dog类型值用于需要Speaker接口的地方时,编译器会:
funcMakeAnimalSpeak(s Speaker){fmt.Println(s.Speak())}funcmain(){dog:=Dog{name:"Buddy"}// 可以传递Dog给需要Speaker的函数MakeAnimalSpeak(dog)// ✓ 自动通过检查// 也可以赋值给接口变量varspeaker Speaker speaker=dog// ✓ 编译器自动确认Dog实现了Speaker}// 空接口:可以保存任何值typeAnyinterface{}// 单方法接口typeStringerinterface{String()string}// 多方法接口typeReadWriterinterface{Read(p[]byte)(nint,errerror)Write(p[]byte)(nint,errerror)}// 嵌套接口typeReadWriteCloserinterface{ReadWriter// 嵌入ReadWriter接口Close()error}// 实现示例typeFilestruct{namestring}// 实现Read方法func(f File)Read(p[]byte)(int,error){returnlen(p),nil}// 实现Write方法func(f File)Write(p[]byte)(int,error){returnlen(p),nil}// 实现Close方法func(f File)Close()error{returnnil}// File现在自动实现了:// - Reader (如果只使用Read)// - Writer (如果只使用Write)// - ReadWriter (因为同时有Read和Write)// - ReadWriteCloser (因为同时有Read、Write、Close)// 定义接口typeShapeinterface{Area()float64Perimeter()float64}// 不同实现typeCirclestruct{Radiusfloat64}typeRectanglestruct{Width,Heightfloat64}func(c Circle)Area()float64{returnmath.Pi*c.Radius*c.Radius}func(c Circle)Perimeter()float64{return2*math.Pi*c.Radius}func(r Rectangle)Area()float64{returnr.Width*r.Height}func(r Rectangle)Perimeter()float64{return2*(r.Width+r.Height)}// 使用接口的函数 - 可以接受任何ShapefuncPrintShapeInfo(s Shape){fmt.Printf("Area: %.2f, Perimeter: %.2f\n",s.Area(),s.Perimeter())}funcmain(){shapes:=[]Shape{Circle{Radius:5},Rectangle{Width:3,Height:4},}for_,shape:=rangeshapes{PrintShapeInfo(shape)}// 输出:// Area: 78.54, Perimeter: 31.42// Area: 12.00, Perimeter: 14.00}// 小接口typeReaderinterface{Read(p[]byte)(nint,errerror)}typeWriterinterface{Write(p[]byte)(nint,errerror)}typeCloserinterface{Close()error}// 组合成新接口typeReadWriterinterface{Reader Writer}typeReadWriteCloserinterface{Reader Writer Closer}// 实现小接口,自动获得大接口typeBufferstruct{data[]byte}func(b*Buffer)Read(p[]byte)(int,error){copy(p,b.data)returnlen(b.data),nil}func(b*Buffer)Write(p[]byte)(int,error){b.data=append(b.data,p...)returnlen(p),nil}func(b*Buffer)Close()error{b.data=nilreturnnil}// Buffer自动实现了:// - Reader, Writer, Closer// - ReadWriter, ReadWriteCloser// 空接口可以保存任何值funcStoreAnything(){varanythinginterface{}anything=42fmt.Printf("int: %v\n",anything)anything="hello"fmt.Printf("string: %v\n",anything)anything=[]int{1,2,3}fmt.Printf("slice: %v\n",anything)}// 作为函数参数funcPrintValue(vinterface{}){fmt.Printf("Value: %v, Type: %T\n",v,v)}funcProcessValue(vinterface{}){// 方法1:类型断言ifstr,ok:=v.(string);ok{fmt.Printf("是字符串: %s\n",str)return}// 方法2:类型switchswitchval:=v.(type){caseint:fmt.Printf("是整数: %d\n",val)casefloat64:fmt.Printf("是浮点数: %.2f\n",val)casebool:fmt.Printf("是布尔值: %v\n",val)case[]int:fmt.Printf("是整型切片,长度: %d\n",len(val))default:fmt.Printf("未知类型: %T\n",val)}}typeMoverinterface{Move()}// 值接收者实现typeCarstruct{modelstring}func(c Car)Move(){fmt.Println(c.model,"moving")}// 指针接收者实现typeBikestruct{modelstring}func(b*Bike)Move(){fmt.Println(b.model,"moving")}funcmain(){varmover Mover car:=Car{"Toyota"}mover=car// ✓ 值可以赋值给接口mover=&car// ✓ 指针也可以(会自动解引用)bike:=Bike{"Giant"}// mover = bike // ✗ 错误!Bike没有实现Movermover=&bike// ✓ 只有指针实现了接口}typeWriterinterface{Write([]byte)(int,error)}funcmain(){varw Writer fmt.Println(w==nil)// true - 接口零值是nilvarbuf*bytes.Buffer// buf是nilw=buf// 接口不为nil,但动态值为nilfmt.Println(w==nil)// falsefmt.Printf("w类型: %T, 值: %v\n",w,w)// *bytes.Buffer, <nil>}// 定义接口typeUserStoreinterface{Save(user User)errorFindByID(idint)(User,error)}// 具体实现typeMySQLStorestruct{db*sql.DB}func(m*MySQLStore)Save(user User)error{// 保存到MySQLreturnnil}func(m*MySQLStore)FindByID(idint)(User,error){// 从MySQL查找returnUser{},nil}// 服务层依赖接口typeUserServicestruct{store UserStore}funcNewUserService(store UserStore)*UserService{return&UserService{store:store}}// 使用funcmain(){// 生产环境用真实存储mysqlStore:=&MySQLStore{db:realDB}service:=NewUserService(mysqlStore)// 测试环境用模拟存储mockStore:=&MockStore{}testService:=NewUserService(mockStore)}// 插件接口typePlugininterface{Name()stringInitialize()errorExecute()error}// 注册插件varplugins=make(map[string]Plugin)funcRegisterPlugin(p Plugin){plugins[p.Name()]=p}// 不同插件实现typeLogPluginstruct{}func(l*LogPlugin)Name()string{return"log"}func(l*LogPlugin)Initialize()error{returnnil}func(l*LogPlugin)Execute()error{fmt.Println("Logging...")returnnil}typeAuthPluginstruct{}func(a*AuthPlugin)Name()string{return"auth"}func(a*AuthPlugin)Initialize()error{returnnil}func(a*AuthPlugin)Execute()error{fmt.Println("Authenticating...")returnnil}funcmain(){// 注册插件RegisterPlugin(&LogPlugin{})RegisterPlugin(&AuthPlugin{})// 执行所有插件for_,plugin:=rangeplugins{plugin.Execute()}}// 技巧:声明一个变量来确保类型实现了接口var_Speaker=(*Dog)(nil)// 编译时检查Dog是否实现Speakervar_Speaker=Dog{}// 检查值类型// 如果Dog没有实现Speaker,这里会编译错误typeMyInterfaceinterface{Method1()Method2()}typeMyTypestruct{}func(m MyType)Method1(){}// func (m MyType) Method2() {} // 注释掉这行会编译错误// 检查是否实现var_MyInterface=MyType{}// 编译错误:缺少Method2理解Go接口的关键是转变思维:不要问"这是什么类型",要问"它能做什么"。只要一个类型有你需要的方法,它就可以被当作接口使用。