Sam Story

JAVA - 인터페이스 분리 원칙 본문

JAVA

JAVA - 인터페이스 분리 원칙

Sam H 2024. 10. 16. 18:18

지난번에 이어서 이번엔

인터페이스 분리원칙에 대해 포스팅 해보려 한다.

 

SOLID 원칙에 관한 내용은 아래 포스팅 참고

 

https://samtistory.tistory.com/49

 

객체지향 프로그래밍 SOLID 원칙

지난번 포스팅 했던 객체지향 프로그래밍에 이어서객체지향 프로그래밍의 5가지 원칙 SOLID 원칙에 대해 포스팅 하려한다.  SOLID 원칙 이란? SOLID란 객체 지향 프로그래밍을 하면서 지켜야하

samtistory.tistory.com

 

 

4. 인터페이스 분리 원칙(ISP)

 

인터페이스 분리 원칙은 인터페이스를 각각 사용에 맞게 끔 잘게 분리해야 한다는 원칙이다.

단일 책임의 원칙이 클래스 단일 책임을 강조한다면,

인터페이스 분리원칙은 인터페이스의 단일 책임을 강조하는 것으로 보면 된다.

 

인터페이스 분리 원칙은 인터페이스를 사용하는 클라이언트를 기준으로 분리함으로써,

클라이언트의 목적과 용도에 적합한 인터페이스 만을 제공하는 것이 목표이다.

 

다만 인터페이스 분리 원칙에서 주의해야 할점은 한번 인터페이스를 분리하여

구성해놓고 나중에 무언가 수정사항이 생겨서

추가로 인터페이스들을 분리하는 행위를 가하지 말아야 한다.

 

 

즉, 인터페이스 분리 원칙이란 인터페이스를 잘게 분리함으로써, 

클라이언트의 목적과 용도에 적합한 인터페이스 만을 제공하는 것이다.

 

 

 

인터페이스 분리 원칙은 마치 단일 책임 원칙과 비슷하게 보이는데,

SRP 원칙이 클래스의 단일 책임을 강조한다면, ISP는 인터페이스의 단일 책임을 강조한다고 말할 수 있다.

 

다만 유의할 점은 인터페이스는 클래스와 다르게 추상화이기 때문에

러개의 역할을 가지는데 있어 제약이 없긴 하다.

 

즉, SRP 원칙의 목표는 클래스 분리를 통하여 이루어진다면,

ISP 원칙은 인터페이스 분리를 통하여 이루어 진다고 볼 수 있다.

 

또한 SRP 원칙의 클래스 책임의 범위에 대해 분리 기준이 다르듯이, 인터페이스를 분리하는 기준은 상황에 따라 다르다.

핵심은 관련 있는 기능끼리 하나의 인터페이스에 모으되 지나치게 커지지 않도록 크기를 제한하라는 점이다.

 

 

아래 그림을 통해 알기 쉽게 보자.

 

동물 인터페이스를 만들고

코끼리 클래스와 호랑이 클래스를 만들어서 가각 동물 인터페이스를 implements 해준다.

 

이렇게 해줬을때 생기는 문제점은

코끼리 클래스의 경우 고기를 먹는 메서드와 털갈이 메서드의 경우 쓰이지 않기 때문에

이 메서드에 대한 예외처리를 해줘야 한다는 것이다.

 

결국 필요하지도 않은 기능을 어쩔 수 없이 구현해야하는 낭비가 발생한다.

 

이런 상황이 발생하지 않게 인터페이스 분리 원칙을 준수하며 수정하게 되면

 

동물 인터페이스를 상속받는 인터페이스들을 만들어서 각각의 필요한 기능들을 구현해 주는 것이다.

 

이렇게 각각의 기능에 맞게 인터페이스를 잘게 나눠주는것이 인터페이스 분리 원칙을 지키는 것이다.

 

 

그럼 이러한 그림으로 나타낸 내용을 코드로 한번 살펴보자.

 

 

Animal Interface

public interface Animal {

    // 숨을 쉰다
    void breath();

    // 고기를 먹는다
    void eatMeat();

    // 풀을 먹는다
    void eatGrass();

    // 물을 마신다
    void drinkWater();

    // 털갈이를 한다
    void molting();

}

 

 

Elephant class , Tiger Class

public class Elephant implements Animal{

    @Override
    public void breath() {
    
        System.out.println("코끼리가 숨을 쉽니다.");
        
    }

