'Golang multi-method interface and struct naming
Golang docs provide a clear guidance on how to name single-method interface (by appending "-er"). But what is the best practice to name a multi-method interface that has only a single struct implementing it?
In C# the interface would have "I" prefix, in Java, the class will have "Impl" suffix. Are there similar guidelines for Golang?
The point of having an interface here is to be able to mock it for unit-testing of components that depend on it.
Here is a specific example of UserManager interface. It is going to be consumed by a web api controller that will translate HTTP requests into method calls on this interface. This web api controller will use most of the methods of UserManager.
type UserManager interface { // Should it be UserManagerInterface ?
GetUser(id int) User
CreateUser(user User)
DeleteUser(id int)
ResetPassword(id int, newPassword string)
VerifyEmail(id int)
}
type UserManagerImpl struct { // -Impl, or -Struct, or something else?
}
Solution 1:[1]
Coming from Java/C# to Go requires a paradigm shift.
Because they are implemented implicitly, interfaces are defined where they're consumed, not where they're implemented.
Because they're implemented implicitly, smaller interfaces are preferred over larger interfaces, hence the focus on single-method "Verber" interfaces. If you have a function that needs to read bytes, it can take a io.Reader
, and the function can be supplied any type that has that method (regardless what other methods it has). You probably don't have a function that calls all 5 of the methods listed in your interface (if so, that function is probably doing too much).
If you feel like naming a struct and an interface the same thing and therefore need some kind of prefix/suffix, you're probably thinking about it the wrong way and might need to reconsider your design.
DBAL is one area where there is sometimes a real case for a larger interface (though it should probably still be composed of smaller interfaces). But in this case, "Impl" doesn't tell you anything - Java and C# love to have pointless interfaces, Go does not. If you'll only ever have one implementation, the interface is probably useless.
If you will have multiple implementations, the differences between them should guide your naming. For example, you might have a PostgresUserManager
, a MongoUserManager
, and a MockUserManager
.
Solution 2:[2]
Do you really need UserManagerImpl
struct be public? It's kinda common to have a public interface and the corresponding private implementation. Check this and this.
type Store interface {
RunRoot() string
GraphRoot() string
...
}
type store struct {
...
}
func New() Store {
return &store{}
}
func (s *store) RunRoot() string {
return s.runRoot
}
func (s *store) GraphRoot() string {
return s.graphRoot
}
I mean, if you came up with a decent name for the interface, you still can use it for the implementation. But in general, it's good to call things reflecting what they really are regardless of the best practices of the given language. Projects are unique, it's barely possible to make a list of best practices suitable for all the use cases of the language.
Solution 3:[3]
The same as @Ivan Velichko I also used to make Interfaces public and struct private like so:
type Service interface {
Do() error
}
type service struct {
dependency dependencies.Dependency
}
func (s *service) Do() error {
return s.depedency.Do()
}
func NewService() Service {
return &service{}
}
However if for some reasons you have to make structs or their properties public and you don't want to write a lot of getters or setters consider examples below:
Make interface
name more specific to actions it does
According this articles:
You should:
By convention, one-method interfaces are named by the method name plus an -er suffix or similar modification to construct an agent noun: Reader, Writer, Formatter, CloseNotifier etc.
type ServiceWorker interface {
HandleMessages() error
}
type Service struct {
dependency dependencies.Dependency
}
func (s *service) HandleMessages() error {
return s.depedency.Do()
}
func NewServiceWorker() Service {
return &service{
Property: "property"
}
}
Create function for getting/modifying struct
type Service interface {
Do() error
Service() *service // this will handle getting and modifying your struct
}
type service struct {
dependency dependencies.Dependency
Property string // property to want to expose
}
func (s *service) Do() error {
return s.depedency.Do()
}
func (s *service) Service() *service {
return s
}
func NewService() Service {
return &service{
Property: "property"
}
}
//...
func main() {
s := pkg.NewService()
fmt.Println("p1:", s.Service().Property) // p1: property
s.Service().Property = "second"
fmt.Println("p2:", s.Service().Property) // p2: second
}
Define getters and setters
Define getters and setters in more Java/C# way:
type Service interface {
Do() error
Get() service
Set() *service
}
type service struct {
dependency dependencies.Dependency
Property string // property to want to expose
}
func (s *service) Do() error {
return s.depedency.Do()
}
func (s service) Get() service {
return s
}
func (s *service) Set() *service {
return s
}
func NewService() Service {
return &service{
Property: "property"
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Adrian |
Solution 2 | Ivan Velichko |
Solution 3 | mikolaj semeniuk |