在Google Go中,抽象类和抽象方法的等效概念是什么?

huangapple go评论143阅读模式
英文:

Equivalence of abstract classes/methods (Java) in Google Go

问题

我是新手,想知道如何在Go语言中实现类似于Java中的抽象类和抽象方法的结构。在Java中,我会这样做:

  1. abstract class A {
  2. static void method1() {
  3. ...
  4. method2();
  5. ...
  6. }
  7. abstract void method2();
  8. }
  9. class B extends A {
  10. void method2() {
  11. ...
  12. }
  13. }
  14. class C extends A {
  15. void method2() {
  16. ...
  17. }
  18. }

我知道Go语言有接口和结构体。我可以创建一个接口,然后再创建一个结构体来实现method1。但是method2怎么办呢?
我知道可以在一个接口中嵌入另一个接口,也可以在一个结构体中将另一个结构体作为字段嵌入。但是我没有看到一种方法可以实现我的结构。

我唯一看到的解决方案是在类B和类C中都实现method1。难道没有其他方法吗?

注意:当然,在我的情况下不只有一个方法。而且我有一个抽象类的层次结构,真的不想把所有东西都移到“子类”中。

我在互联网上找到的示例大多只有一个接口中的一个方法。如果你们中的某个人能给我一个提示,那就太好了!谢谢。

英文:

I am new to Go and I'm wondering how I can implement a structure similar to abstract classes & methods in Java. In Java, I'd do the following:

  1. abstract class A{
  2. static method1(){
  3. ...
  4. method2();
  5. ...
  6. }
  7. abstract method2();
  8. }
  9. class B extends A{
  10. method2(){
  11. ...
  12. }
  13. }
  14. class C extends A{
  15. method2(){
  16. ...
  17. }
  18. }

I know about interfaces and structs. I could build an interface and then a struct to implement method1. But what about method2?
I know that I can embed one interface in another and also a struct as a field of another struct. But I don't see a way to implement my structure with those methods.

The only solution I see is to implement method1 both in class B and class C. Isn't there another way?

Note: of course in my case it's not just one method. Also I've got a hierarchy of abstract classes and don't really want to move everything down to the 'subclasses'.

The examples I've found on the internet are mostly with only one method per interface. It would be great if one of you guys could give me a hint here! Thanks.

答案1

得分: 13

你可以拥有复合接口,例如来自 io 包的接口:

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

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

另外,顺便提一下,不要试图使用 Go 实现 Java 代码,而是要学习 Go 的方式

英文:

You can have composite interfaces, for example from the io package :

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

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

As a side note, don't try to implement java code using go, try to learn the Go Way.

答案2

得分: 12

由于Go语言在面向对象编程(OOP)中没有静态方法,因此通常会将这些类型的方法实现为包级别的函数:

  1. package mypackage
  2. func Method1() { ... } // 下面我将称之为Function

这样的包级别函数将以接口作为参数。在这种情况下,你的代码可能如下所示:

  1. package main
  2. import "fmt"
  3. type Methoder interface {
  4. Method()
  5. }
  6. func Function(m Methoder) {
  7. m.Method()
  8. }
  9. type StructB struct{}
  10. func (s *StructB) Method() { fmt.Println("StructB") }
  11. type StructC struct{} // 通过嵌入基本结构体可以实现一些"继承"
  12. func (s *StructC) Method() { fmt.Println("StructC") }
  13. func main() {
  14. b := &StructB{}
  15. Function(b)
  16. }

输出:

  1. StructB
英文:

Since Go does not have static methods in the OOP sense, you often see those types of methods being implemented as package level functions:

  1. package mypackage
  2. func() Method1() { ... } // Below I will call it Function instead

Such package level functions would then take an interface as an argument. Your code would in that case look something like this:

  1. package main
  2. import "fmt"
  3. type Methoder interface {
  4. Method()
  5. }
  6. func Function(m Methoder) {
  7. m.Method()
  8. }
  9. type StructB struct{}
  10. func (s *StructB) Method() { fmt.Println("StructB") }
  11. type StructC struct{} // You can do some "inheritance" by embedding a base struct
  12. func (s *StructC) Method() { fmt.Println("StructC") }
  13. func main() {
  14. b := &StructB{}
  15. Function(b)
  16. }

Output:

  1. StructB

答案3

得分: 3

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

假设我们有以下组件的包结构:

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

