小工具      在线工具  汉语词典  css  js  c++  java

Go语言流处理,工厂模式,命令参数,序列化,单元测试

Go,golang 额外说明

收录于:152天前

IO流

流是数据在数据源和程序之间所采用的路径。数据源可以是文件数据库或者键盘输入等,程序是运行在内存中的应用程序。

数据从数据源输入到程序的路径是输入流,数据从内存输出到数据源的路径是输出流。

在这里插入图片描述

流式传输基于内存。内存的输入是输入流,数据在内存中的持久化是输出流。

计算机中的流实际上是信息的转换。它是一个有序的流,所以相对于某个对象,我们通常称该对象从外界输入信息(Input),相应地,该对象向外界输出的信息(Output)称为输出流,统称为输出流。称为输入/输出流(I/O Streams)。对象之间交换信息或数据时,总是将对象或数据转换为某种形式的流,然后通过流的传输,流到达目的对象后转换为对象数据。因此,流可以看作是一种数据载体,通过流可以实现数据的交换和传输。

对于计算机来说,流的基本单位是字节流,但是对于程序来说,程序也可以识别字符流。字节流是面向计算机的,字符流是面向应用程序的。

该程序还通过流的方式实现与计算机的数据交换。通常,编程语言提供内置的流(I/O)接口。

Java I/O

alt

Go I/O
在这里插入图片描述
在这里插入图片描述

在计算机软件的工作过程中,需要大量的I/O。例如,软件的本地数据库存储用户的缓存信息等。流是数据持久化的桥梁。将程序中的数据,即内存中的数据写入到显示器或文件系统中。

键盘输入输出

//输出
fmt.Println()   //打印换行
fmt.Print()     //打印
fmt.Printf()    //格式化打印


//输入
fmt.Scanf()
fmt.Scanln()
fmt.Scan()

格式化输入和输出(通常用f)

%v 值的默认格式表示
%T 值的类型的Go语法表示
%t 单词true或false
%d 表示为十进制
%f 有小数部分但无指数部分,如123.456
%s 直接输出字符串或者[]byte

fmt.Printf(format string, a ...interface{}) (n int, err error)

var a = "test"
var b = Student{
    } 
var c = "10"
fmt.Printf("默认输出%v,类型输出%T,十进制输出%d,字符输出%s", a, b, c, a)

在这里插入图片描述

fmt.Scanf(format string, a ...any) (n int, err error)

在格式化输入时,需要注意以下几点:

  1. 输入的格式必须与书面格式一致,与格式化程序相对应即可。
  2. 输入时必须使用接收地址以节省内存。
  3. 书写时,格式化字符不使用标点符号分隔,输入时默认使用空格分隔。
//3特性
var a1 int
var b1 string
fmt.Scanf("%d,%s\n", &a1, &b1)
fmt.Scanf("%d%s\n", &a1, &b1)

输入时,第一个用“,”分隔,第二个用空格分隔。

//特性1
fmt.Scanf("a=%d,b=%s\n", &a1, &b1)
fmt.Scanf("输入一个十进制数%d,输入一个字符串%s\n", &a1, &b1)

在这里插入图片描述
在这里插入图片描述

标准输入和输出

func Println(a ...interface{}) (n int, err error)

a1 := 10
b1 := "qwe"
fmt.Println(a1, b1)

在这里插入图片描述
Println方法标准化输出时相邻参数的输出之间添加空格并在输出结束后添加换行符,Print不会自动换行。

func Scan(a ...interface{}) (n int, err error)

Scan 扫描标准输入中的文本,并将成功读入的以空格分隔的值保存到成功传递给该函数的参数中。 (注意只能用空格分隔输入,并且书写时只能用“,”分隔变量地址接收)

var a1 int
var b1 string
fmt.Scan(&a1,&b1)
fmt.Print(a1, b1)

在这里插入图片描述
Scanln方法能自动换行。

fmt包其他格式化I/O,详见中文开发手册

文件操作

file, err := os.Open("D:\\Go\\Go Files\\unit5\\src\\test\\hello.txt")
//file, err := os.Open("D:/Go/Go Files/unit5/src/test/hello.txt")
//file, err := os.Open("../test/hello.txt")
if err != nil {
    
	fmt.Println("file nnot found")
	return
}
defer file.Close()

