Is there an easier way to implement a virtual function with a default implementation in Go?

735 views
Skip to first unread message

Hǎiliàng

unread,
Nov 8, 2013, 10:40:28 AM11/8/13
to golan...@googlegroups.com
Assume there are struct Task1, Task2, Task3 ... that should satisfy a Doer interface. Task1 and Task2's "Do" methods are partially the same, while Task3's "Do" is not like any others. In a language with inheritance like C++, it can be solved with a virtual method Do with a default implementation that contains the common part of code and calls the specific code as another virtual function Do2 implemented in derived classes Task1 and Task2. Task3 could still be able to override virtual method "Do".

I have tried my best to come up with a Go solution by using embbeded interface, but it still seems verbose to me. Is there an easier (or more idiomatic) way to do this?

Here is my code:
http://play.golang.org/p/V1WMLTbhkt

Hǎiliàng

steve wang

unread,
Nov 8, 2013, 11:05:31 AM11/8/13
to golan...@googlegroups.com
Use of embedded struct can supply default implementation.

package main

import(
    "fmt"
)

type Common struct {
}

func (*Common) Do() {
    fmt.Println("common")
} 

type Task1 struct {
    Common
}

func (p *Task1) Do() {
    p.Common.Do()
    fmt.Println("task1")
}

type Task2 struct {
    Common
}

func (p *Task2) Do() {
    p.Common.Do()
    fmt.Println("task2")
}

type Task3 struct {
}

func (p *Task3) Do() {
    fmt.Println("task3")
}

type Doer interface {
    Do()
}

func Do(doer Doer) {
    doer.Do()
}

func main() {
    Do(&Task1{})
    Do(&Task2{})
    Do(&Task3{})

wkharold

unread,
Nov 8, 2013, 12:21:28 PM11/8/13
to golan...@googlegroups.com
Go doesn't have virtual functions for a reason. I found it counter productive to try to shoehorn C++/Java concepts into my Go code. That said, here is another approach: http://play.golang.org/p/0gez52zBYo

... WkH

Hailiang Wang

unread,
Nov 8, 2013, 11:28:39 PM11/8/13
to wkharold, golang-nuts
Thanks for both replies.

Both approaches define a Do method for Task1 and Task2, and then
redirect the common code to another method. But defining Do for Task1
and Task2 is the way that I try to avoid initially.

The basic requirement is that I have a task scheduler that should
handle different types of tasks. The interface between the task
scheduler and many tasks is the "Doer" interface. However, most of the
tasks (but not all) share some common code which makes these tasks
more suitable to a "Doer2" interface as long as I can manage to
abstract the common code to a single place.

What I'm trying to find is a way that makes Task1 and Task2 only need
to implement Do2 method and keeps the code for the tasks as less as
possible. Maybe I want too much from Go. Although Go's strength is at
composition, sometimes it is not easy to omit the boilerplate code of
composing.

Hǎiliàng

marcos.l...@mercadolibre.com

unread,
Nov 9, 2013, 12:32:45 AM11/9/13
to golan...@googlegroups.com, wkharold
Hi, 

Is this what you're trying to do?


I don't know if I understood correctly the situation, but in the example i've provided you're not redefining Task1 and Task2 Do methods.

Regards, 

Marcos.

Hǎiliàng Wáng

unread,
Nov 9, 2013, 10:18:20 AM11/9/13
to marcos.l...@mercadolibre.com, golan...@googlegroups.com, wkharold
Hi Marcos,

Thanks very much for helping with my question.

Correct me if I'm wrong. With the example you provided, there is no
way to define specific version of Do2 method for Task1 and Task2, that
is, the implementation of Do2 is fixed with Doer2 struct. To have such
flexibility, the Do2 method must be polymorphic, so in Go there has to
be an interface embedded in struct Task1 and Task2, and this interface
is initially nil and has to be assigned, which turns out again to be
the approach that I have posted initially.

Regards,

Hǎiliàng
> --
> You received this message because you are subscribed to a topic in the
> Google Groups "golang-nuts" group.
> To unsubscribe from this topic, visit
> https://groups.google.com/d/topic/golang-nuts/kpLtsPk3f1c/unsubscribe.
> To unsubscribe from this group and all its topics, send an email to
> golang-nuts...@googlegroups.com.
> For more options, visit https://groups.google.com/groups/opt_out.

wkharold

unread,
Nov 9, 2013, 12:59:48 PM11/9/13
to golan...@googlegroups.com, wkharold
It sounds like you're trying to achieve some sort of "implementation inheritance". Don't do that. Read http://www.javaworld.com/javaworld/jw-08-2003/jw-0801-toolbox.html.

Hailiang Wang

unread,
Nov 10, 2013, 1:17:39 AM11/10/13
to wkharold, golang-nuts
Thanks! Very good article! It explains not only why "implementation
inheritance" should be avoided but also what agile development is
really about. You never know what you will learn by posting a
question.

But there should also be an easier way to reuse implementations,
though the correct approach may not be inheritance. I think I have
figured out another way by using decorator pattern. Instead of
embedding the common code into Task1 and Task2, I can instead embed
Task1 and Task2 to a common decorator. And now it looks much better.

The new example is here:
http://play.golang.org/p/z5CjAqFiLX

Hǎiliàng

Hǎiliàng

Carlos Castillo

unread,
Nov 11, 2013, 6:35:15 AM11/11/13
to golan...@googlegroups.com, wkharold
I find this much easier: http://play.golang.org/p/XEkAl4e7AX, depending on your problem, it might be all you need. 

If commonCode needs access to certain fields, you can pass them as arguments, or pointers to such when you need to mutate them.

Alternatively, to get something more like your decorator: http://play.golang.org/p/pj1_pO7RZ1

This way, the user of the code doesn't need to wrap the values themselves (nor do they need to know about the Doer2 type and Do2 methods) to get the common functionality. You could provide a constructor function for each of your types, but at that point you are writing an extra function / method per type, which is the essentially the exact same code overhead per type as my version, but still containing the extra indirection and code brought on by the wrapper type.

Bienlein

unread,
Nov 11, 2013, 11:57:22 AM11/11/13
to golan...@googlegroups.com
Reply all
Reply to author
Forward
0 new messages