Having over-engineered one or two systems in my career, I will try to answer this from a pragmatic point of view.
My first approach would be to establish the convention, that data must not be modified in the presentation layer. If you work alone you know your own conventions anyway. In a small team you should be able to communicate them easily. Yes, this does not prevent violations at compile time, but unintentional violations should be rare and easy to spot (call hierarchy of a certain setter, for example).
If you absolutely need/want to implement this rule in code, putting the controllers and their respective data objects into a single package each, is the next best thing. Also I would not use protected
, but default/package scope (i.e. omit the visibility keyword).
Of the three options you presented, this is the most light weight (and therefore best maintainable and scalable) approach. You shouldn't be too worried about "...making it to difficult to expand the program.", you mereley have to live with potentially a lot of classes in one package. Maybe you can mitigate this by cleverly grouping certain controllers with some objects into their own package.
If this approach also fails, immutable objects are what you want. You could implement them plain (constructor and copy constructor, no setters), with factories/factory methods, or even use the Builder Pattern. Just keep in mind, that the overhead (i.e. boiler plate code) increases significantly with this solution and you should ask yourself if the gain (less time spent hunting/fixing bugs) is really greater than the cost (writing, testing and maintaining boiler plate code).
In fact I would only recommend this solution if you can guarantee, that the objects never need to be modified, and therefore you can ditch the copy code. This does not seem to be the case for your example, though.
Introducing a special getter-interface for each(!) data-class means more overhead than protected/default setters and is only half as strict, as you mentioned yourself: One could always downcast to the actual data-class. To improve this, you could give your data-classes protected or package visibility and put them in the same package as the controllers, but then you are back at square one, with additional interfaces and complexity.
Keeping the controllers and the data-model separated is a good idea. For most applications the model is much more likely to change than the controller logic. And you want to keep that changing part contained, since every change potentially introduces a bug and means additional adaptations in the test- and client code.
By moving and/or wrapping the getters directly into the controller (with or without special interfaces) you also increase the scope of the change. You'd suddenly need to maintain the test- and client code of the controller, instead of just the model, if you want to add/change one field.
Another, simpler problem is, if you need a collection of non-primitive data in your model.
Any software design decision boils down to the question of which solution lets you, your colleagues and users work most efficiently. These three groups will likely have contradictory needs and so it is hard to find the absolutely best solution and trade-offs are almost always a part of it.
It will probably be a lot easier for you if you don't have to pass the Session to every method call on a controller. This puts the requirement for keeping the Session on the view and that's not really where it belongs. It also makes your controller methods very heavy as they will need to always have this parameter, even if it makes no sense.
Instead, why not set up this 'session' when the user logs into the application and make it available through a class? In the simplest model your controller could have a 'SessionContext' or 'UserContext' injected in the constructor and every method could call that context and validate authorization to call the method, throwing exceptions if the requirements are not met.
public class CustomerController
{
private UserContext _context;
public CustomerController(UserContext context)
{
_context = context;
}
public void createCustomerAccount(EntityCustomer obj)
{
if(!_context.isUserAdmin())
throw new UnauthorizedMethodException("User not authorized to call createCustomerAccount");
//do your thing
}
}
(Not Java, but you get the idea)
More advanced ways of doing this could include using a Command Object-driven approach or using Aspects on methods.
Best Answer
That maybe an object oriented way, but not MVC. In MVC, the view typically sends a message to the controller "I want to display information", and the controller does the calculation using the model (or using model attributes or functions). Afterwards, the controller sends the result message back to the view (typically using an interface to decouple from the concrete kind of view).
Information derived out of model's property can be calculated either
I would place such calculation functions in the model only if they are not view-specific and have a certain chance of beeing reused. Functions totally specific to one view are better suited for the corresponding controller.