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

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

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

问题

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

abstract class A {

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

    abstract void method2();

}

class B extends A {

    void method2() {
        ...
    }

}

class C extends A {

    void method2() {
        ...
    }

}

我知道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:

abstract class A{

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

 abstract method2();

}

class B extends A{

 method2(){
  ...
 }
    
}

class C extends A{

 method2(){
  ...
 }

}

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

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 的方式

英文:

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

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

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
}

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

答案2

得分: 12

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

package mypackage

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

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

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{} // 通过嵌入基本结构体可以实现一些"继承"

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

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

输出:

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:

package mypackage

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:

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)    
}

Output:

StructB

答案3

得分: 3

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

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

component
  base
    types.go
    abstract.go
  impl1
    impl.go
  impl2
    impl.go
  types.go
  factory.go

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

package component

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

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

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

package base

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

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

package base

type AbstractComponent struct {
    IAbstractComponentMembers
}

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

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

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

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

package impl1

type ComponentImpl1 struct {
    base.AbstractComponent
}

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

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

// 这是如何构建该组件的示例
func New() *ComponentImpl1 {
    impl1 := &ComponentImpl1{}
    abs := &base.AbstractComponent{
        IAbstractComponentMembers: impl1,
    }
    impl1.AbstractComponent = abs
    return impl1
}

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

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

package component

// 默认要使用的组件实现
const defaultName = "impl1"
var instance *Factory

type Factory struct {
    // 组件构造函数的映射
    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
}

// 在工厂中注册实现
func init() {
    Factory().Register("impl1", func() IComponent { return impl1.New() })
    Factory().Register("impl2", func() IComponent { return impl2.New() })
}

希望这能帮到你!

英文:

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

component
  base
    types.go
    abstract.go
  impl1
    impl.go
  impl2
    impl.go
  types.go
  factory.go

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

component/types.go

package component

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

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

package base

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

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

component/base/abstract.go

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
}

And now we proceed to the implementations

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

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
}

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

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() })
}

答案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糖果功能:

package main

import (
        "fmt"
)

type Api interface {
        Sugar()
        Impl()
}

// 通用的糖果API实现
type CommonSugar struct {
        BasicApi interface {
                Impl()
        }
}

func (s CommonSugar) Sugar() {
        fmt.Println("在Sugar中调用Impl")
        s.BasicApi.Impl()
        fmt.Println("在Sugar中调用了Impl\n")
}

// A的实现
type ImplA struct {
        CommonSugar
}

func (a *ImplA) Impl() {
        fmt.Println("A的Impl")
}

func NewApiA() Api {
        impl := &ImplA{}
        impl.CommonSugar.BasicApi = impl
        return impl
}

// B的实现
type ImplB struct {
        CommonSugar
}

func (a *ImplB) Impl() {
        fmt.Println("B的Impl")
}

func NewApiB() Api {
        impl := &ImplB{}
        impl.CommonSugar.BasicApi = impl
        return impl
}

func main() {
        api := NewApiA()
        api.Sugar()

        api = NewApiB()
        api.Sugar()
}

以下是程序的输出:

在Sugar中调用Impl
A的Impl
在Sugar中调用了Impl

在Sugar中调用Impl
B的Impl
在Sugar中调用了Impl
英文:

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

package main

import (
        "fmt"
)

type Api interface {
        Sugar()
        Impl()
}

// Common sugar API implementation
type CommonSugar struct {
        BasicApi interface {
                Impl()
        }
}

func (s CommonSugar) Sugar() {
        fmt.Println("Calling Impl in Sugar")
        s.BasicApi.Impl()
        fmt.Println("Called Impl in Sugar\n")
}

// A implementation
type ImplA struct {
        CommonSugar
}

func (a *ImplA) Impl() {
        fmt.Println("A Impl")
}

func NewApiA() Api {
        impl := &ImplA{}
        impl.CommonSugar.BasicApi = impl
        return impl
}

// B implementation
type ImplB struct {
        CommonSugar
}

func (a *ImplB) Impl() {
        fmt.Println("B Impl")
}

func NewApiB() Api {
        impl := &ImplB{}
        impl.CommonSugar.BasicApi = impl
        return impl
}

func main() {
        api := NewApiA()
        api.Sugar()

        api = NewApiB()
        api.Sugar()
}

Here's the program output:

Calling Impl in Sugar
A Impl
Called Impl in Sugar
Calling Impl in Sugar
B Impl
Called Impl in Sugar

答案6

得分: -2

你要翻译的内容如下:

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

public abstract class A {
    void method1(){
        method2();
    }
    abstract void method2();
}

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

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\n")
}

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\n")
}

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语言可能并不容易,这里有一篇详细介绍的帖子:在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.

public abstract class A {
void method1(){
method2();}
abstract void  method2();
}

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

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\n")
}
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\n")
}
func NewC() *C{
c := &C{}
a := &A{c}
c.A = a
return c	
}
func main() {
b := NewB()
b.method1()
c:= NewC()
c.method1()
}

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:

确定