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.