객체지향과 디자인패턴 1
1. 지저분한 코드
최초 요구사항: 메뉴영역에 메뉴1과 메뉴2가 있다. 공통영역에 버튼 1이 있다.
- 메뉴를 누르면 각 메뉴로 화면전환
-버튼을 누르면 각 메뉴에 맞는 화면처리를 한다.
public class Application implements OnClickedListener {
private Menu menu1 = new Menu("menu1");
private Menu menu2 = new Menu("menu2");
private Button button1 = new Button("bu1");
private String currentMenu = null;
public Application(){
menu1.setOnClickListener(this); // 클릭시 이벤트 담당하는 클래스 등록
menu2.setOnClickListener(this);
button1.setOnClickListener(this);
}
public void clicked(Component eventSource){
if(eventSource.getId().equals("menu1"))
changeUIToMenu1();
else if (eventSource.getId().equals("menu2"))
changeUIToMenu2();
else if (eventSource.getId().equals("button1"))
if(currentMenu == null)
return;
if(currentMenu.equals("menu1"))
precessButton1WhenMenu1();
else if (currentMenu.equals("menu2")) {
processButton1WhenMenu2();
}
}
private void processButton1WhenMenu2() {
//..
}
private void precessButton1WhenMenu1() {
//..
}
private void changeUIToMenu2() {
//..
}
private void changeUIToMenu1() {
//..
}
}
- 두 개의 메뉴와 한 개의 버튼에서 이벤트가 발생하면, 그 이벤트를 clicked()메서드에서 처리한다. (각 버튼에 OnClickedListener 클래스(clicked 메서드 가짐) 등록
- 위 클래스는 하나의 클래스가 메뉴1,2,버튼1도 포함하면서, 화면처리 이벤트도 처리한다.
- 이런경우 만약 메뉴3과 버튼2가 포함된다고 가정해보자. -> 메뉴와 버튼 클릭처리를 위해 if-else구조가 중첩될 것이다.
- 하나의 클래스에서 여러 버튼의 책임을 혼자서 책임지려다보니 코드가 복잡해진다.
2. 수정하기 좋은 코드로 변경
- 이 상황을 객체 지향 방식으로
- 요구사항을 다시 생각해보면, 메뉴가 선택되면 해당 화면을 보여준다. 버튼이 클랙되면 선택된 메뉴에 맞는 처리를 한다.
- 위 두개의 공통 동작을 인터페이스화 해보자
- 인터페이스
public interface ScreenUI {
public void show();
public void handleButton1Click();
}
- 구현체
public class Menu1UI implements ScreenUI {
@Override
public void show() {
//..
}
@Override
public void handleButton1Click() {
//..
}
}
public class Application implements OnClickedListener {
private Menu menu1 = new Menu("menu1");
private Menu menu2 = new Menu("menu2");
private Button button1 = new Button("bu1");
private ScreenUI currentMenu=null;
public Application(){
menu1.setOnClickListener(this);
menu2.setOnClickListener(this);
button1.setOnClickListener(this);
}
public void clicked(Component eventSource){
String ev = eventSource.getId();
if(ev.equals("menu1")) {
currentMenu = new Menu1UI();
currentMenu.show();
} else if (ev.equals("menu2")) {
currentMenu = new Menu2UI();
currentMenu.show();
} else if (ev.equals("button1")) {
if(currentMenu==null)return;
currentMenu.handleButton1Click();
}
}
- 버튼1을 클릭을 처리하는 코드는 현재 화면이 메뉴1인지 2인지 상관없이, currentMenu에 handleButtonClick()를 호출한다.
- 위와 같이 수정할 수 있다. 각 메뉴화면과 버튼 클릭시 처리에 대한 책임을 메뉴가 가지도록 변경했다.
- 메뉴3이나, 버튼이 추가되면, 이전과 다르게 ScreenUI를 구현한 메뉴3을 추가 혹은 button2를 처리하는 메서드를 ScreenUI에 추가해서 각 클래스별로 구현하도록 하면 된다.
3. clicked메서드에서 버튼을 처리하는 것과 메뉴 처리하는 것 분리
private ScreenUI currentMenu=null;
public Application(){
menu1.setOnClickListener(menuListener);
menu2.setOnClickListener(menuListener);
button1.setOnClickListener(buttonListener);
}
private OnClickedListener menuListener = new OnClickedListener() {
@Override
public void clicked(Component eventSource) {
String ev = eventSource.getId();
if (ev.equals("menu1")) {
currentMenu = new Menu1UI();
} else if (ev.equals("menu2")) {
currentMenu = new Menu2UI();
currentMenu.show();
}
}
};
private OnClickedListener buttonListener = new OnClickedListener() {
@Override
public void clicked(Component eventSource) {
if (currentMenu == null) return;
String ev = eventSource.getId();
if (ev.equals("button1"))
currentMenu.handleButton1Click();
}
};
- 메뉴와 버튼이 다른 OnClickedListener를 등록하도록 하여, 클릭시 서로 다른 clicked()가 호출됨
- 각 메뉴와 버튼은 메뉴와 버튼이 추가, 삭제 될 때 마다 바뀔텐데 기존처럼 하나의 메서드에서 이를 다 처리하면, 가독성이 떨어진다.
- 객체지향코드로 바뀌면서 클래스가 추가되었지만, 기존보다 유지보수가 쉬워졌다.
> 새로운 메뉴 추가시 버튼 처리 코드가 영향 받지 않는다.(clicked())
> 한 메뉴 관련 코드가 하나의 클래스로 모여서 코드 분석 수정이 용이하다.
> 서로 다른 메뉴에 대한 처리 코드가 섞여 있지 않아 수정이 용이하다.
- 각 객체의 책임을 분리하여 다른 클래스로 작성하였기 때문에, 특정 객체를 수정해야할 때 불필요하게 다른 객체의 코드를 볼 필요가 없어졌다.