How to avoid duplicate code without generic support for similar logic?

473 views
Skip to first unread message

AllenDang

unread,
Sep 7, 2011, 9:07:46 AM9/7/11
to golang-nuts
I'm trying to implement a type related collection, logic is very
simple like below.
1. Creates a vector.
2. When user tries to add a struct to it, check whether duplicate one
exists, if not add it.

Then I found out that I have to copy-paste similar logic each time
when creating an new type related collection. I felt very bad about
this, but without generic support or C++ like inherit, I cannot figure
out a better approach to solve this. I thought about to use reflect,
but the code turns very ugly.

Any good idea about how to deal with this situation?

Below code shows the detail.

=============First typed struct=======================
type GeneralEventManager struct {
handlers vector.Vector
}

func (this *GeneralEventManager) Attach(handler GeneralEventHandler) {
isExists := false
for _, v := range this.handlers {
if f, ok := v.(GeneralEventHandler); ok {
if f == handler {
isExists = true
break
}
}
}

if !isExists {
this.handlers.Push(handler)
}
}

=============Second typed struct=======================
type MouseEventManagerA struct {
handlers vector.Vector
}

===================================================
The code in this method is almost the same
===================================================
func (this *MouseEventManagerA) Attach(handler MouseEventHandlerA) {
isExists := false
for _, v := range this.handlers {
if f, ok := v.(MouseEventHandlerA); ok {
if f == handler {
isExists = true
break
}
}
}

if !isExists {
this.handlers.Push(handler)
}
}

Jan Mercl

unread,
Sep 7, 2011, 9:26:23 AM9/7/11
to golan...@googlegroups.com
On Wednesday, September 7, 2011 3:07:46 PM UTC+2, AllenDang wrote:
I'm trying to implement a type related collection, logic is very
simple like below.
1. Creates a vector.
2. When user tries to add a struct to it, check whether duplicate one
exists, if not add it.

Then I found out that I have to copy-paste similar logic each time
when creating an new type related collection. I felt very bad about
this, but without generic support or C++ like inherit, I cannot figure
out a better approach to solve this. I thought about to use reflect,
but the code turns very ugly.

Any good idea about how to deal with this situation?

From you code it seems that the type 'GeneralEventHandler' ('MouseEventHandlerA', ...) has the equality ('==') operator defined (c.f. 'if f == handler'). If it has also the '!=' defined then you might store the collection in a map set. Something like 'map[interface{}]bool' or 'map[SomeEventHandler]bool' for 'SomelEventManager'. The test for "is-present-in-the-colelction" would then be pretty simple. Alternatively a 'map[SomeEventHandler]struct{}' or 'map[SomeEventHandler]whatever' could be used also.

Daniel Jo

unread,
Sep 7, 2011, 9:27:59 AM9/7/11
to AllenDang, golang-nuts

Personally, I'd generate a unique key for each struct instance and use a map. If you need an ordered list, then the map could store a linked list element or a slice index instead of the struct itself. You'll still have some duplicated code, but not nearly as much.

Also, the container/vector package is deprecated. Use slices and append instead.

-Daniel

Rich Wareham

unread,
Sep 7, 2011, 9:40:11 AM9/7/11
to AllenDang, golang-nuts
On Wed, Sep 07, 2011 at 06:07:46AM -0700, AllenDang wrote:
> I'm trying to implement a type related collection, logic is very
> simple like below.
> 1. Creates a vector.
> 2. When user tries to add a struct to it, check whether duplicate one
> exists, if not add it.
>
> Then I found out that I have to copy-paste similar logic each time
> when creating an new type related collection. I felt very bad about
> this, but without generic support or C++ like inherit, I cannot figure
> out a better approach to solve this. I thought about to use reflect,
> but the code turns very ugly.

How about something like:

type Value interface{}

func (c *Collection) AnyEqual(v Value, cmp func(a, b Value) bool) bool {
for _, item := range c {
if cmp(item, v) {
return True
}
}
return False
}

Then using the function inside Attach() with appropriate closures? The
AnyEqual method specifies the algorithm, the cmp function passed
specifies the mechanism.

--
Dr Rich Wareham

Miki Tebeka

unread,
Sep 7, 2011, 9:57:18 AM9/7/11
to golan...@googlegroups.com
You can create an "EventHandler" interface which has Equals(other EventHandler) bool and have both handlers implement it.
See https://gist.github.com/1200622 for example.

Monnand

unread,
Sep 7, 2011, 5:17:55 PM9/7/11
to golang-nuts


