Up to Main Index Up to Journal for August, 2013 JOURNAL FOR THURSDAY 22ND AUGUST, 2013 ______________________________________________________________________________ SUBJECT: The epiphany - Communicate between packages using interfaces DATE: Thu 22 Aug 21:39:17 BST 2013 In my last journal entry I said I didn't think writing about the trials and tribulations of coding on WolfMUD was a good idea. From the emails I got it seems that I was wrong - people do want to read about the dead ends and rewrites and false starts. It lets people see what it takes to create WolfMUD and why sometimes it takes ages for code changes to appear. Having fresh code suddenly appear 'fully formed' seems to be of less interest. As one person put it "If you don't write about the ups and downs what are you going to write about?". A good question indeed - not writing anything last week was just a coincidental fluke. I'd been writing project proposals and specifications for over a week and the last thing I wanted to do was write even more :( So now for all you gentle readers out there I will try to explain my epiphany. In a nutshell the epiphany is: Communicate between packages using interfaces. It's simple and when you think about it, maybe even quite obvious. Although in all the reading I did - forum posts, documentation, source code and books - it was barely even hinted at. Maybe it's so obvious that people just didn't think to write about it? Or I simply just missed it somewhere? Anyway, interfaces in Go are a lot more powerful than I initially assumed. If you keep thinking OOP or try to refer to OOP concepts you probably wont realise how powerful they are - which is what happened to me. So how does "Communicate between packages using interfaces" help with WolfMUD and larger Go projects in general? This is just my take and I've taken it a little further than the original epiphany so any and all comments are welcome! 1. I found it easier to avoid cyclic dependencies by putting interfaces into their own packages. Other packages can import other packages including interface packages but interface packages should only import other interface packages. I found this also makes the code less tightly coupled. For an example look at io/io.go 2. If a method is exported it's parameters should be basic types - int, string, etc. - or an interface type. This again makes the code feel a lot lighter. All you need is an interface with just the methods needed by the method you are passing the parameter to. Quite often you are only calling one or two methods on a parameter and don't actually need to import the parameter's package - just a light interface with a few methods defined suffices. But see point 3 next. 3. If a type is self contained in a package and can only be used by and manipulated by methods in that package then using that type as a parameter in an exported method should be fine as you are only passing it around. A good example of this seems to be bytes.Buffer. 4. If a method can be defined in terms interfaces only - define it with the interfaces as a function. Remember, interfaces cannot be receivers so it needs to be a function. One example of many in io/io.go is WriteString As I said this is my take on my epiphany and you can take it with a pinch of salt[1]. As with any 'rules' there are also times when it might make sense to break them. Following my own points above seems to be working well although I have a lot of refactoring to do - at the moment nothing is working and I've got compiler errors everywhere :( If you do try using the points above I'd like to hear how it worked out for you - good :) or bad :( If I need to update or add to the points above I'll be sure to let everyone know this time instead of holding back ;) -- Diddymus [1] 'grain of salt' seems more common in the US? Up to Main Index Up to Journal for August, 2013