From d2f3dbd2f2b8536f62fb5b1a33caac5b367e6a5a Mon Sep 17 00:00:00 2001 From: Sebastian Steger Date: Thu, 23 Oct 2025 12:58:13 +0000 Subject: [PATCH] composite design pattern --- go/04-design-pattern/go.mod | 3 + go/04-design-pattern/go.sum | 0 go/04-design-pattern/main.go | 81 +++++++++++++++++++ go/04-design-pattern/patterns/composite.go | 41 ++++++++++ .../patterns/composite_test.go | 71 ++++++++++++++++ 5 files changed, 196 insertions(+) create mode 100644 go/04-design-pattern/go.mod create mode 100644 go/04-design-pattern/go.sum create mode 100644 go/04-design-pattern/main.go create mode 100644 go/04-design-pattern/patterns/composite.go create mode 100644 go/04-design-pattern/patterns/composite_test.go diff --git a/go/04-design-pattern/go.mod b/go/04-design-pattern/go.mod new file mode 100644 index 0000000..40b6024 --- /dev/null +++ b/go/04-design-pattern/go.mod @@ -0,0 +1,3 @@ +module gitty.informatik.hs-mannheim.de/steger/pr3-code/go/04-design-pattern + +go 1.25.0 diff --git a/go/04-design-pattern/go.sum b/go/04-design-pattern/go.sum new file mode 100644 index 0000000..e69de29 diff --git a/go/04-design-pattern/main.go b/go/04-design-pattern/main.go new file mode 100644 index 0000000..d5e2913 --- /dev/null +++ b/go/04-design-pattern/main.go @@ -0,0 +1,81 @@ +package main + +import ( + "fmt" + "os" + + "gitty.informatik.hs-mannheim.de/steger/pr3-code/go/04-design-pattern/patterns" +) + +type ConstValueCalculator float64 + +func NewConstValueCalculator(valueString string) *ConstValueCalculator { + var value float64 + if _, err := fmt.Sscanf(valueString, "%f", &value); err != nil { + return nil + } + cvc := ConstValueCalculator(value) + return &cvc +} + +func (c ConstValueCalculator) Calc() float64 { + return float64(c) +} + +/* +parses the expression and builds a binary calculation tree. Returns the tree, the remaining expression after parsing, and an error + +example Input: +1. [2] => 2 +2. [+ 2 3] => 5 +3. [* 2 3] => 6 +4. [+ * 2 3 4] == [+ 6 4] => 10 +*/ +func buildBinaryCalculationTree(expression []string) (patterns.Calculator, []string, error) { + + if len(expression) == 0 { + return nil, nil, fmt.Errorf("empty argument") + } + + //1. attempt: try a composite calculator from an operator + composite := patterns.NewCompositeCalculatorFromOperator(expression[0]) + if composite != nil { + expression = expression[1:] + for i := 0; i < 2; i++ { + arg, remaining, err := buildBinaryCalculationTree(expression) + if err != nil { + return nil, nil, err + } + expression = remaining + composite.Add(arg) + } + return composite, expression, nil + } + + //2. attempt: try a const value calculator from a value + constValue := NewConstValueCalculator(expression[0]) + if constValue != nil { + return constValue, expression[1:], nil + } + + //3. failure, no valid symbol + return nil, nil, fmt.Errorf("invalid symbol: %s", expression[0]) +} + +// run for example with go run . + "*" 2 2 4 +// note the "*" because * would otherwise expand +func main() { + calc, remainingArgs, err := buildBinaryCalculationTree(os.Args[1:]) + + if err != nil { + fmt.Println(err) + return + } + + if len(remainingArgs) != 0 { + fmt.Println("Invalid Input - remaining arguments", remainingArgs) + return + } + + fmt.Println(calc.Calc()) +} diff --git a/go/04-design-pattern/patterns/composite.go b/go/04-design-pattern/patterns/composite.go new file mode 100644 index 0000000..aa076f2 --- /dev/null +++ b/go/04-design-pattern/patterns/composite.go @@ -0,0 +1,41 @@ +package patterns + +type Calculator interface { + Calc() float64 +} + +type Aggregator func(float64, float64) float64 + +type CompositeCalculator struct { + initialValue float64 + aggregator Aggregator + children []Calculator +} + +var _ Calculator = CompositeCalculator{} + +func NewCompositeCalculator(initialValue float64, aggregator Aggregator) *CompositeCalculator { + return &CompositeCalculator{initialValue, aggregator, nil} +} + +func NewCompositeCalculatorFromOperator(operator string) *CompositeCalculator { + switch operator { + case "+": + return NewCompositeCalculator(0.0, func(a, b float64) float64 { return a + b }) + case "*": + return NewCompositeCalculator(1.0, func(a, b float64) float64 { return a * b }) + } + return nil +} + +func (c CompositeCalculator) Calc() float64 { + result := c.initialValue + for _, child := range c.children { + result = c.aggregator(result, child.Calc()) + } + return result +} + +func (c *CompositeCalculator) Add(child Calculator) { + c.children = append(c.children, child) +} diff --git a/go/04-design-pattern/patterns/composite_test.go b/go/04-design-pattern/patterns/composite_test.go new file mode 100644 index 0000000..44e7668 --- /dev/null +++ b/go/04-design-pattern/patterns/composite_test.go @@ -0,0 +1,71 @@ +package patterns_test + +import ( + "testing" + + "gitty.informatik.hs-mannheim.de/steger/pr3-code/go/04-design-pattern/patterns" +) + +type valueCalc float64 + +func (v valueCalc) Calc() float64 { + return float64(v) +} + +func TestCompositeCalculator_Empty(t *testing.T) { + empty := patterns.NewCompositeCalculator(5.0, func(a, b float64) float64 { return a + b }) + got := empty.Calc() + want := 5.0 + if got != want { + t.Errorf("Empty: got %v, want %v", got, want) + } +} + +func TestCompositeCalculator_Addition(t *testing.T) { + add := patterns.NewCompositeCalculatorFromOperator("+") + add.Add(valueCalc(5)) + add.Add(valueCalc(3)) + add.Add(valueCalc(2)) + got := add.Calc() + want := 10.0 + if got != want { + t.Errorf("Addition: got %v, want %v", got, want) + } +} + +func TestCompositeCalculator_Multiplication(t *testing.T) { + mul := patterns.NewCompositeCalculatorFromOperator("*") + mul.Add(valueCalc(2)) + mul.Add(valueCalc(3)) + mul.Add(valueCalc(4)) + got := mul.Calc() + want := 24.0 + if got != want { + t.Errorf("Multiplication: got %v, want %v", got, want) + } +} + +func TestCompositeCalculator_Nested(t *testing.T) { + // + 5 3 2 * 2 3 + 4 4 = 5 + 3 + 2 + (2 * 3 * (4+4)) = 5+3+2+48 = 58 + add := patterns.NewCompositeCalculatorFromOperator("+") + add.Add(valueCalc(5)) + add.Add(valueCalc(3)) + add.Add(valueCalc(2)) + + mul := patterns.NewCompositeCalculatorFromOperator("*") + mul.Add(valueCalc(2)) + mul.Add(valueCalc(3)) + + add2 := patterns.NewCompositeCalculatorFromOperator("+") + add2.Add(valueCalc(4)) + add2.Add(valueCalc(4)) + + mul.Add(add2) + add.Add(mul) + + got := add.Calc() + want := 58.0 + if got != want { + t.Errorf("Nested: got %v, want %v", got, want) + } +}