Go v.s. Java
接口:组合 vs 继承
Java:从抽象到具体(Top-Down)
- Java 开发倾向于先设计抽象(接口、基类),再去实现它们。
- 鼓励开发者先思考如何构建通用的核心功能,基于核心功能通过继承构建其他功能。
- Java 使用
implement
关键字实现接口。
1 2 3 4 5 6 7
interface Animal { void speak(); } class Dog implements Animal { public void speak() { System.out.println("Woof"); } }
你一开始就得知道你需要什么“动物接口”,再去设计 Dog。
Go:从具体到抽象(Bottom-Up)
- Go 倾向于先实现具体类型和方法,然后在具体使用场景中提炼出接口。
- 鼓励开发者进行”行为驱动的面向对象开发”,先专注于用代码解决问题,在此过程中进行抽象。
- Go 使用的是“结构化相等”——如果这两个东西的形状相同,它们就可以相互替换。因此只需实现接口的所有方法,就视为实现了此接口。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
type Dog struct{} func (d Dog) Speak() { fmt.Println("Woof") } // Somewhere else: type Speaker interface { Speak() } func MakeItSpeak(s Speaker) { s.Speak() } MakeItSpeak(Dog{}) // Dog 自动实现 Speaker
Dog 并不知道 Speaker 存在,但因为方法匹配,自动就“被认为”实现了接口。
虽然 implements
对熟悉静态类型系统的程序员来说很合理,但它会在接口声明和实现之间创建时间依赖关系,也就是说,接口声明必须先于实现出现,这一点制造了代码耦合、演进困难、先后依赖等问题。现代语言 Go
、TypeScript
已经意识并改进了这一点。
类型断言 vs 隐式类型转换
interface{}
vs Object
本文由作者按照 CC BY 4.0 进行授权