Go泛型应用工厂方法及泛型使用

 更新时间:2022年7月15日 10:04  点击:202 作者:​ TodoCoder  ​

前言

由于网上关于泛型使用的文章太多了,这里就不聊怎么使用泛型了,今天我们结合工厂方法+泛型方法来看一下泛型到底是如何在业务场景中使用的。本文涉及到的点如下:

  • 接口是怎么实现泛化编程的。
  • 泛型是怎么解决接口的局限性的。
  • 泛型使用的最佳时机。
  • 关于功能设计的简单建议。

话不多说,我们开始吧,具体泛型怎么使用及语法,请自行查阅相关资料。

接口实现泛化编程

平时我们编写结构体和方法的时候,一般是使用具体的类型:要么是基本类型,要么是自定义类型。但是如果要编写可以应用于多种类型的代码的时候,那么这种限制对程序的束缚就会比较大。

那么我们想编出一些泛化的方法和接口,怎么办呢? 这个时候我们想到了接口,如果方法的参数是一个接口,而不是一个结构体,这样对程序的限制就会放开了许多。因为任何实现该接口的结构体都可以作为该方法的接口参数,这样就可以保证后面在添加其他同类功能的时候,只需实现这个接口就可以满足需求了。

比如如下一个需求:

定义两个手机品牌结构体华为,苹果 ,并打印出各自品牌的名字。保证程序的扩展性,后面我们可能还要加入小米

import "fmt"
//手机统一接口
type Phone interface {
  PrintBrand()
}
​
type HuaweiPhone struct {
}
func (hw *HuaweiPhone) PrintBrand() {
  fmt.Printf("品牌名字:华为")
}
​
type Iphone struct {
}
func (ip *Iphone) PrintBrand() {
  fmt.Printf("品牌名字:苹果")
}
//统一打印方法
func PrintBrand(phone Phone) {
  phone.PrintBrand()
}
func main() {
  hw := HuaweiPhone{}
  ip := Iphone{}
  PrintBrand(ip)
  PrintBrand(hw)
}

如上面代码,我们定义了两个手机品牌的结构体,我们想打印各个手机的品牌名字,需要调用统一打印方法就可以了,如果后面添加其他品牌的话,我们只需要实现Phone这个接口就可以,如下添加小米手机品牌:

type XiaomiPhone struct {
}
func (xm XiaomiPhone) PrintBrand() {
  fmt.Printf("品牌名字:小米")
}

以上代码我们可以看到,通过接口也可以定义一些泛化的行为。

工厂+泛型来实现更通用的泛化编程

可是有时候,即便我们使用了接口,对程序的约束依然还是很强,因为一旦我们指明了具体的接口,就会要求我们必须使用特定的接口。而我们希望编写更通用的代码,要使代码能够应用于某种不具体的类型,而不是具体的一个接口或者结构体。这个要怎么办呢?

比如,我们基于以上的需求继续加需求

由于我们对接的品牌增大到20种,除了上面三种还有 魅族、三星、诺基亚、中兴。。。。等等

这个时候我们基于当前的代码已经不能满足,那么我们想到了工厂设计模式,在工厂中用泛型来泛化所有的类型,我们通过传入类型名字来打印出具体的品牌名。我们引入工厂模式继续优化我们的代码,

如下:

var cache sync.Map
//工厂方法 可以传入任意的类型
func PhoneFactory[T any]() (t *T) {
  target := reflect.TypeOf(t)
  v, ok := cache.Load(t)
  if ok {
    return v.(*T)
  }
  v = new(T)
  v, _ = cache.LoadOrStore(target, v)
  return v.(*T)
}
func main() {
  PrintBrand(PhoneFactory[Iphone]())
  PrintBrand(PhoneFactory[HuaweiPhone]())
  PrintBrand(PhoneFactory[XiaomiPhone]())
}

代码中我们编写了个工厂方法,泛型类型为 any, 接收任意的类型,在工厂中我们创建对象返回相应的类型并缓存类型对象防止重复创建。这样我们后面再加其他类别的时候可以通过这个工厂方法来统一的创建,我们还可以通过反射在创建前后根据业务需要做一些操作。

泛型使用的最佳时机

泛型的加入,无疑增加了代码的复杂度,那么我们使用泛型的最佳时机是什么时候呢?

Go 泛型主要设计者 Ian Lance Taylor 给出了简要的泛型使用方针,当开发者发现自己多次编写完全相同的代码,而这些副本之间的唯一区别仅在于使用了不同类型,这时候便可以考虑使用类型参数。换句话说,即开发者应避免使用类型参数,直到发现自己要多次编写完全相同的代码。

关于功能设计的简单建议

