이 장에서는 좋은 시스템을 구성하기 위해 관심사의 분리를 잘해야 한다라고 말하고 있다.
class A {
public Service getService(){
if (service == null){
service = new MyServiceImpl(...);
}
return service;
}
}
위 예제를 보면 class A 에서는 2가지의 책임을 가지고 있다.
로직을 구성하는데 있어 MyServiceImpl 클래스를 생성하는 책임
본인의 비즈니스 로직을 진행하는 책임
SRP 원칙에 어긋남
-> MyServiceImpl이 변경되면, classA또한 변경이 될 수 있는 구조
위 책임은 다음과 같은 방법으로 줄일 수 있다.
class A {
public Service getService(){
if (service == null){
service = ServiceFactory.getMyServiceInstance();
}
return service;
}
}
추상 팩토리 패턴을 이용하면, Class A는 어떤 클래스가 service에 부여됐는지는 모른다.
다만 service가 가진 인터페이스를 통해서 오롯이 비즈니스 로직을 처리를 할뿐이다.
의존성 주입(Dependency Injection)
추상 팩토리로 주입 받은 객체가 어떤 객체인지는 모르지만, 객체의 라이프 사이클은 class A에 종속된다. (일반적인 Abstract Factory Pattern 라면..)
객체의 생성뿐만아니라 객체의 라이프 사이클, 주입하는 역할 또한 개발자가 아닌 프레임워크나, 기타 컨테이너가 관리하는 기법을 의존성 주입이라고 한다.
이를 IoC(Inverse of Conversion) 제어의 역전
이라고 말한다.
MyService myService = (MyService)(jndiContext.lookup("nameOfMyService"));
책에서는 Dependency Injection이라고 썼는데, 내가 생각하기에는 Dependency Lookup 방식
Dependency lookup은 개발자가 객체에 어떤 클래스를 주입해야 될지 선택하는 책임을 가진다.
Dependency Injection은 객체에 어떤 클래스를 주입해야 될지또한 컨테이너나 프레임워크가 가진다.
class A {
@Autowired
MyService myservice; //컨테이너에서 알아서 어떤 객체를 주입해야할지 결정한다.
public Service getService(){
return service;
}
}
스프링 예제 코드를 보는게 좋은 것 같아서 그렇게 보여주자
핵심관심과 횡단관심을 분리하라
핵심관심과 횡단관심에 대한 용어설명이 필요 할 것 같다.
다시 돌아와서 핵심관심과 횡단 관심을 분리하라
어떻게 보면 당연하다. 코드에 비즈니스 로직만 들어가있다면 당연히 응집도가 높은 코드가 될 것이고, 코드를 이해하는 것 또한 훨씬 수월할 것이다.
public Service getService(){
log.info("나는 로그를 써야하고");
transaction.begin();
lon.info("트랜잭션 처리도 할꺼고ㅈ");
transaction.commit();
log.info("다한 다음에");
return service; //본래의 비즈니스 로직이 나온다면 당연히 더러운 코드가 나올 수 밖에 없다.
}
그렇다고 또 로그를 없애자니, 트랜잭션을 없애는 것 또한 서비스의 기능이나, 유지보수에 안 좋은 영향을 끼칠 수 밖에 없다.
이를 해결하기 위해서 책은 2가지 패턴을 제시
자바프록시, 프록시패턴(Proxy Pattern)
자바 프록시는, 특정 언어에 종속되어있기 때문에, 패턴으로 대체 (대충 비슷한 논리라고만 생각하셈)
GoF 에서 제시한 패턴, 횡단관심 분리하기 위해 많이 사용
public class ServiceLogProxy implements Service{
Service service;
public ServiceLogProxy(Service service) {
this.service = service;
}
@Override
public Service getService() {
System.out.println("여기는 로그만 처리할꺼");
return service.getService();
}
}
public class MyServiceImpl implements Service{
@Override
public Service getService() {
//오롯이 비즈니스 로직에 집중
return this;
}
}
```
- Spring AOP, AspectJ
사실 자바 프록시를 사용하기 편하게 만든거라 보면됨.
특정 비즈니스 로직에 원하는 횡단관심을 주입하는 행위를 `위빙`(Weaving) 이라 하는데 해당 시점이 다르다.
Spring AOP : 런타임 시점에 Weaving, 가볍지만 제한적
AspectJ : 컴타일 시점에 Weaving, 컴파일 시간 증가, 거의 모든 시점에 횡단 관심을 주입가능
```gradle
plugins {
id 'java'
id "io.freefair.aspectj.post-compile-weaving" version "5.1.1"
}
group 'org.example'
version '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.aspectj:aspectjrt:1.9.6'
implementation 'org.aspectj:aspectjweaver:1.9.6'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.0'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.0'
}
test {
useJUnitPlatform()
}
스프링 없이 Gradle로 aspectJ 사용
@Aspect
public class AspectJ {
@Before("execution(* com.dg.*.get*(..))")
public void before(){
System.out.println("로그 처리");
}
@Before("execution(* com.dg.*.get*(..))")
public void trnsact(){
System.out.println("트랜잭션 처리");
}
}
위빙을 해준다.
암튼 결과는 같다.
밥벌이 해먹고 살려면 관심사 분리를 잘해서 유지보수 깔끔한 코드를 만들자.
근데 이게 왜 시스템 단위인지는 아직 잘 모르겠다.
제목을 관심사 분리라고 하면 개인적으로 더 직관적이었을듯