如何在下面干燥?

问题描述:

添加了getName()函数的典型示例。如何在下面干燥?

我不知道怎么能不写getName()circlerect两次?

package main 

import "fmt" 
import "math" 

// Here's a basic interface for geometric shapes. 
type geometry interface { 
    area() float64 
    perim() float64 
    getName() string 
} 

// For our example we'll implement this interface on 
// `rect` and `circle` types. 
type rect struct { 
    width, height float64 
    name string 
} 
type circle struct { 
    radius float64 
    name string 
} 

// To implement an interface in Go, we just need to 
// implement all the methods in the interface. Here we 
// implement `geometry` on `rect`s. 
func (r rect) area() float64 { 
    return r.width * r.height 
} 
func (r rect) perim() float64 { 
    return 2*r.width + 2*r.height 
} 
func (r rect) getName() string { 
    return r.name 
} 

// The implementation for `circle`s. 
func (c circle) area() float64 { 
    return math.Pi * c.radius * c.radius 
} 
func (c circle) perim() float64 { 
    return 2 * math.Pi * c.radius 
} 
func (c circle) getName() string { 
    return c.name 
} 

// If a variable has an interface type, then we can call 
// methods that are in the named interface. Here's a 
// generic `measure` function taking advantage of this 
// to work on any `geometry`. 
func measure(g geometry) { 
    fmt.Println(g) 
    fmt.Println(g.area()) 
    fmt.Println(g.perim()) 
    fmt.Println(g.getName()) 
} 

func main() { 
    r := rect{width: 3, height: 4, name: "rect5"} 
    c := circle{radius: 5, name: "circle2"} 

    // The `circle` and `rect` struct types both 
    // implement the `geometry` interface so we can use 
    // instances of 
    // these structs as arguments to `measure`. 
    measure(r) 
    measure(c) 
} 
+3

不,你不能,因为类型'geometry'是一个接口,因此没有暴露字段,只暴露方法。 – Adrian

+0

您需要为每种类型定义方法以满足接口。你不能让这个方法比'return c.name'更简单,所以没有任何功能去重复数据。 – JimB

+0

@Adrian哎呀,我的错! –

您可以 - 也可能应该 - 将您的几何类型嵌入另一个包含该名称的结构类型。除非名称是“圆形”或“方形”或您有什么名称,否则名称实际上与几何图形本身没有任何关系。所以你可以有:

type namedGeometry struct { 
    geometry 
    name string 
} 

func (ng *namedGeometry) getName() string { 
    return ng.name 
} 

这将服务于相同的目的,保持干爽,并保持关注点分离。通过嵌入geometry,您仍然可以在namedGeometry实例上调用geometry方法。

我会采用类似Adrian's的方法,但是我会在其他类型中嵌入一个基本类型,它具有通用功能。

type baseShape struct { 
    name string 
} 

func (s *baseShape) getName() string { 
    return s.name 
} 

type rect struct { 
    width, height float64 
    baseShape 
} 

type circle struct { 
    radius float64 
    baseShape 
} 

做完这些之后,您只需要实现不同形状之间每种类型的功能。

+0

通常我们嵌入类型本身,而不是指向类型的指针。这只是增加了一个不必要的间接级别,因此'baseShape'应该嵌入到'rect'和'circle'(而不是'* baseShape')中。 –

+1

@KavehShahbazian你是对的。当返回一个满足一个接口的类型时,如果这个类型有所有方法的指针接收器,那么你必须返回一个指向类型的指针([我的意思是一个例子])(https://play.golang.org/p/GngXC1Rze2))。我认为这里适用,但显然不适用。 – Gavin