golang并不像python,java等语言,在方法定义的时候可以提供默认值,不过我们可以通过其他的手段设置默认值
比如说我们有一个可以结构体DouBanClient,可以往豆瓣中获取一些信息,由于豆瓣的一些限制,我们写爬虫的时候需要声明头部的User-Agent。创建一个client的时候,会有相应的参数,如果我们对这些参数不满意,那么我们可以自定义这些参数。
在调用创建对象的方法中,首先设置好默认参数,如果用户想要自定义,在传参的时候传入满足一定条件的参数,就可以修改我们已经定义好的默认参数,实现自定义参数
虽然说这样在定义上会比较麻烦,但是使用的灵活性大大提高
package main
import (
"fmt"
"io/ioutil"
"net/http"
)
// 定义一个结构体
// opts为可选参数,如果没有设置,那么选用默认值
type DouBanClient struct {
client http.Client
opts options
}
// 保存可以取默认值的参数,这里演示只列一个
// 如果有多个的话,继续进行添加即可
type options struct {
header http.Header
}
// 默认参数设置,当然可以设置其他的参数
// 这里演示,只使用一个空的请求头
// 当然你可以往中间添加更多的东西,视情况而定
func defaultOptions() options {
return options{
header: http.Header{},
}
}
// 提供一个创建客户端的接口
func NewDouBanClient(client http.Client, opts ...Option) *DouBanClient {
d := DouBanClient{
client: client,
// 在声明对象的时候就先设置默认参数
opts: defaultOptions(),
}
// 这里是重点
// 选项全部使用接口来接收,接口中实现了apply方法
// apply方法接收一个*options,可以改变client中opts中的值
// 如果默认参数不够用户使用,用户可以使用这样的方式进行扩展
// 这样定义的参数会修改默认的参数值
for _, opt := range opts {
opt.apply(&d.opts)
}
return &d
}
// 选项接口
type Option interface {
apply(*options)
}
// funcOption主要是为了能够更加简单的创建选项
// funcOption实现了Option接口apply函数
// newFuncOption可以返回一个Option接口对象
type funcOption struct {
f func(*options)
}
func (fo funcOption) apply(o *options) {
fo.f(o)
}
func newFuncOption(f func(*options)) *funcOption {
return &funcOption{
f: f,
}
}
// 设置一个带有请求头的选项
// 如果使用这个选项,那么定义的类型中的请求头为下面设置好的
// 而不是默认的值
func withHeaderUserAgent() Option {
var header = http.Header{}
header.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36")
return newFuncOption(func(o *options) {
o.header = header
})
}
// 一个简单的get请求方法
func (d *DouBanClient) Get(url string) (*http.Response, error) {
req, err := http.NewRequest("GET", url, nil)
if err != nil {
fmt.Println("Error: ", err)
return nil, err
}
req.Header = d.opts.header
return d.client.Do(req)
}
func main() {
// 所有的自定义参数都可以在这里声明
opts := []Option{
withHeaderUserAgent(),
}
client := http.Client{}
d := NewDouBanClient(client, opts...)
res, err := d.Get("https://movie.douban.com/cinema/later/shaoyang/")
if err != nil {
fmt.Println(err)
return
}
body, err := ioutil.ReadAll(res.Body)
if err != nil {
fmt.Println(err)
}
defer res.Body.Close()
fmt.Println(string(body))
}