I also come across these "spaghetti" when I launch the complex MVC Swing application. This is how I do it. But this is my own way of doing, maybe you will not agree with me! If someone does not agree with any of my words, just let me know. I'm not a professional, just trying to help. :)
Reminder
First, Boris Spider has already introduced you to the good principles of MVC / MVP. I just remember the basics:

This means that your controller needs direct access to the model and view objects. The view has access to the model object. The model works on its own. This means that if you change your model (by changing the API used, by changing the algorithms, ...), the view does not care, it just shows it in several ways.
The controller must listen to user actions (buttons, fields, etc.) and modify the model data in accordance with this.
A view can only display data from a model. Nothing more.
How to implement
Instance of objects
First I create a model and view objects.
public static void main(String[] args) { EntityManager entManager = new EntityManager(); Map mapView = new Map(entManager); }
You create your objects by passing the links necessary for each of them through the designer parameters. A model is always the first to be created because it is independent.
Then you create a view that needs a model to get data values.
I create a controller inside the view after creating all the GUI objects. Thus, I attach them to the listener in the attachListeners() method.
Drawing objects
My approach is that the presentation requires a method for each Entity type. For example, the drawCity() method, then drawRoad() or drawOilStation() . Because representation is the only object that needs to know how to draw them. A model should not deal with it.
Since your example should paint all components, the paintComponents() method will be rather messy, yes. In my opinion, your “approach 1” is the best way to do this.
@Override public void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2d = (Graphics2D) g; for (Entity e : manager.getEntities()) { if (!e.isVisible()) { continue; } if (e instanceof CityLabel) { drawCity(); } else if (e instanceof MilitaryBase) { drawMilitaryBase(); } } }
Suppose you have a GUI with some JList and JPanel. Your loading methods (for reading data displayed from the model) will be called separately, each of which receives the data that they need from the model.
For instance:
public void createInterface(){
View Update
1) Change the types of user actions
Often the controller responds when an interface event occurs. The dispatcher’s job is to let the model change while updating the view calling the “load” methods.
Let's say you have 2 views, Map and EntityDetails . You have a controller for each of them, called MapListener and EntityDetailsController . Both of them have the corresponding representation object as an attribute. They capture all user actions from the view.
public class MapListener implements ActionListener{ private Map mapView; @Override public void actionPerformed(ActionEvent e){
Something I often do is add a link to the main view inside the secondary view. I mean, Map is the main frame, and sometimes it shows a second kind of EntityDetails . Therefore, I am adding a Map link to the EntityDetailsListener , if necessary, of course.
public class EntityDetailsListener implements ActionListener{ private EntityDetails entityView; private Map mapView; @Override public void actionPerformed(ActionEvent e){
2) Autostart model
When your model changes on its own, your opinion should be notified. Your controller, lying between them, is the object receiving notifications from the model to say: "Hey, I changed, let me update the view, please!"
This is why you should encode some fireXXXChange() methods in model objects. They will tell listeners that they need to do some work.
So, you need to pass your listener to the model object. Since you create it first of all, you need to pass it using a method like addMapListener(MapListener listener) . Then you can notify the controller when the data changes.
Since it's pretty hard to expose, I just give you this Oracle tutorial on how to manage MVC with Swing .
I really hope that this does not bother me and that I helped you a bit! The best way to solve your spaghetti code is to document and view what is best for each application that needs to be encoded, because each case is different.
Documentation: - javaworld.com: MVC meets Swing - link-intersystems: MVC pattern implemented using Swing - oracle.com: Developing Java applications with MVC