본문 바로가기

개발/소프트웨어공학

SOLID - 의존성 역전 원칙(Dependency Inversion Principle)

객체 사이에 서로 도움을 주고 받으면 의존 관계가 발생한다. 의존 역전 원칙 DIP는 이러한 관계를 맺을 때의 가이드라인에 해당한다.

 

DIP는 의존 관계를 맺을 때 변화하기 쉬운 것 또는 자주 변화하는 것 보다는 변화하기 어려운 것 거의 변화가 없는 것에 의존하라는 원칙이다.

 

변하기 쉬운 것과 변하기 어려운 것은 정책, 전략과 같은 어떤 큰 흐름이나 개념 같은 추상적인 것은 변하기 어려운 것에 해당하고 구체적인 방식, 사물 등과 같은 것은 변하기 쉬운 것으로 구분하면 좋다.

 

객체지향의 관점에서는 변하기 어려운 추상적인 것들을 표현하는 수단으로 추상 클래스와 인터페이스가 있다.
DIP를 만족하려면 어떤 클래스가 도움을 받을 때 구체적인 클래스보다는 인터페이스나 추상 클래스와 의존 관계를 맺도록 설계해야 한다.
DIP를 만족하는 설계는 변화에 유연한 시스템이 된다.

 

인터페이스나 추상 클래스와 의존 관계를 맺도록 설계
인터페이스 = 변하지 않는 것
구체클래스 = 변하기 쉬운 것

 

DIP를 만족하면 의존성 주입(Dependency Injection)이라는 기술로 변화를 쉽게 수용할 수 있는 코드를 작성할 수 있다. 의존성 주입이란 말 그대로 클래스 외부에서 의존되는 것을 대상 객체에 인스턴스 변수에 주입하는 기술이다.
의존성 주입을 이용하면 대상 객체를 변경하지 않고도 외부에서 대상 객체의 외부 의존 객체를 바꿀 수 있다.

public class Kid {
    private Toy toy;

    public void setToy(Toy toy) {
        this.toy = toy;
    }

    public void play() {
        System.out.println(toy.toString());
    }
}

Kid 클래스에서 setToy 메서드로 아이가 가지고 노는 장난감을 바꿀 수 있다.
만약 로봇 장난감을 가지고 놀고 싶다면 다음 코드가 그 일을 해줄 것이다.

public class Robot extends Toy {
    public String toString() {
        return "Robot";
    }
}
public class Main {
    public static void main(String[] args) {
        Toy t = new Robot();
        Kid k = new Kid();
        k.setToy(t);
        k.play();
    }
}

레고를 가지고 놀고 싶어진다면 다음 코드로 적용이 가능하다.
Kid, Toy, Robot 등 기존 코드에 영향을 받지 않고 장난감을 바꿀 수 있다.

public class Lego extends Toy {
    public String toString() {
        return "Lego";
    }
}
public class Main {
    public class void main(String[] args) {
        Toy t = new Robot();
        Kid k = new Kid();
        k.setToy(t);
        k.play();
    }
}

만약 Kid 클래스가 다음 클래스 다이어그램처럼 Robot 클래스와 연관 관계를 맺는다면 어떤 일이 발생할까

public class Kid {
    private Robot toy;

    public void setToy(Robot toy) {
        this.toy = toy;
    }

    public void play() {
        System.out.println(toy.toString());
    }
}
public class Main {
    public static void main(String[] args) {
        Robot t = new Robot();
        Kid k = new Kid();
        k.setToy(t);
        k.play();
    }
}

이런 경우 레고로 장난감의 종류를 변경하려면 기존의 Kid 클래스가 다음처럼 바뀌어야 한다.

public class Kid {
    private Lego toy;

    //아이가 가지고 노는 장난감의 종류만큼 메서드가 존재해야 함.
    public void setToy(Lego toy) {
        this.toy = toy;
    }

    public void play() {
        System.out.println(toy.toString());
    }
}

장난감을 바꿀 때마다 코드를 계속 바꿔야 한다. 즉 DIP의 위반이 OCP의 위반을 초래한다.

 

#참고자료

https://blog.bitsrc.io/solid-principles-every-developer-should-know-b3bfa96bb688