Metamation Developer Notes

  • Developer Notes
  • Resource Map
  • Tools
    • Code Formatting
    • Configuring Git
    • Assignments
    • Testing with Windows Sandbox
    • Single Binary Applications
    • Self-Updating Applications
  • Flux Workflow
    • Flux Workflow
    • Flux Development Style Guide
    • Release notes for Flux
    • SonarCloud analysis for Flux
    • Build a Flux SDK Program
    • Shipping Flux
    • Flux.Test
      • BasicBendAuto Test
      • LaserAuto.cs Detailed Documentation
      • Laserexample:Test1
  • Technical Notes
    • File formats
      • .FX File Format
      • Au(Gold) streams
      • .GEO File Format
      • .LST File Format
      • .LST file - technology
      • .BNC File Format
      • .WAD Files: Flux Resource Packages
      • .Curl Files: ASCII config information
    • Folders App
      • Data\Bendtools
      • Data\Machines
    • FluxCode
      • Welcome & Repo Map
      • Architecture & System Context
      • Subsystem *Bend* (Press Brake)
      • Subsystem *BCell* (Bend Cell Automation)
      • API Maps
      • Data Model Catalog
      • Design Patterns -- A Reader's Guide
      • Tests and Golden Paths
      • Guided Code Tours
      • CAM Glossary with Code Mapping
      • Assignments
      • Flux Onboarding Handbook
    • Data flow
      • Part
        • Part.Dwg
        • Part.Load
        • Part.Load to Part.Dwg
      • BendTech.cs
      • Bend Technology
      • Laser Cutting Technology
    • LTS (Long term support mechanism)
    • Machine ID Scheme
  • FluxTester
    • Commands and Scripts
    • Command Cookbook
    • Script Commands
    • BendTooling.cs Test Inventory
    • LaserTooling.cs Test Steps
    • Fluxtester Recipe Book
    • Nesting.cs Overview
    • Nesting.cs Recipe Book
    • Job.cs Overview
    • Job.cs Recipe Book
  • Software license
  • 3d party software
    • Python
  • Documentation
    • Bento documentation
      • Structure of a Bento page
      • Attributes
    • Software
      • Software Requirement
      • Installation
      • Git workflow for documentation contributors
    • Quick start guide
    • Workflow
      • Roles and responsibilities
      • Analyze
      • Edit
      • Proofread
      • Pre-Publish
      • Publish
      • Release cycle
      • Information required for Documentation Team
      • Checklist for Documenters
    • Styleguide
    • Flavoring
    • Translation
      • Ordering translation for a new document
    • Citations
    • Annotation
Technical Notes
/
FluxCode
/
Design Patterns -- A Reader's Guide

Design Patterns -- A Reader's Guide

Goal: recognize the recurring design patterns Flux actually uses (which is not the same as a textbook tour). For each pattern: what it looks like in code, where to find canonical examples, and what to watch out for when modifying. Reading time: 45 minutes.


1. How to read this chapter

Flux is ~25 years of evolved code. Some patterns are pristine; some are intentional pragmatic deviations. We list both.

For each pattern:

  • Form in Flux — how it looks in this codebase, not in a textbook.

  • Where — concrete file references.

  • Watch out for — pitfalls when extending.


2. Strategy — selectable algorithm behind an interface

Form in Flux

A small interface (IBendCalc, IFlexSpine, Glob subtype hierarchy) plus concrete implementations chosen at runtime by a property accessor or factory.

Canonical example: IBendCalc

Where: Src\Core\Bend\BendCalc.cs.

public interface IBendCalc {
   void GetDieVRange (double thickness, out int min, out int max);
   double SuggestDieWidth (double thickness, double radius, double interiorAngle);
   double SuggestPunchRadius (double thickness);
   double GetBendRadiusFactor (double thickness, double interiorAngle);
   BendStaples GetBendStaples (Workpiece workpiece, ToolSet toolSet, EBendKind kind);
}

Implementations: StdBendCalc, TBSBendCalc (Trumpf). Selection: in BendMachine.BendCalc:

