I'm trying to understand the topic of interfaces in Go.
I wrote a first application in which a json parser is generated in a package and is to be made available to another package via an interface.
package jsonparser
/*type Parser interface {
Perform([]byte, any) error
}*/
type ParserImpl struct {
ctx context.Context
}
// factory method to create parser
func New(ctx context.Context) *ParserImpl {
return &ParserImpl{ctx: ctx}
}
// interface impl
func (parser *ParserImpl) Perform(data []byte, v any) error {
return json.Unmarshal([]byte(data), v)
}
// interface impl
func (parser *ParserImpl) PerformSecondMethod(str string, v any) error {
...
}
package oc
type JsonParserInterface interface {
Perform([]byte, any) error
}
type Context struct {
appCtx context.Context
jsonCtx JsonParserInterface
}
type OC struct {
ctx Context
}
func New(ctx context.Context, parser JsonParserInterface) *OC {
oc_ctx := Context{appCtx: ctx, jsonCtx: parser}
return &OC{ctx: oc_ctx}
}
package app
func createOc(ctx context.Context) *oc.OC {
jsonparser := jsonparser.New()
return oc.New(ctx, http, jsonparser)
}
My app package contains the main method and should connect all packages (jsonparser, oc etc.) with each other so that each package has the dependencies necessary for its work.
I read in the documentation that the interface should be defined when it is used. Is my approach correct? If the interface or its methods are required in several packages, the interfaces are also defined in all of these packages. Are there other ways to reduce the code duplicate?
If I understand it correctly, each package can decide for itself which method of another package it overwrites via interface. In Go, interfaces are only an instrument to reduce the decupling between packages, but not, as in c , for example, a way of limiting the public interface of a class / package. Any public method of the jsonparser could be used as an interface by another package.
CodePudding user response:
Not really, and I think you may have misunderstood the documentation which you read. You can define the interface in the same package as your structs and functions. As an example: see this in the Sort package.
CodePudding user response:
An interface is a type which defines only methods which you need in your function argument. You can have two, three or four packages and use one function for differnt interfaces but the main rule of interface - variable should implement the interface of argument:
type StructA struct {
counter int
}
func (a *StructA) Increase() {
a.counter
}
func (a *StructA) Decrease() {
if a.counter != 0 {
a.counter--
}
}
type StructB struct {
counter int
numbers []int
}
func (b *StructB) Increase() {
b.counter
b.numbers = append(b.numbers, b.counter)
}
func (b *StructB) Decrease() {
if b.counter != 0 {
b.counter--
}
}
func (b StructB) GetNumbers() []int {
return b.numbers
}
type CounterInterface interface {
Increase()
Decrease()
}
func TimeCounter(counter CounterInterface) {
for i := 0; i < 100; i {
if i%3 == 0 {
counter.Increase()
} else {
counter.Decrease()
}
}
}
func main() {
counterA := StructA{}
counterB := StructB{}
TimeCounter(&counterA)
TimeCounter(&counterB)
}
As you see in example above, StructA
and StructB
has the same interfaces, even one of them has one more other function. And it works because in function TimeCounter
you don't need other functions, only Increase
and Decrease
. You can create another interface with different name but with the same list of functions and it'll work!
type JokeCounter interface {
Increase()
Decrease()
}
func CountJokes(counter JokeCounter) {
// ...
}
func main() {
counterA := StructA{}
counterB := StructB{}
CountJokes(&counterA)
CountJokes(&counterB)
}
Code above woks the same as before but an interface have the other name. An interface is simply a statement for an argument, which should have the methods that the interface tells you to provide, becase function will not work without any of this methods.