Notice
Recent Posts
Recent Comments
Link
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
Tags
more
Archives
Today
Total
관리 메뉴

개발자이야기

의존성 주입(dependency injection) 본문

디자인패턴

의존성 주입(dependency injection)

개발자가되고싶어 2021. 6. 17. 01:58
의존성 주입

 

이번엔 의존성주입에 좀더 자세히 설명을 해볼까 합니다.

 

의존성 주입이 무엇일까?

개발을 하다보면 필연적으로 객체 내에서 객체를 사용하게 됩니다.

예를들어 자동차를 만든다고 가정해 봅시다. Car 클래스 라는 껍데기가 있고 그 안에 엔진, 바퀴 등등의 각각의 구성요소(객체)가 들어갑니다. 이것들을 직접 의존하는것이 아닌 추상된 것에 의존하고 외부로 부터 구체화 된 것을 주입 받겠다는 것 입니다.

 

위의 말은 간단하게 아래의 코드와 같이 특정 객체를 직접 의존해선 안된다는 것 입니다. 이것이 의존성을 갖게 되는것이죠.

public class Car {
    private Gasoline engine;
    private SnowTier tier;
}

그럼 장점은 무엇일까?

1. 코드의 재사용성이 높아집니다.

2. unit test가 용이해 집니다.

3. 확장성이 좋아집니다.

 

장점은 많습니다 하지만 위 정도만 알아도 충분할 것 같네요.

 

 

객체에 의존하지 마라, 인터페이스에 의존하라

 

코드를 보기 전 위의 키워드를 기억 합시다.

public class Person {

    Bus bus = new Bus(); //객체를 직접 생성하면 안됨!

    public void work() {
        System.out.println("출근시작!");
        System.out.println("버스타러 가야지!");
        move();
    }

    public void move() {
        bus.pay(new Money(2000)); //객체를 직접 생성하면 안됨!
        bus.move();
    }
}

위 코드의 상황을 봅시다. 출근을 하기위해 버스를 타러 가려나 봅니다. 버스는 잘 타겠군요!

출근을 지하철을 탈지도 모르는데 말이죠..

Person클래스에 Bus 객체와의 결합이 강하게 생겼습니다.

public class Bus {
    public void pay(Money money) {
        System.out.println(money.getMoney());
    }

    public void move() {
        System.out.println("버스이동중");
    }
}

public class Subway {
    public void payCard(Card card) {
        System.out.println(card.getCard());
    }

    public void move() {
        System.out.println("지하철이동중");
    }
}

Bus 클래스와 Subway 클래스는 2개의 메소드를 가지고 있고 심지어 돈을 지불하는 메소드의 이름조차 다르며 파라미터인 지불방식은 돈과 카드로 서로 다릅니다. 

 

버스가 파업을 하는바람에 지하철을 타는 상황이 되었습니다..

public class Person {

    Subway subway = new Subway();

    public void work() {
        System.out.println("출근시작!");
        System.out.println("지하철 타러 가야지!");
        move();
    }

    public void move() {
        subway.payCard(new Card("어른용카드"));
        subway.move();
    }
}

위처럼 겨우 Person 클래스에서 Bus 객체를 Subway 객체로 수정해주고 돈을 지불하는 방법도 카드로 지불하도록 바꿔주었습니다. 휴~

 

위처럼 되어있는 경우 이미 비대해진 서비스를 가지고있는 무언가라면 아마 Person을 수정하지 않고 Person2를 Subway 전용으로 새로만들어서 사용하게 될겁니다. 이미 구축되어진 서비스는 어떤 이슈가 생겨날지 모르니까요.

 

위의 경우 문제점은 두개입니다.

1. 사람이 버스와, 지하철 객체에 의존을 하고있다.

2. 이용요금의 지불방법이 다르다.

 

 

 

이제 개선을 해 봅시다.

먼저 운송수단과 요금의 지불방법을 interface를 구현하는 구현체로 만들어야 겠습니다.(다형성)

public class Bus implements Transport {

    @Override
    public void pay(PayMent payMent) {
        payMent.payment();
    }

    @Override
    public void move() {
        System.out.println("버스이동중");
    }
}

public class Money implements PayMent {

    int money;

    public Money(int money) {
        this.money = money;
    }

    @Override
    public PayMent payment() {
        return new Money(money);
    }
}

이제 버스는 Transport interface 구현하고 있게 되었고 지불 방법도 PayMent interface에 의존하게 되었습니다.

 

public class Person {

    Transport transport;
    PayMent payMent;

    public Person(Transport transport, PayMent payMent) {
        this.transport = transport;
        this.payMent = payMent;
    }

    public void work() {
        System.out.println("출근시작!");
        System.out.println("대중교통 타러 가야지!");
        move();
    }

    public void move() {
        transport.pay(payMent);
        transport.move();
    }
}

 

주입 방법으로는 생성자 주입을 사용했습니다.

오 벌써 무언가 깔끔해진거같네요!

이제 Person 클래스에선 더이상 특정 객체에 의존하지 않고 interface 에만 의존하게 되었습니다.

public class Client {
    public static void main(String[] args) {
        Person person = new Person(new Bus(), new Money(2000));
        person.work();
    }
}

출근시작!
대중교통 타러 가야지!
버스이동중

이제 Person객체를 생성할때 객체를 주입만 해주면 됩니다.

이처럼 의존성주입은 변화에 유연하게 대처할 수 있습니다.

실제로도 많은 코드들이 객체들간의 강한 결합으로 애를먹는경우가 많습니다.

반드시 알아야 하는 개념이며 필요한 내용이라고 생각합니다.

 

사실 개발을 하며 의문이 생기실 수도 있습니다. 아니 모든 객체에 interface가 생기면 파일이 너무 많아지는거 아냐??

위처럼 생각이 될 수도 있습니다. 하지만 설계 단계부터 interface를 우선 설계하고 진행한다면 규칙을 미리 정하여 개발 할 수 있을거라 생각합니다. 위처럼 Bus class와 Subway class를 만들때 메소드명과 메소드의 파라미터의 타입들이 달라지는 일을 막을 수 있으니까요. 그리고 의존성 주입은 다형성과 깊은 연관이 있습니다. 

 

틀린점은 지적 부탁드리며 읽어주셔서 감사합니다.