Skip to content

Instantly share code, notes, and snippets.

@caelifer
Last active November 19, 2015 03:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save caelifer/826948cb8ee995633547 to your computer and use it in GitHub Desktop.
Save caelifer/826948cb8ee995633547 to your computer and use it in GitHub Desktop.
package main
import "fmt"
// board
type Board struct {
NailsIn, NailsNeeded int
}
// fasets
type Nail struct{}
// faset tool interface
type NailPuller interface {
PullNail(b *Board)
}
type NailDriver interface {
DriveNail(b *Board)
}
// tools
type Mallet struct{}
func (Mallet) String() string {
return "Mallet"
}
// Implement NailDriver interface
func (Mallet) DriveNail(b *Board) {
b.NailsIn++
}
type Crowbar struct{}
func (Crowbar) String() string {
return "Crowbar"
}
func (c Crowbar) PullNail(b *Board) {
if b.NailsIn > 0 {
b.NailsIn--
} else {
fmt.Printf("%s: no nails in this board; what gives!?\n", c)
}
}
// Contractor carries out the task of securing boards.
type Contractor struct{}
func (Contractor) resupplyNails(nailSupply chan<- Nail) {
// fill nail supply with nails
for {
select {
case nailSupply <- Nail{}:
fmt.Println("Contractor: added one nail to my nail supply.")
default:
fmt.Println("Contractor: nail supply is full")
return
}
}
}
// fasten will drive nails into a board.
func (c Contractor) fasten(d NailDriver, nailSupply chan Nail, b *Board) {
for b.NailsIn < b.NailsNeeded {
GET_NAIL:
for {
select {
case <-nailSupply:
break GET_NAIL
default:
fmt.Println("Contractor: need more nails!")
c.resupplyNails(nailSupply)
}
}
d.DriveNail(b)
fmt.Printf("Contractor: pounded a nail into the board using %s.\n", d)
}
}
// unfasten will remove nails from a board.
func (Contractor) unfasten(p NailPuller, nailSupply chan<- Nail, b *Board) {
for b.NailsIn > b.NailsNeeded {
p.PullNail(b)
fmt.Printf("Contractor: pulled a used nail from the board using %s.\n", p)
// return the used nail into our nail supply
select {
case nailSupply <- Nail{}:
fmt.Println("Contractor: put a used nail back into my nail supply.")
default:
fmt.Println("Contractor: too many nails (nail supply is full), throw this one away.")
}
}
}
// processBoards works against boards.
func (c Contractor) processBoards(dp Toolbox, nailSupply chan Nail, boards []Board) {
for i := range boards {
b := &boards[i]
fmt.Printf("Contractor: examining board #%d: %+v\n", i+1, b)
switch {
case b.NailsIn < b.NailsNeeded:
c.fasten(dp.NailDriver, nailSupply, b)
case b.NailsIn > b.NailsNeeded:
c.unfasten(dp.NailPuller, nailSupply, b)
}
}
}
// =============================================================================
// Toolbox can contains any number of tools.
type Toolbox struct {
NailDriver NailDriver
NailPuller NailPuller
NailSupply chan Nail
}
// =============================================================================
func main() {
// Inventory of old boards to remove, and the new boards
// that will replace them.
boards := []Board{
// Rotted boards to be removed.
{NailsIn: 3},
{NailsIn: 1},
{NailsIn: 6},
// Fresh boards to be fastened.
{NailsNeeded: 6},
{NailsNeeded: 9},
{NailsNeeded: 4},
}
// Fill a toolbox.
tb := Toolbox{
NailDriver: Mallet{},
NailPuller: Crowbar{},
NailSupply: make(chan Nail, 5),
}
// Display the current state of our toolbox and boards.
displayState(tb, boards)
// Hire a Contractor and put our Contractor to work.
var c Contractor
c.processBoards(tb, tb.NailSupply, boards)
// Display the new state of our toolbox and boards.
displayState(tb, boards)
}
// displayState provide information about all the boards.
func displayState(tb Toolbox, boards []Board) {
fmt.Printf("Box: %#v with %d nail(s)\n", tb, len(tb.NailSupply))
fmt.Println("Boards:")
for _, b := range boards {
fmt.Printf("\t%+v\n", b)
}
fmt.Println()
}
@caelifer
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment