Notice
Recent Posts
Recent Comments
Link
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
Tags
more
Archives
Today
Total
관리 메뉴

Hello, Dino

다형성(Polymorphism)을 활용한 예제 (Scheduler Program) 본문

JAVA

다형성(Polymorphism)을 활용한 예제 (Scheduler Program)

초보디노 2020. 3. 17. 14:38

Scheduler 프로그램을 개발해보자 👩‍💻

 

1. 기능

  • 이벤트 등록 (Add Event)
  • 이벤트 리스트 조회 (List)
  • 이벤트 검색 (Show)
  • 프로그램 종료 (Exit)

 

2. 이벤트 종류

  • OneDay
  • Duration
  • Deadline

 

세 종류의 이벤트 클래스

 

 

세 종류의 Event 정보를 배열로 관리한다고 했을 때,

OneDay [] oneDays = new OneDay [100];
Duration [] durations = new Duration [100];
Deadlined [] deadlineds = new Deadlined [100];

세 종류의 이벤트 클래스는 전혀 공통점이 없는 클래스가 아닌 이벤트라는 공통점이 있는 클래스이기 때문에

위 소스처럼 이벤트 별 배열을 만들어 관리를 하는 것은 객체지향 프로그래밍과는 조금 거리가 느껴진다.

 

 

 

그렇다면 세 종류의 Event를 어떻게 하나의 배열로 관리할까?

바로 다형성을 활용하는 것이다.

 

 

 

우선, 세 종류의 Event 클래스가 상속 받을 상위 클래스를 생성하자.

public class Event {
	// OneDay/Duration/Deadlined Event의 공통 멤버를 관리하는 상위 클래스
	public String title;
	
	public Event(String title) {
		this.title = title;
	}
}

OneDay / Duration / Deadlined Event 클래스는 모두 'title'이라는 필드를 공통적으로 가지고 있다.

'title' 필드는 각 서브 클래스에서 상속 받아 사용하면 되기 때문에 Event 클래스 (상위 클래스)의 멤버로 분리한다.

 

 

Event 클래스를 상속받는 세 종류의 Event 클래스

 

// 다형성 활용 X
OneDay [] oneDays = new OneDay [100];
Duration [] durations = new Duration [100];
Deadlined [] deadlineds = new Deadlined [100];
int oneDaysCount, durationsCount, deadLindedCount;

// 다형성 활용
Event[] events = new Event [100];
int eventsCount = 0;

이벤트 클래스 별로 배열을 만들어 관리하던 소스를

상위 클래스의 데이터 타입인 배열 한 개로 세 종류의 이벤트 클래스를 관리할 수 있다.

 

 

 

만약, 배열의 크기보다 더 많은 데이터가 배열로 관리가 되어야한다면 ?

최초 배열의 크기를 100으로 지정했는데 이 보다 많은 200개의 데이터를 배열로 관리해야하는 상황이 발생했을 때

어떻게 해야할까? 최초 지정한 배열의 크기는 변경할 수 없다. 이때는 '배열 재할당' 하자

 

배열 재할당을 하는 방법은 기존 배열보다 더 큰 크기의 배열을 생성하고, 새로 생성한 배열에 기존 데이터를 복사하면 된다.

 

해당 내용을 코드로 표현하면 이렇게 된다.

// 현재 배열 수용 데이터 개수보다 큰 임시 배열을 생성한다.
Event[] temp = new Event [capacity * 2];

// 반복문을 사용하여 기존 배열 데이터를 새로 생성한 배열에 복사한다.
for (int i=0; i<eventsCount; i++) {
	temp[i] = events[i];
}

// 새로 생성한 임시 배열을 참조한다.
// 기존 배열 데이터는 GC에 의해 정리된다.
events = temp;

 

 

 


 

 

 

package scheduler.data;

public class MyDate {
	int year;
	int month;
	int day;
	
	public MyDate(int year, int month, int day) {
		this.year = year;
		this.month = month;
		this.day = day;
	}
	
	public String toString() {
		return year + "/" + month + "/" + day;
	}
}

 

package scheduler.event;

public class Event {
	// OneDay/Duration/Deadlined Event의 공통 멤버를 관리하는 상위 클래스
	public String title;
	
	public Event(String title) {
		this.title = title;
	}
}

 

package scheduler.event;

import scheduler.data.MyDate;

