Embedding
Embedding is Go's answer to subclassing.
When you embed a type inside another, the embedded type is called the inner type while the other type is called the outer type.
The outer type has access to all the exported fields of the inner type.
The outer and inner types has to be properly initialized.
Custom Randomizer
We will write a custom randomizer which will embed Go's math/rand functionality.
It will count the number of times we call a random function.
It will retrieve a random number between two values.
type Rand
func New(src Source) *Rand
func (r *Rand) ExpFloat64() float64
func (r *Rand) Float32() float32
func (r *Rand) Float64() float64
func (r *Rand) Int() int
func (r *Rand) Int31() int32
func (r *Rand) Int31n(n int32) int32
func (r *Rand) Int63() int64
func (r *Rand) Int63n(n int64) int64
func (r *Rand) Intn(n int) int
func (r *Rand) NormFloat64() float64
func (r *Rand) Perm(n int) []int
func (r *Rand) Read(p []byte) (n int, err error)
func (r *Rand) Seed(seed int64)
func (r *Rand) Shuffle(n int, swap func(i, j int))
func (r *Rand) Uint32() uint32
func (r *Rand) Uint64() uint64
func (r *Rand) Seed(seed int64)
package main
import "math/rand"
type customRand struct {
*rand.Rand
count int
}
func NewCustomRand(i int64) *customRand {
return &customRand{
Rand: rand.New(rand.NewSource(i)),
count:0,
}
}
func NewSource(seed int64) Source
func New
func New(src Source) *Rand
Intn
func (r *Rand) Intn(n int) int
Intn returns, as an int, a non-negative pseudo-random number in the half-open interval [0,n). It panics if n <= 0.
This method gets a random number between 0 and n which is supplied of an argument.
func (cr *customRand) RandRange(min, max int) int {
cr.count++
return cr.Rand.Intn(max-min) + min
}
Let's start by implementing a method called RandRange which is attached to the customRand struct pointer.
The method will increment the count field in the customRand struct by 1.
Then it will return a random value between the min argument and the max argument.
This could be done by getting a random value between 0 and (max-min)+min.
As shown here the methods belonging to the inner Rand struct type are accessible from within our customRand struct simply using the inner type name(cr.Rand.Intn) and again Go will take care of the pointer indirections for us.
So this will create a random number between 0 and (max-min)+min.
func (cr *customRand) GetCount() int {
return cr.count
}
func main() {
cr := NewCustomRand(time.Now().UnixNano())
fmt.Println(cr.RandRange(5, 30))
fmt.Println(cr.GetCount())
}
func main() {
cr := NewCustomRand(time.Now().UnixNano())
fmt.Println(cr.RandRange(5, 30))
fmt.Println(cr.Intn(10))
fmt.Println(cr.GetCount())
}
Why we use time pacakge?
Computer logic and arithmetic operations are not suitable for generating random values.
The generated random value is a pseudo-random value, not a completely random value.
Currently, computers cannot generate completely random values, so a pseudo-random algorithm is used.
Since the initial value from which a random value is calculated is the same, simply using the rand.Intn () function is created in the same way every time.
This initial value is called a random seed.
To generate a different random value each time the program is executed, the most used method is to set the current time value.
29
4
1
Embedding gotcha1
If the outer type implements a method that has the same name as a method implemented by the inner type, the outer type method will be given priority.
func (cr *customRand) Intn(n int) int {
cr.count++
return cr.Rand.Intn(n) +1
}
func (cr *customRand) Intn(n int) int {
fmt.Println("Outer Intn called...")
cr.count++
return cr.Rand.Intn(n) +1
}
10
Outer Intn called...
5
2
Embedding gotcha 2
Outer type stays as a different type than the inner type, meaning that a value of the inner type can not be assigned to a value of the outer type or vice versa.
var r *rand.Rand = cr
So for example, if it creates a Rand struct pointer variable and try to assign a value to it that is of type customRand.
This program will show an error citing the fact that the two types are different.
So an embedded type cannot be assigned a value of the outer type.
If we try the other way around , we still get an error.
cr = &rand.Rand{}
Readers
Reader => Read data from a slice of bytes
type Reader interface { Read(p []byte) (n int, err error) }
A very common interface in GO
Can be considered a read stream
Examples: File reads, JSON prasing, TCP communications, http requests, and cryptography
Writers
Writer => Write data to a slice of bytes
Type Writer interface{ Write(p []byte) (n int, err error) }
A very common interface in Go
Can be considered a writer stream
Examples : Write to Json, http responses, and cryptograpy
Writers on the other hand are used to write data to byte slices.
They are very common in the language.
We use them whenever we write to files, write http responses encode some data and so forth.
You can think of any type that implements a writer interface as a write stream.
We can actually also embed interface type inside other interfaces.
type ReadWriter interface { Reader Writer }`
So what happens if we embed our reader interface and writer interface under a new interface.
We get a type that can do both read and write.
So this is an equivalent of a stream type that can be both an input and an output and Go actually has a native type that it's just that this type can be found in Go's IO package which defines a lot of the major interfaces in Go related to input and output streams.
I would recommend looking at the IO package page at comment io for further reading into what this package can do.
from Mastering Go Programming Section 4 - 19
Section 5.4.25 Channel Generators (0) | 2022.03.11 |
---|---|
Section 5.2.24 Timers and Tickers (5) | 2022.03.05 |
01/03 Syncs and Locks (0) | 2022.03.02 |
25/02 The Singleton Pattern –Building the Hydra Custom Logger (0) | 2022.02.26 |
23/02 Factory Design Pattern in Go (1) | 2022.02.24 |
댓글 영역