0

I have a struct which I initiate in some process like following, and this is working as expected.

This is specific runner

type TestRunner struct {
    path string
    name string
}

func NewRunner(p string, n string) *TestRunner {
    return &TestRunner{
        path: p,
        name: n,
    }
}

Now I want in the same package to create another runner so I do it like this e.g.

Also specific runner

type TestRunner2 struct {
    path string
    name string
}

func NewRunner(p string, n string) *TestRunner2 {
    return &TestRunner2{
        path: p,
        name: n,
    }
}

Now I get error that the func NewRunner is exist

I have another file (in the same package) which include the interface

This is generic implementation (different file in the same package)

type Runner interface {
    Run(path string) error         
    ChangePath(newPath string) 
}

So maybe the NewRunner should be there, where its recommended to put the new object ?

obviously I can create NewRunner1 and NewRunner2 method in the file but im not sure if it’s recommended

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
Jenny Hilton
  • 1,297
  • 7
  • 21
  • 40

2 Answers2

4

First, you should name your runners according to their functionality, not by number. FastRunner and SlowRunner or LocalRunner vs RemoteRunner. You get the idea. Then you should create a construct for each one:

func NewFastRunner( ... ) *FastRunner {
    return &FastRunner{ ... }
}

func NewSlowRunner( ... ) *SlowRunner {
    return &SlowRunner{ ... }
}

This is standard practice, and makes for very readable, unambiguous code.

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
  • The return type should generally be the concrete type though, so callers get access to specific methods and fields without type assertions. – Peter Feb 15 '18 at 13:08
  • @Peter - can you give example of what do you mean ? should it be like `func NewRunner1(p string, n string) *TestRunner2 { return &TestRunner2{ path: p, name: n, } }` – Jenny Hilton Feb 15 '18 at 13:14
  • @Flimzy - thanks, btw, when you suggest to use factory than? if I need to support different version or ? – Jenny Hilton Feb 15 '18 at 14:05
  • The Factory pattern rarely makes sense in Go. Do you have an example of a use case that isn't met with a simple constructor method? – Jonathan Hall Feb 15 '18 at 14:06
  • @Flimzy - Yes for example if I need to create NewFastRunner that support more then one version of this fast runner,each version can have different logic inside... – Jenny Hilton Feb 15 '18 at 14:45
-1

You can use method pointer type receivers for each runner and then implement interface. That way you need not to return any thing you can directly assign values like path and name to runner using pointer.

package main

import (
    "fmt"
)

type TestRunner1 struct {
    path string
    name string
}

type TestRunner2 struct {
    path string
    name string
}

type Runner interface {
    Run(path string) error
    ChangePath(newPath string)
}

func (tr1 *TestRunner1) NewRunner(p string, n string) {
    tr1.path = p
    tr1.path = n
}

func (tr2 *TestRunner2) NewRunner(p string, n string) {
    tr2.path = p
    tr2.path = n
}

func main() {
    fmt.Println("Hello, playground")
}

Check the code here

Himanshu
  • 12,071
  • 7
  • 46
  • 61
  • 3
    Convention is that a method called `NewX` should return an instance of `X`, not be a receiver for `X`. – Jonathan Hall Feb 15 '18 at 12:32
  • @Flimzy we cannot create two methods with same name using interface without having different method receivers that's why the error `NewRunner` is exist. I have given my solution on basis of error received too – Himanshu Feb 15 '18 at 12:43
  • @Flimzy - How would you do it ? – Jenny Hilton Feb 15 '18 at 12:52
  • @JennyHilton: With two different constructors. – Jonathan Hall Feb 15 '18 at 12:52
  • @Flimzy - Like `NewTestRunner1` and `NewTestRunner2 ` in there owne file ? – Jenny Hilton Feb 15 '18 at 12:53
  • @JennyHilton: Yes. But whether they are in a single file or separate files is irrelevant. I'd also consider using more descriptive names. Why do you have two runners? Name the runners according to their functionality, rather than by number. `NewFastRunner` and `NewSlowRunner` (or whatever) is a lot better. – Jonathan Hall Feb 15 '18 at 12:54
  • @Flimzy - can you provide it as answer and i'll close the question ? – Jenny Hilton Feb 15 '18 at 12:55
  • 1
    so if there are multiple runners we have to create constructor for each. Are we serious that can be redundant code. @Flimzy please provide a solution. I am in learning phase. I also wants to see best approach – Himanshu Feb 15 '18 at 12:56
  • @Himanshu: Yes, of course I'm serious. There's no other reasonable way. And "bulky code" (whatever that means) isn't a problem, if the code serves a purpose. – Jonathan Hall Feb 15 '18 at 12:57
  • constructor should be created according to functionality not on basis of object names – Himanshu Feb 15 '18 at 12:58
  • @Himanshu: Object names should be created according to functionality. If you have only a single functionality, you should have only a single object type, and thus a single name. If you have two object types, you obviously have two functionalities, so you should have two names, and two constructors. – Jonathan Hall Feb 15 '18 at 12:59