-
Notifications
You must be signed in to change notification settings - Fork 492
Why golint treats this as warning? #210
Comments
It's not a common pattern because callers of that function from other packages will struggle to use it. For instance, they can't put a *sample field in their own struct. |
I agree with |
I have a code which looks like below:- type myMonitor struct {
isEnabled bool
}
var monitorObj *myMonitor // singleton object
// method to ensure singleton behaviour
func GetInstance() *myMonitor {
if monitorObj == nil {
monitorObj = new(myMonitor)
}
return monitorObj
}
func (obj *myMonitor) Hello {
...
}
Should |
The code snippet you presented is still somewhat strange. Since Thus, I believe lint is correct in complaining that GetInstance is returning an unexported type. |
I think that is the way to implement singleton as mentioned here |
Contrary to what that blog recommends, I think it's a poor idea to make the type of the singleton unexported (again, for the reason that the documentation will not show exported methods on an unexported type). I would just make |
If I make |
That is a possibility, but you can document it that people should not do as such. If you want to add the additional boilerplate, you can define an interface: func GetInstance() Instance { ... }
type Instance interface {
Hello()
Goodbye()
private() // This private method prevents others from implementing this interface
} This has the advantage that the methods of the singleton are clearly documented. The reason we want to make sure that the interface cannot be implemented is so that you have the future flexibility of adding more methods to the singleton. If other people can implement the interface, you are barred from ever altering the interface. |
There is the valid use-case that an unexperted struct implements multiple interfaces, e.g. Reader and Writer. If this pattern is forbidden, we end up defining union interfaces like ReaderWriter. This is ugly. |
@sttts can you elaborate, for example with a concrete example? If you're suggesting that this
followed by the client type-asserting o Reader or Writer is better than this
then I have to disagree. |
ReaderWriter is a bad example as they are concepts that appear as a pair often. Instead, something like |
Yes, that would be the canonical approach, if you can't export the concrete type. |
A local super interface doesn't work unfortunately: type A interface { Foo() }
type B interface { Foo() }
type C interface {
A
B
} This fails because |
package waterbottle
type WaterBottle interface {
Liters() int
}
func NewWaterBottle() WaterBottle {
return &plasticWaterBottle{liters: 1}
}
struct plasticWaterBottle type {
liters int
}
func (b *plasticWaterBottle) Liters() int {
return b.liters
} This is illegal because That's unfortunate: we are trying to encapsulate implementation behind an interface. A client has no business working with |
This allows `hyperion` to not have to import `hyperion/marathon`, `hyperion/kubernetes`, etc. but those things can import `hyperion` now (without a cyclic import) at that lets the `NewManager` funcs in each to return a `Manager` interface instead of a pointer to a private struct, which triggers lint warnings like this: ``` exported func NewManager returns unexported type *dockerswarm.manager, which can be annoying to use ``` and there are some good reasons for that lint warning: golang/lint#210
What about this case? Look at these packages:
Lets try to use it (in non-proper way):
And another one:
I guess, compile time errors are better than runtime errors. It's called encapsulation and I can't understand why it's bad? |
It's common to use this pattern/structure in go.
golint
warns "exported func NewSample returns unexported type *main.sample, which can be annoying to use (golint)"The text was updated successfully, but these errors were encountered: