组合
- 在面向对象的世界中,对象由更小的对象组合而成。
- 术语:对象组合或组合
- Go 通过结构体实现组合(composition)。
- Go 提供了“嵌入”(embedding)特性,它可以实现方法的转发(forwarding)
- 组合是一种更简单、灵活的方式。
组合结构体
package main
import "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 main
import "fmt"
type report struct {
sol int
temperature temperature
location location
}
type temperature struct {
high, low celsius
}
type location struct {
lat, long float64
}
type celsius float64
func 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 main
import "fmt"
type report struct {
sol int
location location
temperature temperature
}
type temperature struct {
high, low celsius
}
type location struct {
lat, long float64
}
type celsius float64
func (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 main
import "fmt"
type report struct {
sol int
temperature
location
}
type temperature struct {
high, low celsius
}
type location struct {
lat, long float64
}
type celsius float64
func (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 main
import "fmt"
type sol int
type report struct {
sol
location
temperature
}
type temperature struct {
high, low celsius
}
type location struct {
lat, long float64
}
type celsius float64
func (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 main
import "fmt"
type sol int
type report struct {
sol
location
temperature
}
type temperature struct {
high, low celsius
}
type location struct {
lat, long float64
}
type celsius float64
func (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 main
import (
"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())
}