Go语言中如何抽象Stripe迭代器以复用GetAll函数?
针对Stripe多类型迭代器的通用GetAll函数实现方案
针对你遇到的Stripe多类型迭代器无法复用GetAll逻辑的问题,这里有几种实用的Go语言解决方案:
方案1:接口抽象+适配器模式
先定义通用迭代器接口,把不同类型的Stripe迭代器包装成适配该接口的结构体,实现类型统一。
步骤1:定义通用迭代器接口
// 通用迭代器接口,统一Next和获取当前元素的行为 type StripeIterator interface { Next() bool Current() interface{} Err() error // 统一错误检查方法 }
步骤2:为每个Stripe迭代器写适配器
以Coupon迭代器为例,其他类型(如Customer、Product)同理:
// Coupon迭代器适配器 type CouponIterAdapter struct { iter *coupon.Iter } func (a *CouponIterAdapter) Next() bool { return a.iter.Next() } func (a *CouponIterAdapter) Current() interface{} { return a.iter.Coupon() } func (a *CouponIterAdapter) Err() error { return a.iter.Err() }
步骤3:定义通用迭代器工厂类型和GetAll函数
// 迭代器工厂函数:接收起始ID,返回通用迭代器 type IteratorFactory func(startingAfter string) StripeIterator // 通用GetAll函数 func GetAll(factory IteratorFactory, processItem func(item interface{}) error) error { okToContinue := true for okToContinue { lastId := "" pageLineCount := 0 iter := factory(lastId) for iter.Next() && okToContinue { item := iter.Current() if err := processItem(item); err != nil { return err } // 提取当前项ID用于下一页分页 if obj, ok := item.(interface{ GetID() string }); ok { lastId = obj.GetID() } pageLineCount++ } // 检查迭代器错误 if err := iter.Err(); err != nil { return err } okToContinue = pageLineCount > 0 } return nil }
步骤4:调用通用函数
// Coupon的迭代器工厂 func couponFactory(startingAfter string) StripeIterator { params := &stripe.CouponListParams{} params.Filters.AddFilter("limit", "", config.Config.Input.StripeMaxPageSize) if startingAfter != "" { params.Filters.AddFilter("starting_after", "", startingAfter) } return &CouponIterAdapter{iter: coupon.List(params)} } // 调用示例 func main() { err := GetAll(couponFactory, func(item interface{}) error { coupon, ok := item.(*stripe.Coupon) if !ok { return fmt.Errorf("无效的Coupon类型") } // 这里写你的数据处理逻辑(如存文件、数据库) fmt.Printf("处理Coupon:%s\n", coupon.ID) return nil }) if err != nil { log.Fatal(err) } }
方案2:Go泛型实现(Go 1.18+)
如果你的项目用Go 1.18及以上版本,泛型可以让代码更简洁,避免重复写适配器:
步骤1:定义泛型迭代器接口
// 泛型迭代器接口,指定元素类型T type GenericStripeIter[T any] interface { Next() bool Get() T Err() error }
步骤2:包装Stripe迭代器实现泛型接口
// Coupon泛型迭代器包装器 type CouponGenericIter struct { iter *coupon.Iter } func (i *CouponGenericIter) Next() bool { return i.iter.Next() } func (i *CouponGenericIter) Get() *stripe.Coupon { return i.iter.Coupon() } func (i *CouponGenericIter) Err() error { return i.iter.Err() }
步骤3:泛型版GetAll函数
type GenericIterFactory[T any] func(startingAfter string) GenericStripeIter[T] func GetAllGeneric[T any](factory GenericIterFactory[T], processItem func(item T) error) error { okToContinue := true for okToContinue { lastId := "" pageLineCount := 0 iter := factory(lastId) for iter.Next() && okToContinue { item := iter.Get() if err := processItem(item); err != nil { return err } if obj, ok := any(item).(interface{ GetID() string }); ok { lastId = obj.GetID() } pageLineCount++ } if err := iter.Err(); err != nil { return err } okToContinue = pageLineCount > 0 } return nil }
步骤4:调用泛型函数
func couponGenericFactory(startingAfter string) GenericStripeIter[*stripe.Coupon] { params := &stripe.CouponListParams{} params.Filters.AddFilter("limit", "", config.Config.Input.StripeMaxPageSize) if startingAfter != "" { params.Filters.AddFilter("starting_after", "", startingAfter) } return &CouponGenericIter{iter: coupon.List(params)} } // 调用示例 err := GetAllGeneric(couponGenericFactory, func(coupon *stripe.Coupon) error { fmt.Printf("处理Coupon:%s\n", coupon.ID) return nil })
方案3:闭包直接封装(最简洁)
不用定义接口和适配器,直接用闭包包装Stripe迭代器的方法,快速实现复用:
// 通用GetAll函数,接收迭代逻辑闭包和处理回调 func GetAllSimple( getIter func(startingAfter string) (next func() bool, current func() interface{}, err func() error), processItem func(item interface{}) error, ) error { okToContinue := true for okToContinue { lastId := "" pageLineCount := 0 next, current, getErr := getIter(lastId) for next() && okToContinue { item := current() if err := processItem(item); err != nil { return err } if obj, ok := item.(interface{ GetID() string }); ok { lastId = obj.GetID() } pageLineCount++ } if err := getErr(); err != nil { return err } okToContinue = pageLineCount > 0 } return nil }
调用示例
err := GetAllSimple( func(startingAfter string) (func() bool, func() interface{}, func() error) { params := &stripe.CouponListParams{} params.Filters.AddFilter("limit", "", config.Config.Input.StripeMaxPageSize) if startingAfter != "" { params.Filters.AddFilter("starting_after", "", startingAfter) } iter := coupon.List(params) return iter.Next, func() interface{} { return iter.Coupon() }, iter.Err }, func(item interface{}) error { coupon := item.(*stripe.Coupon) fmt.Printf("处理Coupon:%s\n", coupon.ID) return nil }, )
内容的提问来源于stack exchange,提问作者Chris
相关产品推荐
相关产品推荐

