I looked at the OP again and I have created a struct embedding with 
interface type for a similar purpose as you have in mind. 

I'm not sure how to condense it exactly, but I'll just explain the salient 
points:


   1. The data has to be referred to via an interface{} which you load with 
   a struct, and inside the struct is your storage structure, which can be 
   just a slice array, and in my case it was necessary to embed because slices 
   don't talk with interface{} in go
   2. The functions that are specific to the data type have to be 
   overridable, so that those that are general can call these functions 
   without knowing or caring about what the actual data type is. For this I 
   added the function collection to the primary struct defining the 
   superclass, if I can call it that, so that you can then use abstract calls 
   that pass through the interface to the implementation (which can be in a 
   separate source file if the names are capitalised to be exported)
   3. Type specifications for all of the type-specific functions need to be 
   made, and these form the list of functions inside the superclass type 
   struct, and then you create an initialiser that loads in the specific 
   implementation functions

One little tip I can give with the initialiser is that it can be easier to 
use closures to specify the type specific function set. Someone pointed out 
that my code was missing the search/insert/delete functions and I actually 
didn't realise I'd removed them as I had actually written them 
previously... but part of the reason I probably did that was because I 
didn't yet know (until pretty much yesterday or the day before) how to bind 
and pass interface variables around... so I will need to put those 
functions back in now that I know how to abstract the data accessors and 
pass through values from the caller without needing to know their types.

Ideally you narrow the amount of parts that deal with the abstractions as 
much as possible, without sacrificing the readability of your code, though 
that's a matter of personal preference (I like code that reads like bad 
english - not english, but still understandable). In my case, the 
search/insert/delete functions could be part of the function set bound to 
the type, but they can stay in the higher level of the code if you don't 
address their types directly but use the accessor functions set to do this 
for you, then inside these functions all the types are known, but the 
operations can be tailored to the concrete types.

So in other words, there is a number of ways to do various different parts 
of it and you'll need to read up on them to make your own decision how you 
want to implement it. I won't say that my code is exemplary but it will at 
least give you some clues as to at least the options I have decided to use 
for this.

On Tuesday, 24 April 2018 09:58:46 UTC+3, Kaveh Shahbazian wrote:
>
> Thanks @Louki,
>
> The names are absolutely made up and are just for the purpose of 
> demonstration.
>
> The problem is the package state, one whole package to just hold a type.
>
> On Tuesday, April 24, 2018 at 10:36:29 AM UTC+4:30, Louki Sumirniy wrote:
>>
>> The name could use some work, stutter is a no-no in Go. What kind of 
>> state does it hold? User profiles? MMO game world database? Is your scope 
>> too broad? I see that it looks like a geography database, so it would make 
>> more sense to call it 'geo' or something like this. Also, for such a 
>> database system there is waypoints, paths and regions as subtypes that I 
>> can think of off the top of my head. Or maybe 'location' is a better name 
>> for it. Will there be a history ledger also?
>>
>> I hope that helps you... it's just a namespace question really. The 
>> important thing about how to define it has to do with what will be done 
>> with it not what it is, as a matter of ontology. Object oriented design 
>> methodology tends to look at things like everything can be abstracted and 
>> in the real world, categories are containers, like 'car' is the general, 
>> 'engine', 'transmission', 'wheels', and inside engine is 'carb/injector' 
>> 'pistons' 'cams' 'ignition' inside ignition is ignition timing, throttle 
>> etc etc. If you follow a model of containers you will get clean boundaries 
>> between things, and the path from user to data will be much more clear.
>>
>> On Tuesday, 24 April 2018 08:49:45 UTC+3, Kaveh Shahbazian wrote:
>>>
>>> @Jake @Bryan Thanks!
>>>
>>> Current solution I use:
>>>
>>> A package that holds the shared data type State:
>>>
>>> package state
>>>
>>> type State struct {
>>>     Latitude  float64
>>>     Longitude float64
>>>     Mark      string
>>>     Name      string
>>>     // ...
>>> }
>>>
>>> Three packages with different implementations and algorithms:
>>>
>>> package concrete1
>>>
>>> import (
>>>     "gitlab.com/dc0d/gist/2018/Q1/draft/cmd/draft/state"
>>> )
>>>
>>> type Concrete1 struct{}
>>>
>>> func (c *Concrete1) Clone() (*state.State, error) { panic("N/A") }
>>>
>>> And:
>>>
>>> package concrete2
>>>
>>> import (
>>>     "gitlab.com/dc0d/gist/2018/Q1/draft/cmd/draft/state"
>>> )
>>>
>>> type Concrete2 struct{}
>>>
>>> func (c *Concrete2) Clone() (*state.State, error) { panic("N/A") }
>>>
>>> And:
>>>
>>> package concrete3
>>>
>>> import (
>>>     "gitlab.com/dc0d/gist/2018/Q1/draft/cmd/draft/state"
>>> )
>>>
>>> type Concrete3 struct{}
>>>
>>> func (c *Concrete3) Clone() (*state.State, error) { panic("N/A") }
>>>
>>> And the consumer package, which will be given a concrete implementation 
>>> based on some logic (this is where "Accepting interfaces" is happening):
>>>
>>> package consumer
>>>
>>> import (
>>>     "gitlab.com/dc0d/gist/2018/Q1/draft/cmd/draft/state"
>>> )
>>>
>>> func Use(cln cloner) {}
>>>
>>> type cloner interface {
>>>     Clone() (*state.State, error)
>>> }
>>>
>>> The part in question is the *state.State data type. This is the best I 
>>> could come up with.
>>>
>>> But I do not like:
>>>
>>>
>>>    1. Having a package just to hold a single type,
>>>    2. The consumer package is accepting a concrete type and depends on 
>>>    it - seems this one can not be avoided since there is nothing like 
>>>    structural typing for structs in Go.
>>>
>>>
>>> There might be a better way to do this.
>>>
>>

-- 
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+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to