본문 바로가기

Programming/Java

[Java] MVC 패턴으로 Swing 계산기 만들기

반응형

개인적으로 소프트웨어 아키텍처에 관심이 많다. 개발을 하다보면 코드 양은 계속 많아지는데 관리는 소홀해지는 경험을 많이 했기 때문이다. 코드를 효율적으로 짤 수 있는 방법에 항상 관심이 많다보니 컴포넌트들의 독립성을 향상시키고 효율을 높이는 방법론에 대한 갈망이 항상 있다.

MVC 패턴(또는 MVC모델이라고도 부르나 MVC의 Model과 겹치는 이유 때문인지 패턴으로 더 많이 불리는 듯함)에 대한 개론적인 설명은 위키피디아에 자세히 나와있다. 자세한 설명은 생략하고 중요한 부분만 정리하면, MVC 패턴은 약어 그대로 프로그램을 모델-뷰-컨트롤러로 분리하여 설계함으로서 UI(User Interface, 사용자 인터페이스)로부터 비즈니스 로직을 분리할 수 있어 상호간의 영향 없이 쉽게 고칠 수 있는 장점이 있다. MVC 패턴은 소프트웨어공학에서 중요하게 여기는 개념중 하나인 '낮은 결합도'의 애플리케이션을 만들도록 돕는다.

사실 MVC 패턴은 웹 애플리케이션에 많이 쓰인다. 아무래도 웹의 특성상 데이터베이스는 Model, 사용자 인터페이스는 View, 내부 비즈니스 로직은 Controller 로 쉽게 나눠서 적용해볼 수 있고 이 세가지는 철저하게 분리되어 있어야 이상적이기 때문인 것 같다. 하지만 MVC패턴은 여전히 어느 플랫폼, 어느 애플리케이션이든 널리 사용되는 소프트웨어 디자인 패턴이다.

여기서는 MVC 패턴의 이해를 돕기 위한 간단한 예시로 계산기 프로그램을 MVC 패턴을 적용하여 만들어보자. (편의상 덧셈기능만 있다.)

Java Swing을 사용하였다.

 

/* MCalculator.java */
package jake_calculator_mvc;

import java.util.ArrayList;

public class MCalculator {
	private int data;
	private ArrayList list = new ArrayList();
	
	public void registerObserver(Observer o) {
		list.add(o);
	}
	
	public void notifyObservers() {
		for(Observer o : list) {
			o.update(data);
		}
	}
	
	public void add(int num1, int num2) {
		data = num1 + num2;
		notifyObservers();
	}
}

 

/* VCalculator.java */
package jake_calculator_mvc;

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.FlowLayout;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;

public class VCalculator extends JFrame implements Observer {
	private static final long serialVersionUID = 1L;
	private final int WNDSIZE_W = 300;
	private final int WNDSIZE_H = 100;
	private final String S_TITLE = "Calculator - using MVC";
	private Container ct;
	
	private JTextField jtfNum1;
	private JLabel jlAdd;
	private JTextField jtfNum2;
	private JButton jbtnCalc;
	private JTextField jtfResult;
	private JPanel jpCalc;
	
	private void initComps(){
		ct = getContentPane();
		jtfNum1 = new JTextField(10);
		jtfNum2 = new JTextField(10);
		jtfResult = new JTextField(10);
		jlAdd = new JLabel("+");
		jbtnCalc = new JButton("계산");
		jpCalc = new JPanel();
	}
	
	private void addComps(){
		jpCalc.setLayout(new FlowLayout(FlowLayout.CENTER));
		ct.setLayout(new BorderLayout(5, 5));
		
		jpCalc.add(jtfNum1);
		jpCalc.add(jlAdd);
		jpCalc.add(jtfNum2);
		jpCalc.add(jbtnCalc);
		jpCalc.add(jtfResult);
		ct.add(jpCalc);
	}
	
	private void initWnd(){
		setSize(WNDSIZE_W, WNDSIZE_H);
		setTitle(S_TITLE);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setVisible(true);
	}
	
	public VCalculator(){
		initComps();
		addComps();
		initWnd();
	}
	
	public int getNum1() {
		return Integer.parseInt(jtfNum1.getText());
	}
	
	public int getNum2() {
		return Integer.parseInt(jtfNum2.getText());
	}
	
	public void setCalcSolution(int result) {
		jtfResult.setText(Integer.toString(result));
	}
	
	public void setCalculatorListener(ActionListener listener){
		jbtnCalc.addActionListener(listener);
	}

	@Override
	public void update(int data) {
		setCalcSolution(data);
	}
	
}

 

/* CCalculator.java */
package jake_calculator_mvc;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class CCalculator implements ActionListener {
	private MCalculator model;
	private VCalculator view;
	
	public CCalculator(MCalculator model, VCalculator view) {
		this.model = model;
		this.view = view;
		this.view.setCalculatorListener(this);
	}

	@Override
	public void actionPerformed(ActionEvent arg0) {
		try {
			int num1 = view.getNum1();
			int num2 = view.getNum2();
			model.add(num1,  num2);
			
		} catch (RuntimeException e) {
			
		}
	}
}

 

 

/* Observer.java */
package jake_calculator_mvc;

public interface Observer {
	public void update(int data);
}

 

 

/* test.java */
package jake_calculator_mvc;

public class test {
	public static void main(String[] args) {
		VCalculator view = new VCalculator();
		MCalculator model = new MCalculator();
		model.registerObserver(view);
		CCalculator controller = new CCalculator(model, view);
		view.setVisible(true);
	}
}

 

 

실행 화면이다.

 

 

 

 

내가 느낀 장점 : 프로그램을 구조적으로 짤 수 있다. 비즈니스 로직 설계가 간편해진다. 모듈간의 결합도가 낮아 패턴 적용하지 않은 프로그램과 대비하여 유지보수가 매우 쉽다.

내가 느낀 단점 : 간단한 프로그램 개발에는 오히려 비효율적이다. View의 변화가 잦은 프로그램에는 부적절한 것 같다.

반응형