In the process of answering your questions I realised there was a number of 
changes I needed to make to my code, here is the state of it at the commit 
I just made with this in mind: (this is a permalink and shows how it is at 
this exact 
stage) 
https://github.com/calibrae-project/bast/tree/418af6fc24480cbbc2d7b72bab794f0375439f9c

On Tuesday, 24 April 2018 10:19:56 UTC+3, Louki Sumirniy wrote:
>
> 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