Model-View-Controller
Separate what an application knows (Model) from how it is shown (View) from how input is handled (Controller), so a change to the display never has to touch business logic.
The problem
A thermostat widget that reads input, applies temperature rules, and draws pixels - all in one class - works fine until any one concern needs to change independently of the others. Add a phone-app view, and it duplicates the range-clamping logic. Change the clamp range, and you have to remember every place that copied it, including the code that drew the screen.
The tell is a class that has to change for unrelated reasons: a new UI framework, a new temperature rule, a new physical button layout. Each of those is legitimate; none of them should require touching the same file as the other two.
The solution
Split the class into three roles with one job each. The Model owns state and the rules for changing it, and answers to nobody about how it is displayed. The View reads the Model and renders it, and never writes to it. The Controller is the seam between the outside world and the Model: it takes a raw input event and turns it into a call the Model understands, then lets the View know to refresh.
The View commonly listens for Model changes (often via Observer) so it redraws itself rather than being told to by the Controller directly - either wiring keeps the same separation.
- 1Someone taps the "warmer" button. The button knows only that it should call the controller - nothing about temperatures.
- 2The controller translates a raw input event into a domain operation on the model.
- 3The model enforces its own rules (say, a 10-32C range) - the controller never duplicates that logic.
- 4The model announces that its state changed, without knowing or caring who is listening.
- 5The view pulls the fresh values it needs directly from the model.
- 6The view redraws using only what it just read. It never mutates the model.
- 7A "cooler" press arrives later. Same controller method, same path, opposite delta.
- 8Business rules and validation live in exactly one place regardless of which button triggered them.
Structure
Notice ThermostatModel has no arrow pointing outward in this diagram - nothing points from
Model to Controller or View. Everything flows toward the Model, never away from it.
Code
Same example three ways: a physical warmer/cooler button driving a thermostat.
When to use it
- The same underlying state needs more than one presentation (a physical display and a phone app, a web page and an API response) and duplicating rules across them is already a problem.
- Input handling, business rules, and rendering currently live in one class and change for three different reasons, tripping over each other in code review.
Pitfalls
- The fat controller. Business rules that migrate into the Controller "because that's where the input arrives" recreate the exact coupling MVC exists to remove. Rules belong in the Model; the Controller only translates.
- The talkative view. A View that reaches back and mutates the Model directly (beyond simple, well-understood bindings) blurs the Controller's role and makes the write path hard to trace.
- Assuming one true MVC. Web MVC, desktop MVC, and the original Smalltalk MVC all draw the lines slightly differently (who owns the Observer wiring, whether the View can write). Match the flavor your framework actually uses instead of arguing textbook purity.
Don't confuse it with
- Observer. Observer is a plausible implementation detail inside MVC (the View subscribing to the Model), not a substitute for it. MVC names three roles at a coarser grain than any single GoF pattern operates at.
- Mediator. A Controller can resemble a Mediator hub, but Mediator's job is eliminating mutual dependencies among peer components in general; a Controller has one specific job - turning input into Model operations - and the Model and View are not peers negotiating with each other through it.
- MVP and MVVM. Both are later variations that move where the "glue" logic lives (Presenter, ViewModel) and change exactly how much the View is allowed to know. They share MVC's core idea - separate state, display, and input-handling - with different wiring.
Check yourself
Which role is allowed to know about both of the other two?