So i have this import cycle to solve, and my project structure basically likes this:
model.go -> procedure.go -> function.go
In my function i need model and i use interface to handle it. Currently my code basically like this:
type imodel interface {
foo()
}
type model struct {
}
func (m *model) run() {
proc := &procedure{}
proc.run(m)
}
func (m *model) foo() {
//bla bla
}
type procedure struct {
}
func (p *procedure) run(model imodel) {
funct := &function{}
funct.run(model)
}
type function struct {
}
func (f *function) run(model imodel) {
model.foo()
}
My question is should i pass my model using interface thorough every class like that or there's any other workaround?
I would put all of these in the same package. Depending on circumstances, I may put them in different files in the same package.
Also, you do not seem to export imodel, so it would be package-internal and unless you have multiple concrete implementations, you do not need an interface. Then, "imodel" is a less than ideal name, the interface should be named model and each concrete type implementing the interface should be named after what it models.
Related
I have a project that uses a structure like so:
type I interface {
GetName() string
DoSomething()
}
//
// A implements I
//
type A struct {
Name string
}
func (a *A) GetName() string {
return a.Name
}
func (a *A) DoSomething() {
...do something
}
//
// B implements I
//
type B struct {
Name string
}
func (b *B) GetName() string {
return b.Name
}
func (b *B) DoSomething() {
...do something
}
func (b *B) DoSomethingElse() {
...do something else
}
//
// Both
//
func UseAorB(T I) {
name := T.GetName()...
}
Is using GetName the best way to get the name field from either struct A or B when called in a function that takes interface I?
Do I have to redefine DoSomething every time for each thing that is meant to implement the interface? Or is there a better way to do this if DoSomething is the same every time where I can define it just once?
Struct B has a method that the interface does not define (DoSomethingElse). Do I need to use reflect to be able to pass Struct B to a function that takes interface I to call DoSomethingElse? Or should I be defining a new interface that includes this method?
I want to improve my code quality and write a solid library, but I have to say it feels like I am fighting the language and making my life more difficult.
Yes, GetName() is a good way of doing that
If you have similar implementations of a method, you can usually move it to a common struct and embed that:
type Common struct {}
func (c Common) DoSomething() {...}
type A struct {
Common
Stuff
}
type B struct {
Common
Other stuff
}
Above, both A and B have DoSomething method, and they share the implementation
Do not use reflect. There are two ways:
Use type assertion:
func f(in I) {
if b, ok:=in.(B); ok {
// b is of type B, so:
b.DoSomethingElse()
}
}
Use an interface and type assertion:
type DoesSometingElse interface {
DoSomethingElse()
}
func f(in I) {
if x, ok:=in.(DoesSomethingElse); ok{
x.DoSomethingElse()
}
}
If you feel like you're fighting with the language, then either you are modeling something incorrectly, or you don't know the right way to do something in that language. There are good and bad ways of doing things in all languages, and many times these are different for each language. If you are coming to Go from another language, you should first stop trying to think in that other language and translate to Go, and try to work with Go alone.
I'm working on a project that would require some level of abstraction on some data and I would like to keep the packages that use the data as independent as possible so I can swap things out in the future.
Here are 2 possible ways that I thought of, one is having a common data interface and have all the places import it. The other is to have each package define its own interfaces and do a type assertion.
Which is the most Go/general way of doing it?
// Importing interface
// src/model
type IData interface {
myint() int
}
// src/dataextractor
import src/model
type DataExtractor struct {
foo() IData
}
// src/service
import src/model
type ServiceDataExtractor interface {
foo() IData
}
type Service struct {
extractor ServiceDataExtractor
}
func (s Service) serve() {
v = s.extractor.foo()
// do stuff with v
}
vs
// type assertion
// src/dataextractor
type DataExtractorData struct{}
func (d DataExtractorData) myint()int {}
type DataExtractor struct {
foo() interface{} {
reutrn DataExtractorData{}
}
}
// src/service
type ServiceData interface {
myint() int
}
type ServiceDataExtractor interface {
foo() interface{}
}
type Service struct {
extractor ServiceDataExtractor
}
func (s Service) serve() {
data := s.extractor.foo()
v, ok := data.(ServiceData)
// do stuff with v
}
Here's the simple rules to keep in mind. An interface type should be declared in the package that wants to receive that interface type. To satisfy an interface, you don't have to import it. All you need to do is declare the methods as are specified by the interface.
When do you use interfaces? Typically, when you have a field, or parameter, that you want to be able to accept multiple data types, and you can do all you need to do with it by calling methods. How do you determine what goes in the interface? You simply ask yourself: "What methods would I need to call on this value to make use of it?"
The only seemingly meaningful method in your example is myint() int, and the only place it seems you intend to use it is in Service.
type Service struct {
extractor interface {
myint() int
}
}
func (s Service) serve() {
s.extractor.myint()
}
Below you can find three different ways of calling a method Name() of a customer struct. The result is exactly the same but each one of the three package exports different things:
package main
import (
"customer1"
"customer2"
"customer3"
"fmt"
"reflect"
)
func main() {
c1 := customer1.NewCustomer("John")
fmt.Println(c1.Name())
c2 := customer2.NewCustomer("John")
fmt.Println(c2.Name())
c3 := customer3.NewCustomer("John")
fmt.Println(c3.Name())
}
Output
John
John
John
customer1.go (Export Customer struct and Name() method)
package customer1
type Customer struct {
name string
}
func NewCustomer(name string) * Customer{
return &Customer{name: name}
}
func (c *Customer) Name() string {
return c.name
}
customer2.go (Do not export customer struct. Export only Name() method)
package customer2
type customer struct {
name string
}
func NewCustomer(name string) *customer {
return &customer{name: name}
}
func (c *customer) Name() string {
return c.name
}
customer3.go (Do not export customer struct. Export Customer Interface)
package customer3
type Customer interface {
Name() string
}
type customer struct {
name string
}
func NewCustomer(name string) Customer {
return &customer{name: name}
}
func (c *customer) Name() string {
return c.name
}
My question is which approach you would recommend and why? Which is better in terms of extensibility and maintainability? Which one you would use for a large project?
It seems that customer3 approach is officially discouraged (// DO NOT DO IT!!!) as you can read here https://github.com/golang/go/wiki/CodeReviewComments#interfaces
Interfaces in Go work (and are used) a bit differently that what you'd expect if you come from other languages such as Java.
In Go, the object implementing the interface does not need to explicitly say it implements it.
That has subtle ramifications, such as the consumer of a type being able to decouple from the implementation even if the implementing side did not bother (or think about) creating an interface in the first place.
So, the idiomatic way in Go would be to use a variation of your first approach.
You'd define customer1.go exactly as you did (so implementation of the type is as simple as possible).
Then if necessary you can de-couple in the consumer (your main package in this case) by defining an interface there:
type Customer interface {
Name() string
}
func main() {
var c1 Customer
c1 := customer1.NewCustomer("John")
fmt.Println(c1.Name())
}
That way, your main implementation can work with any type that has a Name() method, even if the package implementing the type in the first place did not think about that need.
To achieve extensibility, this is usually also applied for functions you export that receive parameters.
If you are exporting a function like this:
func PrintName(customer Customer) {
fmt.Println(customer.Name())
}
Then that function can be invoked with any object that implements Customer (any of your implementations would work for example).
I have 3 structures that are similar about 70%. So I'd like to extract some common part and create some extensions with specific methods and fields.
Final structure will work as follows:
method .Start() from Common is called
Start() calls method .Name() from specific part the latter return a string
the returned result is being processed in (*Common).Process(), sometimes it should call specific's Format()
But! Specific part have to call Common part's method, for example GetVerbosity()
Like this:
package common
type Common struct {
Specificer
}
func (c *Common) Start() {
...
name := Specific.Name()
}
func (c *Common) GetVerbosity() {...}
type Specificer interface {
Name() string
Format() string
}
And specific part:
package specific1
// should implement common.Specificer interface
type Specific1 struct {
// unexported fields
}
func (s *Specific1) Name() string {...}
func (s *Specific1) Format() string {
// How to call it???
Common.Method()
}
This is similar to some frameworks - when another code calls your code, and also you call it's code.
How to implement this better? And how to create new structures?
I tried:
Embed Specific to Common, and embed vise versa:
type Common struct {
Specificer
}
type Specific1 struct {
common.Common
...
}
// But this is little bit insane:
func NewSpecific1() *common.Common {
var s = Specific1{}
return &common.Common{Specificer: &s}
}
Define 2 interfaces: Commoner and Specificer. And combined interface:
package common
type CommonSpecificer interface {
Commoner
Specificer
}
type Common struct {...} // implements all the methods from Commoner
func New() *Common {...}
//////
package specific1
type Specific1 struct { // implements all the methods from common.Specificer
Common
...
}
func NewSpecific1() *Specific1 {
c := common.NewCommon(...)
s := &Specific1{Common: c}
return s
}
The following code fails, because after using B.Assign(A), the information on the specific type of B is lost (at least that's what I think goes wrong here):
package main
import "fmt"
type Methods interface {
Method()
Assign(Methods)
Set(Methods)
}
type Parent struct {
Methods
assigned Methods
}
type ChildA struct {
Parent
}
type ChildB struct {
Parent
msg string
}
func (this *Parent) Assign(other Methods) {
//Some other logic here
other.Set(this)
}
func (this *Parent) Set(other Methods) {
this.assigned = other
}
func (c *ChildA) Method() {
fmt.Println("I'm child A")
}
func (c *ChildB) Method() {
fmt.Println("I'm child B, and I approve this message:", c.msg)
}
func main() {
A := new(ChildA)
B := new(ChildB)
B.msg = "my message"
B.Assign(A)
A.assigned.Method()
}
Now, in order to avoid this, I would have to make another method, that has exactly the same definition as Parent.Assign(), but different receiver:
func (this *Parent) Assign(other Methods) {
//Some other logic here
other.Set(this)
}
func (this *ChildB) Assign(other Methods) {
//Same as above
other.Set(this)
}
This is rather ugly. Is there a way to preserve the information about B's type when calling the method from it's embedded struct Parent, without duplicating the code?
Is there a way to preserve the information about B's type when calling the method from it's embedded struct Parent, without duplicating the code?
No. When you call the embedded method, it is called with a pointer to the embedded struct.
Unfortunately as attractive as it first seems, embedded objects don't make Go into an object oriented language.
You might be better off just having one type with function pointers for the implementation.