JAVA Basic) 인터페이스와 다형성
Programming/Java 기초

JAVA Basic) 인터페이스와 다형성

728x90

 

목차


     

    <인터페이스의 역할>

    '인터페이스(interface)'는 어디에 쓰이는 코드일까?
    자바 8에서 새롭게 추가된 '디폴트 메서드''정적 메서드 구현부(implementation part)'가 없다면 인터페이스는 그야말로 껍데기이다. 그렇다면 '메서드 선언부(declaration part)'만 있는 인터페이스는 대체 왜 쓰는지 살펴보자.

    인터페이스클라이언트 프로그램에 어떤 메서드를 제공하는 지 알려주는 '명세(specification)'또는 약속의 역할을 한다.

    예를 들면, Abc인터페이스를 구현한 A클래스가 있다. 이 클래스를 사용하는 Z프로그램이 있다고 가정.

    • Abc인터페이스에는 구현할 추상 메서드가 모두 선언되어 있고, 어떤 매개변수가 사용되는지, 어떤 자료형 값이 반환되는지 선언되어있다.
    • Z 프로그램에서 A클래스의 구현 코드 전체를 살펴보지 않고 Abc 인터페이스의 선언부만 봐도 이 A클래스를 어떻게 사용할지 알 수 있는 것이다.

    만약 Z프로그램에서 Abc인터페이스를 구현한 다른 클래스인 B를 사용하고 싶다면, 인터페이스 명세에 따라 A클래스에서 B클래스로 교체해서 사용할 수 있다.

    Abc abc;
    abc = new A();
    abc = new B();
    abc = new C();

    A, B, C클래스 모두 Abc인터페이스를 구현했으므로 위 처럼 Z프로그램에서 Abc 클래스형으로 선언하여 코드를 작성할 수 있다.

    정리하자면,

    • '인터페이스'의 역할인터페이스를 구현한 클래스가 어떤 기능의 메서드를 제공하는지 명시하는 것.
    • '클라이언트 프로그램'인터페이스에서 약속한 명세대로 구현한 클래스를 생성해서 사용하면 되는 것.

    <인터페이스와 다형성>

    인터페이스를 사용하면 '다형성을 구현'하여 확장성 있는 프로그램을 만들 수 있다.
    클라이언트 프로그램을 많이 수정하지 않고 기능을 추가하거나 다른 기능을 사용할 수 있는 것.

    다음 예제를 보자. '고객 상담 전화 배분 프로그램'을 만드는 시나리오다.

    예제 시나리오

    1. 먼저 상담원에게 전화 업무를 배분하는 기능을 구현하기 위해 'Scheduler 인터페이스'를 만든다.
      Scheduler 인터페이스에는 시나리오 1~3에서 모두 공통으로 사용하는 메서드를 선언.
      getNextCall()다음 전화를 가져오는 기능.
      sendCallToAgent()는 상담원에게 전화를 배분하는 기능을 담당.
      각 클래스는 인터페이스에 선언한 메서드를 정책에 맞게 구현하게 된다.
    2. Scheduler 인터페이스를 구현하는 RoundRobin(순서대로), LeastJob(짧은 대기열 우선), PriorityAllocation(우선순위에 따라) 3개의 클래스를 직접 구현해 어떻게 활용되는지 보자.

     

    package scheduler;
    
    public interface Scheduler {
    	public void getNextCall();
    	public void sendCallToAgent(); 
    }

    Scheduler 인터페이스는 추상 메서드 2개를 선언. 따라서 Scheduler 인터페이스를 구현하는 각 클래스들도 모두 2개의 추상 메서드를 구현해야한다.

     

    package scheduler;
    
    //상담원 한 명씩 돌아가며 동일하게 상담 업무 배분
    public class RoundRobin implements Scheduler{
    	@Override
    	public void getNextCall() {
    		System.out.println("상담 전화를 순서대로 대기열에서 가져옵니다.");
    	}
    	@Override
    	public void sendCallToAgent() {
    		System.out.println("다음 순서 상담원에게 배분합니다.");
    	}
    }

    RoundRobin클래스는 예제 시나리오 1번을 구현한 것.
    고객 센터에서 걸려온 상담 전화를 순서대로 가져와 상담원에게 배분한다.
    (실제 구현은 어려울 수 있으니 문장 출력으로 대신한다.)

     

    package scheduler;
    
    //상담 업무가 없거나 상담 대기가 가장 적은 상담원에게 배분
    public class LeastJob implements Scheduler {
    	@Override
    	public void getNextCall() {
    		System.out.println("상담 전화를 순서대로 대기열에서 가져옵니다.");
    	}
    	@Override
    	public void sendCallToAgent() {
    		System.out.println("현재 상담업무가 없거나 대기가 짧은 상담원에게 배분합니다.");
    	}
    }

    LeastJob클래스는 예제 시나리오 2번을 구현한 것.
    고객 센터에 걸려온 상담 전화를 순서대로 가져와 상담 업무가 없거나 대기가 짧은 상담원에게 배분.

     

     

    package scheduler;
    
    //고객 등급이 높은 고객의 전화부터 업무 능력이 높은 상담원에게 우선 배분
    public class PriorityAllocation implements Scheduler{
    	@Override
    	public void getNextCall() {
    		System.out.println("대기열에서 고객 등급이 높은 고객을 먼저 가져옵니다.");
    	}
    	@Override
    	public void sendCallToAgent() {
    		System.out.println("능률이 좋은 상담원에게 우선적으로 배분합니다.");
    	}
    }

    PriorityAllocation클래스는 예제 시나리오 3번을 구현한 것.
    고객 센터에 걸려온 상담 전화 중 고객 등급이 높은 고객의 전화를 우선으로 가져와 능률이 좋은 상담원에게 배분.

    이제 고객 상담 전화 배분 프로그램을 실행해보자.
    사용자가 콘솔에 문자를 하나 입력하면 그 입력 문자에 따른 배분 정책을 정하고 실행하는 프로그램이다.

    package scheduler;
    import java.io.IOException;
    
    public class SchedulerTest {
    	public static void main(String[] args) throws IOException {
    		//문자를 입력받는 System.in.read()를 사용하려면 IOException에서 오류를 처리해야 한다.
    		
    		System.out.println("전화 상담 할당 방식을 선택하세요.");
    		System.out.println("R : 한 명씩 차례로 할당");
    		System.out.println("L : 쉬고 있거나 대기가 짧은 상담원에게 할당");
    		System.out.println("P : 우선순위가 높은 고객 우선 할당");
    		
    		int ch = System.in.read();	//할당 방식을 입력받아 ch변수에 대입
    		Scheduler scheduler = null;
    		
    		if(ch == 'R' || ch == 'r') {
    			scheduler = new RoundRobin();
    		}// 입력받은 값이 R 또는 r이면 RoundRobin클래스 생성
    		
    		else if(ch == 'L' || ch == 'l') {
    			scheduler = new LeastJob();
    		}// 입력받은 값이 L 또는 l이면 LeastJob클래스 생성
    		
    		else if(ch == 'P' || ch == 'p') {
    			scheduler = new PriorityAllocation();
    		}// 입력받은 값이 P 또는 p이면 PriorityAllocation클래스 생성
    		
    		else {
    			System.out.println("지원되지 않는 기능입니다.");
    			ruturn;
    		}
    		
    		scheduler.getNextCall();
    		scheduler.sendCallToAgent(); //어떤 정책인가와 상관없이 인터페이스에 선언한 메서드 호출
    	}
    }
    • 문자를 입력받으면 입력 문자에 해당하는 배분 정책 클래스를 생성하여 대입. (int ch = System.in.read( ))
    • RoundRobin, LeastJob, PriorityAllocation클래스로 생성된 인스턴스는 모두 Scheduler형 변수에 대입할 수 있다. 그리고 사용할 인스턴스가 어떤 클래스로 생성되었는지와 상관 없이 인터페이스에서 제공하는 메서드를 호출하면 된다. 다형성을 구현한 것이라고 할 수 있다.

    SchedulerTest프로그램 실행 중 (입력 대기)
    'L'을 입력받고 메서드 실행 후 프로그램이 종료됨



    클라이언트가 클래스를 사용하는 방법

    앞에서 정의한 상담 전화 배분 정책은 언제든 바뀔 수 있다. 예를 들어, 이 고객 상담 전화 배분 프로그램을 판매하려 하는데...
    "우리는 자동 배분 방식이 아닌, 상담원이 전화를 직접 가져오는 방식을 원해요"
    또는 "VIP 전담 상담원을 따로 관리하여 VIP고객은 대기 없이 바로 상담받을 수 있게 하고싶어요" 등 여러 요구를 할 수 있습니다. 
    이런 경우 추가로 만들어야 하는 배분 정책은 앞에서와 마찬가지로 Scheduler 인터페이스를 구현하는 새 클래스로 만들면 된다. 어떤 클래스를 구현하건 클라이언트가 인터페이스를 구현한 클래스를 사용하는 방식은..

    scheduler.getNextCall();
    scheduler.sendCallToAgent();

    이 코드이다.

    이렇게 클라이언트 프로그램은 각 클래스의 구현 방법을 몰라도 인터페이스에서 선언된 매개변수, 반환 값을 보고 클래스를 사용할 수 있다.

    '인터페이스'는 구현된 클래스를 사용하는 '클라이언트 코드와 기능을 제공하는 코드 사이의 약속'이다.

     


    [Do it! 자바 프로그래밍 입문] 도서로 공부하며 정리한 글입니다.

    300x250