# Conflicts:
#	README.md
This commit is contained in:
Edward 2020-05-03 01:32:15 +08:00
commit 3500e141b7
38 changed files with 5174 additions and 55 deletions

1
.gitignore vendored
View File

@ -32,3 +32,4 @@ _testmain.go
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
*.orig

View File

@ -15,96 +15,96 @@ A curated collection of idiomatic design & application patterns for Go language.
| Pattern | Description | Status |
|:-------:|:----------- |:------:|
| [Abstract Factory](/creational/abstract_factory.md) | Provides an interface for creating families of releated objects | |
| [Builder](/creational/builder.md) | Builds a complex object using simple objects | |
| [Factory Method](/creational/factory.md) | Defers instantiation of an object to a specialized function for creating instances | |
| [Object Pool](/creational/object-pool.md) | Instantiates and maintains a group of objects instances of the same type | |
| [Singleton](/creational/singleton.md) | Restricts instantiation of a type to one object | |
| [Abstract Factory](/creational/abstract_factory.md) | Provides an interface for creating families of releated objects | âœ|
| [Builder](/creational/builder.md) | Builds a complex object using simple objects | âœ|
| [Factory Method](/creational/factory.md) | Defers instantiation of an object to a specialized function for creating instances | âœ|
| [Object Pool](/creational/object-pool.md) | Instantiates and maintains a group of objects instances of the same type | âœ|
| [Singleton](/creational/singleton.md) | Restricts instantiation of a type to one object | âœ|
## Structural Patterns
| Pattern | Description | Status |
|:-------:|:----------- |:------:|
| [Bridge](/structural/bridge/main.go) | Decouples an interface from its implementation so that the two can vary independently | |
| [Composite](/structural/composite/main.go) | Encapsulates and provides access to a number of different objects | |
| [Decorator](/structural/decorator.md) | Adds behavior to an object, statically or dynamically | |
| [Facade](/structural/facade/main.go) | Uses one type as an API to a number of others | |
| [Flyweight](/structural/flyweight/main.go) | Reuses existing instances of objects with similar/identical state to minimize resource usage | |
| [Proxy](/structural/proxy.md) | Provides a surrogate for an object to control it's actions | |
| [Bridge](/structural/bridge/main.go) | Decouples an interface from its implementation so that the two can vary independently | âœ|
| [Composite](/structural/composite/main.go) | Encapsulates and provides access to a number of different objects | âœ|
| [Decorator](/structural/decorator.md) | Adds behavior to an object, statically or dynamically | âœ|
| [Facade](/structural/facade/main.go) | Uses one type as an API to a number of others | âœ|
| [Flyweight](/structural/flyweight/main.go) | Reuses existing instances of objects with similar/identical state to minimize resource usage | âœ|
| [Proxy](/structural/proxy.md) | Provides a surrogate for an object to control it's actions | âœ|
## Behavioral Patterns
| Pattern | Description | Status |
|:-------:|:----------- |:------:|
| [Chain of Responsibility](/behavioral/chain_of_responsibility/main.go) | Avoids coupling a sender to receiver by giving more than object a chance to handle the request | |
| [Command](/behavioral/command/main.go) | Bundles a command and arguments to call later | |
| [Mediator](/behavioral/mediator/main.go) | Connects objects and acts as a proxy | |
| [Memento](/behavioral/memento/main.go) | Generate an opaque token that can be used to go back to a previous state | |
| [Observer](/behavioral/observer.md) | Provide a callback for notification of events/changes to data | |
| [Registry](/behavioral/registry.md) | Keep track of all subclasses of a given class | |
| [State](/behavioral/state/main.go) | Encapsulates varying behavior for the same object based on its internal state | |
| [Strategy](/behavioral/strategy.md) | Enables an algorithm's behavior to be selected at runtime | |
| [Template](/behavioral/template/main.go) | Defines a skeleton class which defers some methods to subclasses | |
| [Visitor](/behavioral/visitor/main.go) | Separates an algorithm from an object on which it operates | |
| [Chain of Responsibility](/behavioral/chain_of_responsibility/main.go) | Avoids coupling a sender to receiver by giving more than object a chance to handle the request | âœ|
| [Command](/behavioral/command/main.go) | Bundles a command and arguments to call later | âœ|
| [Mediator](/behavioral/mediator/main.go) | Connects objects and acts as a proxy | âœ|
| [Memento](/behavioral/memento/main.go) | Generate an opaque token that can be used to go back to a previous state | âœ|
| [Observer](/behavioral/observer.md) | Provide a callback for notification of events/changes to data | âœ|
| [Registry](/behavioral/registry.md) | Keep track of all subclasses of a given class | âœ|
| [State](/behavioral/state/main.go) | Encapsulates varying behavior for the same object based on its internal state | âœ|
| [Strategy](/behavioral/strategy.md) | Enables an algorithm's behavior to be selected at runtime | âœ|
| [Template](/behavioral/template/main.go) | Defines a skeleton class which defers some methods to subclasses | âœ|
| [Visitor](/behavioral/visitor/main.go) | Separates an algorithm from an object on which it operates | âœ|
## Synchronization Patterns
| Pattern | Description | Status |
|:-------:|:----------- |:------:|
| [Condition Variable](/synchronization/condition_variable.md) | Provides a mechanism for threads to temporarily give up access in order to wait for some condition | |
| [Lock/Mutex](/synchronization/mutex.md) | Enforces mutual exclusion limit on a resource to gain exclusive access | |
| [Monitor](/synchronization/monitor.md) | Combination of mutex and condition variable patterns | |
| [Read-Write Lock](/synchronization/read_write_lock.md) | Allows parallel read access, but only exclusive access on write operations to a resource | |
| [Semaphore](/synchronization/semaphore.md) | Allows controlling access to a common resource | |
| [Condition Variable](/synchronization/condition_variable.md) | Provides a mechanism for threads to temporarily give up access in order to wait for some condition | âœ|
| [Lock/Mutex](/synchronization/mutex.md) | Enforces mutual exclusion limit on a resource to gain exclusive access | âœ|
| [Monitor](/synchronization/monitor.md) | Combination of mutex and condition variable patterns | âœ|
| [Read-Write Lock](/synchronization/read_write_lock.md) | Allows parallel read access, but only exclusive access on write operations to a resource | âœ|
| [Semaphore](/synchronization/semaphore.md) | Allows controlling access to a common resource | âœ|
## Concurrency Patterns
| Pattern | Description | Status |
|:-------:|:----------- |:------:|
| [N-Barrier](/concurrency/n_barrier/main.go) | Prevents a process from proceeding until all N processes reach to the barrier | |
| [Bounded Parallelism](/concurrency/bounded_parallelism.md) | Completes large number of independent tasks with resource limits | |
| [Broadcast](/concurrency/broadcast.md) | Transfers a message to all recipients simultaneously | |
| [Coroutines](/concurrency/coroutine.md) | Subroutines that allow suspending and resuming execution at certain locations | |
| [Generators](/concurrency/generator.md) | Yields a sequence of values one at a time | |
| [Reactor](/concurrency/reactor.md) | Demultiplexes service requests delivered concurrently to a service handler and dispatches them syncronously to the associated request handlers | |
| [Parallelism](/concurrency/parallelism.md) | Completes large number of independent tasks | |
| [Producer Consumer](/concurrency/producer_consumer.md) | Separates tasks from task executions | |
| [N-Barrier](/concurrency/n_barrier/main.go) | Prevents a process from proceeding until all N processes reach to the barrier | âœ|
| [Bounded Parallelism](/concurrency/bounded/bounded_parallelism.md) | Completes large number of independent tasks with resource limits | âœ|
| [Broadcast](/concurrency/broadcast.md) | Transfers a message to all recipients simultaneously | âœ|
| [Coroutines](/concurrency/coroutine.md) | Subroutines that allow suspending and resuming execution at certain locations | âœ|
| [Generators](/concurrency/generator.md) | Yields a sequence of values one at a time | âœ|
| [Reactor](/concurrency/reactor.md) | Demultiplexes service requests delivered concurrently to a service handler and dispatches them syncronously to the associated request handlers | âœ|
| [Parallelism](/concurrency/parallelism.md) | Completes large number of independent tasks | âœ|
| [Producer Consumer](/concurrency/producer_consumer.md) | Separates tasks from task executions | âœ|
## Messaging Patterns
| Pattern | Description | Status |
|:-------:|:----------- |:------:|
| [Fan-In](/messaging/fan_in.md) | Funnels tasks to a work sink (e.g. server) | |
| [Fan-Out](/messaging/fan_out.md) | Distributes tasks among workers (e.g. producer) | |
| [Futures & Promises](/messaging/futures_promises.md) | Acts as a place-holder of a result that is initially unknown for synchronization purposes | |
| [Publish/Subscribe](/messaging/publish_subscribe.md) | Passes information to a collection of recipients who subscribed to a topic | |
| [Push & Pull](/messaging/push_pull.md) | Distributes messages to multiple workers, arranged in a pipeline | |
| [Fan-In](/messaging/fan_in.md) | Funnels tasks to a work sink (e.g. server) | âœ|
| [Fan-Out](/messaging/fan_out.md) | Distributes tasks among workers (e.g. producer) | âœ|
| [Futures & Promises](/messaging/futures_promises.md) | Acts as a place-holder of a result that is initially unknown for synchronization purposes | âœ|
| [Publish/Subscribe](/messaging/publish_subscribe.md) | Passes information to a collection of recipients who subscribed to a topic | âœ|
| [Push & Pull](/messaging/push_pull.md) | Distributes messages to multiple workers, arranged in a pipeline | âœ|
## Stability Patterns
| Pattern | Description | Status |
|:-------:|:----------- |:------:|
| [Bulkheads](/stability/bulkhead.md) | Enforces a principle of failure containment (i.e. prevents cascading failures) | |
| [Circuit-Breaker](/stability/circuit-breaker.md) | Stops the flow of the requests when requests are likely to fail | |
| [Deadline](/stability/deadline.md) | Allows clients to stop waiting for a response once the probability of response becomes low (e.g. after waiting 10 seconds for a page refresh) | |
| [Fail-Fast](/stability/fail_fast.md) | Checks the availability of required resources at the start of a request and fails if the requirements are not satisfied | |
| [Handshaking](/stability/handshaking.md) | Asks a component if it can take any more load, if it can't, the request is declined | |
| [Steady-State](/stability/steady_state.md) | For every service that accumulates a resource, some other service must recycle that resource | |
| [Bulkheads](/stability/bulkhead.md) | Enforces a principle of failure containment (i.e. prevents cascading failures) | âœ|
| [Circuit-Breaker](/stability/circuit-breaker.md) | Stops the flow of the requests when requests are likely to fail | âœ|
| [Deadline](/stability/deadline.md) | Allows clients to stop waiting for a response once the probability of response becomes low (e.g. after waiting 10 seconds for a page refresh) | âœ|
| [Fail-Fast](/stability/fail_fast.md) | Checks the availability of required resources at the start of a request and fails if the requirements are not satisfied | âœ|
| [Handshaking](/stability/handshaking.md) | Asks a component if it can take any more load, if it can't, the request is declined | âœ|
| [Steady-State](/stability/steady_state.md) | For every service that accumulates a resource, some other service must recycle that resource | âœ|
## Profiling Patterns
| Pattern | Description | Status |
|:-------:|:----------- |:------:|
| [Timing Functions](/profiling/timing.md) | Wraps a function and logs the execution | |
| [Timing Functions](/profiling/timing.md) | Wraps a function and logs the execution | âœ|
## Idioms
| Pattern | Description | Status |
|:-------:|:----------- |:------:|
| [Functional Options](/idiom/functional-options.md) | Allows creating clean APIs with sane defaults and idiomatic overrides | |
| [Functional Options](/idiom/functional-options.md) | Allows creating clean APIs with sane defaults and idiomatic overrides | âœ|
## Anti-Patterns
| Pattern | Description | Status |
|:-------:|:----------- |:------:|
| [Cascading Failures](/anti-patterns/cascading_failures.md) | A failure in a system of interconnected parts in which the failure of a part causes a domino effect | |
| [Cascading Failures](/anti-patterns/cascading_failures.md) | A failure in a system of interconnected parts in which the failure of a part causes a domino effect | âœ|

