1

Consider the following GUI screen: enter image description here

When user selects a person from PersonListView, EditPersonView should show person's first name and last name and allow the user edit. So, I end up with the following UML class Diagram where each Java package represents the layer of the class.

enter image description here

My question is in SelectedPerson and if this is an MVP "model" class. Should this be part of my application layer? Isn't a presentation concern? The reason I added there is so the 2 presenters can observe it. When user selected an element from the list widget, it gets updated and EditPersonViewPresenter refreshes the 2 fields and "apply" button "Enability".

Class Persons is another model, responsible for persons CRUD operations and it makes perfect sense to be part of the application layer (should it be in domain?). It is also responsible to notify its observers that a person had a CRUD operation on it. So, when "Apply" is pressed, PersonListViewPresenter as an observer to Persons is capable to refresh the list and show the new first/last name in the list.

Long story short, the two presenters communicate through Persons and SelectedPerson models. Assume this is a "correct" approach.

Now, the devil comes.

Selection is available only when there are no unsaved changes in the EditPersonView. If the person in record state is named "Jackie Chan" and user edits to "Hackie Chan", the selection is disabled until the user clicks apply or restores the fields to "Jackie" and "Chan".

How PersonListViewPresenter can know whether there are unsaved changes in EditPersonView?

According to the approach taken till now, a new model "EditPersonState" must be added in the application layer and follow the same approach. EditPersonViewPresenter updates the EditPersonState model and PersonListViewPresenter observes it and operate accordingly (disable the selection in list). But, if I have X forms and multiple presenters are interesting in, I will have my application layer with X of such models (that exist only to synchronize presenters). Should it be that way?

On the other hand, the use case for example, is "User can delete the selected person" so having a SelectedPerson model in application layer could allow me to put the Delete operation (of CRUD) there and make the use case(s) more "visible".

As an alternate solution I thought, I could let this state in the view. The view will be treated as "the view" and then keep a hierarchy of views and child views. Each presenter depend on "the view" (say MainView) and then observes any of the interesting (child)views. So SelectedPerson model does not exist neither in application layer, neither in presenter layer. It exists in the child view. Persons model remain (in app layer) to do the CRUD. Uml class Diagram of this approach:

enter image description here

But in this approach there is no one-to-one relationship between a view and a presenter. Not that is is a law, but maybe one day if I have 50 different views I won't be able to know which presenters touches which (proportion of a) view.

What am I missing?

Finally, one thought of mine stands in the following. Martin Fowler in the same page states the following:

Session State is data that the user is currently working on. Session state is seen by the user as somewhat temporary, they usually have the ability to save or discard their work.

So, based on that statement, the person selection is session state (?). According to Craig Larman in Applying UML and Patterns: An Introduction to Object-Oriented Analysis and Design and Iterative Development there is the following figure:

enter image description here

If Larman's session state is the same thing as to Fowler's session state, then my current approach, SelectedPerson in application layer agree with them.

5
  • This depends on your requirements. Would the concept of a 'selected person' still be relevant/meaningful if you were to write a text/console-based UI? or web-based UI? or if the application/business logic was merely a service which exposed an API without a UI attached? i.e. is 'selected user' something which, according to your specific requirements, needs to exist regardless of how a user or other system interacts with the application? or is it merely a GUI feature for your List View? Commented May 21, 2021 at 9:58
  • @BenCottrell I am not quite sure I understood your question. (A lot) Use cases are based on selected person. About service thing you said, the class Persons is a service. A presenter gets the selected person and passes it to Persons service. Notice how persons stand. It is persons.delete(p) not persons.delete() with association to selected person. I like the fact Persons stays as a (notifier) service with more business logic. Say I keep only Persons, and selected person stays in presentation, how EditPersonViewPresenter finds it in order to persons.update(selPerson)?
    – George Z.
    Commented May 21, 2021 at 10:26
  • Another way to think about it - consider the interface for your application logic if it were reused elsewhere under a totally different UI, For example, would a command-line user need to send a command to "set" the selected person before sending a command to modify/delete that person? Would an automated script using an API need to set the selected person before sending a request to modify/delete that person? Or would the application logic receive the selected person as part of a payload which includes the modify/delete command? Commented May 21, 2021 at 10:28
  • From what you say it sounds like the latter (i.e. combining the 'delete' command with the person) - in which case it seems like the selected person is merely supporting the List View in your Java GUI, and doesn't sound like something the application logic would care about Commented May 21, 2021 at 10:31
  • @BenCottrell Ok I got it now. And yes, I'd like to keep Persons as something that says "give me which person you would like to delete. If it comes from selected person in a GUI or whatever, I do not care. Just give me which you would like to delete". So, SelectedPerson becomes a GUI concern. How presenters could access it though? Just moving the class in presentation layer?
    – George Z.
    Commented May 21, 2021 at 10:48

