I am trying to understand how can I implement MVC into my Java application, read couple of tutorials and MVC hello worlds, but I am still not sure about many things, so I would be glad if you could help me understand all this.
Let's say I have a simple GUI application, that is used for storing and working with various buildings. I can add building by chosing type from list and pressing button. These buldings will be stored in some arraylist, displayed in GUI and will be editable (number of rooms, number of floors ..... not important). Buildings will be displayed in JComboBox and after choosing some building, the setting panel for that building will appear.
So far I have 2 "components". Buildings (container) and building. I have created the BuildingsModel class, which holds the buildings, has some methods to work with them and notifies observers after change. Then I have a BuildingsView class, which is observing the BuildingsModel. Then I have BuldingsController class just with constructor method, that takes BuildingsModel and BuildingsView as parameters, binds the view to model as observer, create some initial buildings and add some listeners to the view.
Now I don't know how to continue. There are few things that I am not very happy about.
I have binded listener to the button, that will get the current selection from JList in view, create new object (xxxBuildingModel) and add it to BuildingsModel. However, the JList contains just String representations of all building types and to avoid long if-else statemenets and finding the right class for that String, I had to use reflection. (Each building type has its own class that extends BuildingModel.) Is there any better way to do it?
The second listener is bound to JComboBox that contains already created building instances. After selecting building in that combobox, I want to display settings form for that building. So I guess there should be some view class associated with BuildingModel that will display its current settings (model's state). But I am not sure, how to do it from the BuildingsController's constructor context. I have access only to building's model, so how should I "find" the right view for that instance and display it? Maybe I am doing it all wrong and the combobox shouldn't contain just models but controllers (which has access to both model and view), in that case I could just call the controller's view method, which will get the needed data from model, passes it to view and display it. I don't if I should work with concrete building instance as model, or as controller or as some custom class that encapsulates all these 3 components (controller, model, view) ...
Here are the most important parts of the code. I didn't include the BuildingModel class and its subclasses, because for now they are just blank classes with toString() method.
public class BuildingsController {
public BuildingsController(final BuildingsModel model, final BuildingsView view) {
class addBuildingButtonActionListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
String selectedBuildingType;
if ((selectedBuildingType = view.getSelectedBuildingType()) != null) {
try {
BuildingModel newBuilding = (BuildingModel) Class.forName("building.models." + selectedBuildingType + "BuildingModel").newInstance();
model.addBuilding(newBuilding);
} catch (InstantiationException ex) {
//addToErrorLog
} catch (IllegalAccessException ex) {
//addToErrorLog
} catch (ClassNotFoundException ex) {
//addToErrorLog
}
} else {
//addToLog - NO_BUILDING_SELECTED
}
}
}
class buildingComboBoxSelectListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
BuildingModel selectedBuilding;
if ((selectedBuilding = view.getSelectedBuilding()) != null) {
//display current building form???
}
}
}
model.addObserver(view);
model.addBuilding(new HospitalBuildingModel());
model.addBuilding(new SchoolBuildingModel());
view.fillBuildingTypesList(BuildingsModel.getAllBuildingsTypes());
view.addAddBuildingButtonListener(new addBuildingButtonActionListener());
view.addActiveBuildingsListener(new buildingComboBoxSelectListener());
}
}
public class BuildingsModel extends Observable {
private static String[] allBuildingsTypes = {"School", "Hospital", "Stadion"};
private ArrayList<BuildingModel> buildings = new ArrayList<BuildingModel>();
public void addBuilding(BuildingModel building){
this.buildings.add(building);
this.setChanged();
this.notifyObservers();
}
public BuildingModel[] getAllBuildings(){
return this.buildings.toArray(new BuildingModel[this.buildings.size()]);
}
public static String[] getAllBuildingsTypes(){
return BuildingsModel.allBuildingsTypes;
}
}
public class BuildingsView implements Observer {
private static String name = "Buldings";
private static String addBuildingButtonText = "Add building";
private JComboBox allActiveBuildings = new JComboBox();
private JList buildingTypesList = new JList();
private JButton addBuildingButton = new JButton(BuildingsView.addBuildingButtonText);
@Override
public void update(Observable model, Object o){
BuildingModel[] allBuildings = ((BuildingsModel) model).getAllBuildings();
this.allActiveBuildings.removeAllItems();
for(BuildingModel building : allBuildings){
this.allActiveBuildings.addItem(building);
}
}
public String getSelectedBuildingType(){
return (String) this.buildingTypesList.getSelectedValue();
}
public BuildingModel getSelectedBuilding(){
return (BuildingModel) this.allActiveBuildings.getSelectedItem();
}
public void fillBuildingTypesList(String[] buildingTypes){
this.buildingTypesList.setListData(buildingTypes);
}
public void addAddBuildingButtonListener(ActionListener l){
this.addBuildingButton.addActionListener(l);
}
public void addActiveBuildingsListener(ActionListener l){
this.allActiveBuildings.addActionListener(l);
}
public JComponent display(){
JPanel panel = new JPanel();
Border border = BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder(BuildingsView.name), BorderFactory.createEmptyBorder(5, 5, 5, 5));
panel.setBorder(border);
panel.add(this.allActiveBuildings);
panel.add(this.buildingTypesList);
panel.add(this.addBuildingButton);
panel.setPreferredSize(new Dimension(200, 262));
return panel;
}
}
main:
public static void main(String[] args) {
BuildingsView buildingsView = new BuildingsView();
BuildingsModel buildingsModel = new BuildingsModel();
BuildingsController buldingsController = new BuildingsController(buildingsModel, buildingsView);
mainWindow window = new mainWindow("MVC test", buildingsView);
window.generateDefaultLayout();
window.showMainWindow();
}
window:
public class mainWindow extends JFrame {
private JPanel buildingsPanel = new JPanel();
private BuildingsView buildingsView;
public mainWindow(String title, BuildingsView buildingsView) {
this.buildingsView = buildingsView;
this.setTitle(title);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void generateDefaultLayout(){
this.setLayout(new FlowLayout());
this.setPreferredSize(new Dimension(1200, 920));
this.addBuildingsPanel();
}
public void addBuildingsPanel(){
this.buildingsPanel.add(this.buildingsView.display());
this.add(this.buildingsPanel);
}
public void showMainWindow(){
this.pack();
this.setVisible(true);
}
}
Thanks :)