자바

인터페이스란?

제이G 2022. 8. 25. 22:50
  • "데이터 타입"의 정의
  • 자바에서의 데이터 타입

 

 


인터페이스가 도입된 이유를 설명하기 앞서 "데이터 타입"의 정의부터 살펴보자.

 

Data Type

1. 정의

정의: A data type is a category of data characterized by a collection of values (or domain) and a set of operations that act on those values. (출처: Data Structures & Algorithms in Java)

즉, 하나의 데이터 타입이 되려면 다음과 같은 것들이 정의되어 있어야 한다.

  1. 해당 타입에 속한 값들에 대한 정의
  2. 그 값들에 대해 적용할 수 있는 Operation에 대한 정의

 

2. 자바에서의 데이터타입

1. Primitive Type

int, char, double, long, byte, boolean, ...

 

2. Reference Type (User-defined Type)

1) 클래스와 인터페이스

 

- 클래스와 인터페이스도 데이터 타입이다.

(클래스와 인터페이스에도 해당 값들에 대해 적용할 수 있는 Operation에 대한 정의가 있기 때문)

 

- 클래스와 인터페이스로 정의한 타입은 모두 Reference Type이다. 왜 그럴까? 다음과 같은 코드를 살펴보자.

Student std1 = new Student();

std1을 편의상 Student 객체라고 부르지만, 실제론 new 키워드로 힙메모리 영역에 생성한 객체 인스턴스의 주소값이 std1 변수에 들어가 있다. 즉, 말 그대로 주소를 참조하기 때문에 Reference Type인 것이다.

 

 

2) 배열

- 자바에서 배열도 Reference Type이다. 배열 전체를 하나의 객체로 취급한다.

 

 

3. Abstract Data Type (ADTs) 와 Data Structures

데이터 타입의 정의를 다시 한번 상기해보자.

  1. 이 타입에 속할 수 있는 값들에 대한 정의
  2. 이 타입의 값들에 적용할 수 있는 Operation들에 대한 정의

 

이 데이터 타입은 "어떠한 관점에서 보느냐"에 따라 2가지로 정의할 수 있다.

  • 사용자 측면
  • 구현자 측면

① 사용자 관점에서의 데이터 타입 (ADTs)

정의: An ADT is a data type whose properties (domain and operations) are specified independently of any particular implementation.
- Separation of specification from implementation. (출처: Data Structures & Algorithms in Java)

정리하면 다음과 같다.

 

- 이 타입에 속할 수 있는 유효한 값들은?

- 이 타입의 값들에 대해 적용할 수 있는 Operation은?

- 기능(역할) 과 구현의 분리

 

생각해보면 당연하다. "사용자 관점"에선

해당 데이터 타입이 어떻게 구현되는지 보다 해당 데이터 타입으로 사용할 수 있는 "기능" 혹은 "역할"만 알면 될 것이다.

 

즉, ADTs 는 "역할"과 "구현"을 분리하는 일종의 Encapsulation 역할도 수행해준다.

 

 

② 구현자 관점에서의 데이터 타입 (Data Structures)

구현자 관점에선..

- Collection에 포함돼 있는 element 간의 논리적인 관계를 어떻게 표현할 것인가?

- 여러 Operation들의 효율성은?

 

 

 

4. 자바에서 ADTs를 정의하는 방법

인터페이스 포스팅인데 이런 설명은 왜하는거지? 라는 생각이 들 수 있겠다. 하지만, 여기서 인터페이스의 개념이 등장한다. 지금까지의 내용을 정리하면 다음과 같다.

 

데이터 타입은 "사용자 관점"에 따라 Abstract Data Types 로 볼 수 있다.

"사용"을 하는 관점에서의 데이터 타입이기 때문에 세부적인 구현사항을 알 필요 없이 해당 타입의 유효한 값들(domain)과 해당 타입에 적용할 수 있는 기능(역할)만 알면 된다.

 

인터페이스로 정의

