5 ms·
> In other words, use a pure dependency-injection style, and ZERO globals. And it's madness. With this style, all the functions expose their implementation de
by hota_mazi 9y ago
> In other words, use a pure dependency-injection style, and ZERO globals.
And it's madness.
With this style, all the functions expose their implementation details, every single thing they use to accomplish their task. It totally destroys encapsulation.
Then you have the problem that if one of these functions decides to add one more thing, say log a message, then all its callers need to pass it a logger. And all the callers of its callers also need to pass a logger.
And of course, they need to find a way to materialize such a logger in the first place.
Insane advice.
Dependency injection is popular for a reason.
- jchmbrln 9y ago> With this style, all the functions expose their implementation details, every single thing they use to accomplish their task. Not at all. Under the author's guidelines, a function can encapsulate whatever it wants, so long as it doesn't modify state or have side effects. If it wants to do either of those, it should declare its dependency explicitly. I don't see anything so insane about that.
- mixedCase 9y ago> It totally destroys encapsulation. Not at all. Only the initializer needs to know about the needs. Then in the object world you can use a combination of dependency injection and factories to abstract over all the details you don't want callers to know about, and you can do the same more elegantly in the functional world with partial application (AKA currying).
- baby 9y ago> Then in the object world you can use a combination of dependency injection and factories to abstract over all the details you don't want callers to know about And then you made Go a complicated and awful to read language
- mixedCase 9y agoSorry, forgot about mentioning the very language the OP was about. That was stupid. You would use the "New" constructor function pattern in this case, using a higher order function called at injection time to keep it reusable and hide everything. I also prefer keeping the struct it returns as unexported so you get full encapsulation, but golint doesn't like it. I guess this is a personal issue.
- kornish 9y ago> You would use the "New" constructor function pattern in this case, using a higher order function called at injection time to keep it reusable and hide everything. Spot on. I love the decorator pattern for dependency injection. type Server struct { logger *logrus.Logger // optional store databaste.Store // required } type ServerOption func(Server) Server func WithLogger(logger *logrus.Logger) ServerOption { return func(s Server) Server { s.logger = logger return s } } func NewServer(store database.Store, options ...ServerOption) *Server { s := Server{store: store} for _, option := range options { s = option(s) } return &s } func main() { myServer := NewServer(myStore, WithLogger(myLogger)) }
- weberc2 9y agoThis is all common place in Go, we just don't use the terms that you have associated with heavy weight frameworks. DI is simply assembling all the pieces of your application in one single place instead of breaking it up arbitrarily in dozens of different constructors. The first is easier to read and test than the second. Factories are just functions or objects that can create new instances of a thing of the number or configuration of that thing can't be known at compile time.
- chubot 9y agoThat is why I said you need practice writing in this style. It's quite easy to overdo it and end up with a mess. I don't think it has to an all-or-nothing thing. I generally encapsulate all my state, but not all side effects, like printing to the screen. And yes I believe logging should just be a free function. I think configuration of loggers is overrated. But if you need to use a global for that configuration, so be it. Functions don't expose any implementation details -- they expose the state they modify and the side effects they produce. I don't quite understand your last sentence. Dependency injection IS popular and that is what I am advocating and what this post on Go is advocating. It's perhaps not as popular in Go and Python as it is in Java, and perhaps even some corners of the C++ world.