【问题标题】:How to think when defining interfaces in golang?在 golang 中定义接口时如何思考?
【发布时间】:2017-01-13 21:45:01
【问题描述】:

我注意到在过去 10 年左右的时间里放弃我在 Java 和 PHP 中使用的 OOP 风格编程是多么困难。几周以来,我一直在尝试使用 golang(双关语),但我试图让组合比 golang 的继承原则更自然。

我将如何定义一个有用的接口来确保所有这些结构都可以实现它?我试图想出一个有用的例子,它不涉及狗、人类或奇怪的车辆结构......

package main

type Store struct {
  name  string
  phone string
}

type HardwareStore struct{ Store }

type FoodStore struct{ Store }

type OnlineStore struct {
  Store
  url string
}

我认为这可能是因为我的思考基于他们的状态/数据而不是他们的行为,我有点挣扎。

明显的首选,没有接口,可能是这个(简化的),当然失败了:

s := Store{}
h := HardwareStore{}
f := FoodStore{}
o := OnlineStore{}

stores := []Store{s, h, f, o}

【问题讨论】:

  • 这听起来仍然像你在尝试继承之类的东西,这在 Go 中不会很好地工作。你有什么具体的问题要解决吗?
  • 是的,它围绕着具有不同属性的用户,具体取决于用户的类型,我试图远离继承以在惯用的 go 中做到这一点。
  • 通常在从基于继承的 OOP 转移时,您可以“反转”逻辑。与其拥有不同“类型”的用户,不如使用具有区分它们的属性的用户类型。您还可以基于行为everything,并简单地定义返回您感兴趣的字段的方法,并使用方法集来定义接口。很难说最适合您正在解决的问题的人为示例。
  • 如何使用接口?解耦代码。见stackoverflow.com/a/62297796/12817546。 “动态”调用方法。见stackoverflow.com/a/62336440/12817546。访问 Go 包。见stackoverflow.com/a/62278078/12817546。将任何值分配给变量。见stackoverflow.com/a/62337836/12817546

标签: inheritance go polymorphism


【解决方案1】:

我认为下面是您所追求的。对不起,丑陋的名字,但我不得不让他们脱颖而出,以说明一点。请记住,go 中没有虚函数,即使下面的示例模拟了一个。

package main

import "fmt"


type StoreInterface interface {
    getName() string
    setName(string)
    getPhone() string
    setPhone(string)
}

type Store struct {
   name  string
   phone string
}

func (store *Store) getName() string {
   return store.name;
}

func (store *Store) setName(newName string){
   store.name = newName;
}

func (store *Store) getPhone() string {
   return store.phone;
}

func (store *Store) setPhone(newPhone string){
   store.phone = newPhone;
}

type HardwareStore struct{ Store }

type FoodStore struct{ Store }

type OnlineStore struct {
    Store
    url string
}

func (os *OnlineStore) getName() string{
    return fmt.Sprintf("%v(%v)", os.name, os.url)
}

func main() {
    s := Store{name:"s", phone:"111"}
    h := HardwareStore{Store{name:"h", phone:"222"}}
    f := FoodStore{Store{name:"f", phone:"333"}}
    o := OnlineStore{Store:Store{name:"o", phone:"444"}, url:"http://test.com"}

    fmt.Println ("Printout 1")
    stores := []*Store{&s, &h.Store, &f.Store, &o.Store}
    for _, s := range stores {
        fmt.Printf("\t%v: %v\n", s.name, s.phone);
    }

    fmt.Println ("Printout 2")
    stores2 := []StoreInterface{&s, &h, &f, &o}
    for _, s := range stores2 {
        fmt.Printf("\t%v: %v\n", s.getName(), s.getPhone());
    }
 }

不过,不用说,您需要将思维方式从继承转变为函数、数据结构和行为。很难改掉旧习惯(我也来自多年的 OOP)。但是在罗马时,您应该像罗马人一样思考 :o) 否则您将错过该语言的某些潜力。

【讨论】:

  • 我一直避免在我的 Go 编程中定义 getter 和 setter,因为从惯用的角度来看它感觉不正确,但也许它是唯一的出路?
  • 如果只是为了访问,为什么不把vars写成大写呢?
  • @Patrick 你检查过“Printout 2”的输出吗?并将其与“打印输出 1”进行比较?我只在示例中使用了 getter 和 setter,因为代码中没有任何有用的建议。该代码实际上是在说明如何从 OOP 模拟继承和多态。您显然不需要接口来简单地访问结构字段。请参阅“打印输出 1”。
  • @Patrick 再次查看 getName() 以及它如何根据对象的实际类型在 Printout 2 中产生不同的输出,即使它们都被认为是“向下转换”到 StoreInterface。忽略方法名称 - 它与说明的概念无关。
  • 感谢您的所有意见。我最终以非常相似的方式实现它。
猜你喜欢
  • 2011-04-27
  • 1970-01-01
  • 1970-01-01
  • 2018-03-08
  • 1970-01-01
  • 1970-01-01
  • 2014-01-14
  • 2014-10-15
相关资源
最近更新 更多