public IBendCalc BendCalc => Settings.BendCalc switch {
   Sheaf.EBendCalc.Default => IsTrumpf ? TBSBendCalc.It : StdBendCalc.It,
   _ => StdBendCalc.It,
};

Watch out for

  • The selection key (here Settings.BendCalc) lives in Sheaf. Adding a new strategy requires extending the enum and the switch.

  • Strategy instances are typically singletons (StdBendCalc.It). Don’t add per-instance state.

Other examples

  • IFlexSpine (multiple implementations per bend kind).

  • Crash engines (BendOpCrash vs. NBendCrash) — selected by feature flag, currently both exist (this is the in-flight one).


3. Composite — uniform handling of single + group objects

Form in Flux

The Glob system is a textbook Composite: when the user selects multiple objects, the system synthesizes a composite glob that exposes the shared properties and dispatches commands to all members.

Canonical example

  • BendOpGlob wraps a single BendOp (Src\UI\Bend\BendOpUI.cs line 17).

  • BendOpsGlob wraps multiple (Src\UI\Bend\BendOpUI.cs line 661).

The composite is built automatically by the glob machinery — you only need to expose properties cooperatively. Properties that disagree across members typically fall back to a "mixed" sentinel.

Watch out for

  • Multi-edit semantics. When a property differs across selection, show the disagreement; don’t silently default to the first value. This is a common source of edit bugs.

  • Bulk command dispatch. Commands that mutate must be idempotent across members or wrapped in a single undo unit.


4. Factory method — Make everywhere

Form in Flux

Every non-trivial top-level model is constructed via a static Make factory that returns EResult. The constructor itself is private and assumes valid inputs.

Canonical examples

  • BendTech.Make (Part, BendMachine, EDoFlags = 0) → EResult.

  • Machine.Get (int id) → Machine (cached factory).

  • BendMachine.Get (string sid) → BendMachine.

Watch out for

  • The Make is the validation gate. Don’t add bypass paths that construct directly.

  • The constructor often contains substantial logic (BendTech constructor is 250 lines). Read it as the spec for the object’s invariants.


5. Cascaded dictionary — the Sheaf

Form in Flux

A custom pattern: each settings holder (ISheafSite) has a local Dictionary<SH, object> and a parent. Reads walk up the chain.

Canonical example

Where: Src\Core\Settings\Sheaf.cs.

public interface ISheafSite {
   string SHName { get; }
   Dictionary<SH, object> SHDict { get; set; }
   ISheafSite SHParent { get; }
}

A read for SH.RamVBend on a BendOp walks: BendOp.SHDict → BendTech.SHDict → BendMachine.SHDict → defaults.

Watch out for

  • Where you write matters. Setting a value at the op level shadows the tech and machine values. Setting at the machine level affects every tech that uses that machine.

  • The SH enum is a closed registry. Adding a setting means extending the enum and (often) adding metadata.


6. Service locator — Hub

Form in Flux

Static class Hub exposes named delegate slots. The App layer fills them at startup. Core/UI code invokes them with null-safe calls.

Canonical example

Where: Src\Core\Hub.cs.

public static Func<string, string, IList<string>, int, bool, object, int> BigChooser;
public static Action CloseBlankMainWindow;
public static Func<string, bool, int> ClosePartByJobID;

Usage: Hub.ClosePartByJobID?.Invoke (jobId, force).

Watch out for

  • Always null-safe. A null slot is a valid headless state (Engine flavor).

  • Don’t add slots casually. The Hub is meant for genuine cross-layer plumbing, not as an alternative to passing arguments.

  • The pattern was introduced as a replacement for the older "Hub whiteboard" anti-pattern (see Notes\Dev\1002.BuildingFlux.ftx lines 31—​61). Don’t regress.


7. Observer — INotifyPropChanged + undo

Form in Flux

Flux has its own INotifyPropChanged (note the abbreviated name). Property setters typically route through BA.Set<T>(ref field, value) which performs three things at once:

  1. Equality check (skip notification if unchanged).

  2. Notification to subscribers.

  3. Undo registration.

Canonical example

Throughout the domain code, e.g.:

