我是Go的新手,我想知道如何实现类似于Java中抽象类和方法的结构。 在Java中,我将执行以下操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
abstract class A{

 static method1(){
  ...
  method2();
  ...
 }

 abstract method2();

}

class B extends A{

 method2(){
  ...
 }

}

class C extends A{

 method2(){
  ...
 }

}

我了解接口和结构。 我可以先构建一个接口,然后构建一个结构以实现method1。 但是method2呢?
我知道我可以将一个接口嵌入另一个接口,也可以将一个结构作为另一个结构的字段嵌入。 但是我看不到用这些方法来实现我的结构的方法。

我看到的唯一解决方案是在类B和类C中都实现method1。

注意:当然,就我而言,这不仅是一种方法。 另外,我有一个抽象类的层次结构,我真的不想将所有内容都移到"子类"中。

我在Internet上找到的示例大多数都是每个接口只有一种方法。 如果你们中的一个能在这里给我提示的话,那太好了! 谢谢。

  • 不用像用语言Y那样用X语言实现事物的尝试。这种方法通常会导致难以阅读/编写/调试意大利面条代码。 Go没有班级,请在使用Go语言进行设计时不要再对班级进行思考。

您可以具有复合接口,例如从io包中:

http://golang.org/src/pkg/io/io.go?s=2987:3047#L57

1
2
3
4
5
6
7
8
9
10
11
type Reader interface {
    Read(p []byte) (n int, err error)
}
type Writer interface {
    Write(p []byte) (n int, err error)
}

type ReadWriter interface {
    Reader
    Writer
}

附带说明一下,不要尝试使用go来实现Java代码,而要学习Go Way。

  • 谢谢。让我看看我是否理解正确。您的意思是我应该为method1创建一个接口,为method2创建一个接口,并为复合接口。但是,我在哪里将使用method2的method1的实现放在哪里(也许我没有说清楚)?这样我的ID仍然有重复的代码,对不对?
  • 我不太确定我是否了解您,但是如果1个结构可以满足多个接口,或者您可以定义在interfaceAinterfaceB上执行操作的函数并将结构传递给它们,而不是在结构本身上实现代码。

由于Go在OOP的意义上没有static方法,因此您经常会看到将这些类型的方法实现为包级函数:

1
2
3
package mypackage

func() Method1() { ... } // Below I will call it Function instead

然后,此类程序包级别的函数将接口作为参数。在这种情况下,您的代码将如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package main

import"fmt"

type Methoder interface {
    Method()
}

func Function(m Methoder) {
    m.Method()
}

type StructB struct{}

func (s *StructB) Method() { fmt.Println("StructB") }

type StructC struct{} // You can do some"inheritance" by embedding a base struct

func (s *StructC) Method() { fmt.Println("StructC") }

func main() {    
    b := &StructB{}
    Function(b)    
}

输出:

1
StructB
  • 像这个答案一样,并且想补充一点,如果您需要像抽象类一样具有结构接口,则可以使用与@ANisus所写相同的原理,但是要添加getters方法。这样您就可以调用像Model.getName这样的结构参数,其中Model可以解析出您创建的某些接口的确切结构。

这是我实现抽象类的一种方式,这是避免遇到循环引用并保持良好工厂模式的简便方法。

让我们假设我们的组件具有以下封装结构

1
2
3
4
5
6
7
8
9
10
component
  base
    types.go
    abstract.go
  impl1
    impl.go
  impl2
    impl.go
  types.go
  factory.go

定义组件的定义,在此示例中,将在此处定义:

component / types.go

1
2
3
4
5
6
7
8
package component

type IComponent interface{
    B() int
    A() int
    Sum() int
    Average() int
}

现在,假设我们要创建一个仅实现Sum和Average的抽象类,但是在此抽象实现中,我们希望可以使用已实现的A和B返回的值

为此,我们应该为抽象实现的抽象成员定义另一个接口

component / base / types.go

1
2
3
4
5
6
package base

type IAbstractComponentMembers {
    A() int
    B() int
}

然后我们可以继续实现抽象的"类"

component / base / abstract.go

1
2
3
4
5
6
7
8
9
10
11
12
13
package base

type AbstractComponent struct {
    IAbstractComponentsMember
}

