dongsui4658 2018-11-09 10:47
浏览 68
已采纳

在Golang中使用现有接口时如何添加更多功能?

Suppose I have an interface Foo, and I am adding a struct which needs methods of Foo and few additional methods also. In that case out of following two, which is considered to be best practice? Or if there is some other more appropriate third way, then do suggest.

Approach 1

type Foo interface {
   methodA()
}

type Bar struct {
}

func (b Bar) methodA () {
   ...
}

func (b Bar) methodB () {
   ...
}

Approach 2

type Foo interface {
   methodA()
}

type Bar struct {
   Foo    // this can be initialized with any concrete implementation of Foo
}

func (b Bar) methodB () {
   ...
}

Also, it will be great if it can be pointed out in which scenarios above approaches are better fit for? Thanks!

  • 写回答

1条回答 默认 最新

  • duanpendan8067 2018-11-09 11:40
    关注

    Technical note: first method assures you (besides mistakes in initializing the struct) you can call methodA on Bar, second one don't because you have to initialize the interface field with something respecting that interface to not have a nil deference error.

    Note that using the second method methodA is not called on Bar but on the Foo embedded object!

    Second method is useful if you have a common implementation that can be shared by many objects and is self containing, i.e. consider you wanna know if an object implements a Log method to be sure you can log something with that object: in this case you can have a method that returns a Logger and set the interface field with that. Example follows:

    package main
    
    import "fmt"
    
    type Logger interface {
        Log(string)
    }
    
    type ConsoleLogger struct {} // See? No external dependencies
    
    func (Cl ConsoleLogger) Log(msg string) {
        fmt.Println(msg)
    }
    
    type A1 struct {
        Logger
    }
    
    type A2 struct {
        Logger
    }
    
    func main() {
        a := A1{ConsoleLogger{}}
        b := A2{ConsoleLogger{}}
        a.Log("Test")
        b.Log("Test")
    }
    

    Embedding objects is useful for dispatching method calls, remember it's just sintactic sugar in the end so besides passing the containing object you don't have any way to use its fields.

    If Logger interface had to use outer object (A1 and A2) data in some sort of way than this method would be awkward because you would have to initialize the interface object which would then store some reference to the needed data with a waste of memory in some cases.

    IMHO method one forces you to write more code but you are more free in the interface implementation and you can mix the two approaches by embedding a Logger and then override the Log method in A1 struct.

    Additionally, you can nonetheless pass something to build something implementing an interface:

    package main
    
    import "fmt"
    
    type Logger interface {
        Log(string)
    }
    
    type ConsoleLogger struct {
        Prepend string // string to prepend to log message
    }
    
    func (Cl ConsoleLogger) Log(msg string) {
        fmt.Println(Cl.Prepend + "-" + msg)
    }
    
    type A1 struct {
        Logger
    }
    
    type A2 struct {
        Logger
    }
    
    func (a A2) Log(msg string) { // Overriding implementation
        fmt.Println("In A2")
        a.Logger.Log(msg) // Call the original interface value!
    }
    
    func main() {
        a := A1{ConsoleLogger{"A1"}}
        b := A2{ConsoleLogger{"A2"}}
        a.Log("Test")
        b.Log("Test")
    }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?