public ISheafSite SHParent => Owner;
public bool BGAdjusted {
   get => Get (EFlags.BGAdjusted);
   set => Set (EFlags.BGAdjusted, value);
}

Watch out for

  • A direct field assignment (mField = value) bypasses notification and undo. It’s a smell unless explicitly justified.

  • The Set patterns differ between flag bits (Set (EFlags.X, value)) and full-typed fields (BA.Set (ref _field, value)). Match the surrounding code.


8. Template method — VModel.Draw

Form in Flux

The base class VModel defines the lifecycle (construction, going live, draw, detach). Subclasses override one method: Draw().

Canonical example

Where: Src\Base\Lux\VModel.cs (base) and any subclass like BendMountVM (Src\Core\Bend\BendVM.cs).

The override emits scene-graph nodes via Lux.DrawMesh, sets Lux.Layer and Lux.Mood, and uses using blocks for shading scopes.

Watch out for

  • All draw work must be deterministic and side-effect-free apart from the scene graph.

  • Don’t call back into model mutation from Draw() — that’s a re-entrancy bug waiting to happen.


9. Migration via [PostLoad]

Form in Flux

A method annotated [PostLoad] runs after Au-deserialization. It uses AuType.ObjVersion to detect older shapes and migrate them forward.

Canonical example

BendSetup.cs lines 64—​76 (see chapter 04 §9.5).

Watch out for

  • Migrations are forward-only.

  • Always check AuType.ObjVersion < N (not ==) — a file that sat through several versions still gets correctly upgraded.

  • After migrating, the in-memory object should be indistinguishable from one created at version N.


10. Result / status enums instead of exceptions

Form in Flux

Predictable failure modes return an EResult rather than throw. Exceptions (CodingError) are reserved for invariant violations.

Canonical examples

  • BendTech.Make (...) → EResult.

  • Part.DoFoldTo3D () → EResult.

  • Tests assert .Is (EResult.OK).

Watch out for

  • Adding a new failure mode? Audit switch/Is callers — the enum is exhaustively used.

  • Don’t conflate user-error and coding-error in the same enum.


11. Visitor (light) — model-walking helpers

Form in Flux

There is no formal IVisitor interface, but model walks are concentrated in Model3 (Src\Core\Model\Model3.cs, 71 KB) and similar containers. The pattern is iteration with type filters:

foreach (var flex in model.Entities.OfType<E3Flex> ()) { ... }

Watch out for

  • Reflection-flavored OfType<T> is fine for clarity; use index lookups (Entities[i] is T) when performance matters.


12. Memo — capture-and-restore

Form in Flux

When a tech is replaced, the previous state can be captured into a memo to allow recovery. Pattern: *Memo types adjacent to *Tech.

Canonical example

  • BendTechMemo (Src\Core\Bend\Ops\BendTechMemo.cs) constructed when Part.BendTech is replaced.

Watch out for

  • A memo holds references; consider whether it should be a deep copy or a reference snapshot.


13. Singletons by convention

Form in Flux

Static instances named .It (e.g. StdBendCalc.It). This is a convention, not a runtime-enforced singleton — but every call site uses it.

Canonical example

public static readonly BToolPieceDescending It = new ();

Watch out for

  • Don’t add per-instance state to a .It type.


14. The big anti-patterns (here be dragons)

14.1 The Hub whiteboard (legacy)

The previous incarnation of Hub was an object-typed dictionary used to pass arbitrary state across layers. Replaced by typed delegate slots — see §6. Any code resembling the old form is a refactor target.

14.2 Two crash engines

BendOpCrash.cs and NBendCrash.cs co-exist (chapter 02 §7.1). Treat any change to collision logic as a coordinated change to both, or you risk hidden divergence.

14.3 Constructors with substantial logic

The BendTech constructor is ~250 lines. This is intentional — the type’s invariants are enforced once, in one place — but it is unusual. Don’t refactor toward small constructors unless you have a good reason.

14.4 partial classes spanning multiple files

BendOp and others are split across files via partial. A Grep for a method may not find it in the file you’d guess. When editing, search the whole solution.

14.5 Static state that hides re-entrancy bugs

