Let me write it out front

 Golang When building structures in , You need to create... Through optional parameters , How do we design a flexible API To initialize the structure .

Let's pass the following code snippet , Step by step, the flexibility based on the optional parameter mode is explained API How to design .

flexible API Create structure description

v1 edition

as follows Client It's a Client's sdk Structure , Yes host and port Two parameters , Our general usage is as follows :

package client

type Client struct {
host string
port int
} // NewClient By passing parameters
func NewClient(host string, port int) *Client {
return &Client{
host: host,
port: port,
}
} func (c *Client) Call() error {
// todo ...
return nil
}

We can see through host and port Two parameters can create a client Of sdk.

The code that is invoked is typically as follows :

package main

import (
"client"
"log"
) func main() {
cli := client.NewClient("localhost", 1122)
if err := cli.Call(); err != nil {
log.Fatal(err)
}
}

  

And then one day ,sdk Made an upgrade , Several new parameters have been added , Such as timeout Timeout time ,maxConn maximum connection , retry Retry count ...

v2 edition

sdk Medium Client To define and create a structure API Change to the following :

package client

import "time"

type Client struct {
host string
port int
timeout time.Duration
maxConn int
retry int
} // NewClient By passing parameters
func NewClient(host string, port int) *Client {
return &Client{
host: host,
port: port,
timeout: time.Second,
maxConn: 1,
retry: 0,
}
} // NewClient adopt 3 Parameter creation
func NewClientWithTimeout(host string, port int, timeout time.Duration) *Client {
return &Client{
host: host,
port: port,
timeout: timeout,
maxConn: 1,
retry: 0,
}
} // NewClient adopt 4 Parameter creation
func NewClientWithTimeoutAndMaxConn(host string, port int, timeout time.Duration, maxConn int) *Client {
return &Client{
host: host,
port: port,
timeout: timeout,
maxConn: maxConn,
retry: 0,
}
} // NewClient adopt 5 Parameter creation
func NewClientWithTimeoutAndMaxConnAndRetry(host string, port int, timeout time.Duration, maxConn int, retry int) *Client {
return &Client{
host: host,
port: port,
timeout: timeout,
maxConn: maxConn,
retry: retry,
}
} func (c *Client) Call() error {
// todo ...
return nil
}

Through the above creation API We found that creating Client All at once  NewClientWithTimeout/NewClientWithTimeoutAndMaxConn/NewClientWithTimeoutAndMaxConnAndRetry...

We can see through host and port And other parameters can create a client Of sdk.

The code that is invoked is typically as follows :

package main

import (
"client"
"log"
"time"
) func main() {
cli := client.NewClientWithTimeoutAndMaxConnAndRetry("localhost", 1122, time.Second, 1, 0)
if err := cli.Call(); err != nil {
log.Fatal(err)
}
}

This is the time , We found that v2 Version of API The definition is very unfriendly , The number of parameter combinations is also very large .

v3 edition

We need to refactor the parameters , Is it possible to merge configuration parameters into one structure ?

good , Let's put the parameters into Config in ,Client Define a cfg member

package client

import "time"

type Client struct {
cfg Config
} type Config struct {
Host string
Port int
Timeout time.Duration
MaxConn int
Retry int
} func NewClient(cfg Config) *Client {
return &Client{
cfg: cfg,
}
} func (c *Client) Call() error {
// todo ...
return nil
}

We can see that by defining Config Parameter can create a client Of sdk.

The code that is invoked is typically as follows :

package main

import (
"client"
"log"
"time"
) func main() {
cli := client.NewClient(client.Config{
Host: "localhost",
Port: 1122,
Timeout: time.Second,
MaxConn: 1,
Retry: 0})
if err := cli.Call(); err != nil {
log.Fatal(err)
}
}

  

Here we find new problems ,Config All configured members should start with uppercase , It can only be used when it is open to the public , But as a sdk, We generally do not recommend exporting these members .

