mirror of
https://github.com/crazybber/awesome-patterns.git
synced 2024-11-21 20:36:01 +03:00
# Conflicts: # README.md
This commit is contained in:
commit
3500e141b7
3
.gitignore
vendored
3
.gitignore
vendored
@ -31,4 +31,5 @@ _testmain.go
|
||||
.idea/
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
*.out
|
||||
*.orig
|
||||
|
96
README.md
96
README.md
@ -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 | âœ|
|
||||
|
@ -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)
|
||||
|
@ -40,11 +40,11 @@ type (
|
||||
)
|
||||
|
||||
type (
|
||||
eventObserver struct{
|
||||
eventObserver struct {
|
||||
id int
|
||||
}
|
||||
|
||||
eventNotifier struct{
|
||||
eventNotifier struct {
|
||||
// Using a map with an empty struct allows us to keep the observers
|
||||
// unique while still keeping memory usage relatively low.
|
||||
observers map[Observer]struct{}
|
||||
@ -84,10 +84,10 @@ func main() {
|
||||
tick := time.NewTicker(time.Second).C
|
||||
for {
|
||||
select {
|
||||
case <- stop:
|
||||
case <-stop:
|
||||
return
|
||||
case t := <-tick:
|
||||
n.Notify(Event{Data: t.UnixNano()})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
40
playground/basic/address_test.go
Normal file
40
playground/basic/address_test.go
Normal 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
BIN
playground/cbor/2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 16 MiB |
53
playground/cbor/cbor2_test.go
Normal file
53
playground/cbor/cbor2_test.go
Normal 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)
|
||||
|
||||
}
|
48
playground/cbor/cbor3_test.go
Normal file
48
playground/cbor/cbor3_test.go
Normal 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))
|
||||
|
||||
}
|
104
playground/cbor/cbor_test.go
Normal file
104
playground/cbor/cbor_test.go
Normal 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
BIN
playground/cbor/on.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
17
playground/codecoverage/size.go
Normal file
17
playground/codecoverage/size.go
Normal 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"
|
||||
}
|
26
playground/codecoverage/size_test.go
Normal file
26
playground/codecoverage/size_test.go
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
86
playground/cron/cron_test.go
Normal file
86
playground/cron/cron_test.go
Normal 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"))
|
||||
}
|
35
playground/cron/iso8601parser.go
Normal file
35
playground/cron/iso8601parser.go
Normal 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)
|
||||
}
|
14
playground/factory/robot.go
Normal file
14
playground/factory/robot.go
Normal 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())()
|
||||
}
|
||||
}
|
8
playground/factory/robot_test.go
Normal file
8
playground/factory/robot_test.go
Normal file
@ -0,0 +1,8 @@
|
||||
package factory
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestSay(t *testing.T) {
|
||||
say("xxx")
|
||||
say("hi")
|
||||
}
|
16
playground/factory/robotfuncs.go
Normal file
16
playground/factory/robotfuncs.go
Normal 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
|
||||
}
|
24
playground/goroutine/basic_chan_test.go
Normal file
24
playground/goroutine/basic_chan_test.go
Normal 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
|
||||
}
|
25
playground/goroutine/basic_test.go
Normal file
25
playground/goroutine/basic_test.go
Normal 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 ...")
|
||||
}
|
32
playground/goroutine/basic_waitgroup_test.go
Normal file
32
playground/goroutine/basic_waitgroup_test.go
Normal 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")
|
||||
}
|
11
playground/goroutine/deadlock_test.go
Normal file
11
playground/goroutine/deadlock_test.go
Normal 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
|
||||
}
|
39
playground/goroutine/pipeline_test.go
Normal file
39
playground/goroutine/pipeline_test.go
Normal 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)
|
||||
}
|
48
playground/http/get_test.go
Normal file
48
playground/http/get_test.go
Normal 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))
|
||||
}
|
46
playground/http/postyamlstring_test.go
Normal file
46
playground/http/postyamlstring_test.go
Normal 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))
|
||||
}
|
63
playground/http/upload_test.go
Normal file
63
playground/http/upload_test.go
Normal 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))
|
||||
}
|
15
playground/mocklib/mocks/Robot.go
Normal file
15
playground/mocklib/mocks/Robot.go
Normal 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()
|
||||
}
|
45
playground/mocklib/robot.go
Normal file
45
playground/mocklib/robot.go
Normal 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()
|
||||
}
|
||||
}
|
32
playground/mocklib/robot_test.go
Normal file
32
playground/mocklib/robot_test.go
Normal 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)
|
||||
}
|
10
playground/panic/trypanic.go
Normal file
10
playground/panic/trypanic.go
Normal file
@ -0,0 +1,10 @@
|
||||
package trypanic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func Try() {
|
||||
var test *string
|
||||
fmt.Println(*test)
|
||||
}
|
14
playground/parse/parseInt_test.go
Normal file
14
playground/parse/parseInt_test.go
Normal 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)
|
||||
}
|
||||
}
|
26
playground/pointer/pointer_test.go
Normal file
26
playground/pointer/pointer_test.go
Normal 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)
|
||||
}
|
27
playground/singleton/internal/singleton.go
Normal file
27
playground/singleton/internal/singleton.go
Normal 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
|
||||
}
|
10
playground/singleton/main.go
Normal file
10
playground/singleton/main.go
Normal file
@ -0,0 +1,10 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/weichou1229/go-patterns/playground/singleton/internal"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var s = internal.GetSingletonObject()
|
||||
s.SayHi()
|
||||
}
|
52
playground/syncmap/syncmap_test.go
Normal file
52
playground/syncmap/syncmap_test.go
Normal 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
4089
playground/xml/2018Q1.xml
Executable file
File diff suppressed because it is too large
Load Diff
63
playground/xml/xml_test.go
Normal file
63
playground/xml/xml_test.go
Normal 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)
|
||||
}
|
Loading…
Reference in New Issue
Block a user