reader := bufio.NewReader(file)

for {
    
	str, err := reader.ReadString('\n')
	if err == io.EOF {
    
		break
	}

	fmt.Print(str)
}

对于Go来说没有严格的/\\区分,均可以读取。

filebyte, err := ioutil.ReadFile("D:\\Go\\Go Files\\unit5\\src\\test\\hello.txt")
if err != nil {
    
	fmt.Println("read file err=", err)
}
fmt.Printf("%v", string(filebyte))
  • 创建文件并写入文件
filepath := "./abc.txt"
file, err := os.OpenFile(filepath, os.O_RDONLY|os.O_CREATE, 0666)
if err != nil {
    
	fmt.Printf("open file err=%v\n", err)
	return
}

defer file.Close()

str := "hello,gardon\n"

writer := bufio.NewWriter(file)
//循环写入几行
for j := 0; j < len(str); j++ {
    
	writer.WriteString(str)
}

writer.Flush()

工厂模式

在Go语言中,一条件下某些结构体并不需要多次创建,只需要单一的实例即可即工厂模式或者单例模式。例如在数据的datasource创建时的工厂模式单例即可。

Go语言中没有像Java那样的static关键字。如果需要单例模式,只需要通过指针进行操作,即返回变量的地址。

package main

import "fmt"

func main() {
    
	//
	var student = StudentFactory(1, "xuwenhui")
	fmt.Println((*student).id, (*student).name)
}

type student struct {
    
	id   int
	name string
}

func StudentFactory(id int, name string) *student {
    
	return &student{
    
		id:   id,
		name: name,
	}
}

在上面代码中student结构体要设为单例模式,需要设置访问权限,通过公开访问方法StudentFactory返回结构体的地址,并用指针操作结构体,实现工厂模式。

类型断言

在Go的多态特性中,不同的特性需要使用断言来调用不同的方法。

type Person struct {
    
	name string
	sex  string
}

type Student struct {
    
	person Person
	sid    string
}

// 需要一个父类是两个结构体的父接口
// 空接口是任何类型的父类
type Per interface {
    
}

在上述代码中需要一个参数能同时接收Person和Student类,同时调用其方法,Go语言中,通过断言来决策。

断言能够将子类用父类接收实现多态性,具体用法是变量.(类型)

func main() {
    
	var a Per
	var b = a.(Person)
	fmt.Println(b.name, b.sex)

	var c = a.(Student)
	fmt.Println(c.person.name, c.person.sex, c.sid)
}

type Person struct {
    
	name string
	sex  string
}

type Student struct {
    
	person Person
	sid    string
}

// 需要一个父类是两个结构体的父接口
// 空接口是任何类型的父类
type Per interface {
    
}

上面的代码中,a接口通过断言转换为任意类型(Person和Student)。使用断言转换任何已知类型并调用转换后的成员和方法。

package main

import "fmt"

func main() {
    
	// var a Per
	// var b = a.(Person)
	// fmt.Println(b.name, b.sex)
	// b.showPn()

	// var c = a.(Student)
	// fmt.Println(c.person.name, c.person.sex, c.sid)
	// c.showStu()

	stu1 := Student{
    
		person: Person{
    
			"xu",
			"nan",
		},
		sid: "s001",
	}

	identufy(stu1)

}

type Person struct {
    
	name string
	sex  string
}

func (pn Person) showPn() {
    
	fmt.Println(pn.name, pn.sex)
}

type Student struct {
    
	person Person
	sid    string
}

func (stu Student) showStu() {
    
	fmt.Println(stu.person.name, stu.person.sex, stu.sid)
}

// 需要一个父类是两个结构体的父接口
// 空接口是任何类型的父类
type Per interface {
    
}

func identufy(per Per) {
    
	//通过断言做类的变换
	var x = per.(Student)
	fmt.Println(x.person.name, x.person.sex, x.sid)
	x.person.showPn()
	x.showStu()

}

在上面代码中identufy方法接收Per的参数,使用断言将参数转化为已指对象Student,就可以使用其成员与方法。同时也可以接收Person参数,或者接收任意已知类型。