比如说上面的业务,其实我们开始设计的时候设计到接口层面就可以了,如果一开始就引入工厂方法,其实这算是过度设计,我们设计一个功能的原则是,抓住上下文,适度设计,因为一旦我们投入了过多的精力到灵活设计上,势必会影响本应该完成的需求。同时,过多的功能会引入更多潜在的问题,而修复问题也会耗费我们的时间和精力。而且在当前这个敏捷开发的时代,更是如此。

最后

为了提高可阅读性,以上代码都是以最简单的方式呈现的,实际业务远比这要复杂的多,这里只是提供一种方向。

到此这篇关于Go泛型应用工厂方法及泛型使用的文章就介绍到这了,更多相关Go泛型应用内容请搜索猪先飞以前的文章或继续浏览下面的相关文章希望大家以后多多支持猪先飞!

原文出处:https://juejin.cn/post/7120104935953285134

[!--infotagslink--]

相关文章

  • Go应用中优雅处理Error的技巧总结

    在程序员中,尤其是go新手,经常听到的一个讨论话题是:如何处理错误,这篇文章主要给大家介绍了关于Go应用中优雅处理Error的一些相关技巧,需要的朋友可以参考下...2021-09-08
  • Django def clean()函数对表单中的数据进行验证操作

    这篇文章主要介绍了Django def clean()函数对表单中的数据进行验证操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-07-09
  • C#泛型类型知识讲解

    这篇文章主要介绍了C#泛型类型知识,文中代码非常详细,帮助大家更好的理解和学习,感兴趣的朋友可以了解下...2020-06-25
  • 详解C#泛型的类型参数约束

    这篇文章主要介绍了C#泛型的类型参数约束的相关资料,文中讲解非常细致,帮助大家更好的理解和学习c#,感兴趣的朋友可以了解下...2020-07-31
  • golang官方嵌入文件到可执行程序的示例详解

    这篇文章主要介绍了golang官方嵌入文件到可执行程序,本文通过示例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-02-20
  • go浮点数转字符串保留小数点后N位的完美解决方法

    这篇文章主要介绍了go浮点数转字符串保留小数点后N位解决办法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2020-05-11
  • Go语言使用读写OPC详解

    这篇文章主要介绍了Go语言使用读写OPC详解,图文讲解的很清晰,有感兴趣的同学可以学习下...2021-03-05
  • Go中string与[]byte高效互转的方法实例

    string与[]byte经常需要互相转化,普通转化会发生底层数据的复制,下面这篇文章主要给大家介绍了关于Go中string与[]byte高效互转的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考下...2021-09-20
  • Go项目的目录结构详解

    这篇文章主要介绍了Go项目的目录结构,对基础目录做了讲解,对项目开发中的其它目录也一并做了介绍,需要的朋友可以参考下...2020-05-01
  • Go 容器遍历的实现示例

    Go 语言提供的基础容器,免不了要查询容器中的数据,那么是如何实现遍历的呢?本文将会介绍几种常用容易的遍历及其使用。感兴趣的可以了解一下...2021-06-13
  • JS跨浏览器解析XML应用过程详解

    这篇文章主要介绍了JS跨浏览器解析XML应用过程详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下...2020-10-16
  • vivo X9如何查出后台偷跑流量应用?vivo X9查出后台偷跑流量应用的方法

    vivo X9如何查看后台流量偷跑的情况?小编教你轻松查到!还不了解的小伙伴快来看看吧! 1)打开手机自带的【i管家】应用,打开后点击【流量监控】选项。(如下图) 2)接着选...2016-12-31
  • 创建第一个Go语言程序Hello,Go!

    这篇文章主要介绍了创建第一个Go语言程序Hello,Go!本文详细的给出项目创建、代码编写的过程,同时讲解了GOPATH、Go install等内容,需要的朋友可以参考下...2020-05-01
  • PHP云存储Redis的应用场景与Redis实现排行榜功能

    Redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。本文我们来讲解Redis的应用场景实例。 C...2016-11-25
  • 在Django中使用MQTT的方法

    这篇文章主要介绍了在Django中使用MQTT的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-05-10
  • go语言中的Carbon库时间处理技巧

    这篇文章主要介绍了go语言中的Carbon库时间处理,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-02-05
  • 使用Redis获取数据转json,解决动态泛型传参的问题

    这篇文章主要介绍了使用Redis获取数据转json,解决动态泛型传参的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-01-15
  • MyBatis在DAO层定义接口返回类型泛型无效的解决

    这篇文章主要介绍了MyBatis在DAO层定义接口返回类型泛型无效的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-07-31
  • go嵌套匿名结构体的初始化详解

    这篇文章主要介绍了go嵌套匿名结构体的初始化详解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-12-16
  • PHP explode()函数的几个应用和implode()函数有什么区别

    explode()函数介绍explode() 函数可以把字符串分割为数组。语法:explode(separator,string,limit)。 参数 描述 separator 必需。规定在哪里分割字符串。 string...2015-11-08