Yeah, it's not going to be easy

Search

Search IconIcon to open search

Tucker's Golang programming Ch27 ~ Ch30

Nov 5, 2023

This post is a translation of the original Korean post.


This text is a summary of chapters 27 to 30 of the book “Golden Rabbit: [ Tucker’s Golang programming ]”.

Ch27 ~ Ch30

  • Object-Oriented Design Principles SOLID
  • Tests and benchmarks
  • Web server with Go
  • Creating a RESTful API server

# Object-oriented design principles SOLID

Principles to reduce coupling and increase cohesion for good design.

  • Coupling ⬇️
    • The degree to which modules are strongly coupled to each other and cannot be separated.
  • Cohesion ⬆️
    • The degree to which a module can stand on its own.

# Single responsibility principle

“Every object should have only one responsibility.”

  • There should be only one reason for a change to an object or module.

# Open-Closed principle

“Open to extensions, closed to changes.”

  • Adding features should minimize changes to existing code

# Liskov substitution principle

“If q(x) is a property that can be proved for an object x of type T, then q(y) must be provable for objects y of a subtype S of T.”

Must honor function contract relations

  • The implementation must behave according to the caller’s intent (expected behavior).

Inheritance can easily violate the principle of liskov substitution.

  • Methods in the parent class and overridden methods in the child class can have different behavior.

e.g. Function to increase the horizontal size of an image (argument: Rectangle type)

  • Image object is of type Rectangle (interface) and has method setWidth
  • Call setWidth of the image object
  • ⚠️ problem occurs
    • The Square type, which inherits (implements) the Rectangle type, is passed as a parameter.
    • The Square type’s setWidth changes both width and height.
    • We only wanted to increase the width, but it also increased the height (‼️ unexpected behavior)

# Interface segregation principle

“Clients should not rely on methods they don’t use.”

  • Separate interfaces so that there are no dependencies on unnecessary methods.

# Dependency inversion principle

“By reversing the traditional dependency relationship in which a higher layer depends on a lower layer, a higher layer can be made independent of the implementation of a lower layer.”

  • Materialized objects should have dependency relationships with abstracted objects

Principle

  • Parent, child modules all depend on abstract modules.
  • An abstract module should not depend on a concrete module. A concrete module must depend on an abstract module.

benefit

  • Less coupling, increasing portability
  • Maintain independence from each other

# Tests and benchmarks

# Test code

1
2
3
go test

go test -run testname # run only some tests
  • The filename must end with _text.go.
  • Must import the testing package
  • The function name must start with Test.
    • Only one parameter must exist, t *test.T

stretchr/testify

  • The assert object
    • Contains a variety of methods to make it easy to create test code
      • Equal(), Greater(), Len(), NotNilf(), NotEqualf()
  • mock package
    • Provides mock objects that mimic the behavior of the module
  • suite package
    • Makes it easy to do test prep work or clean up afterward

Test Driven Development

Write tests -> test fails -> write code -> test succeeds -> improve (based on SOLID principles)

  • Repeat the above process

# Benchmark code

Measure the performance of your code

1
go test -bench .
  • Should take the form of func BenchmarkXxxx(b *testing.B)

# Create a webserver in Go

# Web server

  1. Register the handler
    • Register with http.HandleFunc() -> DefaultServeMux
    • http.NewServeMux()
      • Creating a ServeMux Object
      • Call HandleFunc() of the created ServeMux object to register it
  2. Start the web server
    • http.ListenAndServe(addr string, handler http.Handler)
      • addr
        • port number
      • handler
        • Use *http.ServeMux, an implementation of http.Handler.
        • If nil, use DefaultServeMux internally

Structure

tucker-golang-programming-ch29.png

# FileServer

1
2
3
4
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))  
if err := http.ListenAndServe(":3000", nil); err != nil {  
    panic(err)
}
  • Serve the file corresponding to the request URL.Path inside “/static”
    • Remove “/static/” from the request URL.Path with http.StripPrefix.

# Test code

1
2
3
// Return a ResponseRecorder
httptest.NewRecorder() // ResponseRecorder: an implementation of http.ResponseWriter that logs variations for later inspection in the test
httptest.NewRequest("GET", "/", nil) // Returns a new incoming server request.

# Sending JSON data

Encoder, Decoder

  • Encoder
    • has io.Writer
  • Decoder
    • has io.Reader
  • Encodes and decodes stream data instead of storing it all at once.

Marshal, Unmarshal

  • Marshal
    • Takes a value of any type and converts it to a byte slice.
  • Unmarshal
    • Takes a byte slice and stores the parsed result in any type (address value)
  • Memory intensive because all data is stored in byte slices.

ref: https://blog.devgenius.io/to-unmarshal-or-to-decode-json-processing-in-go-explained-e92fab5b648f

# HTTPS

Uses public key cryptography

ref: https://ko.khanacademy.org/computing/computer-science/cryptography/modern-crypt/v/diffie-hellman-key-exchange-part-1

Certified by an external authorized organization

For phishing sites, hackers can create a public key and decrypt it with their own secret key.

  • Certificate authority authenticates the website and issues a public key that encrypts the website’s public key (included in the certificate)

Generate a key and issue a certificate

1
2
3
4
5
# localhost.key: secret key, localhost.csr: certificate file
openssl req -new -newkey rsa:2048 -nodes -keyout localhost.key -out localhost.csr

# Issue a self-certificate
openssl x509 -req -days 365 -in localhost.csr -signkey localhost.key -out localhost.crt

Run the HTTPS server

1
http.ListenAndServeTLS(":3000", "localhost.crt", "localhost.key", nil)

# Create a RESTful API server

REST (Representational State Transfer)

  • A way to represent data and behavior with URLs and methods.

Features

  • A combination of URLs and methods define what data and what behavior to perform.
  • Server (backend) is a data provider and only provides data, client (frontend) processes and displays data on screen
  • Stateless
    • The server does not maintain the state of the client
  • Caching
    • Simpler server makes it easier to enforce cache policies

Code

1
2
3
4
5
router := mux.NewRouter()  
router.HandleFunc("/students", GetStudentListHandler).Methods("GET")  
router.HandleFunc("/students/{id:[0-9]+}", GetStudentHandler).Methods("GET")  
router.HandleFunc("/students", PostStudentHandler).Methods("POST")  
router.HandleFunc("/students/{id:[0-9]+}", DeleteStudentHandler).Methods("DELETE")

# References