首先,我们在component/types.go中定义组件的接口:

  1. package component
  2. type IComponent interface {
  3. B() int
  4. A() int
  5. Sum() int
  6. Average() int
  7. }

接下来,我们想创建一个只实现SumAverage的抽象类,但在这个抽象实现中,我们希望能够使用已实现的AB的返回值。为了实现这一点,我们需要为抽象实现的抽象成员定义另一个接口。

component/base/types.go中定义抽象成员的接口:

  1. package base
  2. type IAbstractComponentMembers interface {
  3. A() int
  4. B() int
  5. }

然后,我们可以开始实现抽象类:

  1. package base
  2. type AbstractComponent struct {
  3. IAbstractComponentMembers
  4. }
  5. func (a *AbstractComponent) Sum() int {
  6. return a.A() + a.B()
  7. }
  8. func (a *AbstractComponent) Average() int {
  9. return a.Sum() / 2
  10. }

现在,我们可以进行具体实现了。

component/impl1/impl.go中实现具体实现(impl2类似):

  1. package impl1
  2. type ComponentImpl1 struct {
  3. base.AbstractComponent
  4. }
  5. func (c *ComponentImpl1) A() int {
  6. return 2
  7. }
  8. func (c *ComponentImpl1) B() int {
  9. return 4
  10. }
  11. // 这是如何构建该组件的示例
  12. func New() *ComponentImpl1 {
  13. impl1 := &ComponentImpl1{}
  14. abs := &base.AbstractComponent{
  15. IAbstractComponentMembers: impl1,
  16. }
  17. impl1.AbstractComponent = abs
  18. return impl1
  19. }

我们之所以使用单独的接口而不是使用相同的IComponent接口,是因为如果在这种情况下在impl*中导入base包以使用抽象类,并且在component包中导入impl*包以使工厂能够注册它们,将会出现循环引用的问题。

因此,我们可以有如下的工厂实现:

  1. package component
  2. // 默认要使用的组件实现
  3. const defaultName = "impl1"
  4. var instance *Factory
  5. type Factory struct {
  6. // 组件构造函数的映射
  7. ctors map[string]func() IComponent
  8. }
  9. func (f *Factory) New() IComponent {
  10. ret, _ := f.Create(defaultName)
  11. return ret
  12. }
  13. func (f *Factory) Create(name string) (IComponent, error) {
  14. ctor, ok := f.ctors[name]
  15. if !ok {
  16. return nil, errors.New("component not found")
  17. }
  18. return ctor(), nil
  19. }
  20. func (f *Factory) Register(name string, constructor func() IComponent) {
  21. f.ctors[name] = constructor
  22. }
  23. func Factory() *Factory {
  24. if instance == nil {
  25. instance = &Factory{ctors: map[string]func() IComponent{}}
  26. }
  27. return instance
  28. }
  29. // 在工厂中注册实现
  30. func init() {
  31. Factory().Register("impl1", func() IComponent { return impl1.New() })
  32. Factory().Register("impl2", func() IComponent { return impl2.New() })
  33. }

希望这能帮到你!

英文:

This is the way I achieved implementing an abstract class the easy way avoiding to run into cyclic references and maintaining good factory patterns.

Let us assume we have the following package structure for our component

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

Define the definition of the component, in this example it will be defined here:

component/types.go

  1. package component
  2. type IComponent interface{
  3. B() int
  4. A() int
  5. Sum() int
  6. Average() int
  7. }

Now let's assume we want to create an abstract class that implements Sum and Average only, but in this abstract implementation we would like to have access to use the values returned by the implemented A and B

To achieve this, we should define another interface for the abstract members of the abstract implementation

component/base/types.go

  1. package base
  2. type IAbstractComponentMembers {
  3. A() int
  4. B() int
  5. }

And then we can proceed to implement the abstract "class"

component/base/abstract.go

  1. package base
  2. type AbstractComponent struct {
  3. IAbstractComponentsMember
  4. }
  5. func (a *AbstractComponent) Sum() int {
  6. return a.A() + a.B()
  7. }
  8. func (a *AbstractComponent) Average() int {
  9. return a.Sum() / 2
  10. }

And now we proceed to the implementations

component/impl1/impl.go // Asume something similar for impl2

  1. package impl1
  2. type ComponentImpl1 struct {
  3. base.AbstractComponent
  4. }
  5. func (c *ComponentImpl1) A() int {
  6. return 2
  7. }
  8. func (c *ComponentImpl1) A() int {
  9. return 4
  10. }
  11. // Here is how we would build this component
  12. func New() *ComponentImpl1 {
  13. impl1 := &ComponentImpl1{}
  14. abs:=&base.AbstractComponent{
  15. IAbstractComponentsMember: impl1,
  16. }
  17. impl1.AbstractComponent = abs
  18. return impl1
  19. }

