The distinction between association, aggregation and composition as you describe it is a legacy going back to the old times of manual memory management. For example in C++ the memory used by objects has to be manually released and it is thus paramount to carefully design the lifecycle of composed objects. While the distinction between aggregation and composition is still being taught by many text books, it is essentially irrelevant when programming in environments with automatic memory management. If you have garbage collection all of them are just composition, period.
Encapsulation on the other hand is a much more general principle than what you describe. It is foremost the idea of bundling data and the functions that operate on this data in one module. One way to implement this is by keeping the state of the module private and expose changes to that state through public services. So client cannot access the state on their own but have to tell the module their intend by sending messages. So encapsulation is not limited to objects but applies to services as well. Actually, one way of looking at objects is to look at them as services.
Here's an example of encapsulation
public class Counter {
private int n = 0;
public int inc() { return n++; }
}
or the same using lambda functions
var counter = (function() {
var n = 0;
var inc = function() { return n++; }
return inc;
})();
In both cases the data, that is the variable n
, is bundled together with the function inc
that operates on it. And there is no way any other function could ever access n
, thus we have an encapsulated module that provides counting as a service.
NB: exposing all of an object's inner state through accessors is actually a violation of encapsulation. Alas, it is such a common violation that many will confuse it with good object-oriented design.
The key in understanding MVC lies in the separation of the responsibilities, as MVC is simply SRP applied to UI code. It separates what data has to be displayed, from how to display it, from how to handle screen events. But an important (and often missed) detail of the original definition of MVC is that it was designed for a far more granular level. For instance, you'd have ButtonModel, ButtonView and ButtonController objects, "just" to present a single button on a screen. Missing this detail is what causes so many different opinions on the subject. You can check the Java Swing architecture to see what I mean.
The point of MVC is to allow the code that serves each responsibility to be changed without affecting the code for the others. For instance, you would be able to switch the rendering of (a component on) the screen without having to touch the data representation nor the event-handling logic. So, to a degree, this goes along with what you say here:
From this, I reasoned that given any Model and View, swapping only the controller should not change the data the page initially renders because the controller should change only the behaviour and not the 'content' of the page. I think this aligns with the conceptual visualisation of the controller as a 'station controller' in a rail system, a plan of the rail road as the model and the actual physical manifestation and look/feel of the tracks (in different flavours, say 'Real' or 'Virtual 3D') as the view.
However, in your context, the level of granularity is off; you have a SessionView that seems to be responsible for a whole screen. At this level, the responsibilities get too coupled to fully separate as intended by MVC, thus it may not provide the full benefits.
Your problem is in separating the three UI responsibilities (rendering, the data and the event-handling) for both sessions and processes, a total of six. Because of the level of granularity of the components (whole screen), this becomes impossible, and causes the dissonance you find yourselves into.
You want to separate the rendering and event-handling responsibilities for both Sessions and Processes, but you'd couple their data. Your colleague wants to decouple the data, but couple the event-handling.
So in the end, this is an SRP problem. My way out would be to decrease the level of granularity down to a point where you can clearly separate Sessions from Processes. If you can't do that due to economics, you guys simply have to weight both sides of the trade-off, choose the least worst, and sign it off as technical debt. This is, after all, what design decisions are all about. :)
Best Answer
Generic programming is your friend:
I am not a Java programmer, so please excuse any mistakes. It should give you an idea however.
Please note that the type parameter constraints are not necessary to build up your type hierarchy, but they better communicate the purpose of the generic base interfaces.