View File

@ -33,7 +33,7 @@
* [Semaphore](/synchronization/semaphore.md)
* [Concurrency Patterns](/README.md#concurrency-patterns)
* [N-Barrier](/concurrency/barrier.md)
* [Bounded Parallelism](/concurrency/bounded_parallelism.md)
* [Bounded Parallelism](/concurrency/bounded/bounded_parallelism.md)
* [Broadcast](/concurrency/broadcast.md)
* [Coroutines](/concurrency/coroutine.md)
* [Generators](/concurrency/generator.md)

View File

@ -1,6 +1,6 @@
# Bounded Parallelism Pattern
[Bounded parallelism](https://blog.golang.org/pipelines#TOC_9.) is similar to [parallelism](parallelism.md), but allows limits to be placed on allocation.
[Bounded parallelism](https://blog.golang.org/pipelines#TOC_9.) is similar to [parallelism](../parallelism.md), but allows limits to be placed on allocation.
# Implementation and Example

View File

@ -0,0 +1,40 @@
package basic
import (
"fmt"
"testing"
)
func TestPrintAddress(t *testing.T) {
var a int
fmt.Printf("%T, %v, %p \n", a, a, &a)
passByVariable(a)
passByPointer(&a)
}
func passByVariable(a int) {
fmt.Printf("%T, %v, %p \n", a, a, &a)
}
func passByPointer(a *int) {
fmt.Printf("%T, %v, %p \n", a, a, &a)
fmt.Printf("%T, %v, %p \n", *a, *a, &*a)
}
type robot struct{}
func TestStructAddress(t *testing.T) {
var a robot
fmt.Printf("%T, %v, %p \n", a, a, &a)
passStructByVariable(a)
passStructByPointer(&a)
}
func passStructByVariable(a robot) {
fmt.Printf("[passStructByVariable] %T, %v, %p \n", a, a, &a)
}
func passStructByPointer(a *robot) {
fmt.Printf("[passStructByPointer] %T, %v, %p \n", a, a, &a)
fmt.Printf("[passStructByPointer] %T, %v, %p \n", *a, *a, &*a)
}

BIN
playground/cbor/2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 MiB

View File

@ -0,0 +1,53 @@
package cbor
import (
"bytes"
"fmt"
"testing"
"github.com/2tvenom/cbor"
)
type Image struct {
Name string
Content []byte
}
func TestCborEncode2(t *testing.T) {
// Read origin file
b := new(bytes.Buffer)
err := getImageBytes("./on.png", b)
if err != nil {
fmt.Println(err)
return
}
origin := b.Bytes()
//image := &Image{Name:"on",Content:origin}
// Encode
fmt.Println("--------- Encode ---------")
var buf bytes.Buffer
encoder := cbor.NewEncoder(&buf)
ok, error := encoder.Marshal(origin)
//check binary string
if !ok {
fmt.Errorf("Error decoding %s", error)
} else {
fmt.Println("Size: ", len(buf.Bytes()))
}
// Decode
//fmt.Println("--------- Decode ---------",buf.Bytes())
//var img []byte
//ok, err = encoder.Unmarshal(buf.Bytes(), &img)
//if !ok {
// fmt.Printf("Error Unmarshal %s", err)
// return
//}
////output
//fmt.Printf("%v", img)
}

View File

@ -0,0 +1,48 @@
package cbor
import (
"bytes"
"fmt"
"testing"
"go.mozilla.org/cose"
)
func TestCborEncode3(t *testing.T) {
// Read origin file
b := new(bytes.Buffer)
err := getImageBytes("./on.png", b)
if err != nil {
fmt.Println(err)
return
}
origin := b.Bytes()
//image := &Image{Name:"on",Content:origin}
fmt.Println("Size: ", len(origin))
// Encode
fmt.Println("--------- Encode ---------")
encoded, error := cose.Marshal(origin)
if error != nil {
fmt.Errorf("Error decoding %s", error)
} else {
fmt.Println("Size: ", len(encoded))
//fmt.Println("Content: ",string(encoded))
}
// Decode
fmt.Println("--------- Decode ---------")
//var img Image
unmarshal, err := cose.Unmarshal(encoded)
if err != nil {
fmt.Printf("Error Unmarshal %s", err)
return
} else {
fmt.Println("Size: ", len(unmarshal.([]byte)))
//fmt.Println("Content: ",unmarshal.([]byte))
}
writeToFile("3", unmarshal.([]byte))
}

View File

@ -0,0 +1,104 @@
package cbor
import (
"bufio"
"bytes"
"fmt"
"image"
"image/jpeg"
"image/png"
"log"
"os"
"testing"
"github.com/ugorji/go/codec"
)
func TestCborEncode(t *testing.T) {
// Read origin file
buf := new(bytes.Buffer)
err := getImageBytes("./on.png", buf)
if err != nil {
fmt.Println(err)
return
}
origin := buf.Bytes()
fmt.Println("size:", len(origin))
writeToFile("1", origin)
// Encode
encoded, err := encodeBinaryValue(buf.Bytes())
if err != nil {
fmt.Println(err)
return
}
fmt.Println("size:", len(encoded))
writeToFile("2", encoded)
// Decode
decoded, err := decodeBinaryValue(encoded)
fmt.Println("size:", len(decoded))
writeToFile("3", decoded)
}
func encodeBinaryValue(b []byte) ([]byte, error) {
var ch codec.CborHandle
buf := new(bytes.Buffer)
enc := codec.NewEncoder(buf, &ch)
err := enc.Encode(&b)
return buf.Bytes(), err
}
func decodeBinaryValue(b []byte) ([]byte, error) {
var ch codec.CborHandle
var decoded []byte
var bufReader = bufio.NewReader(bytes.NewReader(b))
var dec = codec.NewDecoder(bufReader, &ch)
var err = dec.Decode(&decoded)
return decoded, err
}
func getImageBytes(imgFile string, buf *bytes.Buffer) error {
// Read existing image from file
img, err := os.Open(imgFile)
if err != nil {
return err
}
defer img.Close()
// TODO: Attach MediaType property, determine if decoding
// early is required (to optimize edge processing)
// Expect "png" or "jpeg" image type
imageData, imageType, err := image.Decode(img)
if err != nil {
return err
}
// Finished with file. Reset file pointer
img.Seek(0, 0)
if imageType == "jpeg" {
err = jpeg.Encode(buf, imageData, nil)
if err != nil {
return err
}
} else if imageType == "png" {
err = png.Encode(buf, imageData)
if err != nil {
return err
}
}
return nil
}
func writeToFile(fileName string, b []byte) {
f, _ := os.Create(fileName)
n2, err := f.Write(b)
if err != nil {
log.Fatal(err)
}
fmt.Printf("wrote file %d bytes\n", n2)
}

BIN
playground/cbor/on.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -0,0 +1,17 @@
package codecoverage
func Size(a int) string {
switch {
case a < 0:
return "negative"
case a == 0:
return "zero"
case a < 10:
return "small"
case a < 100:
return "big"
case a < 1000:
return "huge"
}
return "enormous"
}

View File

@ -0,0 +1,26 @@
package codecoverage
import "testing"
type Test struct {
in int
out string
}
var tests = []Test{
{-1, "negative"},
{0, "zero"},
{5, "small"},
{99, "big"},
{100, "huge"},
{10001, "enormous"},
}
func TestSize(t *testing.T) {
for i, test := range tests {
size := Size(test.in)
if size != test.out {
t.Errorf("#%d: Size(%d)=%s; want %s", i, test.in, size, test.out)
}
}
}

View File

@ -0,0 +1,86 @@
package cron
import (
"fmt"
"testing"
"time"
"github.com/edgexfoundry/edgex-go/pkg/models"
"github.com/robfig/cron"
)
type Job struct {
schedule models.Schedule
scheduleEvents []models.ScheduleEvent
}
func (job Job) Run() {
fmt.Println(job.schedule.Name, job.schedule.Frequency)
}
func TestCronWithAddJob(t *testing.T) {
var job = Job{
schedule: models.Schedule{
Id: "xxx",
Name: "5sec-schedule",
Frequency: "PT5S",
},
scheduleEvents: []models.ScheduleEvent{},
}
var job2 = Job{
schedule: models.Schedule{
Id: "xxx",
Name: "2sec-schedule",
Frequency: "PT2S",
},
scheduleEvents: []models.ScheduleEvent{},
}
// init cron
c := cron.New()
// add cron job
var spec = fmt.Sprintf("@every %v", ParseDuration(job.schedule.Frequency))
c.AddJob(spec, job)
spec = fmt.Sprintf("@every %v", ParseDuration(job2.schedule.Frequency))
c.AddJob(spec, job2)
// start cron
c.Start()
time.Sleep(10 * time.Second)
// keep alive
//select {}
}
func TestCronWithAddFunc(t *testing.T) {
// init cron
c := cron.New()
// add cron job
var duration = ParseDuration("PT2S")
var spec = fmt.Sprintf("@every %v", duration)
c.AddFunc(spec, func() {
// @every 2s
fmt.Println(spec)
})
// start cron
c.Start()
// keep alive
select {}
}
func TestParseISO8601(t *testing.T) {
var duration = ParseDuration("PT2S")
// PT2S -> 2s
fmt.Println(duration)
// PT15M -> 15m0s
fmt.Println(ParseDuration("PT15M"))
// P12Y4MT15M -> 108000h15m0s
fmt.Println(ParseDuration("P12Y4MT15M"))
}

View File

@ -0,0 +1,35 @@
package cron
import (
"regexp"
"strconv"
"time"
)
func ParseDuration(str string) time.Duration {
durationRegex := regexp.MustCompile(`P(?P<years>\d+Y)?(?P<months>\d+M)?(?P<days>\d+D)?T?(?P<hours>\d+H)?(?P<minutes>\d+M)?(?P<seconds>\d+S)?`)
matches := durationRegex.FindStringSubmatch(str)
years := ParseInt64(matches[1])
months := ParseInt64(matches[2])
days := ParseInt64(matches[3])
hours := ParseInt64(matches[4])
minutes := ParseInt64(matches[5])
seconds := ParseInt64(matches[6])
hour := int64(time.Hour)
minute := int64(time.Minute)
second := int64(time.Second)
return time.Duration(years*24*365*hour + months*30*24*hour + days*24*hour + hours*hour + minutes*minute + seconds*second)
}
func ParseInt64(value string) int64 {
if len(value) == 0 {
return 0
}
parsed, err := strconv.Atoi(value[:len(value)-1])
if err != nil {
return 0
}
return int64(parsed)
}

View File

@ -0,0 +1,14 @@
package factory
import "log"
var speakFuncs = make(map[string]interface{})
func say(funcName string) {
speakFunc, ok := speakFuncs[funcName]
if !ok {
log.Println("speakFunc not exist")
} else {
speakFunc.(func())()
}
}

View File

@ -0,0 +1,8 @@
package factory
import "testing"
func TestSay(t *testing.T) {
say("xxx")
say("hi")
}

View File

@ -0,0 +1,16 @@
package factory
import "log"
func sayHi() {
log.Print("Hi~")
}
func sayHello() {
log.Print("Hello~")
}
func init() {
speakFuncs["hi"] = sayHi
speakFuncs["hello"] = sayHello
}

View File

@ -0,0 +1,24 @@
package goroutine
import (
"fmt"
"testing"
"time"
)
func TestFiveGopherWithChan(t *testing.T) {
c := make(chan int)
for i := 0; i < 5; i++ {
go sleepyGopherWithChan(i, c)
}
for i := 0; i < 5; i++ {
gopherID := <-c
fmt.Println("gopher ", gopherID, " has finished sleeping")
}
}
func sleepyGopherWithChan(id int, c chan int) {
time.Sleep(2 * time.Second)
fmt.Println("... ", id, " snore ...")
c <- id
}

View File

@ -0,0 +1,25 @@
package goroutine
import (
"fmt"
"testing"
"time"
)
func TestOneGopher(t *testing.T) {
go sleepyGopher(1)
time.Sleep(3 * time.Second)
}
func TestFiveGopher(t *testing.T) {
c := make(chan int)
for i := 0; i < 5; i++ {
go sleepyGopherWithChan(i, c)
}
time.Sleep(3 * time.Second)
}
func sleepyGopher(id int) {
time.Sleep(2 * time.Second)
fmt.Println("... ", id, " snore ...")
}

View File

@ -0,0 +1,32 @@
package goroutine
import (
"fmt"
"sync"
"testing"
"time"
)
func TestWaitGophers(t *testing.T) {
var gophers = []string{"tom", "peter", "john", "brown"}
var waitGroup sync.WaitGroup
waitGroup.Add(len(gophers))
for i := 0; i < len(gophers); i++ {
go func(wg *sync.WaitGroup, name string) {
sleepyGopherSnore(name)
wg.Done()
}(&waitGroup, gophers[i])
}
waitGroup.Wait()
fmt.Println("All done")
}
func sleepyGopherSnore(name string) {
fmt.Println(name, ": ... start snore")
time.Sleep(2 * time.Second)
fmt.Println(name, ": ... snore")
}

View File

@ -0,0 +1,11 @@
package goroutine
import "testing"
// A deadlock happens when a group of goroutines are waiting for each other and none of them is able to proceed.
// The program will get stuck on the channel send operation waiting forever for someone to read the value.
// Go is able to detect situations like this at runtime.
func TestDeadlock(t *testing.T) {
c := make(chan int)
<-c
}

View File

@ -0,0 +1,39 @@
package goroutine
import (
"fmt"
"strings"
"testing"
)
func sourceGopher(downstream chan string) {
for _, v := range []string{"hello world", "a bad apple", "goodbye all"} {
downstream <- v
}
close(downstream)
}
func filterGopher(upstream, downstream chan string) {
for item := range upstream {
fmt.Println("filter ", item)
if !strings.Contains(item, "bad") {
downstream <- item
}
}
}
func printGopher(upstream chan string) {
for item := range upstream {
fmt.Println("print ", item)
}
}
// Pipeline source->filter->print
func TestPipeline(t *testing.T) {
c0 := make(chan string)
c1 := make(chan string)
go sourceGopher(c0)
go filterGopher(c0, c1)
printGopher(c1)
}

View File

@ -0,0 +1,48 @@
package http
import (
"fmt"
"io/ioutil"
"log"
"math/rand"
"net/http"
"sync"
"testing"
"time"
)
func TestGet(t *testing.T) {
counts := 2
var wg sync.WaitGroup
wg.Add(counts)
for i := 0; i < counts; i++ {
go func() {
defer wg.Done()
getValue()
}()
}
wg.Wait()
}
func getValue() {
rand.Seed(time.Now().UnixNano())
x := rand.Intn(10)
var url string
if x > 5 {
url = "http://localhost:48082/api/v1/device/5bae2ef4f37ba14693a5e4fc/command/5bae2ef4f37ba14693a5e4eb"
} else {
url = "http://localhost:48082/api/v1/device/5bae2d1bf37ba14693a5e4e9/command/5bae2d05f37ba14693a5e4e2"
}
resp, err := http.Get(url)
if err != nil {
fmt.Println(err)
}
resBody, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(resBody))
}

View File

@ -0,0 +1,46 @@
package http
import (
"bytes"
"fmt"
"io/ioutil"
"net/http"
"testing"
)
func TestPostYamlString(t *testing.T) {
var filePath = "/Users/bruce/Desktop/HVAC-CoolMasterNet.yml"
var url = "http://localhost:48081/api/v1/deviceprofile/upload"
// read file to byte
yamlFile, err := ioutil.ReadFile(filePath)
if err != nil {
t.Fatal(err)
}
fmt.Println(string(yamlFile))
// create http post request
req, err := http.NewRequest(http.MethodPost, url, bytes.NewReader(yamlFile))
if err != nil {
t.Fatal(err)
}
// submit request
client := &http.Client{}
res, err := client.Do(req)
if err != nil {
t.Fatal(err)
}
// check response
fmt.Println("== upload finish ==")
resBody, err := ioutil.ReadAll(res.Body)
if err != nil {
t.Fatal(err)
}
fmt.Println(res.StatusCode)
fmt.Println(res.Header)
res.Body.Close()
fmt.Println(string(resBody))
}

View File

@ -0,0 +1,63 @@
package http
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"mime/multipart"
"net/http"
"path/filepath"
"testing"
)
func TestUploadFormFile(t *testing.T) {
var filePath = "/Users/bruce/Desktop/HVAC-CoolMasterNet.yml"
var url = "http://localhost:48081/api/v1/deviceprofile/uploadfile"
// Retch file
fmt.Println("Read file: ", filepath.Base(filePath))
yamlFile, err := ioutil.ReadFile(filePath)
if err != nil {
t.Fatal(err)
}
// create form data
body := new(bytes.Buffer)
//body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
formFileWriter, err := writer.CreateFormFile("file", filepath.Base(filePath))
if err != nil {
t.Fatal(err)
}
_, err = io.Copy(formFileWriter, bytes.NewReader(yamlFile))
if err != nil {
t.Fatal(err)
}
writer.Close()
// create http post request
req, err := http.NewRequest(http.MethodPost, url, body)
if err != nil {
t.Fatal(err)
}
req.Header.Add("Content-Type", writer.FormDataContentType())
// submit request
client := &http.Client{}
res, err := client.Do(req)
if err != nil {
t.Fatal(err)
}
// check response
fmt.Println("== upload finish ==")
resBody, err := ioutil.ReadAll(res.Body)
if err != nil {
t.Fatal(err)
}
fmt.Println(res.StatusCode)
fmt.Println(res.Header)
res.Body.Close()
fmt.Println(string(resBody))
}

View File

@ -0,0 +1,15 @@
// Code generated by mockery v1.0.0. DO NOT EDIT.
package mocks
import mock "github.com/stretchr/testify/mock"
// Robot is an autogenerated mock type for the Robot type
type Robot struct {
mock.Mock
}
// SayHi provides a mock function with given fields:
func (_m *Robot) SayHi() {
_m.Called()
}

View File

@ -0,0 +1,45 @@
package mocklib
import "fmt"
// Robot
type Robot interface {
SayHi()
}
// ServiceRobot is kind of Robot can offer services
type ServiceRobot struct {
}
func (robot *ServiceRobot) SayHi() {
fmt.Println("Hi, I'm service robot")
}
// IndustrialRobot is kind of Robot can do some jobs
type IndustrialRobot struct {
}
func (robot *IndustrialRobot) SayHi() {
fmt.Println("Hi, I'm industrial robot")
}
func StartRobots() {
robots := initializeRobots()
makeRobotsSayHi(robots)
}
// initialize all robots
func initializeRobots() []Robot {
robots := []Robot{
&ServiceRobot{},
&IndustrialRobot{},
}
return robots
}
// makeRobotsSayHi is used for making robots say hi
func makeRobotsSayHi(robots []Robot) {
for _, robot := range robots {
robot.SayHi()
}
}

View File

@ -0,0 +1,32 @@
package mocklib
import (
"github.com/weichou1229/go-patterns/playground/mocklib/mocks"
"testing"
)
func TestStartRobots(t *testing.T) {
StartRobots()
}
func TestMakeRobotsSayHi(t *testing.T) {
// create an instance of our test object
mockRobotA := new(mocks.Robot)
mockRobotB := new(mocks.Robot)
// setup expectations
mockRobotA.On("SayHi").Return(nil, nil)
mockRobotB.On("SayHi").Return(nil, nil)
robots := []Robot{
mockRobotA,
mockRobotB,
}
// Act
makeRobotsSayHi(robots)
// Assert that the expectations were met
mockRobotA.AssertExpectations(t)
mockRobotB.AssertExpectations(t)
}

View File

@ -0,0 +1,10 @@
package trypanic
import (
"fmt"
)
func Try() {
var test *string
fmt.Println(*test)
}

View File

@ -0,0 +1,14 @@
package parse
import (
"fmt"
"strconv"
"testing"
)
func TestParseInt(t *testing.T) {
var _, err = strconv.ParseInt("127", 0, 8)
if err != nil {
fmt.Println(err)
}
}

View File

@ -0,0 +1,26 @@
package pointer
import (
"fmt"
"testing"
)
func TestBasic(t *testing.T) {
answer := 42
fmt.Println(&answer) // & is address operator
address := &answer
fmt.Println(*address) // * is dereferencing, which providers the value that a memory address refers to.
fmt.Printf("address is a %T \n", address) // print the pointer type
var address2 *int // declare a pointer
address2 = address // address2 can store some pinter type
fmt.Println(*address2)
}
func TestPointer(t *testing.T) {
var test *string = new(string)
*test = "123"
fmt.Println(test)
}

View File

@ -0,0 +1,27 @@
package internal
import (
"fmt"
"sync"
)
// singleton is private struct, it should be created and fetched by GetSingletonObject func
type singleton struct {
}
func (singleton) SayHi() {
fmt.Println("Hi!")
}
var (
once sync.Once
instance singleton
)
func GetSingletonObject() singleton {
once.Do(func() {
instance = singleton{}
})
return instance
}

View File

@ -0,0 +1,10 @@
package main
import (
"github.com/weichou1229/go-patterns/playground/singleton/internal"
)
func main() {
var s = internal.GetSingletonObject()
s.SayHi()
}

View File

@ -0,0 +1,52 @@
package syncmap
import (
"fmt"
"sync"
"testing"
)
type Order struct {
Id int
}
func TestSync(t *testing.T) {
var waitGroup sync.WaitGroup
waitGroup.Add(10)
var cache sync.Map
for i := 0; i < 10; i++ {
go func() {
cache.Store("test", &Order{})
order, _ := cache.Load("test")
o := order.(*Order)
fmt.Println(o)
waitGroup.Done()
}()
}
waitGroup.Wait()
}
func TestSync2(t *testing.T) {
var cache sync.Map
cache.Store("test", nil)
if order, ok := cache.Load("test"); ok && order != nil {
fmt.Println(order)
} else {
fmt.Println("not exist")
}
}
func TestSync3(t *testing.T) {
var cache sync.Map
orders := make([]Order, 2)
cache.Store("test2", orders[0])
cache.Store("test", orders[0])
order, ok := cache.Load("test")
fmt.Println(ok, order)
}

4089
playground/xml/2018Q1.xml Executable file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,63 @@
package xml
import (
"fmt"
xj "github.com/basgys/goxml2json"
"github.com/buger/jsonparser"
"os"
"testing"
)
type Person struct {
Name string `xml:"name"`
}
func TestParse(t *testing.T) {
xmlFile, err := os.Open("2018Q1.xml")
if err != nil {
fmt.Println(err)
return
}
defer xmlFile.Close()
json, err := xj.Convert(xmlFile)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(json.String())
//err = jsonparser.ObjectEach(json.Bytes(), objectEach,"xbrl")
//if err != nil {
// fmt.Println(err)
// return
//}
// 營業收入
var revenue string
_, err = jsonparser.ArrayEach(json.Bytes(), func(value []byte, dataType jsonparser.ValueType, offset int, err error) {
revenue, err = jsonparser.GetString(value, "#content")
fmt.Println(revenue)
}, "xbrl", "Revenue")
if err != nil {
fmt.Println(err)
return
}
}
func parse(b []byte) {
err := jsonparser.ObjectEach(b, objectEach, "xbrl")
if err != nil {
fmt.Println(err)
return
}
}
func objectEach(key []byte, value []byte, dataType jsonparser.ValueType, offset int) error {
fmt.Printf("[Key]: [%s]\n Value: '%s'\n Type: %s\n", string(key), string(value), dataType)
return nil
}
func arrayEach(value []byte, dataType jsonparser.ValueType, offset int, err error) {
fmt.Printf("Array Value: '%s'\n Type: %s\n", string(value), dataType)
}