(I'm writing a large MATLAB GUI that makes significant use of MVC, so my example is written in MATLAB. However, I believe this question is language-agnostic. Please correct me if I'm wrong.)
I have a class Model
which is obviously the model for my MVC application. Suppose I have the following:
classdef Model < handle
properties
objectA
objectB
objectC
end
end
objectA
, objectB
, and objectC
are all objects that can be observed:
classdef objectA < handle
events
ObjectAEvent
end
methods
function raise(this)
this.notify('ObjectAEvent', someEventData);
end
end
end
etc.
Now say I want my View
to respond to changes to my Model
. In particular I want the View
to respond to the ObjectAEvent
event. There are two ways to do this:
-
Let the
View
directly subscribe to theObjectAEvent
event. We've now coupled theView
into the internals of theModel
, which I believe violates the Law of Demeter. I'm pretty sure this also violates the concept of MVC by definition. -
Let the
Model
"forward" the event to theView
, as follows.classdef Model < handle properties objectA objectB objectC listeners end events eventFromObjectA ... end methods function this = Model this.listeners(1) = addlistener(this.objectA, 'ObjectAEvent', @(src, eventdata) this.notify('eventFromObjectA', eventdata); ... end end end
Now the
View
directly observes theModel
, but theModel
has to pass the events and associated data to theView
. I suppose this is kind of the point of MVC, but now there's a coupling between eachobject_
and theModel
directly. I suppose theModel
ought to know about hisobject_
s, but this still feels… clunky.
Which approach is more correct? Are there other alternatives which are cleaner than either of these?
(Answers may have examples in any OOP language; I'm just most familiar with MATLAB.)
Best Answer
MVC isn't about total decoupling. It's about avoiding coupling to elements that are unstable. As for Law of Demeter, you'd have to show why you think it violates it. It's not a hard and fast principle, either, despite its name having the word "Law" in it. It's nearly impossible not to violate it at some point.
View classes tend to be less stable than the model classes, because views are part of the user interface. Users tend to want new ways to view information (it's hard to get the views right the first time), etc. E.g. how many times has the GUI for Microsoft Word changed, yet the model of a document (think of paragraphs, words, characters, fonts, styles, etc.) has not changed much?
In MVC, views can be directly coupled to the model classes, because model classes tend to be more stable. MVC makes this assumption as a design choice. If model classes change (it can happen), then all bets are off for those changes being isolated to the model. You'll probably have to change the views, too.
Model classes have to communicate with the Views, but MVC wants to avoid direct coupling. We get around this problem by disguising Views as "Observers." The Observer API is very stable, even if the views that implement it are not. So as far as I understand MVC, it's OK to have coupling the way you have described it in point #1.
Let's use a real object rather than ObjectA, say
engineTemperature
. The view that displays the temperature would surely know what it's displaying. It might add the units "degrees C" since the view knows the value is a temperature. It might use the color red if the temperature exceeds some upper value. It's normal for views to be directly coupled to the model elements they display.The other way around, however, is different.
engineTemperature
should not know how it's being displayed, what colors are being used, who's displaying it, etc., since that knowledge would make it tightly coupled to the views, and it would lose its stability. Any change in the view might "break" the temperature object. So, with MVC and Observer,engineTemperature
(as a Subject in the Observer pattern) only knows it has Observers, which all support the Observer API.Have a look at http://martinfowler.com/eaaDev/uiArchs.html#ModelViewController for more info on MVC and other UI designs.