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) {
360
+
// ...
361
+
}
362
+
```
363
+
364
+
For callers, they should define **their own interface in their own package** (and **NOT import them** from another package).
365
+
Continuing our example:
366
+
367
+
```go
368
+
package service
369
+
370
+
type Database interface {
371
+
Get(key string) (value string)
372
+
Set(key string, value string)
373
+
}
374
+
375
+
type Service struct {
376
+
database Database
377
+
}
378
+
379
+
func New(database Database) *Service {
380
+
return &Service{database: database}
381
+
}
382
+
```
383
+
384
+
An additional element to note is that you should inject the actual implementation down your call stack.
385
+
Implementation initialisation should ideally occur at the top-most layer of your call stack, such as the `main.main` function.
386
+
387
+
#### Narrow interfaces and composition
388
+
389
+
All your interfaces should be as **narrow** as possible. This means two things:
390
+
391
+
1. Interfaces should only have methods that must be used in your package. For example if you need only a single method and the concrete implementation has 2 methods, your interface should only contain that single method. As a side effect, this also produces smaller generated mock test file.
392
+
2. Interfaces should have one or two methods. If you need a larger interface, compose it using smaller interfaces. For example:
393
+
394
+
```go
395
+
type Database interface {
396
+
Getter
397
+
Setter
398
+
}
399
+
400
+
type Getter interface {
401
+
Get(key string) (value string)
402
+
}
403
+
404
+
type Setter interface {
405
+
Set(key string, value string)
406
+
}
407
+
```
408
+
409
+
This is also useful such that you can use the narrow interfaces (such as `Getter`) in unexported package-local functions.
410
+
411
+
#### Exported interfaces with exported methods only
412
+
413
+
All your interfaces and their methods should be **EXPORTED**. This forces you to either:
414
+
415
+
* dependency inject the concrete implementation, which would force you to export the interface
416
+
* 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.
417
+
* split out the implementation in its own subpackage and then define your exported interface with exported methods.
418
+
419
+
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.
420
+
421
+
### Additional notes on interfaces
422
+
423
+
* The exception to importing interfaces is the standard library. Feel free to use for example `io.Reader`.
424
+
328
425
## Panic
329
426
330
427
In Go, `panic` should only be used when a **programming error** has been encountered.
0 commit comments