본문 바로가기
책 내용 정리/[책] 개발자가 반드시 정복해야 할 객체지향과 디자인 패턴

[1] 추상화와 다형성 예시

by 문자메일 2023. 2. 25.

https://link.coupang.com/a/QoNwV

개발자가 반드시 정복해야 할 객체 지향과 디자인 패턴

COUPANG

www.coupang.com

 
본문의 내용은 위 책에서 정리한 내용 일부이며, 자세히 알고 싶은 분들은 위 책을 구매해서 보시는 것을 추천드립니다.
씹고 뜯고 맛보고 즐겨도 내용이 지루하지 않고, 정말 돈이 아깝지 않은 책!
 
 
 
 
 
소프트웨어의 가치
소프트웨어의 가치는 사용자가 요구하는 기능을 올바르게 제공하는 데 있음.
변화에 잘 대응할 수 있도록 소프트웨어의 구조를 만들고 더 빠른 시간에 더 적은 노력을 들여서 수정할 수 있는 방향이 바람직
변화 가능한 유연한 구조를 만들어 주는 핵심 기법 중 하나가 객체 지향임.
 
 
객체 지향에서는 추상화와 다형성을 이용해 변화되는 부분을 관리한다.
- 추상화란 무엇일까? sw개발에서 변화될 수 있는 로직, 개념  부분을 일반적인 공통점,명칭으로 추출하는 것. 인터페이스?
 - 다형성이란 뭘까? 여러 인스턴스를 하나의 멤버변수로 받을 수 있게 해주어서 sw 로직 변화에서 닫히게 해주는 것?
--> 이 모든게 조립을 통한 재사용(구현) 을 기존 코드 변경 없이 구현하기 위해, 필요한 경우에 다른 인터페이스의 구현체를 넣을 수 있도록 위함인 것 같음

최초 상황은 메뉴1, 메뉴2, 버튼 1이 존재하는 상태.
 
앞의 예제에서 메뉴1, 메뉴2를 선택했을 때 비슷하게 동작하는 것들이 있고, 아래와 같음
 
1. 메뉴가 선택되면 해당 화면을 보여준다.
2, 버튼1을 클릭하면 선택된 메뉴 화면에서 알맞은 처리를 해준다.

- 변화되는 부분은 1.메뉴를 선택하면 화면이 바뀐다. 1.메뉴가 선택된 상태에서 2. 버튼이 눌리면 처리를 해준다. 버튼은 어떤 메뉴인지에 따라 의존적이고, 화면의 변화는 어떤 메뉴를 선택했는지에 따라 이 경우는 의존적이다.
결국 변화의 포인트는 메뉴, 버튼 총 2개이고 버튼은 메뉴에 선택 상태에 따라 의존하므로 저 2가지 변화를 메서드로 추상화하고, 하나의 ScreenUI 인터페이스로 묶는게 적절한 추상화로 보인다.
--> 조립을 위한 재사용을 다형성(인터페이스)로 기존 sw로직의 변경 없이 구현체 변경으로 런타임 동작을 바꾸기 위해 추상화와 다형성이 필요한 것으로 정리가 된다.

 
어떤 메뉴를 선택하였을 때 위 2가지 동작은 메뉴 3,4,5가 추가되더라고 동일하게 동작을 취한다.
 
메뉴 클릭 시 공통 동작 표현을 위하여 ScreenUI 인터페이스를 정의하고, 아래 2개 method를 정의함
show() : 메뉴 버튼 클릭 시 실행될 메서드, 특정 메뉴에 해당하는 알맞은 화면을 보여주기 위해 사용
handleButton1Click() : 버튼 1이 눌렸을 때 실행될 메서드

public interface ScreenUI{
	public void show();
    public void handleButton1Click();
}

 
메뉴 별로 화면에 보이는 구성 요소와 버튼1 클릭을 처리하는 로직이 다르므로 아래 코드와 같이 ScreenUI 인터페이스를 구현한 클래스를 작성한다.

class Menu1ScreenUI implements ScreenUI{

	@Override
	public void show() {
		System.out.println("메뉴1 화면으로 전환");
	}

	@Override
	public void handleButton1Click() {
		System.out.println("메뉴1 화면의 버튼1 처리");
	}
}

class Menu2ScreenUI implements ScreenUI{

	@Override
	public void show() {
		System.out.println("메뉴2 화면으로 전환");
	}

	@Override
	public void handleButton1Click() {
		System.out.println("메뉴2 화면의 버튼1 처리");
	}
}

 
 
추상화와 다형성을 적용하여 ScreenUI 인터페이스를 만들고, 인터페이스를 구현한 concreate 클래스를 활용해서 main 영역에서 아래 코드에서 주석(//) 처리 된 부분과 비교하여 좀 더 객체지향적으로 구현할 수 있다.
주석 처리 된 부분처럼 한 클래스에 모두 구현하면, 새로운 기능이 추가될 때마다 if 조건문이 늘어나고 따라서 코드가 복잡해지며 이런 사항들이 쌓일 수록 점점 더 코드 수정이 어렵게 된다.
-> 직관적으로 설명하자면, 한 클래스에 여러 기능의 코드가 함께 있다면, 특정 기능을 분석하는 과정에서 불필요하게 다른 기능의 코드도 분석하게 될 가능성이 높고, 이것은 분석과 기능 개발 시간을 증가시키는 요인이 된다.
 
추상화와 다형성을 이용하여 인터페이스와 concreate 클래스를 생성하여 구현한 경우는, 클래스 개수는 증가하였지만 메뉴 관련 코드들이 알맞게 분리된 것을 볼 수 있다.

public class Main {
	public static ScreenUI currentScreen = null;

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		menuClicked("menu1");
		menuClicked("menu2");
		
		buttonClicked();
	}
	
	public static void menuClicked(String menu) {
		if("menu1".equalsIgnoreCase(menu)) {
			currentScreen = new Menu1ScreenUI();
			currentScreen.show();
		}
		else if("menu2".equalsIgnoreCase(menu)) {
			currentScreen = new Menu2ScreenUI();
			currentScreen.show();
		}
	}
	
	public static void buttonClicked() {
		if(currentScreen == null) {
			return;
		}
		currentScreen.handleButton1Click();
	}
	
//	private static String currentMenu = null;
//	
//	public static void clicked(String menu) {
//		if("menu1".equalsIgnoreCase(menu)) {
//			changeUIToMenu1();
//		}
//		else if("menu2".equalsIgnoreCase(menu)) {
//			changeUIToMenu2();
//		}
//		else if("button1".equalsIgnoreCase(menu)){
//			if(currentScreen == null) {
//				return;
//			}
//			if(currentMenu.equals("menu1")) {
//				processButton1WhenMenu1();
//			}
//			else if(currentMenu.equals("menu2")) {
//				processButton1WhenMenu2();
//			}
//		}
//	}
//	
//	private static void processButton1WhenMenu2() {
//		System.out.println("메뉴2 화면의 버튼1 처리");
//	}
//
//	private static void processButton1WhenMenu1() {
//		System.out.println("메뉴1 화면의 버튼1 처리");
//	}
//
//	private static void changeUIToMenu2() {
//		currentMenu = "menu2";
//		System.out.println("메뉴2 화면으로 전환");
//	}
//
//	public static void changeUIToMenu1() {
//		currentMenu = "menu1";
//		System.out.println("메뉴1 화면으로 전환");
//	}

}

댓글