命令参数

在多环境开发的情况下,需要更改环境以方便测试,因为不同的环境配置是不同的。这可以直接通过读取代码内部的不同配置文件来实现,但是代码打包后,就无法通过更改代码来实现了。 ,需要传递外部参数。

Go的os命令提供了os.Args切片来存储所有命令行参数。

import (
	"fmt"
	"os"
)

func main() {
    

	//输出命令行参数长度
	fmt.Println("命令行参数长度", len(os.Args))
	//循环输出参数
	for index, value := range os.Args {
    
		fmt.Printf("args[%v]=%v\n", index, value)
	}

}

在这里插入图片描述

请注意,参数之间用空格分隔。

命令参数解析

flag包实现了命令行参数的解析。

os.Args可以满足一些基本的参数要求,但是复杂的在使用改参数就不太方便了,Go也提供了flag包实现了命令行参数的解析。

var usr, pwd, host string
var port int

flag.StringVar(&usr, "user", "", "用户名")
flag.StringVar(&pwd, "password", "", "密码")
flag.StringVar(&host, "h", "localhost", "主机名")
flag.IntVar(&port, "port", 3306, "端口")

flag.Parse()

fmt.Printf("usr=%s,pwd=%s,host=%s,port=%d", usr, pwd, host, port)

flag的var系列的函数将参数绑定到指定的变量上,通过变量输入没有顺序要求。var系列函数有一般有四个参数第一参数为程序的变量,第二个参数为绑定输入变量参数,第三个为默认值,第四个参数为绑定参数描述。

命令行输入时通过-绑定参数 数值输入。

在这里插入图片描述

序列化与反序列化

序列化

Go语言对JSON操作的包均在encoding/json包下,包下提供了Marshal方法来序列化Go内置对象。Marshal函数返回v的json编码。

func Marshal(v interface{}) ([]byte, error)

//创建结构体
type Person struct {
    
	name    string
	address string
}


//序列化
per := Person{
    
	name:    "钢铁侠",
	address: "漫威",
}
perjson, err := json.Marshal(&per)
if err != nil {
    
	panic(err)
}
fmt.Printf("per序列化的json数据为为:%v", perjson)
fmt.Printf("per序列化的json字符串为:%v", string(perjson))

在这里插入图片描述
上面代码中序列化了一个Person对象,但打印时却打印失败,这是由于结构体成员都是小写的,只能在本包内访问,序列化没有意义,也不支持私有类的序列化。

type Person struct {
    
	Name    string
	Address string
}

将结构体改为公共类时就可以序列化了。
在这里插入图片描述

与 Java 万物皆对象的本质不同,Go 除了结构体之外,还有映射、数组、切片等。

//定义一个map类型
func initMap() map[string]string {
    
	var tmp map[string]string = map[string]string{
    }
	tmp["1"] = "aaa"
	tmp["2"] = "bbb"
	tmp["3"] = "ccc"
	return tmp
}

//对map序列化
a := initMap()

ajson, err := json.Marshal(&a)
if err != nil {
    
	panic("ajson序列化失败")
}
fmt.Printf("%v\n", ajson)
fmt.Println(string(ajson))


在这里插入图片描述

//输出序列化时的数据类型
fmt.Printf("%T\n", ajson)
fmt.Printf("%T\n", string(ajson))

在这里插入图片描述
可以看出序列化后时一个字节数组,通过string()函数将其转化为字符串,即为json字符串。

Go序列化时tag标签的使用

序列化时,成员变量的名称全部大写。但在实际使用中,需要统一的书写标准和具体的名称,这就需要在序列化时使用标签。

{
    "Name":"钢铁侠","Address":"漫威"}

Go语言结构体序列化tag使用规则是在结构体字段后添加json:"name"并用反引号包裹。

type Person struct {
    
	Name    string `json:"person_name"`
	Address string `json:"person_address"`
	Age     int
}

如果在“名称”和“地址”中使用标签,它们的序列化名称将成为自定义的 person_names。 Age成员不使用标签,因此不会改变。

