diff --git a/chapter10/listing01/listing01.go b/chapter10/listing01/listing01.go new file mode 100644 index 0000000..998d91d --- /dev/null +++ b/chapter10/listing01/listing01.go @@ -0,0 +1,116 @@ +// Sample program demonstrating struct composition. +package main + +import ( + "errors" + "fmt" + "io" + "math/rand" + "time" +) + +func init() { + rand.Seed(time.Now().UnixNano()) +} + +// ============================================================================= + +// Data is the structure of the data we are copying. +type Data struct { + Line string +} + +// ============================================================================= + +// Xenia is a system we need to pull data from. +type Xenia struct{} + +// Pull knows how to pull data out of Xenia. +func (Xenia) Pull(d *Data) error { + switch rand.Intn(10) { + case 1, 9: + return io.EOF + + case 5: + return errors.New("Error reading data from Xenia") + + default: + d.Line = "Data" + fmt.Println("In:", d.Line) + return nil + } +} + +// Pillar is a system we need to store data into. +type Pillar struct{} + +// Store knows how to store data into Pillar. +func (Pillar) Store(d Data) error { + fmt.Println("Out:", d.Line) + return nil +} + +// ============================================================================= + +// System wraps Xenia and Pillar together into a single system. +type System struct { + Xenia + Pillar +} + +// ============================================================================= + +// pull knows how to pull bulks of data from Xenia. +func pull(x *Xenia, data []Data) (int, error) { + for i := range data { + if err := x.Pull(&data[i]); err != nil { + return i, err + } + } + + return len(data), nil +} + +// store knows how to store bulks of data into Pillar. +func store(p *Pillar, data []Data) (int, error) { + for i, d := range data { + if err := p.Store(d); err != nil { + return i, err + } + } + + return len(data), nil +} + +// Copy knows how to pull and store data from the System. +func Copy(sys *System, batch int) error { + data := make([]Data, batch) + + for { + i, err := pull(&sys.Xenia, data) + if i > 0 { + if _, err := store(&sys.Pillar, data[:i]); err != nil { + return err + } + } + + if err != nil { + return err + } + } +} + +// ============================================================================= + +func main() { + + // Initialize the system for use. + sys := System{ + Xenia: Xenia{}, + Pillar: Pillar{}, + } + + if err := Copy(&sys, 3); err != io.EOF { + fmt.Println(err) + } +} diff --git a/chapter10/listing02/listing02.go b/chapter10/listing02/listing02.go new file mode 100644 index 0000000..26c7700 --- /dev/null +++ b/chapter10/listing02/listing02.go @@ -0,0 +1,128 @@ +// Sample program demonstrating decoupling with interfaces. +package main + +import ( + "errors" + "fmt" + "io" + "math/rand" + "time" +) + +func init() { + rand.Seed(time.Now().UnixNano()) +} + +// ============================================================================= + +// Data is the structure of the data we are copying. +type Data struct { + Line string +} + +// ============================================================================= + +// Puller declares behavior for pulling data. +type Puller interface { + Pull(d *Data) error +} + +// Storer declares behavior for storing data. +type Storer interface { + Store(d Data) error +} + +// ============================================================================= + +// Xenia is a system we need to pull data from. +type Xenia struct{} + +// Pull knows how to pull data out of Xenia. +func (Xenia) Pull(d *Data) error { + switch rand.Intn(10) { + case 1, 9: + return io.EOF + + case 5: + return errors.New("Error reading data from Xenia") + + default: + d.Line = "Data" + fmt.Println("In:", d.Line) + return nil + } +} + +// Pillar is a system we need to store data into. +type Pillar struct{} + +// Store knows how to store data into Pillar. +func (Pillar) Store(d Data) error { + fmt.Println("Out:", d.Line) + return nil +} + +// ============================================================================= + +// System wraps Xenia and Pillar together into a single system. +type System struct { + Xenia + Pillar +} + +// ============================================================================= + +// pull knows how to pull bulks of data from any Puller. +func pull(p Puller, data []Data) (int, error) { + for i := range data { + if err := p.Pull(&data[i]); err != nil { + return i, err + } + } + + return len(data), nil +} + +// store knows how to store bulks of data from any Storer. +func store(s Storer, data []Data) (int, error) { + for i, d := range data { + if err := s.Store(d); err != nil { + return i, err + } + } + + return len(data), nil +} + +// Copy knows how to pull and store data from the System. +func Copy(sys *System, batch int) error { + data := make([]Data, batch) + + for { + i, err := pull(&sys.Xenia, data) + if i > 0 { + if _, err := store(&sys.Pillar, data[:i]); err != nil { + return err + } + } + + if err != nil { + return err + } + } +} + +// ============================================================================= + +func main() { + + // Initialize the system for use. + sys := System{ + Xenia: Xenia{}, + Pillar: Pillar{}, + } + + if err := Copy(&sys, 3); err != io.EOF { + fmt.Println(err) + } +} diff --git a/chapter10/listing03/listing03.go b/chapter10/listing03/listing03.go new file mode 100644 index 0000000..a05e57b --- /dev/null +++ b/chapter10/listing03/listing03.go @@ -0,0 +1,134 @@ +// Sample program demonstrating interface composition. +package main + +import ( + "errors" + "fmt" + "io" + "math/rand" + "time" +) + +func init() { + rand.Seed(time.Now().UnixNano()) +} + +// ============================================================================= + +// Data is the structure of the data we are copying. +type Data struct { + Line string +} + +// ============================================================================= + +// Puller declares behavior for pulling data. +type Puller interface { + Pull(d *Data) error +} + +// Storer declares behavior for storing data. +type Storer interface { + Store(d Data) error +} + +// PullStorer declares behavior for both pulling and storing. +type PullStorer interface { + Puller + Storer +} + +// ============================================================================= + +// Xenia is a system we need to pull data from. +type Xenia struct{} + +// Pull knows how to pull data out of Xenia. +func (Xenia) Pull(d *Data) error { + switch rand.Intn(10) { + case 1, 9: + return io.EOF + + case 5: + return errors.New("Error reading data from Xenia") + + default: + d.Line = "Data" + fmt.Println("In:", d.Line) + return nil + } +} + +// Pillar is a system we need to store data into. +type Pillar struct{} + +// Store knows how to store data into Pillar. +func (Pillar) Store(d Data) error { + fmt.Println("Out:", d.Line) + return nil +} + +// ============================================================================= + +// System wraps Xenia and Pillar together into a single system. +type System struct { + Xenia + Pillar +} + +// ============================================================================= + +// pull knows how to pull bulks of data from any Puller. +func pull(p Puller, data []Data) (int, error) { + for i := range data { + if err := p.Pull(&data[i]); err != nil { + return i, err + } + } + + return len(data), nil +} + +// store knows how to store bulks of data from any Storer. +func store(s Storer, data []Data) (int, error) { + for i, d := range data { + if err := s.Store(d); err != nil { + return i, err + } + } + + return len(data), nil +} + +// Copy knows how to pull and store data from any System. +func Copy(ps PullStorer, batch int) error { + data := make([]Data, batch) + + for { + i, err := pull(ps, data) + if i > 0 { + if _, err := store(ps, data[:i]); err != nil { + return err + } + } + + if err != nil { + return err + } + } +} + +// ============================================================================= + +func main() { + + // Initialize the system for use. + sys := System{ + Xenia: Xenia{}, + Pillar: Pillar{}, + } + + if err := Copy(&sys, 3); err != io.EOF { + fmt.Println(err) + } +} diff --git a/chapter10/listing04/listing04.go b/chapter10/listing04/listing04.go new file mode 100644 index 0000000..b5b1145 --- /dev/null +++ b/chapter10/listing04/listing04.go @@ -0,0 +1,134 @@ +// Sample program demonstrating decoupling with interface composition. +package main + +import ( + "errors" + "fmt" + "io" + "math/rand" + "time" +) + +func init() { + rand.Seed(time.Now().UnixNano()) +} + +// ============================================================================= + +// Data is the structure of the data we are copying. +type Data struct { + Line string +} + +// ============================================================================= + +// Puller declares behavior for pulling data. +type Puller interface { + Pull(d *Data) error +} + +// Storer declares behavior for storing data. +type Storer interface { + Store(d Data) error +} + +// PullStorer declares behavior for both pulling and storing. +type PullStorer interface { + Puller + Storer +} + +// ============================================================================= + +// Xenia is a system we need to pull data from. +type Xenia struct{} + +// Pull knows how to pull data out of Xenia. +func (Xenia) Pull(d *Data) error { + switch rand.Intn(10) { + case 1, 9: + return io.EOF + + case 5: + return errors.New("Error reading data from Xenia") + + default: + d.Line = "Data" + fmt.Println("In:", d.Line) + return nil + } +} + +// Pillar is a system we need to store data into. +type Pillar struct{} + +// Store knows how to store data into Pillar. +func (Pillar) Store(d Data) error { + fmt.Println("Out:", d.Line) + return nil +} + +// ============================================================================= + +// System wraps Pullers and Stores together into a single system. +type System struct { + Puller + Storer +} + +// ============================================================================= + +// pull knows how to pull bulks of data from any Puller. +func pull(p Puller, data []Data) (int, error) { + for i := range data { + if err := p.Pull(&data[i]); err != nil { + return i, err + } + } + + return len(data), nil +} + +// store knows how to store bulks of data from any Storer. +func store(s Storer, data []Data) (int, error) { + for i, d := range data { + if err := s.Store(d); err != nil { + return i, err + } + } + + return len(data), nil +} + +// Copy knows how to pull and store data from any System. +func Copy(ps PullStorer, batch int) error { + data := make([]Data, batch) + + for { + i, err := pull(ps, data) + if i > 0 { + if _, err := store(ps, data[:i]); err != nil { + return err + } + } + + if err != nil { + return err + } + } +} + +// ============================================================================= + +func main() { + + // Initialize the system for use. + sys := System{ + Puller: Xenia{}, + Storer: Pillar{}, + } + + if err := Copy(&sys, 3); err != io.EOF { + fmt.Println(err) + } +} diff --git a/chapter10/listing05/listing05.go b/chapter10/listing05/listing05.go new file mode 100644 index 0000000..dd358d5 --- /dev/null +++ b/chapter10/listing05/listing05.go @@ -0,0 +1,81 @@ +// Sample program demonstrating when implicit interface conversions +// are provided by the compiler. +package main + +import "fmt" + +// ============================================================================= + +// Mover provides support for moving things. +type Mover interface { + Move() +} + +// Locker provides support for locking and unlocking things. +type Locker interface { + Lock() + Unlock() +} + +// MoveLocker provides support for moving and locking things. +type MoveLocker interface { + Mover + Locker +} + +// ============================================================================= + +// bike represents a concrete type for the example. +type bike struct{} + +// Move can change the position of a bike. +func (bike) Move() { + fmt.Println("Moving the bike") +} + +// Lock prevents a bike from moving. +func (bike) Lock() { + fmt.Println("Locking the bike") +} + +// Unlock allows a bike to be moved. +func (bike) Unlock() { + fmt.Println("Unlocking the bike") +} + +// ============================================================================= + +func main() { + + // Declare variables of the MoveLocker and Mover interfaces set to their + // zero value. + var ml MoveLocker + var m Mover + + // Create a value of type bike and assign the value to the MoveLocker + // interface value. + ml = bike{} + + // An interface value of type MoveLocker can be implicitly converted into + // a value of type Mover. They both declare a method named move. + m = ml + + // prog.go:65: cannot use m (type Mover) as type MoveLocker in assignment: + // Mover does not implement MoveLocker (missing Lock method) + ml = m + + // Interface type Mover does not declare methods named lock and unlock. + // Therefore, the compiler can't perform an implicit conversion to assign + // a value of interface type Mover to an interface value of type MoveLocker. + // It is irrelevant that the concrete type value of type bike that is stored + // inside of the Mover interface value implements the MoveLocker interface. + + // We can perform a type assertion at runtime to support the assignment. + + // Perform a type assertion against the Mover interface value to access + // a COPY of the concrete type value of type bike that was stored inside + // of it. Then assign the COPY of the concrete type to the MoveLocker + // interface. + b := m.(bike) + ml = b +} diff --git a/chapter10/listing06/listing06 b/chapter10/listing06/listing06 new file mode 100755 index 0000000..e927805 Binary files /dev/null and b/chapter10/listing06/listing06 differ diff --git a/chapter10/listing06/listing06.go b/chapter10/listing06/listing06.go new file mode 100644 index 0000000..f728ab4 --- /dev/null +++ b/chapter10/listing06/listing06.go @@ -0,0 +1,51 @@ +// Sample program to show how you can personally mock concrete types when +// you need to for your own packages or tests. +package main + +import ( + "github.com/goinaction/code/chapter10/listing06/pubsub" +) + +// publisher is an interface to allow this package to mock the +// pubsub package support. +type publisher interface { + Publish(key string, v interface{}) error + Subscribe(key string) error +} + +// mock is a concrete type to help support the mocking of the +// pubsub package. +type mock struct{} + +// Publish implements the publisher interface for the mock. +func (m *mock) Publish(key string, v interface{}) error { + + // ADD YOUR MOCK FOR THE PUBLISH CALL. + return nil +} + +// Subscribe implements the publisher interface for the mock. +func (m *mock) Subscribe(key string) error { + + // ADD YOUR MOCK FOR THE SUBSCRIBE CALL. + return nil +} + +func main() { + + // Create a slice of publisher interface values. Assign + // the address of a pubsub.PubSub value and the address of + // a mock value. + pubs := []publisher{ + pubsub.New("localhost"), + &mock{}, + } + + // Range over the interface value to see how the publisher + // interface provides the level of decoupling the user needs. + // The pubsub package did not need to provide the interface type. + for _, p := range pubs { + p.Publish("key", "value") + p.Subscribe("key") + } +} diff --git a/chapter10/listing06/pubsub/pubsub.go b/chapter10/listing06/pubsub/pubsub.go new file mode 100644 index 0000000..9b6c7a5 --- /dev/null +++ b/chapter10/listing06/pubsub/pubsub.go @@ -0,0 +1,36 @@ +// Package pubsub simulates a package that provides +// publication/subscription type services. +package pubsub + +// PubSub provides access to a queue system. +type PubSub struct { + host string + + // PRETEND THERE ARE MORE FIELDS. +} + +// New creates a pubsub value for use. +func New(host string) *PubSub { + ps := PubSub{ + host: host, + } + + // PRETEND THERE IS A SPECIFIC IMPLEMENTATION. + + return &ps +} + +// Publish sends the data for the specified key. +func (ps *PubSub) Publish(key string, v interface{}) error { + + // PRETEND THERE IS A SPECIFIC IMPLEMENTATION. + return nil +} + +// Subscribe sets up an request to receive messages for the +// specified key. +func (ps *PubSub) Subscribe(key string) error { + + // PRETEND THERE IS A SPECIFIC IMPLEMENTATION. + return nil +} diff --git a/chapter2/sample/data/data.json b/chapter2/sample/data/data.json index d1922e7..cf8fc59 100644 --- a/chapter2/sample/data/data.json +++ b/chapter2/sample/data/data.json @@ -135,7 +135,7 @@ "type" : "rss" }, { - "site" : "foxnews", + "site" : "cnn", "link" : "http://rss.cnn.com/rss/cnn_topstories.rss", "type" : "rss" },