That post was very well received, with a few exceptions. One of the critics said something like “I’ll keep calling my controllers controllers, and my presenters presenters”. This is fine, of course. If you have a controller, why not calling it “controller”? Or, as Shakespeare said, a controller by any other name would smell as bad :-) . The entire idea was to get rid of controllers, not changing their name!
Now, getting rid of controllers is damn hard in 2012. Almost every UI framework is based on some variation of MVC. Most server-side web-app frameworks are based on MVC as well. Proposing to drop the controller would be just as useful as proposing that we drop html and javascript and start using something serious to write web apps :-).
Still, I can’t really sit here and do nothing while innocent objects get slaughtered, so I decided to write a few posts on living without a controller. I’ll start far away from UI, to avoid rocking the boat. I’ll move closer in some future post.
A well-known problem
I could easily come up with some problem, cleverly designed to prove my point, but I won’t. Instead, I’ll adopt a well-known problem, dating back to 1983, which has been used in numerous articles and books about process control, industrial automation, etc: the mine pump problem (a couple of references: A Framework for Building Dependable Systems by Burns and Lister, and Real-time Systems Specification, Verification and Analysis, edited by Mathai Joseph).
The overall problem is represented here:
Fig.1 |
We want to pump water out of a mine sump. We have two water level sensors (D, E). When D goes on, we pump out water until E goes off (this is to realize a form of hysteresis and avoid “bouncing” around a given level).
There are also a few gas sensors for carbon monoxide, methane and airflow levels (A,B,C). If any of those becomes critical, an alarm must be raised.
Finally, to prevent explosions, the pump must not be operated when methane is above a certain level.
Existing literature is mostly concerned with specification, timing and modeling issues. Software design issues are largely ignored, and Fig.1 already seems to suggest a centralized controller style.
I’m mostly interested in exploring design choices, so I’ll forego timing issues (which ain’t really that hard for this problem, anyway). I’ll try to evolve a design starting from the most primitive style, and highlight what needs to be done to break out of a centralized controller style.
If you got some time, you may want to explore a few design alternatives on your own: there are many sensible (and less sensible :-) alternatives to what I’m going to present here.
The control engineer way
(Apologies to control engineers, I’m mostly thinking of a friend of mine here).
Control engineers will follow a simple, 4-step, repeatable process for system design:
- write 10 pages of differential equations to prove that if water is coming in faster than you can get it out, you’ll drown.
- assume that once you got your equations right, it’s just a matter of mapping some inputs (bits) to outputs (bits).
- start with a simple MainLoop function, reading bits, going through a sequence of conditionals and switch cases, adding state variables as needed (hysteresis requires a simple state machine), until you can write bits.
- add a Kalman filter at some point. If you ain’t gonna use a Kalman filter, you can’t call yourself a control engineer.
More seriously, what’s really common about control engineers is to spend a lot of time modeling the function and then proceed in total disregard of shape*. Therefore, we usually get a monolithic function. Sometimes, a few functional abstractions are indeed identified and extracted, but more often than not, you simply get a long sequence of statements (sometimes justified by “performance”, usually without any data to back up that claim).
[*I’m moving from using “form” to using “shape” because “form” is often confused with “ceremony”, which is definitely not what I mean.]
Don’t get me wrong. There is nothing intrinsically wrong with the monolithic function. It works. We just get into troubles when we expect some non-functional properties which are at odd with that shape.
Indeed, many cultural antipatterns I’ve observed in the industrial automation field can be mapped directly to the mismatch between monolithic software and some desirable non-functional properties.
Consider testability: if your entire process is implemented as one large function, it will be hard to test thoroughly. Antipattern: claim that faults can only be found in the field and neglect testing. You may recognize this as a self-fulfilling expectation: if you don’t test because faults can only be found in the field, you’ll only find them on the field, thereby reinforcing your belief.
The fake-OO way, step 1
There is a wide spectrum from the monolith to what I’m discussing here, including some entirely functional or procedural solutions, but my destination is a true OO design, so let’s take a first step toward objects.
At some point, people get usually tempted by a few low-hanging fruits and introduce some abstractions:
- the DigitalInput, to read from the water sensors
- the AnalogInput, to read from the gas sensors
- the DigitalOutput, to control the pump and the alarm
In hubris, they may even “hide” those abstractions behind an interface, because (and that’s right) it may help to abstract away a few hw/sw details.
In the end, you get something like this (ah, the beauty of a picture, conveying the entire structure with a few strokes; how far is the day when the agile police will allow picture-based conversations again?)
Fig.2 |
Of course, we still have the dreaded controller: all the logic is sitting there. Still, there has been a slight fluctuation in the non-functional properties: now we don’t really care about the details of (e.g.) a digital input. Is it being mapped into a register? Is it coming from some kind of serial bus? Who cares (to some extent; the devil is in the details).
If we take the extra step and introduce interfaces, we actually gain something on the testability side as well: we can then mock I/O (depending on where/how creation is actually implemented).
Those of you who are [still] looking at design from the “principles” perspective will see this tiny step as perhaps inspired by the Single Responsibility Principle and by the Open/Closed Principle. Maintainability, however, is still pretty low. We have one, big gravitational center, and that’s where we’re gonna spend most of our time adding, removing, and tweaking functionality. Reusability is also limited to the I/O. Everything else is copy / paste / tweak.
The fake-OO way, step 2
At this stage, people realize that OO is about “modeling the world”, and perhaps parroting the Domain Driven Design guys, start to babble about the ubiquitous language and introduce a few more abstractions:
- various type of sensors
- the pump
- the alarm
Fig.3 |
This shape is not wrong, of course. Actually, it’s ok, sort of. What is wrong, at this stage, is usually how responsibilities are distributed (or not, meaning: centralized). When you have a controller in your mind, it’s all too easy to end up with stupid classes, because stupid are easier to control.
As you can see, the responsibilities in layer 2 (sensors and actuators) have a 1:1 mapping with responsibilities in layer 1 (I/O). Again, this is not necessarily wrong: layer 2 is closer to problem domain. However, layer2 does precious nothing as far as the controller is concerned.
The entire logic is still in the controller. Writing pump.On() instead of pumpDigitalOutput.Write(1)is an improvement, but it’s not changing the overall design (architecture).
The path not taken
It is perhaps tempting, given the similarities among concrete Sensors (and between Pump and Alarm), to introduce yet another abstraction layer (the Sensor/Actuator as hinted above). Again, this is not wrong, but you shouldn’t take this path too early.
Object oriented design is not about building a deep chain of domain-inspired “abstractions” with little or no behavior. It’s about decomposing the problem so that the intended function emerges from the interaction between intelligent, specialized mini-machines (objects, as seen from Alan Kay).
Adding layers for mere classification is not a terribly useful step, although I understand the intellectual satisfaction :-) in doing that.
Toward "real" OO
A key realization is that to move toward a different set of non-functional properties we must find better abstractions. Those abstractions will eat away the controller, until in the end there will be no controller left.
There is not a single, deterministic process to find those abstractions. We may end up there through domain knowledge, through experience in asking the right questions, through commonality analysis of past implementations, through sheer brightness, and so on. I’m a bit skeptical about the possibility of finding all those abstractions through test-driven design, but some may definitely emerge that way as well. I don’t want to suggest that a specific design process is superior to another, so I’d rather move on and introduce the first abstraction.
The sump probe
We can blame the specification, so to speak. As it usually happens, it does not describe the real problem. It’s more about a specific incarnation of the problem, or about the invariably biased perception of whoever is describing the problem (which is why we used to consider analysis important ;-). The presence in Fig.1 of two stylized sensors was enough to bring into the software counterpart the digital input, and then a level sensor (which is actually a level switch, hence digital).
What if we change sensor technology, and we bring in a submersible level probe, which has an analog output and can tell the actual depth? Well, what we need is to:
- drop the level sensor (ok)
- add a LevelProbe, connected to an AnalogInput (ok)
- tweak the Pump Controller; remove the switch-based level detection logic, replace it to deal with analog measurement. Maybe tweak the state machine too. (not ok).
An alternative would be to introduce, from the very beginning, the concept of SumpProbe. Its sole responsibility is to know when it’s time to drain the sump.
Fig.4 |
In an early implementation, the SumpProbe could be based on Level Sensors. We don’t need an interface yet; we’re just moving some logic outside the controller and into SumpProbe: generalization can wait. In a LevelSensor-based implementation, SumpProbe will sample two sensors and implement the hysteresis state machine.
Now, this change may seem like nothing, but it’s actually a pivotal moment. For the first time so far, we have a class that is actually doing something. The SumpProbe is not exposing data. It’s exposing a service.
If we want to use a LevelProbe in place of a LevelSensor, we have to change the SumpProbe, but the PumpController won’t need to change, because that logic is no longer there. Of course, now polymorphism can easily kick in, and we can turn SumpProbe into an interface with two concrete implementations, one based on LevelSensor and another on LevelProbe. In a LevelProbe-based implementation, SumpProbe will poll just one sensor. It may or may not be worth sharing the state machine between the two implementations. That’s truly a minor point. I won’t show the polymorphic version in the diagram, keeping only the main structure in place (as my goal is to get rid of the controller, and I want to focus on that).
Interestingly, the SumpProbe, in its LevelSensor-based implementation, would also be the perfect place to add some fault-detection code. If the higher sensor is triggered, the lower sensor must be triggered. That’s a nice invariant for the LevelSensor-based SumpProbe class, which reminds me I seldom talk about Design by Contract, so here is another hint that you’re on the right path: you can actually think of an invariant for your class.
Stupid classes have empty invariant; controller classes have unfathomable invariant (which adds to the difficulty of testing).
A useful Gas Sensor
I warned against introducing a Sensor abstraction before. I want to stress that point a little: the wrong abstraction can actually lead you the wrong way. Now, what is a meaningful, little responsibility for a gas sensor (as opposed to a generic sensor)?
Of course (?!), it’s telling you when a critical level has been reached. The beauty of this responsibility is that, for instance, a Methane or CO Sensor will be triggered by an upper bound, while AirFlow by a lower bound, but that's invisible from the outside. A different type of sensor may be triggered by a window, or by any given law.
Note: the actual beauty is not really in respecting the Open/Closed Principle. It’s about having found a natural place for knowledge, more specifically, knowledge about gas type. GasSensor is an excellent gravitational center for highly cohesive knowledge.
This is a first-cut diagram: again, we’re eating a small portion out of PumpController (checking thresholds). It’s ok to eat a small bit: it means we’re creating small abstractions.
Fig.5 |
As simple as it is, this change may generate some discomfort. I am making the sensors “less reusable”, because some domain (hence, application) knowledge has got into it. For instance, a CO sensor may need to operate on a lower bound, instead of an upper bound, in an entirely different problem.
Here is where the designer needs to understand what he’s doing, and avoid being a blind follower of some principles or prescriptions:
- Reusability is just one of the many forces we have to consider.
- The new sensors are perhaps less reusable, but they’re more useful. An empty class is absolutely reusable, but also completely useless.
- Before you discard the smarter sensor, consider making it even smarter, and maybe get a smaller codebase in exchange. If we implement just one super-policy (the high/low window) everything else is just a matter of configuration (a threshold being a special case of a window).
Real OO
Ok, so those were still pretty low hanging fruits. The pump controller is still sitting there, asking sensors, deciding whether or not it’s safe to operate the pump, and whether or not to trigger the alarm.
To the experienced designer, these two responsibilities will eventually suggest two new abstractions.
The Safe Engine
I’ll rename Pump into PumpEngine, because that’s what we’re really operating. Now, the problem with the “standard” engine is that it’s dumb: it doesn’t know whether or not it’s safe to turn on. Well, that’s a SafeEngine job:
Fig.6 |
I’ve brought in multiplicities (which I’ve left at the implicit level so far) just to point out how easy it would be to make the SafeEngine watch more than one sensor. Just use a list, or perhaps a composite. Once you have the right structure in place, things get simpler, not harder.
We may also want to consider the option to make the PumpController working polymorphically on PumpEngine. I’ll ignore this issue, as this is already a long post.
The Gas Alarm
It may follow with ease now that we also want to trigger an alarm whenever a Gas Sensor triggers a critical condition.
Fig.7 |
You can see similarities with PumpEngine becoming smarter, except that:
- Here I’m going for 0..n multiplicity from the very beginning. The composite would still be an alternative.
- GasAlarm has-a, not is-a, Alarm. That’s because a GasAlarm is entirely autonomous, with a Watch() responsibility which may actually be run in its own thread (I’m ignoring all the threading and timing issues here, much to the scorn of the existing Mine Pump literature :-).
The End (sort of)
What is left in the controller now? Three distinct responsibilities:
- Creation and configuration of all the other objects
- Watching the SumpProbe and activating the SafePump when MustDrain (now literally a one liner :-)
- Defining the threading model (I’ll ignore this; trust me, it’s a rather marginal issue now).
The second responsibility can be easily factored out into a SumpPump class. After all, it’s all about draining the sump.
I would leave the first to a MinePlant class, which is no longer a controller, but just a builder (in the pattern sense). If you’re in the IoC camp, the MinePlant is particularly trivial to write.
Here is a final diagram. I’m still neglecting a few interfaces which could be useful for mocking and extendibility (like SumpProbe, still a concrete class here), and I’m also leaving some multiplicities implicit, both for sake of space and terseness.
Fig.8 |
Another path not taken
Some of you may recognize the SumpPump / SafeEngine chain as an "and" chain: we pump when we must drain and it’s safe to do so. As usual, if you work with this kind of systems on a regular basis, it could be tempting to generalize on that and build some kind of rule-based infrastructure.
Again, it is not wrong to do so, but always remember the difference between a domain-based abstraction and a math-based abstraction. In my experience (and as I told before, I used to love math-based abstractions) the former is more easily understood and adopted, the latter is often misunderstood and abused. It’s not a general rule or “principle”. Just experience.
The inevitable extension
Ok, so, I didn’t tell you the whole story. If you read the literature on the mine pump problem, you’ll find one extra requirement: “if, due to a failure of the pump, the water cannot be pumped out, the mine must be evacuated” (which I read as: if the water level does not fall after X minutes, the alarm must be triggered).
In a centralized controller style, we know what to do: we add more stuff to the controller. Is the shape above suggesting a natural place for this responsibility? Is it ok to put it there? What about coupling and reuse? Where would you encode that knowledge? Is it better to change a few dependencies?
Note: I’ve ignored the logging requirement as well. Logging is a cross-cutting concern in the structure above, which is kinda obvious, since I’ve been breaking apart a large class. There is little to be said/added here, if not that this is exactly why AOP should be taken more seriously.
Critics
This is just one among many shapes. I took a few design choices, and this is the final result. You may take different choices, still leading to a controller-free shape. For instance, everything above is based on a polling loop (easier to deal with in safety critical stuff), but an event-based approach is ok too, and may lead you to a different structure.
No shape is perfect. So, while I know I shouldn't be ending my post with a critic of my work, I'll ignore the good advice and do it anyway:
- Structure is more complex. I have many classes, and quite a few dependencies. It is worth noting that in practice, most of those classes would be 5 to 10 lines long. Easy to write (bug-free), easy to test, and easy to understand. But the overall shape may not be easy. Here is where I see the value of a diagram as a communication tool.
- The centralized controller is easier for the junior guy. Which is another way to say Worse is Better. Yeah, well, it’s true. Beginners are usually better at understanding complex logic than complex structure and interactions. There is also some evidence that a diagram can help there. But overall, it’s a non-trivial choice about the kind of software you want to write (for beginners or for expert programmers).
- The process is no longer “visible”. This is true, as there is no longer a central piece of software encoding the entire process. It has been scattered among cooperating objects. This is another facet of the same problem: for those who don’t get OO, this shape is harder to understand. For those who do, it provides a number of non-functional benefits.
- It’s easier to add stuff in the centralized controller. Again, this is a facet of the same problem, with a different slant. Every once in a while, someone tells me that when he’s working with a sophisticated structure, he needs to actually think before adding stuff. Where do I put this logic without breaking the conceptual integrity of the whole? When you have little or no structure to begin with, you just don’t care, and you can take the path of least resistance.
Of course we know where this leads: to the big ball of mud, the natural destiny of those who surrender to gravity. Depending on your business model and professionalism, you may still not care.
Class size
I mentioned that most (all) classes above would be 5 to 10 LOC. This is not what happens in most software, where there is a power law distribution for LOCs. Today, I got why. I’ll cover that in my next blog post.
If you liked this post, you should follow me on twitter!
There is a follow-up to this post in "Episode 2: the Controller Strikes Back"
... And another in "No Controller, Episode 3: Inglorious Objekts"
20 comments:
Un altro post straordinariamente stimolante! In particolare mi ha intrigato molto il concetto di emersione delle soluzioni dal comportamento degli oggetti. Senza andare a scomodare le tecniche di IA, reti neurali e simili, credo molto nel comportamento bionico (http://it.wikipedia.org/wiki/Bionica) del software. Il self healing dei sistemi moderni, ma anche e soprattutto la creazione di software in grado di comportarsi in modo coerente e “corretto” in casi non contemplati durante l’analisi degli use cases in virtù della buona definizione dell’astrazione dei componenti che lo compongono.
Mi piace pensare ad un sistema software composto da una moltitudine di componenti che collaborano che esibisce comportamenti (corretti) di complessità superiore alla somma dei suoi componenti. Assomiglia concettualmente all’aggregazione di cellule (semplici) che danno vita ai sistemi biologici più complessi :-)
Sin dai tempi degli articoli su Computer Programming quando ero ancora uno studente, leggere i tuoi articoli di design è sempre particolarmente intrigante.
Adesso però è ormai qualche anno che lavoro come sviluppatore nel campo delle TLC... quando si lavora in progetti grossi con moltissima gente dal livello + diverso...è purtroppo davvero difficile non arrendersi alla gravità, e probabilmente qualche anno di lavoro in Java con i vari web framework ha reso davvero difficile staccarsi dal paradigma della fake OO (qualcuno ha detto JavaBeans?)
Miki: senza arrivare a software che si autoripara (che cmq sicuramente non verrebbe molto bene con il controllo centralizzato :-), spesso ho osservato come una parte della complessita' procedurale "evapori" una volta creati i giusti oggetti e le giuste interazioni. Una domanda interessante sarebbe "e quindi dove va?" :-) e non e' neppure semplice rispondere (dire "va nelle collaborazioni" e' un po' sbrigativo :-))
lunatico: che tristezza : )))
ti capisco, comunque, ma e' un po' come dire che siccome i grandi condominii son pur sempre dei casermoni, bisogna arrendersi all'idea e rinunciare all'architettura [e all'arte].
diciamo che io non amo la resa :-)), e a volte dare il buon esempio aiuta, anche nei grandi progetti (penso ad es. ad un progetto bancario iniziato un paio di anni fa, dove alla fine abbiamo fatto un buon lavoro, creando una struttura OO "compatibile" con il team).
I wrote a comment that is a bit too long to be accepted by blogger, so I put it here.
note: the link above is broken, but there is a copy of that long comment on my website (http://www.eptacom.net/blog/pump_controller_post.zibibbo.txt)
See my post "the controller strikes back" for my answer.
Un bel design. Mi piace anche per la caratteristica che ha di far intuire la dinamica guardando la sola struttura statica (qualità cui sempre tendere secondo me).
Come dici all'inizio del tuo post l'uso del controller ricorre continuamente nelle GUI, sia web che stand-alone. Si può escludere solo qualche nicchia come quella che segue la filosofia dei "naked object" nata proprio dalla critica al concetto di controller (info).
All'interno del pattern MVC, se non sbaglio, il controller è (o vuole essere) un'implementazione di un altro pattern, il Mediator:
il suo scopo è quello di concentrare le dipendenze di interazione fra vari oggetti poiché:
1) le dipendenze sono specifiche della particolare applicazione
2) gli oggetti o widget hanno da essere generici per essere riusabili
3) customizzarli per derivazione è tedioso essendo tanti [riporto testualmente da “Design Pattern” di Gamma e colleghi!]
La 3 è certamente questionabile: specializzando i widget per derivazione si salvano la 1 e la 2, e si ottiene un design senza controller. Il controller delle GUI in effetti non ha proprio un bell'aspetto!
Nascono quindi due domande. La prima sposta la tua critica al controller sul pattern Mediator: secondo te qual è il contesto in cui il pattern ha valore e quale quello in cui non ne ha?
E ancora: secondo la tua esperienza quanto è applicabile l'approccio "senza controller" o "a dipendenza inversa" nelle GUI?
La seconda domanda potrebbe avere una risposta in condizioni ideali (che azzarderei positiva ma non posso essere certo del tuo pensiero) e una in condizioni quotidiane. Mi riferisco al fatto che quotidianamente ci si trova a dover usare per altri vincoli un framework o un ambiente che di solito sono completamente impostati sul pattern MVC. Ad esempio credo che parecchi IDE fatichino ad usare oggetti custom e comunque sono fatti per elencare una serie di eventi da far gestire al Mediator. Comunque entrambe le risposte sono per me importanti.
p.s. lo so che ti sto spingendo a scrivere la seconda parte... ;-)
[avevo gia' scritto una risposta poi una bella scossetta di terremoto ha fatto cascare le linee 3G ed ho perso tutto :-), quindi la riscrivo un po' diversa]
Michelangelo, come hai ben capito una risposta completa richiede almeno un intero post, non un commento (peraltro sarebbe ingiusto scrivere tutto questo in Italiano). Ne approfitto invece per spostare leggermente il focus su una tematica che non trovo mai il tempo di affrontare.
In genere, salvo poche eccezioni (spesso lavori d'artista) e' molto semplice distinguere un manufatto (es. un tavolo) da qualcosa di organico, "naturale" (es. un albero). Entrambi sono "fatti di legno", ma i manufatti sono influenzati sia dalla funzione, sia da un certo approccio alla costruzione degli stessi, da una forma mentis, ecc. In genere sono caratterizzati da linee piu' nette, spigoli, superfici piane, parti molto ben distinte. In natura troviamo forme frattali, autoidentiche, le parti hanno la stessa forma del tutto. Pero' e' proprio la natura frattale di un albero che gli consente di crescere (scalare :-) cambiando radicalmente la forma complessiva ma mantenendo la propria integrita' strutturale e concettuale (ovvero resta sempre un albero).
Ora, cosa succede se prendi il classico MVC (e' piu' facile seguire il ragionamento pensando alla forma originale pensata per i rich client che la forma imbastardita usata sui web server) e lo applichi ricorsivamente, creando una forma frattale? Ottieni, piu' o meno per forza, una serie di widget compositi, business-aware a certi livelli, e sempre piu' generici ad altri. Una "form" per inserire una anagrafica si spezza con widget per l'indirizzo, per i dati personali, ecc. Un widget per l'indirizzo si spezza in widget per la citta' e per la via. La citta' si spezza in widget per regione / provincia / comune /ecc.
Quando esamini i controller di questi widget, noti che sono diventati piccolissimi. Emergono poi problematiche infrastrutturali, brutte da gestire in modo custom/procedurale ogni volta (A e' disabilitato se B e' vuoto, ecc). Questa era l'idea delle gui rules cui accennavo secoli fa.
Quando un controller ha 4-5 righe, e' sempre un controller? Serve sempre? Non sono allora forse un controller anche GasAlarm, SafeEngine, SumpPump?
Purtroppo MVC piace proprio perche' permette di non pensare. Propone linee nette di demarcazione, peraltro piuttosto maldestre perche' la View non rispetta un minimo di information hiding, non solo rispetto alla tecnologia di UI adottata, ma spesso nemmeno rispetto ai singoli widget elementari utilizzati. Pero' e' semplice, ed in genere le sovrasemplificazioni piacciono, basta ammantarle con qualche "principio" del tipo "non vogliamo logica nelle view!" oppure prendere una -ility (es. la testability che va di moda) e portarla all'estremo senza curarsi degli equilibri.
Le forme organiche, invece, richiedono un pensiero costante, via via che sviluppi, una attenzione alla nascita di centri gravitazionali degenerativi e la loro frammentazione in sotto-centri meaningful. E' piu' difficile. Troppe persone preferiscono un lavoro tedioso di programmazione senza pensiero. Alcuni progettisti, non fidandosi dei propri collaboratori, preferiscono dar loro uno schema rigido in cui incasellare le cose. E' una forma di legge di Conway, alla fine. Software con confini netti che viene da barriere comunicative, di ruolo e di fiducia altrettanto nette.
Ultimo spunto: quanto codice inutile, sia in MVC che in MVVM, viene scritto soltanto perche' il linguaggio non ha capito una questione fondamentale della fisica del software (entanglement), e quindi mi costringe ad implementare a mano meccanismi di notifica, di osservazione, di reazione, che dovrebbero essere gratuiti? :-)
Software con confini netti che viene da barriere comunicative, di ruolo e di fiducia altrettanto nette.
mmm non so se sei troppo buono qui anche con i progettisti esperti. Spesso è semplicemente un punto di vista diverso, nel senso che la maggior parte (tutti?) credono davvero nella validità di MVC (o quello che sia) e semplicemente vanno per quella strada. Alle volte mi sembra che l'esperienza è vista solo come il bagaglio di conoscenze (ne so di più quindi sono più esperto) mentre invece non si tengono in considerazione l'elasticità mentale e la capacità di pensare (non differentemente, a volte proprio di pensare). E' per questo che spesso in Italia (non ho molta visibilità della situazione all'estero, ma qui in Inghilterra a giudicare dallo stipendio medio direi che c'è più considerazione) la figura del programmatore è vista come persona di serie B perchè tanto è un lavoro meccanico! Il problema secondo me è proprio quello che hai più volte presentato riguardo la programmazione ad oggetti in genere: nessuno sa pensare o progettare davvero ad oggetti! Ricordo ancora quando ti feci una domanda da un milione di dollari: Conosci qualche progetto a visibilità pubblica che si possa considerare davvero ad oggetti? E la risposta fu uno scontatissimo no!
Devo dire che solo adesso sto realizzando davvero l'essenza della serie NOSD sulla fisica del software, forse è davvero la strada giusta anche per insegnare la "natura" del software, ma quanti di quelli che in un certo senso "guidano" l'informatica ne sono a conoscenza?
Dovresti allargare un po' il giro :))
mmm non so se sei troppo buono qui anche con i progettisti esperti
--
io? ma se sono cattivissimo : )
Comunque, sul tema dell'architettura e della fiducia, ho recuperato un bel post di Cook: http://www.johndcook.com/blog/2011/05/26/software-architecture-and-trust/
ma quanti di quelli che in un certo senso "guidano" l'informatica ne sono a conoscenza? Dovresti allargare un po' il giro :))
--
Premesso che molte idee della serie NOSD non sono pronte per il prime time, perche' le racconto via via che mi sembra di averle capite :-) e quindi ogni tanto cambio idea, ormai mi e' abbastanza chiaro che sono argomenti talmente impopolari rispetto alla cultura dominante che difficilmente potranno uscire dalla piccola cerchia di chi riesce ad apprezzarli.
Rimane un lavoro a cui sono affezionato e che non voglio vedere come un prodotto, come qualcosa che voglio "vendere" o promuovere, ma semplicemente come qualcosa che mi fa piacere condividere.
"Proposing to drop the controller would be just as useful as proposing that we drop html and javascript and start using something serious to write web apps :-)." well overdue if you ask me, about dropping html+js
Petar: IT WAS A JOKE. C'mon. Of course html+js sucks.
Post veramente interessante. Sarei curioso di conoscere la tua Sua opinione la questione di quanto costerebbe in piu' (o in meno) usare un approccio controller-less e se sia concretamente fattibile.
Infatti, parlo per un framework che uso(spring MVC), mi sembra che il concetto di controller sia cosi' intimamente definito nell'architettura da rendere oneroso svuotarlo di senso, salvo ridurre il controller ad un puro "router". Tuttavia, anche qui, sarebbe difficile coesistere con tutte quelle annotazioni (penso ad es. a quelle di security) che legano l'eseguibilita' d'un metodo ad un certo ruolo per un dato sub-URL, col rischio di trovarsi sul "controller-router" logiche di sicurezza che dovrebbero stare in ben altri posti.
Grazie.
Roberto Colmegna
Post a Comment