Part.Current, Part.Active, BendMachine.Default, the Hub slots — all static. A subtle bug class is "during a long operation, the user changes Part.Current". The auto-tooler guards against this with the Part.EFlags.AbortTooling check; other places may not.

14.6 Direct field assignment

A pure mField = value instead of BA.Set skips notification and undo. Sometimes intentional (private fixup), often a bug.


15. How to spot a pattern in unfamiliar code

A short checklist when you encounter a class for the first time:

  • Does it implement ISheafSite? → it owns settings; reads cascade.

  • Does it implement INotifyPropChanged? → its property setters fire change events.

  • Are there *VM siblings? → the rendering lives next to the model.

  • Is there a Make factory? → constructor is private; use the factory.

  • Is there a Dump (TextWriter) method? → there is a golden-file test that compares against it.

  • Is there a [PostLoad] method? → there are persisted older versions in the wild.

  • Does it have a .It static? → it’s a stateless helper / strategy.


16. Self-check

  1. What pattern is BendOpsGlob an instance of? (§3)

  2. Why is Hub not a "service locator" in the textbook sense? (§6 — typed slots, null-safe, App-driven)

  3. What does BA.Set do that a plain field assignment doesn’t? (§7)

  4. Where is the [PostLoad] migration of BendSetup? (§9 + chapter 04 §9.5)

  5. Why are some constructors very long in Flux? (§14.3)

  6. What’s the canonical way to choose between StdBendCalc and TBSBendCalc? (§2 + chapter 02 §7.5)


17. If you take just one thing from this chapter

Patterns in Flux are pragmatic, not idealized. The Sheaf cascade, the Make factory, the Hub slots, and the BA.Set helper are the four most pervasive idioms. Recognize them, and ~80% of unfamiliar code becomes immediately readable.


Last updated: 2026-05-04. Pattern attributions verified against Src\Core\Bend\BendCalc.cs, Src\Core\Hub.cs, Src\Core\Settings\Sheaf.cs, Src\Base\Lux\VModel.cs, Src\UI\Bend\BendOpUI.cs, and Notes\Dev\1002.BuildingFlux.ftx.

Contents

  • 1. How to read this chapter
  • 2. Strategy — selectable algorithm behind an interface
  • Form in Flux
  • Canonical example: IBendCalc
  • Watch out for
  • Other examples
  • 3. Composite — uniform handling of single + group objects
  • Form in Flux
  • Canonical example
  • Watch out for
  • 4. Factory method — Make everywhere
  • Form in Flux
  • Canonical examples
  • Watch out for
  • 5. Cascaded dictionary — the Sheaf
  • Form in Flux
  • Canonical example
  • Watch out for
  • 6. Service locator — Hub
  • Form in Flux
  • Canonical example
  • Watch out for
  • 7. Observer — INotifyPropChanged + undo
  • Form in Flux
  • Canonical example
  • Watch out for
  • 8. Template method — VModel.Draw
  • Form in Flux
  • Canonical example
  • Watch out for
  • 9. Migration via [PostLoad]
  • Form in Flux
  • Canonical example
  • Watch out for
  • 10. Result / status enums instead of exceptions
  • Form in Flux
  • Canonical examples
  • Watch out for
  • 11. Visitor (light) — model-walking helpers
  • Form in Flux
  • Watch out for
  • 12. Memo — capture-and-restore
  • Form in Flux
  • Canonical example
  • Watch out for
  • 13. Singletons by convention
  • Form in Flux
  • Canonical example
  • Watch out for
  • 14. The big anti-patterns (here be dragons)
  • 14.1 The Hub whiteboard (legacy)
  • 14.2 Two crash engines
  • 14.3 Constructors with substantial logic
  • 14.4 partial classes spanning multiple files
  • 14.5 Static state that hides re-entrancy bugs
  • 14.6 Direct field assignment
  • 15. How to spot a pattern in unfamiliar code
  • 16. Self-check
  • 17. If you take just one thing from this chapter
Data Model Catalog Tests and Golden Paths

      Copyright © 2025 TRUMPF Metamation. All rights reserved.

    Compiled by Bento