The reason we use a separate interface for this instead of using the same IComponent interface, is because if we use the same interface in this case, if we import the base package in impl* to use the abstract "class" and also we import the impl* packages in the components package, so the factory can register them, we'll find a circular reference.

So we could have a factory implementation like this

component/factory.go

  1. package component
  2. // Default component implementation to use
  3. const defaultName = "impl1"
  4. var instance *Factory
  5. type Factory struct {
  6. // Map of constructors for the components
  7. ctors map[string]func() IComponent
  8. }
  9. func (f *factory) New() IComponent {
  10. ret, _ := f.Create(defaultName)
  11. return ret
  12. }
  13. func (f *factory) Create(name string) (IComponent, error) {
  14. ctor, ok := f.ctors[name]
  15. if !ok {
  16. return nil, errors.New("component not found")
  17. }
  18. return ctor(), nil
  19. }
  20. func (f *factory) Register(name string, constructor func() IComponent) {
  21. f.ctors[name] = constructor
  22. }
  23. func Factory() *Factory {
  24. if instance == nil {
  25. instance = &factory{ctors: map[string]func() IComponent{}}
  26. }
  27. return instance
  28. }
  29. // Here we register the implementations in the factory
  30. func init() {
  31. Factory().Register("impl1", func() IComponent { return impl1.New() })
  32. Factory().Register("impl2", func() IComponent { return impl2.New() })
  33. }

答案4

得分: 0

Go从Small Talk中获得了其面向对象的概念,而不是像C++从Simula中获得的,也不是像Java从C++中获得的。记住这一点,Go将会变得清晰和明显。

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

Go类型不是“实现”接口,它们只是实现了某个接口中的消息。

重申一遍:在Go中没有类,没有抽象基类,因此没有“基于类的设计”(就像Small Talk中那样)。

难怪在Go中尝试实现抽象基类会变得一团糟,正如我们在这里清楚地看到的那样。

英文:

Go gets its object oriented concepts from Small Talk, not Simula like C++ and not from Java which gets its OO concepts from C++. Remember this and Go will suddenly become clear and obvious.

Thus, there is no concept of class in Go. Just objects, sending and receiving messages. Go's interfaces can be conceptually explained as collection of messages.

Go types do not “implement” interfaces, they just implement messages that are part of some interface.

Repeat: there is no class, no abstract base class and thus there is no “class based design” in Go (as in Small Talk).

No wonder trying to implement ABC in Go is a mess, as we see here clearly.

答案5

得分: 0

我使用这个技巧来实现一些常见的API糖果功能:

  1. package main
  2. import (
  3. "fmt"
  4. )
  5. type Api interface {
  6. Sugar()
  7. Impl()
  8. }
  9. // 通用的糖果API实现
  10. type CommonSugar struct {
  11. BasicApi interface {
  12. Impl()
  13. }
  14. }
  15. func (s CommonSugar) Sugar() {
  16. fmt.Println("在Sugar中调用Impl")
  17. s.BasicApi.Impl()
  18. fmt.Println("在Sugar中调用了Impl\n")
  19. }
  20. // A的实现
  21. type ImplA struct {
  22. CommonSugar
  23. }
  24. func (a *ImplA) Impl() {
  25. fmt.Println("A的Impl")
  26. }
  27. func NewApiA() Api {
  28. impl := &ImplA{}
  29. impl.CommonSugar.BasicApi = impl
  30. return impl
  31. }
  32. // B的实现
  33. type ImplB struct {
  34. CommonSugar
  35. }
  36. func (a *ImplB) Impl() {
  37. fmt.Println("B的Impl")
  38. }
  39. func NewApiB() Api {
  40. impl := &ImplB{}
  41. impl.CommonSugar.BasicApi = impl
  42. return impl
  43. }
  44. func main() {
  45. api := NewApiA()
  46. api.Sugar()
  47. api = NewApiB()
  48. api.Sugar()
  49. }

以下是程序的输出:

  1. Sugar中调用Impl
  2. AImpl
  3. Sugar中调用了Impl
  4. Sugar中调用Impl
  5. BImpl
  6. Sugar中调用了Impl