What should we do ?

v4 edition

We return to the original definition ,Client Or that one? Client, There are many configuration member variables , We use the optional parameter mode to sdk refactoring .

The code after refactoring is as follows

package client

import "time"

type Client struct {
host string
port int
timeout time.Duration
maxConn int
retry int
} // Create... With optional parameters
func NewClient(opts ...func(client *Client)) *Client {
// Create an empty Client
cli := &Client{}
// Call the optional parameter functions one by one , Copy the parameters of each function configuration to cli in
for _, opt := range opts {
opt(cli)
}
return cli
} // hold host Parameters , Pass to function parameters c *Client
func WithHost(host string) func(*Client) {
return func(c *Client) {
c.host = host
}
} func WithPort(port int) func(*Client) {
return func(c *Client) {
c.port = port
}
} func WithTimeout(timeout time.Duration) func(*Client) {
return func(c *Client) {
c.timeout = timeout
}
} func WithMaxConn(maxConn int) func(*Client) {
return func(c *Client) {
c.maxConn = maxConn
}
} func WithRetry(retry int) func(*Client) {
return func(c *Client) {
c.retry = retry
}
} func (c *Client) Call() error {
// todo ...
return nil
}

  

We can choose parameters freely , Create a client Of sdk.

The code that is invoked is typically as follows :

package main

import (
"client"
"log"
"time"
) func main() {
cli := client.NewClient(
client.WithHost("localhost"),
client.WithPort(1122),
client.WithMaxConn(1),
client.WithTimeout(time.Second))
if err := cli.Call(); err != nil {
log.Fatal(err)
}
}

From the code you call, you can see , our sdk The definition becomes flexible and beautiful .

Open source best practices

Finally, let's look at the best practice projects in this way .

gRpc

grpc.Dial(endpoint, opts...)

// Dial creates a client connection to the given target.
func Dial(target string, opts ...DialOption) (*ClientConn, error) {
return DialContext(context.Background(), target, opts...)
} func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *ClientConn, err error) {
cc := &ClientConn{
target: target,
csMgr: &connectivityStateManager{},
conns: make(map[*addrConn]struct{}),
dopts: defaultDialOptions(),
blockingpicker: newPickerWrapper(),
czData: new(channelzData),
firstResolveEvent: grpcsync.NewEvent(),
} for _, opt := range opts {
opt.apply(&cc.dopts)
}
// ...
}

End .

Have fun ~

