左耳听风_113_112_Go_编程模式Go_Generation
你好,我是陈浩网名左尔朵house.这节课呢我们来学习一下构语言代码生成的玩法,构语言里面的代码生成啊,主要还是用来解决编程泛型的问题。
泛型编程呢主要是解决这样一个问题,就是因为静态类型语言呢有类型,所以相关的算法或者对数据处理的程序啊会因为类型不同而需要复制一份。
那这样呢就会导致数据类型与算法功能相耦合。
那我之所以说泛型编程呢可以解决这样的问题,就是说啊在写代码的时候呢,不用关心处理数据的类型,只需要关心相关的处理逻辑。
泛型编程呢是静态语言当中啊非常非常重要的特征。
那如果没有泛醒呢,我们就很难做到多态,也很难完成抽象。
那这样呢就会导致我们的代码冗余量很大。
那为了帮你更好的理解呢,我举一个现实当中的例子,我们用螺丝刀来打个比方,螺丝刀呢本来只有一个拧螺丝的作用,但是呢因为螺丝的类型太多,有平口的,有十字口的,有六角的等等,而且呢螺丝还有不同的尺寸。
那这样呢就导致我们的螺丝刀呢为了要适配各种千奇百怪的螺丝类型,也是各种样式的。
而真正的抽象呢应该是这样,螺丝刀不关心螺丝的类型,他只需要关注自己的功能是不是完备,并且呢让自己可以适配不同类型的螺丝就行了。
那这个呢就是所谓的泛性编程要解决的实际问题。
我们先从构元的类型检查开始讲起。
因为go语言呢目前并不支持真正的泛型,所以呢只能用interface这样的类似于word新的过渡泛型来玩。
那这样呢就导致我们要在实际的过程中啊进行类型检查。
Go语言的类型检查呢有两种技术,一种是type assersert,一种呢是reflection.我们先说type result,这种技术呢一般啊是对某一个变量进行点type的转型操作,它呢会返回两个值,分别是variable和error, variable呢是转换好的类型。
而error呢表示如果不能转换类型呢容易报错。
在文章的示例中啊,我们有一个通用类型的容器,可以进行put和get.注意在这里啊,我们使用了interface做泛型。
但是呢我把数据取出来的时候啊,因为类型是intervace,所以说你还要做一个转型,那只有转型成功啊,才能进行后续的操作。
文章里呢有一个type字儿的示例,你可以看一下,我们再来说一说reflection.对于reflection呢,我们需要把前面的代码修改一下,那这里的代码呢并不难懂,这是完全使用reflection的玩法。
我呢简单解释一下,首先在调用new container的时候呢,会根据参数的类型初始化一个slice.在调用put的时候呢,会检查value是不是跟slice的类型一致。
在调用get的时候呢,我们需要用一个入参的方式。
因为我们没有办法返回reflect点value或者是interface,不然呢还要做type ssert.不过呢因为有类型检查,所以呢必然会有检查不对的时候,因此呢需要返回error.于是在使用这段代码的时候呢,type字儿是不用了,但是呢用反射写出来的代码还是有点复杂的。
那么有没有什么好的办法呢?那这个呢就要提到他山之石了。
对于泛型编程最牛的语言c加加来说啊,这类问题都是用template来解决的。
C加加的编译器呢会在编译池分析代码,根据不同的变量类型来自动化生成相关类型的函数,或者类在c加加里面呢叫模板的具体化。
这个技术呢是在编译时完成的,所以呢我们不需要在运行时进行任何的类型识别,我们的程序呢也会变得比较干净。
那么我们能不能在go里面也使用c加加的这种技术呢?答案是肯定的,只是go的编译器啊,不会帮你干你需要自己来动手,也就是go generator, go的代码生成。
要玩够的代码生成呢,你需要三个东西。
首先呢是一个函数模板,在里面呢设置好相应的占位服务。
其次呢是一个脚本,用于按规则来替换文本,并生成新的代码。
另外呢还需要一行注释代码。
我们现来说函数模板,我们把前面的示例呢改成模板,取名叫做container time go放在tempted的路径下面。
从文章里的代码,你会发现这个函数模板中呢,我们有三个占位符。
第一个是package name,就是booming.第二个是generic name,就是名字,第三个是generic type,这是实际的类型。
嗯,其他的代码呢都是一样的。
然后呢,我们创建一个叫站点SH的生成脚本,我贴到了文章里。
那这里呢需要四个参数,分别是模板的源文件包名,实际需要具体化的类型,还有用于构造目标文件名的后缀。
然后呢我们用下d命令去替换刚刚的函数模板并生成到目标文件中。
那接下来呢我们只需要在代码中啊打一个特殊的注释,其中第一个注释呢是声成包名栈类型呢是u int三十二,目标文件名呢以container为后缀。
第二个注释呢是生成包名站类型是string,而目标文件名呢也是以container为后缀。
然后呢,我在工程目录中啊直接执行go generous命令,那这样呢就会生成两份代码,一份文件名呢叫做u in ter三十二container go.而另一份文件名呢是string container的go.这两份代码可以让我们的代码完全编译通过,而付出的代价呢就是需要多执行一步。
Go generate命令。
那听到这儿呢,如果我们再回头看一看,上节课里那些用反射整出来的例子,你就会发现有了这样的技术啊,我们就不用在代码里用那些晦涩难懂的反射来做运行时的类型。
检查了我们可以写出很干净的代码,让编译器啊在编译时检查类型,对不对?那文章里呢有一个filter的模板文件叫做filter点time点go.那有了这个文件呢,我们就可以在需要使用这个的地方加上相关的go generate注释。
那我就不多说了,你就要看一下代码。
那最后呢我还想说一句,我们并不需要自己手写栈点SH这样的工具类,我们可以直接使用第三方已经写好的工具。
比如说jenny占站不占,那我把这些工具的链接呢都放在了文章里。
好了,这节课呢就到这里。
如果你觉得今天的内容对你有所帮助啊,欢迎你帮我分享给更多人。