package main

// 结构体嵌入和匿名成员

// 考虑一个二维的绘图程序，
// 提供了一个各种图形的库，例如矩形、椭圆形、星形和轮形等几何形状。
// 这里是其中两个的定义：

/**
type Circle struct {
	X, Y, Radius int
}
type Wheel struct {
	X, Y, Radius, Spokes int
}
*/

// 一个Circle代表的圆形类型包含了标准圆心的X和Y坐标信息，
// 和一个Radius表示的半径信息。
// 一个Wheel轮形除了包含Circle类型所有的全部成员外，
// 还增加了Spokes表示径向辐条的数量。

// 随着库中几何形状数量的增多，我们一定会注意到它们之间的相似和重复之处，
// 所以我们可能为了便于维护而将相同的属性独立出来：
/**
type Point struct {
	X, Y int
}
type Circle struct {
	Center Point
	Radius int
}
type Wheel struct {
	Circle Circle
	Spokes int
}
*/
// 这样改动之后结构体类型变的清晰了，但是这种修改同时也导致了访问每个成员变得繁琐：

// Point
// Go语言有一个特性让我们只声明一个成员对应的数据类型而不指名成员的名字；这类成员就叫匿名成员。
// 匿名成员的数据类型必须是命名的类型或指向一个命名的类型的指针。
// 下面的代码中，Circle和Wheel各自都有一个匿名成员。
// 我们可以说Point类型被嵌入到了Circle结构体，同时Circle类型被嵌入到了Wheel结构体。
type Point struct {
	X, Y int
}
type Circle struct {
	Point
	Radius int
}
type Wheel struct {
	Circle
	Spokes int
}

func main() {
	/**
	var w Wheel
	w.Circle.Center.X = 10
	w.Circle.Center.Y = 20
	w.Circle.Radius = 5
	w.Spokes = 20
	*/

	// 得益于匿名嵌入的特性，我们可以直接访问叶子属性而不需要给出完整的路径：
	var w Wheel
	w.X = 10     // 等于w.Circle.Point.X
	w.Y = 20     // 等于w.Circle.Point.Y
	w.Radius = 5 // 等于w.Circle.Radius
	w.Spokes = 20

	// 同时上面注释中的全叶子写法也可以使用
	w.Circle.Point.X = 10 // 同 w.X
	w.Circle.Point.Y = 20 // 同 x.Y

	// 不幸的是，结构体字面值并没有简短表示匿名成员的语法， 因此下面的语句都不能编译通过：
	//w = Wheel{8, 8, 5, 20}                       // compile error: unknown fields
	//w = Wheel{X: 8, Y: 8, Radius: 5, Spokes: 20} // compile error: unknown fields

	// 结构体字面值必须遵循形状类型声明时的结构，所以我们只能用下面的两种语法，它们彼此是等价的：
	w = Wheel{Circle{Point{10, 20}, 5}, 20}
	w = Wheel{
		Circle: Circle{
			Point: Point{
				X: 10,
				Y: 20,
			},
			Radius: 5,
		},
		Spokes: 20,
	}
}
