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))
}