public class OneDay extends Event{
	public MyDate date;
	
	public OneDay(String title, MyDate date) {
		// Event Class (부모 클래스)의 생성자 호출
		super(title);
		this.date = date;
	}
	
	public String toString() {
		return title + ", " + date.toString();
	}
}

 

package scheduler.event;

import scheduler.data.MyDate;

public class Duration extends Event {
	public MyDate begin;
	public MyDate end;
	
	public Duration(String title, MyDate begin, MyDate end) {
		super(title);
		this.begin = begin;
		this.end = end;
	}
	
	public String toString() {
		return title + ", " + begin.toString() + "~" + end.toString();
	}
}

 

package scheduler.event;

import scheduler.data.MyDate;

public class Deadlined extends Event {
	public MyDate deadline;
	
	public Deadlined(String title, MyDate deadline) {
		super(title);
		this.deadline = deadline;
	}
	
	public String toString() {
		return title + ", " + "~" + deadline;
	}
}

 

package scheduler;

import java.io.IOException;
import java.util.Scanner;

import scheduler.data.MyDate;
import scheduler.event.Event;
import scheduler.event.OneDay;

public class Scheduler {
	private int capacity = 100;
	Event[] events = new Event [100]; // 이 배열은 Event타입을 상속 받는 하위 클래스 타입의 데이터를 저장할 수 있음.
	int eventsCount = 0;
	private Scanner scanner; 
	public void ProcessCommand() throws IOException {
		scanner = new Scanner(System.in);
		while(true){
			System.out.print("$ ");
			String command = scanner.next();
			if (command.equalsIgnoreCase("addEvent")) {
				
				String type = scanner.next();
				try {
					if (type.equalsIgnoreCase("OneDay")) {					
						handleAddOneDayEvent();						
					} else if (type.equalsIgnoreCase("duration")) {
						handleAddDurationEvent();
					} else if (type.equalsIgnoreCase("deadline")) {
						handleAddDeadlineEvent();
					} else {
						UndefinedCommand(type);
					}
				} catch (Exception e) {
					e.printStackTrace();
				}
				
			} else if (command.equalsIgnoreCase("list")) {
				handleList();
			} else if (command.equalsIgnoreCase("show")) {

			} else if (command.equalsIgnoreCase("exit")) {
				break;
			} else {
				UndefinedCommand(command);
			}			
		}
		// resource leak를 막기 위해 scanner의 기능이 모두 완료되는 시점에 close 해줘야 한다.
		// close() => scanner가 참조하고 있는 스트림을 닫는 기능. 즉, System.in 스트림을 닫는 것을 의미. 
		scanner.close();
	}
	
	private void handleList() {
		for (Event event : events) {
			if(event == null) break;
			
			System.out.println("    " + event.toString());
		}
	}

	private void UndefinedCommand(String command) {
		System.out.println(String.format("'%s' is undefined command", command));
	}

	private void handleAddOneDayEvent() throws Exception {
		System.out.print(" when: ");
		String dateStr = scanner.next(); // Sample=> 2019/1/20
		MyDate date = parseDateString(dateStr);
		
		System.out.print(" title: ");
		String title = scanner.next();
		
		OneDay oneDay = new OneDay(title, date);
		addEvent(oneDay);
	}

	private void handleAddDurationEvent() {
				
	}

	private void handleAddDeadlineEvent() {
		
	}
	
	private void addEvent(OneDay event) {
		// 배열 재할당
		if(capacity >= eventsCount) {
			reallocate();
		}
		
		events[eventsCount++] = event;
	}

	private void reallocate() {
		Event[] temp = new Event [capacity * 2];
		for (int i=0; i<eventsCount; i++) {
			temp[i] = events[i];
		}
		
		events = temp;
		capacity = events.length;
	}

	private MyDate parseDateString(String dateStr) throws Exception {
		String[] dateArray = dateStr.split("/");
		
		if(dateArray.length != 3) {
			// Invalid date format exception
			throw new Exception("Invalid date format (date format 'yyyy/MM/dd')");
		}
		
		int year = Integer.parseInt(dateArray[0]);
		int month = Integer.parseInt(dateArray[1]);
		int day = Integer.parseInt(dateArray[2]);

		return new MyDate(year, month, day);
	}
	
	public static void main(String[] args) {
		Scheduler app = new Scheduler();
		try {
			app.ProcessCommand();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}	
}