func (a *AbstractComponent) Sum() int {
    return a.A() + a.B()
}

func (a *AbstractComponent) Average() int {
    return a.Sum() / 2
}

现在我们继续执行

component / impl1 / impl.go //假定与impl2类似

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package impl1

type ComponentImpl1 struct {
    base.AbstractComponent
}

func (c *ComponentImpl1) A() int {
    return 2
}

func (c *ComponentImpl1) A() int {
    return 4
}

// Here is how we would build this component
func New() *ComponentImpl1 {
    impl1 := &ComponentImpl1{}
    abs:=&base.AbstractComponent{
        IAbstractComponentsMember: impl1,
    }
    impl1.AbstractComponent = abs
    return impl1
}

我们为此使用单独的接口而不是使用相同的IComponent接口的原因是,如果在这种情况下使用相同的接口,则如果我们在impl *中导入基本包以使用抽象的"类",并且还导入了组件包中的impl *包,因此工厂可以注册它们,我们将找到一个循环引用。

所以我们可以有一个这样的工厂实现

component / factory.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package component

// Default component implementation to use
const defaultName ="impl1"
var instance *Factory

type Factory struct {
    // Map of constructors for the components
    ctors map[string]func() IComponent
}

func (f *factory) New() IComponent {
    ret, _ := f.Create(defaultName)
    return ret
}

func (f *factory) Create(name string) (IComponent, error) {
    ctor, ok := f.ctors[name]
    if !ok {
        return nil, errors.New("component not found")
    }
    return ctor(), nil
}

func (f *factory) Register(name string, constructor func() IComponent) {
    f.ctors[name] = constructor
}

func Factory() *Factory {
    if instance == nil {
        instance = &factory{ctors: map[string]func() IComponent{}}
    }
    return instance
}

// Here we register the implementations in the factory
func init() {
    Factory().Register("impl1", func() IComponent { return impl1.New() })
    Factory().Register("impl2", func() IComponent { return impl2.New() })
}

Go是从Small Talk中获得面向对象的概念的,而不是从像C ++这样的Simula中获得的,而不是从C ++中获得其OO概念的Java中获得的。记住,Go会突然变得清晰可见。

因此,Go中没有类的概念。只是对象,发送和接收消息。 Go的接口可以在概念上解释为消息的集合。

Go类型不"实现"接口,它们只是实现属于某些接口的消息。

重复:没有类,没有抽象基类,因此Go中没有"基于类的设计"(如Small Talk中一样)。

正如我们在此处清楚看到的那样,难怪在Go中实现ABC就是一团糟。


除非您从method1中删除static关键字,否则您要询问的示例将无法在Java中编译,因此Java中正确的抽象类会喜欢这样。

1
2
3
4
5
public abstract class A {
    void method1(){
    method2();}
    abstract void  method2();
}

要提供等效的go-lang语言,您必须通过以下方式使用界面:Go Playground

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
package main

import (
   "fmt"
)

type AI interface {
    method2()
}

type A struct {
    AI
}

func (a * A) method1() {
   a.method2()
}

type B struct {
    *A
}
func (b *B) method2() {
   fmt.Print("Hello from B method1\
")
}

func NewB() *B{
   b := &B{}
   a := &A{b}
   b.A = a
   return b
}

type C struct {
    *A
}

func (c *C) method2() {
    fmt.Print("Hello from C method1\
")
}

func NewC() *C{
    c := &C{}
    a := &A{c}
    c.A = a
    return c    
}

func main() {

     b := NewB()
     b.method1()

    c:= NewC()
    c.method1()
}

由于这仍然不容易,如何将java抽象类/多继承转换/实现为go-lang,这里是全面详细的文章。 Golang中的抽象类

  • 海事组织,这是非常令人费解的,并且是令人困惑的嵌入滥用。我从不让代码通过代码审查。此外,这些注释非常容易引起误解,没有"继承"或"抽象",这些注释隐藏并混淆了Go嵌入在做什么。
  • @Dave C-不反对这有点令人费解,说过提出更好的建议后,我很想看到一种抽象类实现的简单方法。总而言之,如果您不需要像功能这样的抽象类,只需不要使用提供的模式。