per := Person{
    
	Name:    "钢铁侠",
	Address: "漫威",
	Age:     18,
}
perjson, err := json.Marshal(&per)
if err != nil {
    
	panic(err)
}
fmt.Printf("per序列化的json数据为为:%v", perjson)
fmt.Printf("per序列化的json字符串为:%v", string(perjson))

在这里插入图片描述
struct的tag只会在序列化时起作用不会影响结构体的使用。

反序列化

反序列化就是将序列化后的json字符串或者字节数组转换成编程语言内置的数据结构。这种反序列化可以在相同语言或不同语言中发生。例如,Java类被序列化,然后通过网络传输到前端进行json反序列化,或者任何其他后端语言序列化内置数据,然后传输。到前端。

反序列化序列化数据的语言相同,Java实现和Go实现。

/** go语言实现 */

//定义结构体
type Person struct {
    
	Name    string `json:"person_name"`
	Address string `json:"person_address"`
	Age     int    `json:"person_age"`
}

//序列化
per := Person{
    
	Name:    "钢铁侠",
	Address: "漫威",
	Age:     18,
}
perjson, err := json.Marshal(&per)
if err != nil {
    
	panic(err)
}
fmt.Printf("per序列化的json数据为为:%v", perjson)
fmt.Printf("per序列化的json字符串为:%v", string(perjson))

在这里插入图片描述
序列化后为一个字节数组,可以通过string()方法转化为字符串。

func json.Unmarshal(data []byte, v any) error
该方法时反序列化方法,第一个参数为需要反序列化的字节数组,第二个参数为反序列化后数据的赋值变量。

//反序列化赋值变量
var per1 Person
err1 := json.Unmarshal(perjson, &per1)
if err1 != nil {
    
	panic(err1)
}
fmt.Printf("反序列化的数据per1%s,%s,%d", per1.Name, per1.Address, per1.Age)

在这里插入图片描述

str := `{"person_name":"钢铁侠","person_address":"漫威","person_age":18}`

var per2 Person
err2 := json.Unmarshal([]byte(str), &per2)
if err2 != nil {
    
	panic(err2)
}
fmt.Println(per2)

对字符串也可以反序列化,需要将字符串转化为字节数组,通过[]byte()方法。

单元测试

在Go语言包中,需要测试某些方法是否正确,某些业务逻辑是否合理。这些测试需要通过。一般需要在主包中引入模块,然后测试模块的方法或逻辑。这样做的缺点是测试代码需要写在主类中。但在实际项目开发中,主类有自己的逻辑。在主类中编写测试代码非常繁琐。另外,项目运行时,不能写在主类中。修改类中的测试代码。而且,这些测试代码在生产环境中需要删除。

为了解决这些问题,Go语言提供了单元测试,可以对包和模块的某些方法进行编程代码测试。而且这些测试模块都是独立的,不需要写在主类中,不会影响主要业务和代码。部署生产环境时,可以直接删除测试模块的代码,非常方便。

Go语言中提供了testing单元测试框架,能够很好的解决这些问题。testing单元测试框架提供go test命令来实现单元测试和性能测试,基于这个框架针对相应的函数写测试用例。

testing 提供对 Go 包的自动化测试的支持。通过 go test 命令,能够自动执行如下形式的任何函数:

func TestXxx(*testing.T)

其中 Xxx 可以是任何字母数字字符串,用于识别测试用例。要编写一个新的测试套件,需要创建一个名称以 _test.go 结尾的文件,该文件包含 TestXxx 函数,如上所述。 将该文件放在与被测试的包相同的包中。该文件将被排除在正常的程序包之外,但在运行 “go test” 命令时将被包含。

单元测试的机制是编译器创建一个主类,该类将引入xxx_test.go包并调用TestXXX测试方法。这些过程都是由测试框架完成的。

因此,在使用测试框架时,必须按照其规范编写测试用例。

测试用例规范

  1. 测试的包必须使用_test.go后缀结尾;
  2. 测试包需要引入testing
  3. 测试方法必须以Test开头即TestXXX,且参数为测试包的的指针参数,一般为*testing.T

在这里插入图片描述

//mathutil.go

package mathutil

func add(x int, y int) int {
    
	return x + y
}

