Clean Fracanto

Clean Fracanto #3 — DRY

DRY — One Pattern, Five Abstraction Levels

One Pattern, Five Abstraction Levels

Don't Repeat Yourself — every piece of knowledge shall have a single unambiguous representation within the system.

The vtable pattern in fracanto is itself an example of consistent DRY: The same mechanism — a struct of function pointers, an instance with const ops_t *ops and void *ctx, inline wrappers with NULL checks — is reused across five abstraction levels:

  1. HAL level: fracanto_hal_ops_t abstracts CAN hardware.
  2. Panel level: panel_ops_t abstracts physical controls.
  3. IO level: io_ops_t abstracts analog I/O.
  4. Audio level: audio_ops_t abstracts DMA-based audio streaming.
  5. Module level: module_ops_t abstracts the module logic itself.

The pattern need not be reinvented for each use case. Anyone who understands fracanto_hal_ops_t also understands panel_ops_t, io_ops_t, and all other vtables — the structure is identical, only the content of the function pointers differs.

The Dependency Injection in the drivers also follows DRY: All five peripheral drivers (ADS8866, DAC8552, PCM5102A, Encoder, SSD1306) use the same schema — an _hw_t struct with function pointers and void *hw_ctx, passed during initialization and copied internally. Knowing one driver means knowing the pattern of all drivers.

Type erasure through void *ctx is the DRY enabler: Without this mechanism, each abstraction level would need to invent its own polymorphism mechanism. Instead, all use the same one — function pointers plus opaque context — and differ only in the semantics of their operations.