[Generic Advanced] Factory Method of Go Generic Application + Evolution of Generic Use

  Hello everyone, I'm Coder, today we're going to talk about generics, go generics have been sharpened in ten years, and ten years is not too long. After all, Conan is still a primary school student, and it is not too short. Yes, hahaha.

Since there are too many articles on the use of generics on the Internet, we will not talk about how to use generics here. Today, we will combine the factory method + generic method to see how generics are used in business scenarios. The points covered in this article are as follows:

  1. How does an interface implement generalized programming?
  2. How generics solve the limitations of interfaces.
  3. The best time to use generics.
  4. Simple advice on functional design.

Without further ado, let's get started. How to use generics and their syntax, please check the relevant information yourself.

Interface to achieve generalized programming

  Usually when we write structures and methods, we generally use specific types: either basic types or custom types . But if you want to write code that can be applied to multiple types of code, then this restriction will be more restrictive to the program.

  So we want to compile some generalized methods and interfaces, what should we do? At this time, we thought of the interface. If the parameter of the method is an interface instead of a structure, the restrictions on the program will be released a lot. Because any structure that implements this interface can be used as the interface parameter of this method, it can be ensured that when other similar functions are added later, only this interface can be implemented to meet the requirements.

For example the following requirement:

Define two mobile phone brand structures, Huawei and Apple, and print out the names of their respective brands. To ensure the scalability of the program, we may also add Xiaomi later

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)
}
复制代码

As shown in the above code, we define the structure of two mobile phone brands. If we want to print the brand name of each mobile phone, we need to call the unified printing method. If other brands are added later, we only need to implement Phonethis interface, as follows Add Xiaomi phone brand:

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

As we can see from the above code, some generalized behaviors can also be defined through interfaces.

Factory + generics for more generalized programming

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

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

由于我们对接的品牌增大到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 给出了简要的泛型使用方针,当开发者发现自己多次编写完全相同的代码,而这些副本之间的唯一区别仅在于使用了不同类型,这时候便可以考虑使用类型参数。换句话说,即开发者应避免使用类型参数,直到发现自己要多次编写完全相同的代码。

关于功能设计的简单建议

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

最后

  感谢各位能看到最后,希望本篇的内容对你有帮助,有什么意见或者建议可以留言一起讨论,看到后第一时间回复,也希望大家能给个赞,你的赞就是我写文章的动力,再次感谢。

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

本文正在参加技术专题18期-聊聊Go语言框架

Guess you like

Origin juejin.im/post/7120104935953285134