constructor

// All material is licensed under the Apache License Version 2.0, January 2004
// http://www.apache.org/licenses/LICENSE-2.0

// https://play.golang.org/p/uB4c33sbfj

// Sample program demonstrating decoupling with interface composition.
package main

import "fmt"

// =============================================================================

// Board represents a surface we can work on.
type Board struct {
    NailsNeeded int
    NailsDriven int
}

// =============================================================================

// NailDriver represents behavior to drive nails into a board.
type NailDriver interface {
    DriveNail(nailSupply *int, b *Board)
}

// NailPuller represents behavior to remove nails into a board.
type NailPuller interface {
    PullNail(nailSupply *int, b *Board)
}

// NailDrivePuller represents behavior to drive and remove nails into a board.
type NailDrivePuller interface {
    NailDriver
    NailPuller
}

// =============================================================================

// Mallet is a tool that pounds in nails.
type Mallet struct{}

// DriveNail pounds a nail into the specified board.
func (Mallet) DriveNail(nailSupply *int, b *Board) {

    // Take a nail out of the supply.
    *nailSupply--

    // Pound a nail into the board.
    b.NailsDriven++

    fmt.Println("Mallet: pounded nail into the board.")
}

// Crowbar is a tool that removes nails.
type Crowbar struct{}

// PullNail yanks a nail out of the specified board.
func (Crowbar) PullNail(nailSupply *int, b *Board) {

    // Yank a nail out of the board.
    b.NailsDriven--

    // Put that nail back into the supply.
    *nailSupply++

    fmt.Println("Crowbar: yanked nail out of the board.")
}

// =============================================================================

// Toolbox can contains any type of Driver and Puller.
type Toolbox struct {
    NailDriver
    NailPuller

    nails int
}

// =============================================================================

// Contractor carries out the task of securing boards.
type Contractor struct{}

// Fasten will drive nails into a board.
func (Contractor) Fasten(d NailDriver, nailSupply *int, b *Board) {
    for b.NailsDriven < b.NailsNeeded {
        d.DriveNail(nailSupply, b)
    }
}

// Unfasten will remove nails from a board.
func (Contractor) Unfasten(p NailPuller, nailSupply *int, b *Board) {
    for b.NailsDriven > b.NailsNeeded {
        p.PullNail(nailSupply, b)
    }
}

// ProcessBoards works against boards.
func (c Contractor) ProcessBoards(dp NailDrivePuller, nailSupply *int, boards []Board) {
    for i := range boards {
        b := &boards[i]

        fmt.Printf("Contractor: examining board #%d: %+v\n", i+1, b)

        switch {
        case b.NailsDriven < b.NailsNeeded:
            c.Fasten(dp, nailSupply, b)

        case b.NailsDriven > b.NailsNeeded:
            c.Unfasten(dp, nailSupply, b)
        }
    }
}

// =============================================================================

// main is the entry point for the application.
func main() {

    // Inventory of old boards to remove, and the new boards
    // that will replace them.
    boards := []Board{

        // Rotted boards to be removed.
        {NailsDriven: 3},
        {NailsDriven: 1},
        {NailsDriven: 6},

        // Fresh boards to be fastened.
        {NailsNeeded: 6},
        {NailsNeeded: 9},
        {NailsNeeded: 4},
    }

    // Fill a toolbox.
    tb := Toolbox{
        NailDriver: Mallet{},
        NailPuller: Crowbar{},
        nails:      10,
    }

    // Hire a Contractor and put our Contractor to work.
    var c Contractor
    c.ProcessBoards(&tb, &tb.nails, boards)
}
Contractor: examining board #1: &{NailsNeeded:0 NailsDriven:3}
Crowbar: yanked nail out of the board.
Crowbar: yanked nail out of the board.
Crowbar: yanked nail out of the board.
Contractor: examining board #2: &{NailsNeeded:0 NailsDriven:1}
Crowbar: yanked nail out of the board.
Contractor: examining board #3: &{NailsNeeded:0 NailsDriven:6}
Crowbar: yanked nail out of the board.
Crowbar: yanked nail out of the board.
Crowbar: yanked nail out of the board.
Crowbar: yanked nail out of the board.
Crowbar: yanked nail out of the board.
Crowbar: yanked nail out of the board.
Contractor: examining board #4: &{NailsNeeded:6 NailsDriven:0}
Mallet: pounded nail into the board.
Mallet: pounded nail into the board.
Mallet: pounded nail into the board.
Mallet: pounded nail into the board.
Mallet: pounded nail into the board.
Mallet: pounded nail into the board.
Contractor: examining board #5: &{NailsNeeded:9 NailsDriven:0}
Mallet: pounded nail into the board.
Mallet: pounded nail into the board.
Mallet: pounded nail into the board.
Mallet: pounded nail into the board.
Mallet: pounded nail into the board.
Mallet: pounded nail into the board.
Mallet: pounded nail into the board.
Mallet: pounded nail into the board.
Mallet: pounded nail into the board.
Contractor: examining board #6: &{NailsNeeded:4 NailsDriven:0}
Mallet: pounded nail into the board.
Mallet: pounded nail into the board.
Mallet: pounded nail into the board.
Mallet: pounded nail into the board.

Last updated

Was this helpful?