On Sep 7, 9:07 am, AllenDang <allen...@gmail.com> wrote:
> I'm trying to implement a type related collection, logic is very
> simple like below.
> 1. Creates a vector.
> 2. When user tries to add a struct to it, check whether duplicate one
> exists, if not add it.
>
You may write something like this:

/* First, define an interface which has only one method: Equals */
type Comparable interface {
Equals(interface{}) bool
}

/* Secondly, Define a container which has no duplicated elements. You
can name it as you want. I just call it SomeContainer */
type SomeContainer struct {
elements vector.Vector
}

/* Now, define a method to this container. Everything is nearly the
same as your code */
func (c *SomeContainer) Attach (e Comparable) {
isExists := false
for _, v := range c.elements {
if e.Equals(v) {
isExists = true
break
}
}
if !isExists {
c.elements.Push(e)
}
}

/* Here's your new event manager. Using composite type. If you haven't
heard composite type, then treat it as inherit class. */
type GeneralEventManager struct {
SomeContainer
}

type FooBarEventManager struct {
SomeContainer
}

That's it. Now both GeneralEventManager and FooBarEventManager have
method Attach(). All you need to do is defining the Equals method for
the elements.

Regards,
-Monnand

Nigel Tao

unread,
Sep 7, 2011, 6:57:05 PM9/7/11
to AllenDang, golang-nuts
On 7 September 2011 23:07, AllenDang <alle...@gmail.com> wrote:
> Then I found out that I have to copy-paste similar logic each time
> when creating an new type related collection. I felt very bad about
> this, but without generic support or C++ like inherit, I cannot figure
> out a better approach to solve this.

The Search and Sort functions in the sort package are the classic
binary search and quicksort algorithms that are examples of code that
is generic in the "applies to different types" sense without being
generic in the "C++ templates" sense.

See $GOROOT/src/pkg/sort/{search,sort}.go.

AllenDang

unread,
Sep 8, 2011, 7:24:02 AM9/8/11
to golang-nuts
Good point! Never thought this way!

On 9月8日, 上午6时57分, Nigel Tao <nigel...@golang.org> wrote:

AllenDang

unread,
Sep 8, 2011, 7:25:10 AM9/8/11
to golang-nuts
container/vector is deprecated??!!! One month ago, I did a performance
test against slice and vector, vector is far more faster. How come it
will be deprecated?

On 9月7日, 下午9时27分, Daniel Jo <ost...@gmail.com> wrote:
> Personally, I'd generate a unique key for each struct instance and use a
> map. If you need an ordered list, then the map could store a linked list
> element or a slice index instead of the struct itself. You'll still have
> some duplicated code, but not nearly as much.
>
> Also, the container/vector package is deprecated. Use slices and append
> instead.
>
> -Daniel

chris dollin

unread,
Sep 8, 2011, 7:49:32 AM9/8/11
to AllenDang, golang-nuts
On 8 September 2011 12:25, AllenDang <alle...@gmail.com> wrote:
> container/vector is deprecated??!!! One month ago, I did a performance
> test against slice and vector, vector is far more faster. How come it
> will be deprecated?

It would be interesting to see your tests -- since slice+append can
do (most of) what container/vector/Vector does and has less overhead,
I would have thought it would have been faster.

It's being deprecated because once append was added to Go, the
main use of Vector -- easily pushing elements on the end -- was
subsumed; there isn't enough functionality left to justify having
(and maintaining) the package.

Chris

--
Chris "allusive" Dollin

AllenDang

unread,
Sep 8, 2011, 8:10:10 AM9/8/11
to golang-nuts
I have to admit that I was wrong. I redo the test and find out slice
does faster. Thanks for sharing this to me!

On 9月8日, 下午7时49分, chris dollin <ehog.he...@googlemail.com> wrote:

Archos

unread,
Sep 13, 2011, 12:21:01 PM9/13/11
to golang-nuts
But the package Vector allows more than simply pushing elements at the
end.
If those another functions could be used with a slice then I could
understand it

http://golang.org/pkg/container/vector/

On Sep 8, 12:49 pm, chris dollin <ehog.he...@googlemail.com> wrote:
> It's beingdeprecatedbecause once append was added to Go, the
> main use ofVector-- easily pushing elements on the end -- was

Jesse McNelis

unread,
Sep 13, 2011, 12:40:12 PM9/13/11
to golan...@googlegroups.com
On 14/09/11 02:21, Archos wrote:
> But the package Vector allows more than simply pushing elements at the
> end.
> If those another functions could be used with a slice then I could
> understand it
>
> http://golang.org/pkg/container/vector/

Most of the functionality of the container/vector package can be
replicated using append and copy.

http://code.google.com/p/go-wiki/wiki/SliceTricks

Reply all
Reply to author
Forward
0 new messages