learnings

Builder

Builder - is a creational design pattern that separates the construction of a complex object from its representation, so that the same construction process can result in different representations. This is typically implemented using a method chaining where each method returns the builder instance.

This pattern is used when:

The pattern operates with four entities:

  1. Builder - defines an abstract interface for manufacturing parts of a product object
  2. СoncreteBuilder
    1. constructs and assembles parts of a product by implementing the Builder interface
    2. defines the view being created and monitors it
    3. provides an interface for accessing the product
  3. Director - constructs an object using the Builder interface
  4. Product:
    1. represents a complex object being constructed. ConcreteBuilder builds an internal representation of the product and defines its assembly process
    2. includes classes that define the component parts, including interfaces for assembling the final result from the parts

Implementations

Golang

package main

import (
	"encoding/json"
	"errors"
	"fmt"
)

type AbstractPCBuilder interface {
	SetupMotherboard(mb Motherboard) error
	SetupCPU(cpu CPU) error
	SetupRAM(ram RAM) error
	SetupGPU(gpu GPU) error
	SetupPSU(psu PSU) error

	FinalizePC() (*PC, error)
}

type Motherboard struct {
	RAMSlots int
	Socket   string
}

type CPU struct {
	CoreCount        uint8
	PowerConsumption int
	Socket           string
}
type RAM struct {
	Capacity int
}

type GPU struct {
	PowerConsumption int
}

type PSU struct {
	Power int
}

type PC struct {
	Motherboard *Motherboard
	CPU         *CPU
	RAM         []RAM
	GPU         *GPU
	PSU         *PSU
}

func (c PC) PowerON() {
	fmt.Println("Successfully powered on")
}

type WorkStationBuilder struct {
	pc *PC
}

func NewWorkStationBuilder() AbstractPCBuilder {
	return &WorkStationBuilder{
		pc: &PC{},
	}
}

func (b *WorkStationBuilder) SetupMotherboard(mb Motherboard) error {
	if b.pc.Motherboard != nil {
		return errors.New("Motherboard already installed")
	}

	if len(b.pc.RAM) > mb.RAMSlots {
		return errors.New("inappropriate number of RAM slots")
	}

	if b.pc.CPU != nil && b.pc.CPU.Socket != mb.Socket {
		return errors.New("CPU and motherboard socket mismatch")
	}

	b.pc.Motherboard = &mb

	return nil
}

func (b *WorkStationBuilder) SetupCPU(cpu CPU) error {
	if b.pc.CPU != nil {
		return errors.New("CPU already installed")
	}

	if cpu.CoreCount > 4 || cpu.PowerConsumption > 80 {
		return errors.New("too powerfull cpu")
	}

	if b.pc.PSU != nil && (cpu.PowerConsumption/b.pc.PSU.Power*100) > 60 {
		return errors.New("PSU too weak for these CPU")
	}

	if b.pc.Motherboard != nil && b.pc.Motherboard.Socket != cpu.Socket {
		return errors.New("CPU and motherboard socket mismatch")
	}

	b.pc.CPU = &cpu

	return nil
}

func (b *WorkStationBuilder) SetupRAM(ram RAM) error {
	if b.pc.Motherboard != nil && b.pc.Motherboard.RAMSlots <= len(b.pc.RAM) {
		return errors.New("motherboard RAM slots are full")
	}

	if ram.Capacity < 1 || ram.Capacity > 4 {
		return errors.New("bad RAM capacity")
	}

	b.pc.RAM = append(b.pc.RAM, ram)
	return nil
}

func (b *WorkStationBuilder) SetupGPU(gpu GPU) error {
	return errors.New("workstation doesn't need discrete GPU")
}

func (b *WorkStationBuilder) SetupPSU(psu PSU) error {
	if b.pc.PSU != nil {
		return errors.New("PSU already installed")
	}

	consumption := 0
	if b.pc.CPU != nil {
		consumption += b.pc.CPU.PowerConsumption
	}

	b.pc.PSU = &psu

	return nil
}

func (b *WorkStationBuilder) FinalizePC() (*PC, error) {
	if b.pc.CPU == nil ||
		b.pc.Motherboard == nil ||
		len(b.pc.RAM) == 0 ||
		b.pc.PSU == nil {
		return nil, errors.New("workstation built can't be finalized")
	}
	return b.pc, nil
}

func main() {
	pcBuilder := NewWorkStationBuilder()
	err := pcBuilder.SetupCPU(CPU{
		CoreCount:        4,
		PowerConsumption: 60,
		Socket:           "AM5",
	})
	if err != nil {
		panic(err)
	}
	err = pcBuilder.SetupPSU(PSU{
		Power: 200,
	})
	if err != nil {
		panic(err)
	}

	for range 4 {
		err = pcBuilder.SetupRAM(RAM{
			Capacity: 1,
		})
		if err != nil {
			panic(err)
		}
	}

	err = pcBuilder.SetupMotherboard(Motherboard{
		RAMSlots: 4,
		Socket:   "AM5",
	})
	if err != nil {
		panic(err)
	}

	pc, err := pcBuilder.FinalizePC()
	if err != nil {
		panic(err)
	}

	d, _ := json.MarshalIndent(pc, "", " ")
	fmt.Printf("%s\n", d)

	pc.PowerON()
}