【Golang】 When creating a structure with configuration parameters , How optional parameters should be transferred ? More articles about

  1. 【C++】 Structure / Array of structs / Structure pointer / Nested structure / Function parameter /const

    One . Structure declaration struct Student { // Member list string name; int age; int score; }; //s3; Directly declare when defining int main() { struct ...

  2. go The structure of language foundation makes function parameters Value passing and address passing

    1. The struct transfers function parameter values Example : package main // There must be a main package import "fmt" // Define a structure type type Student struct { ...

  3. go The language structure is used as a function parameter , We use value passing

    After verification ,go The language structure is used as a function parameter , We use value passing . Therefore, for large-scale structural body transfer parameters , Considering the performance loss of value transfer , It is better to use pointer transfer . Verification code : package main import ( "fmt& ...

  4. In depth understanding of C Language - Structure is used as function parameter

    Structure is used as function parameter , stay C Language is a common phenomenon , In this case, for the sake of memory , Non transitive structure , Instead, you pass the address of the structure Structure definition struct Man { char name[64]; int age; }; The structure can be connected with ...

  5. Use class / Structure is about ZeroMomery Wrong usage

    Today, my colleague wrote the following structure : typedef struct _tagInfo { std::list<int> lst; std::vector<int> nVec; } INF ...

  6. use set、map When storing user-defined structures, such as, the precautions for judging whether each element is the same inside the container

    STL As a general template, it greatly facilitates C++ User programming , Because it can store elements of any data type If we want to use it set And map To store custom structures , as follows struct pp { double xx; double y ...

  7. C# Parameters and calling methods in ( Optional parameters 、 Named parameters 、 Nullable parameter )

    Named parameters and Optional parameters yes C# framework 4.0 New features come out . One . General method definition and call public void Demo1(string x, int y) { //do someth ...

  8. golang The structure in is copied as a function parameter or function return value

    1.  When a struct is used as a parameter or return value of a function , Will be copied again if you don't want to copy , Structure pointers can be passed package main import "fmt" type Person struct { ...

  9. WPF in ItemsControl Bound to the Google ProtocolBuffer The performance of the structure of

    background : Recently I met a DataGrid Performance problems of : There's probably something in it 4000 Data , The binding of ItemSource Class has only one layer of data , It's simple List( Each of them is Protocol Buffer A class generated automatically ,1 ...

  10. c The structs in a language are value types , When one structure is assigned to another , Pass... For value

    #include <stdio.h> int main() { struct person { int age; }; }; // Value passed , take p1 The values of all member variables in the p2 The corresponding member variable in ...

Random recommendation

  1. Qt Implement port scanner

    First of all, let's show the effect : Interface by Qt The designer made it . There are two main categories . First, the main function : #include "mainwindow.h" #include <QApplication& ...

  2. Remember a Url rewrite _ After the release iis 404

    hold api The package is finished , Customer requirements app Of url Can we not change ( The customer used it before php Of api Development app, A lot has been developed , So I hope it doesn't change url). But the rule is :xx/api.php?s=/{controller ...

  3. 20151221jquery Learning notes -- Validation plug-ins

    Validation plug-ins (validate.js), It is a plug-in to verify the validity of regular form data . Use it , It greatly liberates the complicated verification process on the form , And the improvement of the error prompt display also increases the user experience . One . Use validate.js Plug in website ...

  4. use max_dump_file_size Parameter limits trc file size

    max_dump_file_size Parameters : This parameter can limit the corresponding process trc file size ( Is the process oracle Corresponding to the background and foreground applications server process) Use cases : If it's one trc The document has 4 ...

  5. How to use it? snapman A person develops a complex software development project management system in three days

    snapman It's a simple and powerful team collaboration software , The information above can be data . It can be rules . It can also be automation code : Most importantly, it's a collaborative platform that can be developed , All information can be applied to all people or machines , Greatly reduced the complexity of the work . soft ...

  6. Java Unit tests JUnit piece

    Unit testing is writing test code , Should be accurate . Quickly ensure the correctness of basic program modules . Good unit testing standards JUnit yes Java Unit test framework , Already in Eclipse Default installation in . JUnit4 JUnit4 To recognize by means of annotations ...

  7. iOS.Animations.by.Tutorials.v2.0 Sinicization ( Four )

    The third chapter transformation In the previous two chapters , You learned how to create views based on position and transparency alpha Animation properties of the animation . however , If you want to add animation to the view or delete animation , How will you handle it ? You can use the methods in the previous chapters to set the animation effect of the interface ...

  8. 17-Flink consumption Kafka write in Mysql

    Stamp more articles : 1-Flink introduction 2- Local environment construction & Build the first Flink application 3-DataSet API 4-DataSteam API 5- Cluster deployment 6- Distributed cache 7- Restart strategy 8-Fli ...

  9. selenium perform JavaScript sentence : Control the scroll bar Focusing on the element Change the drop-down options

    1. perform js Script Control the scroll bar # http://www.cnblogs.com/yoyoketang/p/6128655.html In [347]: js = "window.scrol ...

  10. [ Re posting ]pfSense The use of soft routing system

    The illustration pfSense The use of soft routing system (NAT function ) http://seanlook.com/2015/04/23/pfsense-usage/   Published in  2015-04-23 |   Updated on : 2015- ...