func sub(x int, y int) int {
    
	return (x - y)
}
//mathutil_test.go

package mathutil

import (
	"fmt"
	"testing"
)

func TestAdd(t *testing.T) {
    
	res := add(10, 13)
	if res != (10 + 13) {
    
		fmt.Println("add Fail,expect 23 but found", res)
	}
}

在这里插入图片描述

总结

  1. 测试用例文件名必须以_test.go结尾;
  2. 测试用例函数必须以Test开头;
  3. 测试函数的参数类型必须是*testing.T

在这里插入图片描述

  1. 运行测试用例的指令为go test无日志信息;go test -v打印详细信息
  2. 使用*testing.T的方法t.Fatalf打印错误退出程序和t.Logf方法可以打印日志。
  3. 测试用例独立于主程序,仅在go test命令中生效。 PASS表示运行成功,FAIL表示运行失败。
. . .

相关推荐

额外说明

如果前后端分离,如何调整桌子高度和设置固定立柱?

目录 一、表格设置自定义高度  1、el-table增加max-height属性 2、data增加tableHeight变量 3、mounted获取计算高度

额外说明

信号功能理解

void (*signal (int signo, void (*func)(int))) (int); 等价于声明了signal是一种返回值是void (*p)(int) 的函数指针,假如这种类型的函数指针被typedef为type类型则 s

额外说明

转:laydate日历控件的使用

原文转载自:http://www.cnblogs.com/fengpingfan/p/4660273.html   在日常的网页开发过程中,日期组件已经成为不可或缺的组件之一。同时,随着广大杰出攻城狮的不懈努力,也出现了很多优秀的日期组件,其中我个人觉得

额外说明

nginx、apache、IIS服务器文件大小限制的解决方案client_max_body_size、client_max_body_size、upload_max_filesize...

nginx、apache、IIS服务器的文件大小的限制解决办法 client_max_body_size 、client_max_body_size、 file_uploads、 upload_max_filesize、 post_max_size 1、

额外说明

Python-Mongodb存储

非关系型数据库:    NoSQL基于键值对的,不需要经过SQL层的解析,性能很高 键值存储数据库,代表有 Redis, Voldemort, Oracle BDB 等。 列存储数据库,代表有 Cassandra, HBase, Riak 等。 文档型数

额外说明

【C++STL基础入门】string类的基础使用

文章目录 前言 一、STL使用概述 二、string类概述 1.string类的构造函数 string输出 示例代码 2.string类属性 属性是什么 属性API 示例代码 3.输出 输出全部 输出单个字符 总结 前言 本系列文章使用VS2022,C+

额外说明

【Python 随练】八进制转换为十进制

题目 八进制转换为十进制 八进制转换为十进制 在计算机科学中,我们经常需要在不同进制之间进行转换。其中,八进制(Octal)和十进制(Decimal)是常见的进制系统。本文将介绍如何将八进制数转换为十进制数,并提供一个简单的 Python 代码示例。 八

额外说明

Spark MLlib学习笔记:构建一个机器学习工作流

文章目录 一、任务描述 二、实现步骤 (一)引入包并构建训练数据集 (二)定义工作流阶段 (三)创建工作流,训练出模型 (四)构建测试数据集 (五)利用模型做预测 一、任务描述 查出所有包含“spark”的句子,将包含“spark”的句子的标签设为1,没

额外说明

Java学习笔记2.3.7 运算符与表达式 - 运算优先级与表达式

文章目录 零、本讲学习目标 一、运算优先级 (一)运算优先级概述 (二)运算优先级表 (三)简要说明 二、Java表达式 (一)表达式的概念 (二)表达式的值与类型 (三)表达式的运算顺序 (四)书写表达式的注意事项 三、课后作业 任务1、将数学表达式转

额外说明

安卓学习笔记40:基于套接字网络编程

文章目录 零、学习目标 一、Socket概述 (一)两种传输模式 (二)基于Socket网络编程 三、案例演示 - C/S架构聊天室 (一)运行效果 (二)涉及知识点 (三)实现步骤 1、创建聊天服务器端 (1)创建Java项目 (2)创建聊天服务窗口类

ads via 小工具