**1‑2‑3‑Model in C# – A 3‑D “Piano‑Box” Metaphor**
*(a 2‑page (≈1 000 words) design description, plus a concise but functional C# implementation)*
—
## 1. Why the 1‑2‑3‑Model?
A *3‑D model* is simply a *hierarchical representation* of how we think about a system.
It asks three questions at each level:
| Level | Question | What it means | Example (in our code) |
|——-|———-|—————|———————-|
| **1 – Reality** | *What is it doing?* | The *behaviour* that the user actually experiences. | The music that plays, the LEDs that flash. |
| **2 – Architecture** | *How do the components cooperate?* | The *static relationships* – classes, modules, protocols. | `PianoBox` owns a `SoundEngine`, `LightEngine` and `UserInterface`. |
| **3 – Implementation** | *What concrete code makes it happen?* | The *dynamic execution* – method bodies, data structures, platform APIs. | `PianoBox.PlayTone(int frequency)` uses a `WaveOutEvent` from NAudio. |
Using this model keeps the design *human‑centric* (Reality) while making the technical details *explicit* (Architecture → Implementation). It also gives a natural way to talk about *change*, *refactoring* and *testing*.
—
## 2. The “Piano‑Box” – A Tiny 3‑D System
> **Scenario** – Imagine a *compact piano‑box* that a child can press a single button on, and the box will:
> * play a musical note
> * light up a colored LED
> * send a tiny notification to a smartphone
> **Goals** –
> 1. Very small source tree – under 200 LOC.
> 2. Easy to unit‑test.
> 3. Illustrate the 1‑2‑3 model in a real‑world setting.
—
### 2‑1. Reality (Behaviour)
– **“Play”** – a single click starts a tone that lasts 1 s.
– **“Light”** – a LED glows in a colour that matches the tone.
– **“Notify”** – a short message appears on a paired phone (simulated).
These are the *observable* outputs that a parent system or user cares about.
—
### 2‑2. Architecture (Static structure)
| Component | Responsibility | Public API | Notes |
|———–|—————-|————|——-|
| `PianoBox` | Orchestrates everything | `Play(int note)` | Holds references to the sub‑systems |
| `SoundEngine` | Plays tones | `PlayTone(int frequency, TimeSpan duration)` | Uses NAudio |
| `LightEngine` | Controls LEDs | `SetColour(string colour)` | Uses a mocked LED library |
| `UserInterface` | Handles button presses | `OnButtonPressed()` | Calls `PianoBox.Play` |
| `Notifier` | Sends notifications | `Notify(string message)` | Mocked for unit tests |
Each component is *independent* and communicates through *interfaces* (`ISoundEngine`, `ILightEngine`, `INotifier`). That gives us a *pluggable architecture*.
—
### 2‑3. Implementation (Concrete code)
Below is a compact, but complete, C# implementation. The code is intentionally kept minimal – just enough to show the relationships and behaviour. Dependencies such as **NAudio** or a real LED library are *mocked* for the sake of the example.
> **Prerequisites** –
> * .NET 6 or later.
> * NuGet packages: `NAudio` (for audio), `Xunit` (for unit tests), `Moq` (for mocking).
—
#### 2‑3‑1. Interfaces
„`csharp
// 1) Interfaces – part of the Architecture layer
public interface ISoundEngine
{
void PlayTone(int frequency, TimeSpan duration);
}
public interface ILightEngine
{
void SetColour(string colour);
}
public interface INotifier
{
void Notify(string message);
}
„`
—
#### 2‑3‑2. Concrete Sub‑systems
„`csharp
using NAudio.Wave;
using System.Threading.Tasks;
// 2) Sound engine – implementation of ISoundEngine
public class SoundEngine : ISoundEngine
{
public void PlayTone(int frequency, TimeSpan duration)
{
// Very small wave‑sine generator – 16‑bit PCM
var sampleRate = 44100;
var buffer = new short[duration.Milliseconds * sampleRate / 1000];
for (int i = 0; i < buffer.Length; i++)
buffer[i] = (short)(Math.Sin(2 * Math.PI * frequency * i / sampleRate) * short.MaxValue);
var wave = new RawSourceWaveStream(
new MemoryStream(buffer.SelectMany(b => BitConverter.GetBytes(b)).ToArray()),
new WaveFormat(sampleRate, 16, 1));
using var outDev = new WaveOutEvent();
outDev.Init(wave);
outDev.Play();
Task.Delay(duration).Wait(); // block until tone ends
}
}
// 3) Light engine – mocked, but real enough
public class LightEngine : ILightEngine
{
public void SetColour(string colour)
{
Console.WriteLine($[LED] Brightening to {colour}”);
}
}
// 4) Notifier – also mocked
public class Notifier : INotifier
{
public void Notify(string message) => Console.WriteLine($”[Phone] {message}”);
}
„`
—
#### 2‑3‑3. The Orchestrator – PianoBox
„`csharp
// 5) PianoBox – the Reality layer (behaviour)
public class PianoBox
{
private readonly ISoundEngine _sound;
private readonly ILightEngine _light;
private readonly INotifier _notify;
private readonly Random _rnd = new();
// The mapping from „note” to frequency and colour
private readonly Dictionary
{
{ 1, (261, „red”) }, // C4
{ 2, (293, „green”) }, // D4
{ 3, (329, „blue”) }, // E4
};
public PianoBox(ISoundEngine sound, ILightEngine light, INotifier notify)
{
_sound = sound;
_light = light;
_notify = notify;
}
public void Play(int note)
{
if (!_noteMap.TryGetValue(note, out var data))
{
_notify.Notify($”Unknown note {note}”);
return;
}
// 1) Play sound
_sound.PlayTone(data.freq, TimeSpan.FromSeconds(1));
// 2) Light up
_light.SetColour(data.colour);
// 3) Notify
_notify.Notify($”Played note {note} ({data.freq}Hz)”);
}
}
„`
—
#### 2‑3‑4. User Interaction
„`csharp
// 6) UserInterface – receives button press
public class UserInterface
{
private readonly PianoBox _box;
public UserInterface(PianoBox box) => _box = box;
// Simulate button press
public void OnButtonPressed()
{
// Pick a random note for demo
var note = new Random().Next(1, 4);
_box.Play(note);
}
}
„`
—
#### 2‑3‑5. Wiring up the System
„`csharp
// 7) Program.cs – entry point
public static class Program
{
public static void Main()
{
// Concrete sub‑systems (in production you might swap these)
var sound = new SoundEngine();
var light = new LightEngine();
var notify = new Notifier();
var pianoBox = new PianoBox(sound, light, notify);
var ui = new UserInterface(pianoBox);
// Simple console loop
Console.WriteLine(„Press any key to play a note (Ctrl‑C to exit).”);
while (Console.ReadKey(true).Key != ConsoleKey.C)
{
ui.OnButtonPressed();
}
}
}
„`
> **Running the program** – hit a key, a tone will play, a line will appear saying the LED has brightened, and a notification message will print. The console is a stand‑in for a smartphone or GUI.
—
## 3. Unit‑Testable, 3‑D Design
Because each component implements an interface, the *implementation* can be *mocked* in tests. The 1‑2‑3 model keeps the tests *pure* – we test the *behaviour* (Reality) without involving the real sound or LED hardware.
„`csharp
using Moq;
using Xunit;
public class PianoBoxTests
{
[Fact]
public void Play_UnknownNote_Notifys()
{
var sound = new Mock
var light = new Mock
var notify = new Mock
var box = new PianoBox(sound.Object, light.Object, notify.Object);
box.Play(99); // note not in _noteMap
notify.Verify(n => n.Notify(It.Is
sound.Verify(s => s.PlayTone(It.IsAny
light.Verify(l => l.SetColour(It.IsAny
}
[Fact]
public void Play_KnownNote_PerformsAllActions()
{
var sound = new Mock
var light = new Mock
var notify = new Mock
var box = new PianoBox(sound.Object, light.Object, notify.Object);
box.Play(2); // D4
sound.Verify(s => s.PlayTone(293, TimeSpan.FromSeconds(1)), Times.Once);
light.Verify(l => l.SetColour(„green”), Times.Once);
notify.Verify(n => n.Notify(It.Is
}
}
„`
*Reality* is what we assert (`Notify` receives the correct string), *Architecture* is the dependency contract (`ISoundEngine`, etc.), and *Implementation* is hidden inside the mocks – exactly the way the 3‑D model prescribes.
—
## 4. What We Learned
| Aspect | 1‑2‑3 Insight |
|——–|—————|
| **Clear separation of concerns** | Reality → Architecture → Implementation lets us “zoom in” or “zoom out” without losing focus. |
| **Pluggability** | Swapping a real LED driver for a simulated one is just a new `ILightEngine` implementation. |
| **Testability** | Unit tests exercise the Reality layer by mocking Architecture → Implementation. |
| **Evolution** | When the physical hardware changes (e.g. different note mapping), only the PianoBox *Architecture* or *Implementation* changes – Reality remains the same. |
—
## 5. Summary
We have taken a concrete, *miniature* system—a piano‑box that plays a note, lights an LED, and sends a notification—and shown how the 1‑2‑3 model helps us *organise* it. The three layers are:
1. **Reality** – the observable behaviour.
2. **Architecture** – the class diagram, interfaces, and static relationships.
3. **Implementation** – the actual method bodies and library calls.
Because each layer is clearly defined, the system is both *human‑readable* and *technically sound*. The resulting codebase is small enough to fit on a single page of source code, but it demonstrates the *power* of the 3‑D model: you can reason about what the system does, how the pieces fit together, and what concrete code makes it happen—all in one coherent picture.