    // 사용할 수 없는 메서드에 대한 예외처리를 해줘야함.
    @Override
    public void eatMeat() {
    
        System.out.println("육식을 할 수 없습니다.");
        
    }

    @Override
    public void eatGrass() {
    
        System.out.println("코끼리가 풀을 먹습니다.");
        
    }

    @Override
    public void drinkWater() {
    
        System.out.println("코끼리가 물을 마십니다.");
        
    }

    // 사용할 수 없는 메서드에 대한 예외처리를 해줘야함.
    @Override
    public void molting() {
    
        System.out.println("털갈이를 할 수 없습니다.");
        
    }

}

public class Tiger implements Animal{

    @Override
    public void breath() {
    
        System.out.println("호랑이가 숨을 쉽니다.");
        
    }

    @Override
    public void eatMeat() {
    
        System.out.println("호랑이가 육식을 합니다.");
        
    }
    
    // 사용할 수 없는 메서드에 대한 예외처리를 해줘야함.
    @Override
    public void eatGrass() {
    
        System.out.println("초식을 할 수 없습니다.");
        
    }

    @Override
    public void drinkWater() {
    
        System.out.println("호랑이가 물을 마십니다.");
        
    }

    @Override
    public void molting() {
    
        System.out.println("호랑이가 털갈이를 합니다.");
        
    }

}

 

이렇게 Interface를 이용해 Tiger class와 Elephant class를 작성하게 되면

위에서 말했던 불필요한 메서드에 예외처리를 해줘야 한다.

 

 

그러면 인터페이스 분리원칙을 지킨 코드를 한번 보도록 하자.

 

Animal2 Interface

public interface Animal2 {

    // 숨을 쉰다
    void breath();

    // 물을 마신다
    void drinkWater();

}

 

숨을 쉬는것과 물을 마시는것은 동물이라면 무조건 수행해야 하는 내용이기 때문에 Animal2 Interface에 작성해주고

 

 

Herbivore Interface , Carnivore Interface , CanMoltingAnimal  Interface

// 초식동물 인터페이스
public interface Herbivore extends Animal2{

    void eatGrass();

}

// 육식동물 인터페이스
public interface Carnivore extends Animal2{

    void eatMeat();

}

// 털갈이 가능 동물 인터페이스
public interface CanMoltingAnimal extends Animal2{

    void molting();

}

 

각 동물별 속성에 맞게 Interface를 나눠주고 Animal2 를 상속받는 인터페이스를 만들어준다.

각 인터페이스별로 메서드를 작성해서 인터페이스를 분리해준다.

 

 

Elephant2 class , Tiger2 class

public class Elephant2 implements Herbivore{
    @Override
    public void breath() {

        System.out.println("코끼리가 숨을 쉽니다.");

    }

    @Override
    public void drinkWater() {

        System.out.println("코끼리가 물을 마십니다.");

    }

    @Override
    public void eatGrass() {

        System.out.println("코끼리가 풀을 먹습니다.");

    }

}


public class Tiger2 implements Carnivore,CanMoltingAnimal{

    @Override
    public void breath() {

        System.out.println("호랑이가 숨을 쉽니다.");

    }

    @Override
    public void drinkWater() {

        System.out.println("호랑이가 물을 마십니다.");

    }

    @Override
    public void molting() {

        System.out.println("호랑이가 털갈이를 합니다.");

    }

    @Override
    public void eatMeat() {

        System.out.println("호랑이가 육식을 합니다.");

    }

}

 

 

이렇게 잘게 분리된 인터페이스를 클래스가 지원되는 기능만을 선별하여

implements 하면 인터페이스 분리 원칙이 지켜지게 되는 것이다.

 

 

그리고 인터페이스 분리 원칙에 있어서 중요한 것은

한번 인터페이스를 분리하여 구성해놓고 나중에 수정사항이 생겨

또다시 인터페이스들을 분리하는 행위를 하면 안된다는 것이다.

 

이미 구현되어 있는 프로젝트에 또 인터페이스들을 분리한다면,

이미 해당 인터페이스를 구현하고 있는 온갖 클래스들과

이를 사용하고 있는 사용자에서 문제가 일어날 수 있기 때문이다.

'JAVA' 카테고리의 다른 글

JAVA - 의존 역전 원칙  (2) 2024.10.20
JAVA - 리스코프 치환 원칙  (0) 2024.10.15
JAVA - 개방 폐쇄 원칙  (0) 2024.10.14
JAVA - 단일 책임의 원칙  (1) 2024.10.12
JAVA - 2~36진수 기수변환  (0) 2024.09.29