일반적으로 ADTs는 인터페이스로 정의한다.(클래스로도 당연히 가능하다)

 

정수형을 담는 가방의 인터페이스는 다음과 같이 정의할 수 있다.

public interface IntBag {
	
    void add(int item);
    boolean remove(int item);
    int size();
}

 

1) 가방에 넣는 기능

2) 가방의 요소를 삭제하는 기능

3) 가방의 크기를 파악하는 기능

 

사용자는 인터페이스를 통해 해당 타입의 역할(기능)을 한 눈에 알 수 있게 된다.

 

 

해당 역할에 대한 구현은 클래스를 통해 다음과 같이 구현될 것이다.

public class IntBagImpl implements IntBag{

	@Override
	public void add(int item) {
		//구현
	}

	@Override
	public boolean remove(int item) {
		//구현
		return false;
	}

	@Override
	public int size() {
		//구현
		return 0;
	}
}

 

즉, 인터페이스를 통해

세상을 "역할"과 "구현"으로 나누어서 바라보고,

역할은 인터페이스로 정의하고,

역할을 구현하는 구현체는 클래스로 구현할 수 있게 된다.

 

3. 그래서 인터페이스 왜 쓰는건데? 인터페이스의 장점은?

1. 해당 타입의 역할(기능)을 한 눈에 확인할 수 있다.

 

 

2. 효과적인 통제수단

역할(기능)을 인터페이스로 정의하고 실제 구현체는 인터페이스에 정의된 기능을 반드시 수행해야 한다. 다수의 협업 중 인터페이스 설계를 통한 강제구현은 효과적인 통제수단이 될 수 있다.

(어떻게 구현하든 (막 구현하면 당연히 안되겠지만) 구현체는 인터페이스에 정의된 기능들을 반드시 수행해야하기 때문)

 

 

3. 다형성

인터페이스를 통해 유연하고, 변경이 용이한 개발을 할 수 있다.Why? - 인터페이스로 정의한 타입의 구현체를 갈아끼울 수 있다 !!! (다시 말하면, 어떠한 객체의 행동(역할)을 동적으로 할당할 수 있게된다)

 

다음의 코드를 보면 이해가 될 것이다.

 

public interface Bag {

    void add(int item);
    boolean remove(int item);
    int size();
}

...
//Bag intBag = new IntBagImpl();
//Bag doubleBag = new DoubleBagImpl();
Bag floatBag = new FloatBagImpl();

 

이 것이 왜 가능할까?- low level 에서의 이해 (다음에 업데이트 예정)

 

- high level 에서의 이해

출처: 인프런 김영한님 스프링 기초 강의

위에서 언급했듯이, 세상을 "역할"과 "구현"으로 나누어서 생각해볼 수 있고,"역할"은 인터페이스로 정의하고,"구현"은 클래스로 구현한다고 언급했다.또한, 실제 구현체는 인터페이스로 정의된 "역할"을 반드시 구현해야 한다.

 

즉자동차 역할을 인터페이스로 정의하고 각각의 구현체를 K3, 아반떼, 테슬라 모델3로 구현했다고 하자.운전자는 K3를 타다가 아반떼로 바꿔도 차를 탈 수 있다. (자동차가 바뀌어도 운전자한테 영향이 없다. 세부적으로 K3로 구현했든 아반떼로 구현했든 자동차 역할들은 반드시 구현하고 있을 것이기 때문.)

 

이처럼 인터페이스를 활용하면 유연하고 변경에 용이해지는 장점이 있다.

 

사실 다형성은 인터페이스만의 개념은 아니다. 상속받은 부모클래스나 추상 클래스로도 다형성을 활용할 수 있다. 핵심은 실제 실행 시에 쓰이는 객체가 코드에 고정되지 않도록(정적이 아닌 동적으로 할당할 수 있도록) 상위 형식(super type)에 맞춰 프로그래밍해서 다형성을 활용해야 한다는 것이다. 
(출처: 헤드퍼스트 디자인패턴)