2 Answers 2

1

I believe you are making your design more complicated than needed by having two Presenters. I would design a system like this with a single Presenter that manages 2 Views (or, if you want, 1 main View corresponding to the GUI window, which contains 2 sub-Views corresponding to each panel).

In addition, the Presenter would also have a Mediator that is connected to both (sub-)Views and makes sure that the current selection from the list view is communicated to the edit view and that the "unsaved changed" status of the edit view causes the selection in the list view to be frozen.

The Presenter then only has to deal with the actions that require an actual action from the application, like applying changes.

2
  • having two Presenters. Of course these views are not that small in real application. They are views with many fields or so. If I have only one Presenter, it will probably end up a God class with lack of cohesion (there are other context related to list view, not only persons). Presenter would also have a Mediator, So its not the presenters responsibility to manage the disabled selection? This mediator belongs to presentation layer or view layer? Could you also provide an example in pseudocode (or UML?). About your last sentence, a presenter acts only when model is going to be manipulated?
    – George Z.
    Commented May 21, 2021 at 21:36
  • Also note that I try to follow a Passive View approach. The reason for that is testability. I would like to test only my presenters and leave the view untested. In my current design, testing only the presenter with a Test Double as view gives me the good sign that "everything works".
    – George Z.
    Commented May 21, 2021 at 21:38
0

You should think of any MVP/MVC complex as a single "component" with its connection to the outside world through its Presenter/Controller. MVP/MVC is all about controlling dependencies, coupling and visibility, so each of these components should be considered a black box outside of anything declared in the public interface of its Controller/Presenter.

Strictly speaking, you shouldn't have anything in a Model that isn't used internally by the View or Controller. So in this example, if you had something like "Phone Number" in your main table, but not in your edit window, then you'd need a separate model for the edit window which was different from the table row model. Your Controller for the edit window could observe the currently selected table row model, but then it would need to translate it into an internal Model for the edit window.

It's really contextual whether or not the table row model could/should be used as a component of the edit window model in any case. If the two windows really are meant to always work together, then it's probably OK. Otherwise it's best to create a special purpose class to hold the fields that you need from the table row model and make that class part of the Model for the edit screen. Then the controller for the table window would maintain an observable object of that class that would be passed to the Controller for the edit window in its constructor.

The EditPersonState property should NOT be part of the Model for the edit window. That property should be created by the Controller for the table window, and passed to the edit window Controller as a parameter in its constructor. Then it's that Controller's responsibility to ensure that this state property is kept synchronized with what's happening in the edit screen.

The constructor for the edit window would look like this:

 public EditWindow(PersonEditObservableData personData, ObservableEditState editState) {}

Now it's up to the list view controller to keep it's reference to the personData synchronized with the list selection, and it's up to the edit window controller to keep it synchronized with its screen data AS APPROPRIATE. It's up to the edit window controller to ensure that editState is synchonized with the current status of it's model.

BTW, keeping the personData separate from the Model for the edit screen makes it somewhat easier to control editState. Put a ChangeListener on personData, and any time it's triggered, copy the fields into the Model. Then set the editState so that it reflects whether any of the fields in the Model are different from those in personData.

There's other questions here too. Is there a persistence layer? If there is, then which controller/presenter is responsible for calling it? In a real world application, it's highly likely that the list window row model is just enough data to identify a Person, and the edit window would probably connect to the database and pull a lot more detail and populate its model from that.

It's possible to have "Main" controller which handles all of the persistence layer activity. In that case, the PersonEditObservableData would need to be expanded to hold all that information. Additionally, you'll need to provide some sort of Consumer to handle the save request from the edit screen.

The problem with this is that you end up with a monolithic architecture where your "Main" controller has all kinds of functionality to support dependent components. It's probably best to think of each screen as a complete db ---> gui mini-application that can stand by itself.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.