Golang中抽象类的New()函数实现探讨

Golang中抽象类的New()函数实现探讨 我正在编写一些演示代码,用于创建一个 Stack 接口,然后创建两个实现该接口的具体结构体:LinkedStack 和 ArrayStack。

我为每个具体类型都编写了一个 New() 函数,用于返回一个已正确初始化的新实例。

我希望有一个名为 “Union” 的函数,它接收两个 Stack 并返回一个包含两者内容的新 Stack。如果我将 Union 函数硬编码为返回一个 LinkedStack,这可以正常工作。我可以使用 LinkedStack 的 New() 函数来创建新实例,填充数据,然后返回它。

但我真正希望的是,能够根据传入的第一个参数的类型来创建相应类型的新实例。这里的问题在于如何确定应该调用哪个 New() 函数。有没有办法做到这一点?


更多关于Golang中抽象类的New()函数实现探讨的实战教程也可以访问 https://www.itying.com/category-94-b0.html

7 回复

太棒了!非常感谢!

(另外——这让我对 Go 中类型推断和泛型的工作原理有了非常深入的了解。我可能还需要再消化一下,但我真的非常非常感谢你的帮助!)

更多关于Golang中抽象类的New()函数实现探讨的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


是的,你可以根据第一个参数的类型进行 switch,就像这样:

func NewByType(s Stack) Stack {
	switch s.(type) {
	case LinkedStack:
		return NewLinkedStack()
	case ArrayStack:
		return NewArrayStack()
	default:
		panic("Did not expect this type")
	}
}

你也可以尝试使用反射,不过我不确定是否可以通过函数名来找到一个 func

func Append[S2 Stack[T], S1 Stack[T], T any](s1 S1, s2 S2) S1 {
	var result S1
	switch any(result).(type) {
	case LinkedStack[T]:
		result := NewLinked[T]()
	case ArrayStack[T]:
		result := NewArray[T]()
	default:
		panic("Unsupported Type")
	}
	return result
}

谢谢!any() 这个技巧很巧妙。不过,我仍然遇到错误:

stack/stack.go:127:13: cannot use NewLinked[T]() (value of type LinkedStack[T]) as S1 value in assignment
stack/stack.go:129:13: cannot use NewArray[T]() (value of type ArrayStack[T]) as S1 value in assignment

为什么不尝试进行推断呢?

func Append[S2 Stack[T], S1 Stack[T], T any](s1 S1, s2 S2) S1 {
	var result S1
	switch any(result).(type) {
	case LinkedStack[T]:
		result = any(NewLinked[T]()).(S1)
	case ArrayStack[T]:
		result = any(NewArray[T]()).(S1)
	default:
		panic("Unsupported Type")
	}
	return result
}

谢谢!这在我的情况下似乎不起作用,可能是因为我也在使用泛型:

func Append[S2 Stack[T], S1 Stack[T], T any](s1 S1, s2 S2) S1 {
        var result S1
        switch result.(type) {
                case LinkedStack[T]:
                        result := NewLinked[T]()
                case ArrayStack[T]:
                        result := NewArray[T]()
                default:
                        panic("Unsupported Type")
        }
        return result
}

这会导致错误:无法在类型参数值 result(类型为受 Stack[T] 约束的 S1 的变量)上使用类型开关。

在Go中可以通过类型断言和工厂模式来实现根据参数类型动态创建对应实例。这里提供两种实现方案:

方案一:使用类型开关和注册工厂模式

package main

import "fmt"

type Stack interface {
    Push(item interface{})
    Pop() interface{}
    Len() int
}

// 工厂函数类型
type StackFactory func() Stack

// 工厂注册表
var stackFactories = make(map[string]StackFactory)

func RegisterFactory(name string, factory StackFactory) {
    stackFactories[name] = factory
}

type LinkedStack struct {
    // 实现细节
}

func NewLinkedStack() Stack {
    return &LinkedStack{}
}

type ArrayStack struct {
    // 实现细节
}

func NewArrayStack() Stack {
    return &ArrayStack{}
}

func Union(s1, s2 Stack) Stack {
    // 通过反射获取类型名称
    typeName := fmt.Sprintf("%T", s1)
    
    factory, ok := stackFactories[typeName]
    if !ok {
        // 默认使用LinkedStack
        return NewLinkedStack()
    }
    
    newStack := factory()
    // 合并逻辑
    // ...
    return newStack
}

func init() {
    RegisterFactory("*main.LinkedStack", NewLinkedStack)
    RegisterFactory("*main.ArrayStack", NewArrayStack)
}

方案二:使用类型断言和接口方法

package main

type Stack interface {
    Push(item interface{})
    Pop() interface{}
    Len() int
    NewEmpty() Stack // 添加创建新实例的方法
}

type LinkedStack struct {
    // 实现细节
}

func (ls *LinkedStack) NewEmpty() Stack {
    return &LinkedStack{}
}

type ArrayStack struct {
    // 实现细节
}

func (as *ArrayStack) NewEmpty() Stack {
    return &ArrayStack{}
}

func Union(s1, s2 Stack) Stack {
    // 使用接口方法创建对应类型的新实例
    newStack := s1.NewEmpty()
    
    // 合并逻辑示例
    // 这里简单将s2的元素压入新栈
    for s2.Len() > 0 {
        newStack.Push(s2.Pop())
    }
    
    return newStack
}

方案三:使用泛型(Go 1.18+)

package main

type Stack[T any] interface {
    Push(item T)
    Pop() T
    Len() int
}

type LinkedStack[T any] struct {
    // 实现细节
}

func NewLinkedStack[T any]() Stack[T] {
    return &LinkedStack[T]{}
}

type ArrayStack[T any] struct {
    // 实现细节
}

func NewArrayStack[T any]() Stack[T] {
    return &ArrayStack[T]{}
}

func Union[T any](s1, s2 Stack[T]) Stack[T] {
    // 通过类型断言确定具体类型
    switch s1.(type) {
    case *LinkedStack[T]:
        newStack := NewLinkedStack[T]()
        // 合并逻辑
        return newStack
    case *ArrayStack[T]:
        newStack := NewArrayStack[T]()
        // 合并逻辑
        return newStack
    default:
        return NewLinkedStack[T]()
    }
}

推荐使用方案二,它在接口中添加NewEmpty()方法,这样每个具体类型都能提供自己的实例创建逻辑,代码更清晰且类型安全。方案一需要维护注册表,方案三需要Go 1.18+版本支持。

回到顶部