You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
func (d *Database) Set(key string, value string) {
358
+
// ...
359
+
}
360
+
```
361
+
362
+
For callers, they should define **their own interface in their own package** (and **NOT import them** from another package).
363
+
Continuing our example:
364
+
365
+
```go
366
+
package service
367
+
368
+
type Database interface {
369
+
Get(key string) (value string)
370
+
Set(key string, value string)
371
+
}
372
+
373
+
type Service struct {
374
+
database Database
375
+
}
376
+
377
+
func New(database Database) *Service {
378
+
return &Service{database: database}
379
+
}
380
+
```
381
+
382
+
An additional element to note is that you should inject the actual implementation down your call stack.
383
+
Implementation initialization should ideally occur at the top-most layer of your call stack, such as the `main.main` function.
384
+
385
+
Finally, to enforce such rule, you can use the [`ireturn` linter](https://github.com/butuzov/ireturn) in your [.golangci.yml configuration](#linting).
386
+
387
+
### Narrow interfaces and composition
388
+
389
+
All your interfaces should be as **narrow** as possible.
390
+
391
+
Interfaces should only have methods that must be used in your **production code** of your Go package.
392
+
For example if you need only a single method and the concrete implementation has 2 methods, your interface should only contain that single method.
393
+
As a side effect, this also produces smaller generated mock test file.
394
+
395
+
If you need to call a method on an interface in **test code** but it's not needed in production code, **DO NOT ADD THE METHOD TO THE INTERFACE**.
396
+
Instead do a type assertion on the interface to call the method. For example:
397
+
398
+
```go
399
+
value := myInterface.(*myImplementation).GetValue()
400
+
```
401
+
402
+
Interfaces should have one or two methods. If you need a larger interface, compose it using smaller interfaces. For example:
403
+
404
+
```go
405
+
type Database interface {
406
+
Getter
407
+
Setter
408
+
}
409
+
410
+
type Getter interface {
411
+
Get(key string) (value string)
412
+
}
413
+
414
+
type Setter interface {
415
+
Set(key string, value string)
416
+
}
417
+
```
418
+
419
+
This is also useful such that you can use the narrow interfaces (such as `Getter`) in unexported package-local functions.
420
+
421
+
### Exported interfaces with exported methods only
422
+
423
+
All your interfaces and their methods should be **EXPORTED**. This forces you to either:
424
+
425
+
* dependency inject the concrete implementation, which would force you to export the interface
426
+
* test the actual implementation if it's defined in the same package (you shouldn't interface & mock it), which would force you to remove the interface definition.
427
+
* split out the implementation in its own subpackage and then define your exported interface with exported methods.
428
+
429
+
A side note is that `mockgen` is rather terrible at generating mocks from interfaces with unexported methods, so that's something you can avoid by having exported methods.
430
+
431
+
### Importing interfaces
432
+
433
+
As a general rule, interface should **NOT** be imported in order to have narrower interfaces, decouple code and reduce package dependency contention.
434
+
The exception is to import interfaces from the **standard library**, some commonly imported interfaces are for example: `io.Reader`, `io.Writer`, `io.Closer`, `fmt.Stringer`, `rand.Source`, `sort.Interface`, `http.Handler`.
435
+
328
436
## Panic
329
437
330
438
In Go, `panic` should only be used when a **programming error** has been encountered.
0 commit comments