Spring을 공부하다 보니 DI(Dependency Injection)이라는 부분이 정말 너무 이해가 되지 않았다.
그리고 더욱 슬픈 건 UML조차도 이해가 되지 않았다.
그래서 직접 공부 후 JAVA 예제, Intellij의 Diagram기능을 활용해서 UML로도 분석한 기록을 남긴다.
◆ 예제 코드
public interface Service {
void sendMessage(String message, String recipient);
}
> 서비스 인터페이스를 생성한다
public class FakeService implements Service {
@Override
public void sendMessage(String message, String recipient) {
System.out.println("Fake send message " + recipient + " to " + message);
}
}
public class RealService implements Service{
@Override
public void sendMessage(String message, String recipient) {
System.out.println("Real send message " + recipient + " to " + message);
}
}
> Service를 Impl (implements)하는 클래스 2개를 구현한다 (FakeService, RealService)
public class MessageSender {
private final Service service;
public MessageSender(Service service) {
this.service = service;
}
public void send(String message, String recipient){
service.sendMessage(message, recipient);
}
}
> 그리고 이 서비스들을 주입받아 사용하는 클래스 MessageSender를 구현한다
> 여기서 Service 인터페이스를 활용(다형성) 주입받은 sendMessage를 사용할 수 있게 한다
public class main {
public static void main(String[] args){
MessageSender messageSender = new MessageSender(new RealService());
messageSender.send("helloWorld Java", "hihihihi@email.com");
MessageSender fakeSender = new MessageSender(new FakeService());
fakeSender.send("helloWorld Java", "hihihihi@email.com");
}
}
> Messeage Sender에 각 서비스들을 주입하니 주입한 구현정보들이 출력되는 것을 알 수 있었다.
> 실행 후 콘솔은 아래와 같다.
< console >
Real send message hihihihi@email.com to helloWorld Java Fake send message hihihihi@email.com to helloWorld Java
◆UML ( Feat. Intelli J Ultimate )
> main은 MessageSender를 통해 주입받은 객체를 활용하는 것을 알 수 있다.
> MessageSender는 service라는 interface의 다형성을 주입받아 활용된다는 것을 알 수 있다.
public class BlueSky implements Sky {
@Override
public void Watching(int count, String name) {
System.out.println(name + " 에는 " + getOceanName()+" 을 보려고 " + count + " 명의 사람들이 바닷가를 찾았습니다.");
}
@Override
public String getOceanName() {
return "파란 하늘";
}
}
public class RedSky implements Sky {
@Override
public void Watching(int count, String name) {
System.out.println(name + " 에는 " + getOceanName()+" 을 보려고 " + count + " 명의 사람들이 바닷가를 찾았습니다.");
}
@Override
public String getOceanName() {
return "파란 하늘";
}
}
public class Ocean {
Sky newSky; // 포함 관계
public void showSky(Sky sky){ // Interface Sky를 매개변수로 받는다.
if (sky instanceof BlueSky) // sky가 BlueSky일땐 강릉으로
sky.Watching(10, "강릉");
else if(sky instanceof RedSky) // sky가 RedSky일땐 목포로
sky.Watching(100, "목포");
return;
}
public void changeOcean(){
newSky = new BlueSky();
newSky.Watching(10000, "뉴욕앞바다");
}
}
public class User {
public static void main(String[] args) {
// 다형성을 활용한 다양한 하늘 호출해보기
Ocean ocean = new Ocean();
ocean.showSky(new BlueSky());
ocean.showSky(new RedSky());
// 다형성을 활용한 바다 위치 바꾸기
ocean.changeOcean();
//ocean.newSky.Watching(10,"ddd"); // 오류가난다
}
}
/* CONSOLE OUTPUT
강릉 에는 파란 하늘 을 보려고 10 명의 사람들이 바닷가를 찾았습니다.
목포 에는 파란 하늘 을 보려고 100 명의 사람들이 바닷가를 찾았습니다.
뉴욕앞바다 에는 파란 하늘 을 보려고 10000 명의 사람들이 바닷가를 찾았습니다.
*/
나만의 충격포인트 1. 인터페이스 참조변수만으로도 메서드를 호출할 수 있단 말이야??
" Sky new = new RedSky() "를 통해서 main 메서드에 운영을 하는 것은 많이들 예제에서 나오는 부분이다
하지만 객체를 생성하지도 않았는데 메서드를 호출해서 틀을 만든다는 것은 직접 예제를 만들어보지 않고서는 느끼지 못하는 부분이었다
Ocean 클래스에서 Sky 타입을 가진 sky 변수로 메서드를 호출, 그리고 구현 객체를 가지고 메서드를 호출한다.
Ocean 클래스에서 개별 Sky를 모두 만들어줄 것인가?
결국 각 Sky에 대한 매써드 호출 하는 것이 아닌 하나의 참조변수로 각각의 메서드를 호출한다는 것
나도 다형성으로 한 발걸음은 다가선 게 아닐까..???
나만의 충격포인트 2. 포함 관계
흔히들 " 상속 " 으로 구현을 하는 것보다는 " 포함 " 관계로 작성을 하는 것이 운영에 도움이 된다고들 한다
근데 왜? 저렇게 해야 할까 라는 생각을 하게 되었는데 막상 예제를 만들어 보려고 하니 각각의 구현된 Sky 메서드들을 호출하려고 하니
포함관계 말고는 생각이 나지 않았다, 또한 상속관계로 구현을 하게된다면 Moon, Sun 과 같은 클래스를 추가한다고 했을때 얼마나 어려울지 다시한번 생각해 볼 수 있었다.
추후 BlackSky 클래스를 구현할지라도 Ocean클래스는 단순히
showSky에서 BlackSky 구현 부분만 추가해 주면 Ocean은 지속해서 사람들이 찾을 것이다
만약 다형성이 없었다면..?
Sky 별로 (RedSky, BlueSky) 오버로딩으로 메서드를 만들 생각을 하니 벌써 머리가 지끈지끈하다.