개발

[객체지향프로그래밍] 의존과 DI

Ridiss 2022. 4. 15. 16:30

의존

 1. 기능 구현을 위해 다른 구성요소를 사용하는것

  A.의존의 예 : 객체 생성, 메서드 호출, 데이터 사용

 

2. 의존은 변경이 전파될 가능성을 의미

 A.의존하는 대상이 바뀌면 바뀔 가능성이 높아짐

  - 예 : 호출하는 메서드의 파라미티가 변경

  - 예 : 호출하는 메서드가 발생할 수 있는 익셉션 타입이 추가

 

순환 의존

 1. 순환 의존 -> 변경 연쇄 전파 가능성

  A.클래스, 패키지, 모듈 등 모든 수준에서 순환 의존이 없도록 해야한다.

  B.의존하는 대상이 많을수록 좋지 않음.

 

의존 대상 많을 때 

 1. 기능이 많은 경우
  A.한 클래스에서 많은 기능을 제공하는 경우 기능별로 분리 고려

   - 각클래스마다 의존이 줄어듬
   - 한 기능을 수정할때 다른기능과 관련된 코드를 수정하는일이 발생하지 않음
   - 개별 기능 테스트하기 편함

//변경전
public class UserService {
	public void regist(RegReq regReq) {
    	...
    }
    public void changePw(ChangeReq chgReq) {
    	...
    }
    public void blockUser(String id, String reason) {
    	...
    }
}

//변경후
public class UserRegisteService {
	public void regist(...) {
    	...
    }
}

public class ChangePwService {
	public void changePw(...) {
    	...
    }
}

public class UserBlockService {
	public void blockUser(...) {
    	...
    }
}

 2. 묶어보기

  A.몇 가지 의존 대상을 단일 기능으로 묶어서 생각해보면 의존 대상을 줄일 수 있음

 

의존 대상 객체를 직접 생성하면?

 1. 생성 클래스가 바뀌면 의존하는 코드도 바뀜

  A.추상화참조

 2. 의존 대상 객체를 직접 생성하지 않는 방법

  A.팩토리, 빌더
  B.의존 주입 (DI)

  C. 서비스 로케이터 

 

 

의존 주입 (Dependency Injection)

 1. 외부에서 의존 객체를 주입

  A.생성자나 메서드를 이용해서 주입

public class ScheduleService {
	private UserRepository repository;
    private Calculator cal;
    
    public ScheduleService(UserRepository repository) {
    	this.repository = repository;
    }
    public void setCalculator(Calculator cal) {
    	this.cal = cal;
    }
}

//초기화 코드
UserRepository userRepo = new DbUserRepository();
Calculator cal = new Calculator();

ScheduleService schSvc = new ScheduleService(userRepo);
schSvc.setCalculator(cal);

 

조립기 (Assembler)

 1. 조립기가 객체 생성, 의존 주입을 처리

  A.예 : 스프링 프레임워크

@Configuration
public class Config {
	@Bean
    public ScheduleService scheduleSvc() {
    	ScheduleService svc = new ScheduleService(repo());
        svc.setCalculator(expCal());
        return svc;
    }
    
    @Bean
    public UserRepository repo() {
    	...
    }
    @Bean
    public Calculator expCal() {
    	...
    }
}

//초기화
ctx = new AnnotationConfigApplicationContext(Config.class);
//사용할 객체 구함
ScheduleService svc = ctx.getBean(ScheduleService.class);
//사용
svc.getSchedule(..);

 

DI 장점 

1 . 상위 타입을 사용할 경우 의존 대상이 바뀌면 조립기(설정)만 변경하면 됨.

public class OrderService {
	private Notifier notifier;
    
    public OrderService(Notifier notifier) {
    	this.notifier = notifier;
    }
    public void order(OrderRequest req) {
    	..
        notifier.notify(..);
    }
}


@Configuration
public class Config {
	@Bean
    public Notifier notifier() {
    	return new EmailNotifier();
    }
    @Bean
    public OrderService orderService() {
    	return new OrderService(notifier());
    }
}

// 였던 부분에서 notifier에서 CompositeNotifer로 변경을 하고 싶다면 notifer부분만 바꾸면됨 
// OrderService는 바꿀필요가 없음

@Configuration
public class Config {
	@Bean
    public Notifier notifier() {
    	return new CompositeNotifier(
        	new EmailNotifier(),
            new KakaoNotifier()
        );
    }
    @Bean
    public OrderService orderService() {
    	return new OrderService(notifier());
    }
}

 

2. 의존하는 객체 없이 대역 객체를 사용해서 테스트 가능

private MemoryUserRepository userRepo = new MemoryUserRepository(); //가상의 DB
private ScheduleService svc = new ScheduleService();

@BeforeEach
public void init() {
	svc.setUserRepository(userRepo);
}

@Test
public void givenUser_noCheckPoint_then_getExpectedSchedule() {
	userRepo.addUser("1", new User(...)); //DI를 통해 userRepo를 구현해서 임의로 세팅가능함
    Schedule schedule = svc.getSchedule("1");
    assertEquals(EXPECTED, schedule.getType());
}

 

*의존 객체는 주입받도록 코드작성하는 습관

 

**출처**

인프런 -최범균님 강의

https://www.inflearn.com/course/%EA%B0%9D%EC%B2%B4-%EC%A7%80%ED%96%A5-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%9E%85%EB%AC%B8

'개발' 카테고리의 다른 글

REST API에 Under_Score(_)? Hyphen(-)?  (0) 2023.05.18
[Git] commit 되돌리기  (0) 2022.09.02
[객체지향프로그래밍] 다형성과 추상화  (0) 2022.05.06
[객체지향프로그래밍] 객체, 캡슐화  (0) 2022.04.28
디자인패턴  (0) 2022.03.31