The model view control pattern is basic to object oriented programming. It has the advantage of separating out functionality
The Model
The model handles all the data processing. In our case this means the tip class itself with its methods for calculating the tip, the tax and the total. But it can also mean classes that access files or databases
The View
The view is the display part of the program. It could be console, or web, Android xml or in our case Swing. The view is only concerned with getting user input and displaying results. No calculations are performed in the view
The Controller
The controller's responsibility is to transfer the data from the model to the view.
In this example, the main, located in the class program, instantiates the view, the model and the controller and passes the view and model to the controller for processing.
Classes are fairly loosely coupled. The view is unaware of either the model or the controller. The model is unaware of the controller or the view. Only the controller needs to be aware of the other components and they are passed as objects to it through its constructor.
The program behaves exactly like the other examples.
Here is the UML class diagrams for the Model View Control version of this project
Here is the code for the various classes
program.java
import javax.swing.*;
public class program {
/*******************************
* this class has the main method.
*It instantiates the view, the model and
*the controller. It passes the view
*and the model to the controller
*/
public static void main(String[] args) {
TipModel model = new TipModel();
JFrameView view = new JFrameView();
TipController controller = new TipController(view, model);
view.setBounds(100,100,500,300);
view.setTitle("Tip Calculator MVC");
view.setVisible(true);
}
}
JFrameView.java
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JTextField;
import java.awt.*;
import java.awt.event.ActionListener;
public class JFrameView extends JFrame{
/**
* this method creates the JFrame
* and adds the Panel
* the panel is actually the main part of the view
* I have combined the two into one class
* to make it simpler, so we just have one
* view class
* the view is unaware of the model or the
* controller, although it knows its listener
* methods must be sent in from somewhere else
*/
private JPanel panel;
JPanel buttonPanel;
JLabel lblAmount;
JRadioButton tenPercent;
JLabel choosePercent;
JRadioButton fifteenPercent;
JRadioButton twentyPercent;
JRadioButton rbother;
JTextField txtOther;
JLabel lbltax;
JLabel lblTip;
JLabel lblTaxPercent;
JLabel lblTaxAmt;
JLabel lblTotal;
JTextField txtAmount;
JTextField txtTaxPercent;
JTextField txtTipAmount;
JTextField txtTaxAmount;
JTextField txtTotal;
JLabel lblOther;
JButton calculate;
JButton exit;
double tipPercent;
double amount;
double taxPercent;
private static final long serialVersionUID = 1L;
//constructor
public JFrameView(){
JFrame frame=new JFrame();
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
//call the method that builds the panel
buildPanel();
}
public void buildPanel(){
//instantiate all the objects
//and set their properties
panel=new JPanel();
panel.setLayout(new GridLayout(10,2,5,5));
lblAmount = new JLabel("Enter pre-tax amount:");
txtAmount=new JTextField(10);
tenPercent=new JRadioButton("Ten Percent");
fifteenPercent=new JRadioButton("Fifteen Percent");
fifteenPercent.setSelected(true);
twentyPercent = new JRadioButton("twenty Percent");
rbother =new JRadioButton("Other");
//add radiobuttons to a button group
ButtonGroup group = new ButtonGroup();
group.add(tenPercent);
group.add(fifteenPercent);
group.add(twentyPercent);
group.add(rbother);
lblOther=new JLabel("Enter Other Percent");
txtOther = new JTextField(10);
lblTaxPercent=new JLabel("Enter tax Percent:");
txtTaxPercent=new JTextField(10);
lblTip = new JLabel("Tip Amount:");
txtTipAmount = new JTextField(10);
txtTipAmount.setEditable(false);
lblTaxAmt = new JLabel("Tax Amount:");
txtTaxAmount =new JTextField(10);
txtTaxAmount.setEditable(false);
lblTotal = new JLabel("Total:");
txtTotal = new JTextField(10);
txtTotal.setEditable(false);
//add all the components
//except the buttons to the panel
panel.add(lblAmount);
panel.add(txtAmount);
panel.add(tenPercent);
panel.add(fifteenPercent);
panel.add(twentyPercent);
panel.add(rbother);
panel.add(lblOther);
panel.add(txtOther);
panel.add(lblTaxPercent);
panel.add(txtTaxPercent);
panel.add(lblTip);
panel.add(txtTipAmount);
panel.add(lblTaxAmt);
panel.add(txtTaxAmount);
panel.add(lblTotal);
panel.add(txtTotal);
//add a second panel
//this one uses flow layout
buttonPanel = new JPanel();
buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
//instantiate the buttons and add
//them to the panel
//also add a listener
calculate = new JButton("Calculate");
//calculate.addActionListener(this);
buttonPanel.add(calculate);
exit = new JButton("Exit");
//exit.addActionListener(this);
buttonPanel.add(exit);
//this, in this case the window
//we add the two panels to a border
//layout the main one is in the center
//the button panel is below (SOUTH)
this.setLayout(new BorderLayout());
this.add(panel, BorderLayout.CENTER);
this.add(buttonPanel, BorderLayout.SOUTH);
}
public double GetAmount(){
//get the strings from the text boxes
double amount=0;
try{
String strAmount=txtAmount.getText();
amount= Double.parseDouble(strAmount);
}
catch (Exception e){
lblTotal.setText(e.getMessage());
}
return amount;
}
public double getTipPercent(){
tipPercent=0;
//check which radio button is selected
if(tenPercent.isSelected())
tipPercent=.1;
else if(fifteenPercent.isSelected())
tipPercent=.15;
else if (twentyPercent.isSelected())
tipPercent=.2;
else if (rbother.isSelected()){
String percent=txtOther.getText();
//there is a chance the parse can fail
try{
tipPercent=Double.parseDouble(percent);
}
catch(Exception e){
lblTotal.setText(e.getMessage());
}
}
else
tipPercent=0;
return tipPercent;
}
public double getTaxPercent(){
String taxPerc = txtTaxPercent.getText();
try{
taxPercent=Double.parseDouble(taxPerc);
}
catch(Exception e){
lblTotal.setText(e.getMessage());
}
return taxPercent;
}
public void setTotal(String total){
lblTotal.setText(total);
}
public void setTip(String tip){
lblTip.setText(tip);
}
public void setTax(String tax){
lblTaxAmt.setText(tax);
}
//these are different rather than implement
//the listeners here the controller will return the
//listener to the view
public void addCalculateListener(ActionListener cal){
calculate.addActionListener(cal);
}
public void addExitListener(ActionListener el){
exit.addActionListener(el);
}
}
TipController.java
import java.awt.event.*;
import javax.swing.*;
public class TipController {
/******************************
* So this is the controller. It gets both
* the model and the view from the program
* class which contains the main() method
* It implements two internal classes
* as listeners
*/
private JFrameView m_view;
private TipModel m_model;
public TipController (JFrameView view, TipModel model){
m_view=view;
m_model=model;
//here it returns the listeners to the view
view.addCalculateListener(new CalculateListener());
view.addExitListener(new ExitListener());
}
class CalculateListener implements ActionListener{
public void actionPerformed(ActionEvent e){
//get the user's input values from the view's form
double amt=m_view.GetAmount();
double tPercent=m_view.getTipPercent();
double taxPerc=m_view.getTaxPercent();
//set the values in the model
m_model.setPreTaxAmount(amt);
m_model.setTipPercent(tPercent);
m_model.setTaxPercent(taxPerc);
//do the calculations and pass them back to the view
//I did both in single statements
m_view.setTip(Double.toString(m_model.CalculateTip()));
m_view.setTax(Double.toString(m_model.CalculateTax()));
m_view.setTotal(Double.toString(m_model.CalculateTotal()));
}
}
class ExitListener implements ActionListener{
public void actionPerformed(ActionEvent e){
System.exit(0);
}
}
}
TipModel.java
public class TipModel {
/*************************
* this is the model class. It handles the data
* and does all the calculations. It is totally
* independent of the controller and the view
* it is identical to the tip class in the
* former version
*/
//private fields
private double preTaxAmount;
private double tipPercent;
private double taxPercent;
//constructors
public TipModel(){
preTaxAmount=0;
tipPercent=0;
taxPercent=0;
}
public TipModel(double preTaxAmount, double tipPercent, double taxPercent){
this.setPreTaxAmount(preTaxAmount);
this.setTipPercent(tipPercent);
this.setTaxPercent(taxPercent);
}
//assessors/mutators
public void setPreTaxAmount(double preTaxAmount) {
this.preTaxAmount = preTaxAmount;
}
public double getPreTaxAmount() {
return preTaxAmount;
}
public void setTipPercent(double tipPercent) {
//make sure the tip is a decimal
//or zero
if (tipPercent >=1)
tipPercent /= 100;
this.tipPercent = tipPercent;
}
public double getTipPercent() {
return tipPercent;
}
public void setTaxPercent(double taxPercent) {
if(taxPercent >=1)
taxPercent /=100;
this.taxPercent = taxPercent;
}
public double getTaxPercent() {
return taxPercent;
}
//public methods
public double CalculateTip(){
return preTaxAmount * tipPercent;
}
public double CalculateTax(){
return preTaxAmount * taxPercent;
}
public double CalculateTotal(){
return preTaxAmount + (preTaxAmount * tipPercent)
+ (preTaxAmount * taxPercent);
}
}