Gotype ShapeInterface interface {
GetName() string
}
type Shape struct {
name string
}
func (s *Shape) GetName() string {
return s.name
}
type Rectangle struct {
Shape
w, h float64
}
ShapeGetName()RectangleShapenameGetName()RectangleShapeShapeInterface我一开始以为这和面向对象的继承没有什么区别,把内部结构体看成是父类,通过嵌套一下结构体就能获得父类的方法,而且还能根据需要重写父类的方法,在实际项目编程中我也是这么用的。直到有一天......
由于我们这很多推广类促销类的需求很多,几乎每月两三次,每季度还有大型推广活动。产品经理也是绞尽脑汁想各种玩法来提高用户活跃和订单量。每次都是前面玩法不一样,但最后都是参与任务得积分啦、分享后抽奖啦。于是乎我就肩负起了设计通用化流程的任务。根据每次需求通用的部分设计了接口和基础的实现类型,同时预留了给子类实现的方法,应对每次不一样的前置条件,这不就是面向对象里经常干的事儿嘛。
ShapeInterfacetype ShapeInterface interface {
Area() float64
GetName() string
PrintArea()
}
// 标准形状,它的面积为0.0
type Shape struct {
name string
}
func (s *Shape) Area() float64 {
return 0.0
}
func (s *Shape) GetName() string {
return s.name
}
func (s *Shape) PrintArea() {
fmt.Printf("%s : Area %v\r\n", s.name, s.Area())
}
// 矩形 : 重新定义了Area方法
type Rectangle struct {
Shape
w, h float64
}
func (r *Rectangle) Area() float64 {
return r.w * r.h
}
// 圆形 : 重新定义 Area 和PrintArea 方法
type Circle struct {
Shape
r float64
}
func (c *Circle) Area() float64 {
return c.r * c.r * math.Pi
}
func (c *Circle) PrintArea() {
fmt.Printf("%s : Area %v\r\n", c.GetName(), c.Area())
}
ShapeInterfaceArea()PrintArea()ShapeArea0.0ShapeRectangeArea()RectangeShapePrintArea()到目前为止,这些还都是我的设想,规划完后自己感觉特兴奋,感觉自己已经掌握了组合(Composition)这种思想的精髓...... 按这个思路我就把整套流程都写完了,单元测试只测了每个子功能,前置条件太复杂加上我还管团队里的其他项目自己的时间不太富余,所以就交付给组里的伙伴们使用了让他们顺便帮我测试下整个流程,然后就现场翻车了......
CircleArea()PrintArea()func main() {
s := Shape{name: "Shape"}
c := Circle{Shape: Shape{name: "Circle"}, r: 10}
r := Rectangle{Shape: Shape{name: "Rectangle"}, w: 5, h: 4}
listshape := []c{&s, &c, &r}
for _, si := range listshape {
si.PrintArea() //!! 猜猜哪个Area()方法会被调用 !!
}
}
运行后的输出结果如下:
Shape : Area 0
Circle : Area 314.1592653589793
Rectangle : Area 0RectangleShapePrintArea()RectangleArea()ShapeArea()CirclePrintArea()Area()PrintArea()PrintArea()GooglePrintArea()Area()Shape.PrintArea()Shape.Area()Shapex.ff- 选择器f可以表示类型T的字段或方法f,或者可以引用T的嵌套匿名字段的字段或方法f。遍历到达f的匿名字段的数量称为其在T中的深度。
- 对于类型T或* T的值x(其中T不是指针或接口类型),x.f表示存在f的T中最浅深度的字段或方法。
回到我们的例子中来就是:
Rectanglesi.PrintArea()Shape.PrintArea()RectanglePrintArea()*RectanglePrintArea()Shape.PrintArea()Shape.Area()Rectangle.Area()ShapeRectangleRectangle : Area 0GoShapeInterfacePrintAreafunc PrintArea (s ShapeInterface){
fmt.Printf("Interface => %s : Area %v\r\n", s.GetName(), s.Area())
}
InitShapeShapeInterfaceShapetype ShapeInterface interface {
Area() float64
GetName() string
SetArea(float64)
}
type Shape struct {
name string
area float64
}
...
func (s *Shape) SetArea(area float64) {
s.area = area
}
func (s *Shape) PrintArea() {
fmt.Printf("%s : Area %v\r\n", s.name, s.area)
}
...
func InitShape(s ShapeInterface) error {
area, err := s.Area()
if err != nil {
return err
}
s.SetArea(area)
...
}
RectangleCircleShapeArea()SetArea()Area()areatype Rectangle struct {
Shape
w, h float64
}
func (r *Rectangle) Area() float64 {
return r.w * r.h
}
r := &Rectangle {
Shape: Shape{name: "Rectangle"},
w: 5, 4
}
InitShape(r)
r.PrintArea()
GoGoGo