组合
- 在面向对象的世界中,对象由更小的对象组合而成。
- 术语:对象组合或组合
- Go 通过结构体实现组合(composition)。
- Go 提供了“嵌入”(embedding)特性,它可以实现方法的转发(forwarding)
- 组合是一种更简单、灵活的方式。
组合结构体
package mainimport "fmt"type report struct { sol int high, low float64 lat, long float64}func main() { report := report{sol: 15, high: -1.0, low: -78.0, lat: -4.5895, long: 137.4417} fmt.Printf("%+v\n", report)}
package mainimport "fmt"type report struct { sol int temperature temperature location location}type temperature struct { high, low celsius}type location struct { lat, long float64}type celsius float64func main() { bradbury := location{-4.5895, 137.4417} t := temperature{high: -1.0, low: -78.0} report := report{sol: 15, temperature: t, location: bradbury} fmt.Printf("%+v\n", report) fmt.Printf("a balmy %vº C\n", report.temperature.high)}
package mainimport "fmt"type report struct { sol int location location temperature temperature}type temperature struct { high, low celsius}type location struct { lat, long float64}type celsius float64func (t temperature) average() celsius { return (t.high + t.low) / 2}func (r report) average() celsius { return r.temperature.average()}func main() { t := temperature{high: -1.0, low: -78.0} fmt.Printf("average %vº C\n", t.average()) report := report{sol: 15, temperature: t} fmt.Printf("average %vº C\n", report.temperature.average())}
转发方法
- Go 可以通过 struct 嵌入 来实现方法的转发。
- 在 struct 中只给定字段类型,不给定字段名即可。
package mainimport "fmt"type report struct { sol int temperature location}type temperature struct { high, low celsius}type location struct { lat, long float64}type celsius float64func (t temperature) average() celsius { return (t.high + t.low) / 2}func main() { report := report{ sol: 15, location: location{-4.5895, 137.4417}, temperature: temperature{high: -1.0, low: -78.0}, } fmt.Printf("average %vº C\n", report.average()) fmt.Printf("average %vº C\n", report.temperature.average()) fmt.Printf("%vº C\n", report.high) report.high = 32 fmt.Printf("%vº C\n", report.temperature.high)}
package mainimport "fmt"type sol inttype report struct { sol location temperature}type temperature struct { high, low celsius}type location struct { lat, long float64}type celsius float64func (s sol) days(s2 sol) int { days := int(s2 - s) if days < 0 { days = -days } return days}func main() { report := report{sol: 15} fmt.Println(report.sol.days(1446)) fmt.Println(report.days(1446))}
命名冲突
package mainimport "fmt"type sol inttype report struct { sol location temperature}type temperature struct { high, low celsius}type location struct { lat, long float64}type celsius float64func (s sol) days(s2 sol) int { days := int(s2 - s) if days < 0 { days = -days } return days}func (l location) days(l2 location) int { // To-do: complicated distance calculation return 5}func (r report) days(s2 sol) int { return r.sol.days(s2)}func main() { report := report{sol: 15} d := report.days(1446) fmt.Println(d)}
继承 还是 组合?
- Favor object composition over class inheritance.
- 优先使用对象组合而不是类的继承。
- Use of classical inheritance is always optional; every problem that it solves can be solved another way.
- 对传统的继承不是必需的;所有使用继承解决的问题都可以通过其它方法解决。
作业题

package mainimport ( "fmt" "math")type world struct { radius float64}type location struct { name string lat, long float64}func (l location) description() string { return fmt.Sprintf("%v (%.1fº, %.1fº)", l.name, l.lat, l.long)}type gps struct { world world current location destination location}func (g gps) distance() float64 { return g.world.distance(g.current, g.destination)}func (g gps) message() string { return fmt.Sprintf("%.1f km to %v", g.distance(), g.destination.description())}func (w world) distance(p1, p2 location) float64 { s1, c1 := math.Sincos(rad(p1.lat)) s2, c2 := math.Sincos(rad(p2.lat)) clong := math.Cos(rad(p1.long - p2.long)) return w.radius * math.Acos(s1*s2+c1*c2*clong)}func rad(deg float64) float64 { return deg * math.Pi / 180}type rover struct { gps}func main() { mars := world{radius: 3389.5} bradbury := location{"Bradbury Landing", -4.5895, 137.4417} elysium := location{"Elysium Planitia", 4.5, 135.9} gps := gps{ world: mars, current: bradbury, destination: elysium, } curiosity := rover{ gps: gps, } fmt.Println(curiosity.message())}