分析

项目四个Go文件, 每个文件仅描述一件事, 思路非常清淅, 故此学习并总结一下里面的精华.

  1. 定义Options结构体接受外部定义的配置
  2. 定义入口文件, 整个项目的入口, 并返回一个下一层处理的结构体
  3. 定义逻辑结构体, 下一层结构体担负着处理逻辑的任务,并返回处理结构的结构体.
  4. 定义收尾结构体, 将上一层的处理的结果进行进一步分析,最终展现给用户

入口文件 Goz.go

  • 只初使化了Request结构体.
  • 可以定义初使配置Options, 整个项目所需的配置都在Options结构里
  • options.go#L6
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// NewClient new request object
func NewClient(opts ...Options) *Request {
	req := &Request{}

	if len(opts) > 0 {
		req.opts = opts[0]
	}

	return req
}

配置文件 Options.go

  • 定义了整个项目需要的外部参数
  • 变量首字母都大写
  • options.go#L6
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// Options object
type Options struct {
	BaseURI    string
	Timeout    float32
	timeout    time.Duration
	Query      interface{}
	Headers    map[string]interface{}
	Cookies    interface{}
	FormParams map[string]interface{}
	JSON       interface{}
	Proxy      string
}

核心处理文件 Request.go

  • 定义了自己所需的Request结构体
  • 如果需要上下方法共用一份结果, 必须是指针变量, 这样扩展的方法都可以使用并修改参数变量.
  • 里面的参数是整个文件所需的参数,仅限自己使用, 因为字段名都是小写.
  • request.go#L16
1
2
3
4
5
6
7
// Request object
type Request struct {
	opts Options
	cli  *http.Client
	req  *http.Request
	body io.Reader
}

1. 定义Request所要操作方法

  • 外部使用的方法(首字母大写)
  • 内部使用的方法(首字母小写).如parseClient方法
    • 主要是内部处理逻辑使用, 不暴露给外部使用.
    • 一般细节逻辑交给内部方法完成.
    • 而且每一个方法只处理一件单纯的逻辑,否则拆成两个方法处理.

2. 提供给外部调用的方法返回Response结构体.转向结果处理逻辑

  • 如下代码Get返回了Response结构体,必须是指针类型, 这样response方法拿到的数据是同一份.
  • 而且也要将Request处理的error也装入response结构体里,方便结果分析处理.
1
2
3
4
// Get send get request
func (r *Request) Get(uri string, opts ...Options) (*Response, error) {
	return r.Request("GET", uri, opts...)
}

结果处理 Response.go

这个文件属于结尾文件, 整个文件在此处理收尾逻辑

  • 定义了自己所需的Response结构体
  • 定义结果处理的方法, 将Request传过来的数据进行处理. 也就是Response结构体里的参数.
  • response.go#L11
1
2
3
4
5
type Response struct {
	resp *http.Response
	req  *http.Request
	err  error
}

1. 更人性化的方法

一般返回给调用方的结果是字节流或string或切片或map

  • 这里返回的是字节流, 我们定义自己的字节流类型.
  • response.go#L18
1
type ResponseBody []byte
1
2
3
4
// String fmt outout
func (r ResponseBody) String() string {
	return string(r)
}

2. 利用自定义的类型

  • 如果正常返回[]byte字节流给用户,也许用户还需要进行一步处理.这样有点不方便,我们帮他做了
  • Response的方法返回自定义的类型, 这样我们就可以使用上面定义的类型方法啦. 是不是好方便.
  • response.go#L44
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// GetBody parse response body
func (r *Response) GetBody() (ResponseBody, error) {
	defer r.resp.Body.Close()

	body, err := ioutil.ReadAll(r.resp.Body)
	if err != nil {
		return nil, err
	}

	return ResponseBody(body), nil
}

总结

  • 好的开源项目代码逻辑思路特别清淅
  • 目录或文件处理逻辑分工清淅,也就是所谓单一原则,一个类只描述一件事.
  • 层次清淅, 层层递进. 像套娃一般.哈哈.