英文:

I use this trick to implement some sugar API with common implementations:

  1. package main
  2. import (
  3. "fmt"
  4. )
  5. type Api interface {
  6. Sugar()
  7. Impl()
  8. }
  9. // Common sugar API implementation
  10. type CommonSugar struct {
  11. BasicApi interface {
  12. Impl()
  13. }
  14. }
  15. func (s CommonSugar) Sugar() {
  16. fmt.Println("Calling Impl in Sugar")
  17. s.BasicApi.Impl()
  18. fmt.Println("Called Impl in Sugar\n")
  19. }
  20. // A implementation
  21. type ImplA struct {
  22. CommonSugar
  23. }
  24. func (a *ImplA) Impl() {
  25. fmt.Println("A Impl")
  26. }
  27. func NewApiA() Api {
  28. impl := &ImplA{}
  29. impl.CommonSugar.BasicApi = impl
  30. return impl
  31. }
  32. // B implementation
  33. type ImplB struct {
  34. CommonSugar
  35. }
  36. func (a *ImplB) Impl() {
  37. fmt.Println("B Impl")
  38. }
  39. func NewApiB() Api {
  40. impl := &ImplB{}
  41. impl.CommonSugar.BasicApi = impl
  42. return impl
  43. }
  44. func main() {
  45. api := NewApiA()
  46. api.Sugar()
  47. api = NewApiB()
  48. api.Sugar()
  49. }

Here's the program output:

  1. Calling Impl in Sugar
  2. A Impl
  3. Called Impl in Sugar
  4. Calling Impl in Sugar
  5. B Impl
  6. Called Impl in Sugar

答案6

得分: -2

你要翻译的内容如下:

你提到的示例在Java中无法编译,除非从method1中删除static关键字,因此在Java中,正确的抽象类应该是这样的:

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

要在Go语言中提供相当的功能,你需要使用接口,代码如下:Go Playground

  1. package main
  2. import (
  3. "fmt"
  4. )
  5. type AI interface {
  6. method2()
  7. }
  8. type A struct {
  9. AI
  10. }
  11. func (a *A) method1() {
  12. a.method2()
  13. }
  14. type B struct {
  15. *A
  16. }
  17. func (b *B) method2() {
  18. fmt.Print("Hello from B method1\n")
  19. }
  20. func NewB() *B {
  21. b := &B{}
  22. a := &A{b}
  23. b.A = a
  24. return b
  25. }
  26. type C struct {
  27. *A
  28. }
  29. func (c *C) method2() {
  30. fmt.Print("Hello from C method1\n")
  31. }
  32. func NewC() *C {
  33. c := &C{}
  34. a := &A{c}
  35. c.A = a
  36. return c
  37. }
  38. func main() {
  39. b := NewB()
  40. b.method1()
  41. c := NewC()
  42. c.method1()
  43. }

由于将Java的抽象类和多继承翻译/实现为Go语言可能并不容易,这里有一篇详细介绍的帖子:在Go语言中的抽象类

英文:

The example you are asking would not compile in Java unless you remove static keyword from method1, so the correct abstract class in Java would like like this.

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

To provide equivalent in go-lang you would have to use an interface in the following way: Go Playground

  1. package main
  2. import (
  3. "fmt"
  4. )
  5. type AI interface {
  6. method2()
  7. }
  8. type A struct {
  9. AI
  10. }
  11. func (a * A) method1() {
  12. a.method2()
  13. }
  14. type B struct {
  15. *A
  16. }
  17. func (b *B) method2() {
  18. fmt.Print("Hello from B method1\n")
  19. }
  20. func NewB() *B{
  21. b := &B{}
  22. a := &A{b}
  23. b.A = a
  24. return b
  25. }
  26. type C struct {
  27. *A
  28. }
  29. func (c *C) method2() {
  30. fmt.Print("Hello from C method1\n")
  31. }
  32. func NewC() *C{
  33. c := &C{}
  34. a := &A{c}
  35. c.A = a
  36. return c
  37. }
  38. func main() {
  39. b := NewB()
  40. b.method1()
  41. c:= NewC()
  42. c.method1()
  43. }

Since this still not may be easy how to translate/implement java abstract classes/multi-inheritance to go-lang here is the post with comprehensive details. Abstract Class in golang

huangapple
  • 本文由 发表于 2014年6月16日 02:01:36
  • 转载请务必保留本文链接:https://go.coder-hub.com/24232513.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定