프로그래밍언어/Java

자바(Java) - Interface란 무엇인가(정의, 사용이유, 예제)?

멍토 2020. 8. 26.

1. 인터페이스란?

인터페이스는 자바에서 클래스들이 구현해야하는 동작을 지정하는 용도로 사용되는 추상 자료형이다.  

class 대신 interface라는 키워드를 이용하여 선언할 수 있으며 메소드 시그니처와 상수선언만을 포함할 수 있다.
(java 8이 나오면서 interface에서 default method 정의가 가능해졌다.)

클래스에서 인터페이스를 구현하기 위해서는 implements 키워드를 사용하여 구현하며,
하나의 클래스는 여러개의 인터페이스를 상속받아 구현할 수 있다.  

2. 인터페이스는 왜 써야 할까?

1. 여러명이서 작업을 할때 미리 인터페이스를 작성함으로써 메소드를 정할 수 있다.

2. 상속을 통한 이점을 누릴 수 있다.

3. 개방폐쇄(Open Close)법칙인 확장에는 열려있고 변경에는 닫혀있는 
클래스간 결합도(코드 종속성)를 낮춘 유연한 방식의 프로그래밍이 가능해진다.

예시

  1. 작업을 할 때 미리 정할 수 있다.
체스게임을 만든다고 해보자.

말을 만드는 사람은 이동하는 함수를 pieceName + Move라고 작명을 했는데

게임진행 파트를 만든사람은 movePieceName과 같이 서로 다르게 작성하고 있을 수 있다.

이럴때 미리 인터페이스를 정의한다면 구현내용과 상관없이 어떤기능을 하는지만 안다면 해당 함수명을 사용하면 된다.

 

public interface Piece {

    Piece move(final Position position, final Map<Position, Piece> pieces);

    List<Position> movablePositions(final Map<Position, Piece> pieces);
}

 

  1. 상속을 통한 이점을 누릴 수 있다.
체스의 모든 만들을 구현했다면 각각의 클래스가 존재 할 것이다.

인터페이스없이 구현했다면 다른 클래스이기 때문에 한번에 관리가 안되지만
우리는 이러한 말들을 한번에 관리하고 싶다.

인터페이스를 이용해 구현했기 때문에 말들이 어떻게 동작하는지는 모르지만
사용가능한 메서드 들을 알고있기 때문에 원하는 동작이 가능해진다.

따라서 아래와 같은 동작이 가능하다.

 

public class PieceTest {

    @Test
    public void test() {
        Board board = new Board();
        Map<Position, Piece> pieces = board.pieces();

        Piece piece = new Knight(Color.BLACK, Position.of(0, 0));
        piece.move(Position.of(1, 2), pieces);

        piece = new Bishop(Color.BLACK, Position.of(1,1));
        piece.move(Position.of(2,2), pieces);
    }
}

 

말은 Knight와 Bishop이지만 Piece클래스로 사용이 가능하다.

두 클래스가 어떻게 구현방식의 차이가 있는지는 모르겠지만
해당위치로 이동이 가능하다면 이동하고 이동이 불가능하다면 에러가 나는것을 기대할 수 있다.

 

  1. 코드 종속성이 낮은 유연한 프로그래밍이 가능해진다.
자동차에 들어가는 소프트웨어를 만든다고 가정해본다.

처음에는 한국에 자동차만 들어가는 소프트웨어였다.

한국에서 제한한 최대 속도는 120km라고 가정할때 아래와 같은 코드를 만들 수 있다.

 

public class Car {

    private static final int KOREA_LIMIT_SPEED = 120;

    public int movedCarSpeed(int speed) {
        return Math.min(KOREA_LIMIT_SPEED, speed);
    }
}

 

내가 더 넘는 속도를 입력하더라도 여기서 돌려주는 것은 최대 120까지이다.

그런데 문제가 생겼다.

중국에 자동차를 수출하게 되었는데 중국은 속도제한이 140km라고 한다.

그렇다면 ChinaCar라는 클래스를 만들어서 최대속도를 140으로 둬야할까?

일본에 수출한다면? 미국에도 수출한다면? 계속 수정을 해야할까?

이러한 방식은 유연하지 못한 프로그래밍 방식이라고 볼 수 있다.

이런 문제를 해결하기 위해 인터페이스를 사용한다.

 

public interface InterfaceCarSpeed {

    int speed(int speed);
}

public class KoreaCarSpeed implements InterfaceCarSpeed {

    private static final int MAX_SPEED = 120;

    @Override
    public int speed(int speed) {
        return Math.min(MAX_SPEED, speed);
    }
}

public class ChinaCarSpeed implements InterfaceCarSpeed {

    private static final int MAX_SPEED = 140;

    @Override
    public int speed(int speed) {
        return Math.min(MAX_SPEED, speed);
    }
}

public class JapanCarSpeed implements InterfaceCarSpeed {

    private static final int MAX_SPEED = 100;

    @Override
    public int speed(int speed) {
        return Math.min(MAX_SPEED, speed);
    }
}

 

public class Car {

    private final InterfaceCarSpeed carSpeed;

    public Car(final InterfaceCarSpeed interfaceCarSpeed) {
        this.carSpeed = interfaceCarSpeed;
    }

    public int movedCarSpeed(int speed) {
        return carSpeed.speed(speed);
    }
}

 

이와같은 방식으로 변경하게 된다면 앞으로 어느나라에 수출하더라도 
Car라는 객체는 수정을 하지않아도 된다.

이것이 변경에 대해서 닫혀있는 것이다.

그렇지만 우리는 수출할 나라가 생긴다면 해당 규격에 맞는 클래스를 생성하여 
주입을 해주는 것으로 확장이 용이해졌다.

이것이 확장에는 열려있는 것이다.


지금은 메소드가 1개여서 필요할까? 라는 생각이 들 수 있지만 
만약 수정 내용이 더 많다면 어떻게 될까?

Car객체에 자신이 사용하는 자원을 등록하고 
그것을 체크하는 기능까지 탑제되어 있다고 할때 
중국수소차, 중국전기차, 중국기름차, 
한국가스차, 한국전기차, 한국가스차, 
일본...

끝도없이 만들것인가? 조합이 늘어난다면 점점 부담이 커질것이다.

댓글

💲 광고입니다.