Go design question regarding growing interfaces

281 views
Skip to first unread message

Travis Keep

unread,
Dec 16, 2013, 9:11:32 PM12/16/13
to golan...@googlegroups.com
I have a go type that represents a connection to a home automation center. As new functionality is offered, more methods will be added to this class.

There is a configurable event manager that wraps one of these connection instances and runs commands on it at specified times or when specified events happen.

My event manager could take a connection object, but that makes the API too narrow. What if client wants to do testing with a mock or a fake? What if client wants to log events?  

I could create an interface with all the methods, but the connection type will get new methods. The interface that I create today won't expose all the functionality on the connection object tomorrow. To get to new functionality not in the interface, type assertions would be needed.

I could use an empty interface for the connection object, but then each event object that uses the connection would need to use type assertions to get to the methods that it needs. Then there is the question of how to handle failed type assertions.

Are there other options?






Jesse McNelis

unread,
Dec 16, 2013, 10:27:19 PM12/16/13
to Travis Keep, golang-nuts
On Tue, Dec 17, 2013 at 1:11 PM, Travis Keep <kee...@gmail.com> wrote:
> I have a go type that represents a connection to a home automation center.
> As new functionality is offered, more methods will be added to this class.
>
> There is a configurable event manager that wraps one of these connection
> instances and runs commands on it at specified times or when specified
> events happen.
>
> My event manager could take a connection object, but that makes the API too
> narrow. What if client wants to do testing with a mock or a fake? What if
> client wants to log events?
> I could create an interface with all the methods, but the connection type
> will get new methods. The interface that I create today won't expose all the
> functionality on the connection object tomorrow. To get to new functionality
> not in the interface, type assertions would be needed.

The general rule with interfaces is to declare the minimum you need.
If you later need more and want to expand the functionality without breaking
current users then you'll need some kind of default behaviour for
interface methods required by the new functionality.

A few packages start with a minimal interface and then do type
assertions to try to expand it.
This has some problems (incorrectly assuming methods with the right
signature are there to implement the interface) but it's a reasonable
solution.
If you're going this route you should document the interfaces you're
expanding to and make sure the methods in
them are rather unique to reduce the chance of unrelated methods
accidentally implementing the interface.

The other solution is to have a new function for the additional
functionality that takes the expanded interface
and possibly a wrapper type that implements defaults for the new
methods to make it easier for users to
transition to the new API without having to implement the additional
methods on all their types.

Kyle Lemons

unread,
Dec 16, 2013, 10:35:12 PM12/16/13
to Travis Keep, golang-nuts
I would suggest breaking away from an abstraction in which a function call is created for each command.  Instead, have a single function call that knows how to execute a command object, and construct that type such that it will be flexible enough to handle future kinds of commands.  Return an error when you receive an unknown command.


--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

Francesco Bochicchio

unread,
Dec 17, 2013, 6:20:55 AM12/17/13
to golan...@googlegroups.com
What about :
 
type  APIv1 interface {
    DoThis ();
    DoThat ()
}
 
type APIv2 interface {
   APIv1
   DoThisToo()
}
 
This whay when you delive APIv2, old code using APIv1 will not break; at the same time  upgrade to the latest API  will be just a matter of search/replace. The ideal would be to be able to use both interfaces to refer to the same connection, leaving old code as it is but being abe to exploit the new method in new code.

HTH
 
Ciao
-------
FB





Travis Keep

unread,
Dec 17, 2013, 1:25:46 PM12/17/13
to Jesse McNelis, golang-nuts

Thx Jesse I went with your first solution. Start w minimal interface and expand with type assertions.

Øyvind Teig

unread,
Dec 18, 2013, 3:26:21 AM12/18/13
to golan...@googlegroups.com, Jesse McNelis
Nobody seems to mention channels? The interface system in Go is elaborate, but in the examples I don't see channels being used. Contrary, in the channels description at http://golang.org/doc/effective_go.html#channels (especially the "Channels of channels") - doesn't it also describe a kind of interface system? Since channels are typed, just adding a new value in its struct and code for it adds functionality. In the "Interfaces" chapter of the same page there are "Methods required by sort.Interface." Why couldn't that be "channel ends required by sort.gorotine"?
 
Do I smell first-class channels getting to ride in the interface back seat just because the world's interface thinking is method based?
 
Could somebody "sort out" (pun intended) the difference between interface-based interfaces and channel-based interfaces? (Or don't the latter exist?)
 
(In 2001 I wrote an article that "expands a concurrent language to support implementation inheritance by making block structures of the super process-class pluggable, and to interface inheritance by making the language's protocol inheritable." See "From safe concurrent processes to process-classes?" at http://www.teigfam.net/oyvind/pub/pub_details.html#PLUSSING)
 
Øyvind Teig

Henrik Johansson

unread,
Dec 18, 2013, 3:34:46 AM12/18/13
to Øyvind Teig, golang-nuts, Jesse McNelis
This is a common misconception I hear all the time. Build a messaging based system and people seem to think its automatically loosely coupled when in fact all they have done is moved the coupling somewhere else.

Anywhere where you communicate is an interface even though I think this thread is about the actual interface construct in Go.


--

Øyvind Teig

unread,
Dec 18, 2013, 3:54:46 AM12/18/13
to golan...@googlegroups.com, Øyvind Teig, Jesse McNelis
Is there a point in starting a new thread called "interface-based interfaces and channel-based interfaces"? I could try that, and then refer back to here and paste some.
 
Øyvind Teig

Øyvind Teig

unread,
Dec 18, 2013, 6:30:27 AM12/18/13
to golan...@googlegroups.com, Øyvind Teig, Jesse McNelis
Reply all
Reply to author
Forward
0 new messages