소프트웨어 팀도 도시처럼 각자 분업화(추상화와 모듈화)를 통해 잘 돌아가야한다. 이번 단원에서는 높은 추상화 수준, 즉 시스템 수준에서도 깨끗함을 유지하는 방법을 알아본다.
높은 추상화에서도 깨끗한 코드를 유지하려면 시스템 제작과 사용을 명확히 구분해야함.
시스템 제작과 사용을 명확히 분리하려면 관심사 분리가 필요함.
하지만 많은 사람들이 시작 단계
라는 관심사를 분리하지 않음 → 예시 : 초기화 지연
초기화 지연 기법의 문제
필요할 때까지 객체를 생성하지 않으며 null포인터를 반환하지 않는 장점이 있는 기법.
다른 클래스의 인수에 객체 생성을 의존.
테스트 문제.
단위 테스트에서 해당 메서드를 테스트하기 위한 테스트 전용 객체가 필요함.
→작게나마 srp 깬다.
따라서, 시스템 제작 시 초기화 지연 기법을 한번 이상 사용했을 시에는 설계 오류를 검증할 것.
시스템 생성과 사용을 분리하는 한가지 방법.
// 메인 클래스
public class Main {
public static void main(String[] args) {
// Animal 객체 생성 및 사용 예시
// 객체 생성 단계
Animal dog = new Dog();
Animal cat = new Cat();
// 사용 단계
System.out.print("Dog: ");
dog.makeSound(); // 멍멍!
System.out.print("Cat: ");
cat.makeSound(); // 야옹!
}
}
객체 생성 시점을 애플리케이션이 결정할 필요도 있음. 그럴때는 메인에서 추상 팩토리 패턴으로 해결한다.
// 메인 클래스
public class Main {
public static void main(String[] args) {
// Animal 생성을 담당하는 팩토리 객체 생성
AnimalFactory animalFactory = new AnimalFactory();
// Dog 생성 및 사용 예시
Animal dog = animalFactory.createAnimal("dog");
dog.makeSound(); // 멍멍!
// Cat 생성 및 사용 예시
Animal cat = animalFactory.createAnimal("cat");
cat.makeSound(); // 야옹!
}
}
main
LineItemFactory
구현체를 생성하여 OrderProcessing
에 전달합니다.LineItemFactory 구현
LineItemFactory
인터페이스를 구현한 클래스입니다.MakeLineItem
메서드를 통해 LineItem
객체를 생성합니다.LineItemFactory 인터페이스
MakeLineItem
메서드를 정의하는 인터페이스입니다.LineItem
객체 생성 로직을 캡슐화할 수 있습니다.OrderProcessing
LineItemFactory
를 통해 LineItem
객체를 생성합니다.LineItem
- 실제로 생성되는 객체입니다.
- 비즈니스 로직에서 사용됩니다.
main
main
함수는 프로그램 실행 시 LineItemFactory
의 구현체를 생성합니다.LineItemFactoryImpl
이라는 구체적인 팩토리 클래스를 생성할 수 있습니다.OrderProcessing
객체를 생성하고, LineItemFactory
구현체를 OrderProcessing
에 전달합니다.OrderProcessing
- OrderProcessing
클래스는 비즈니스 로직을 수행합니다.
- 필요한 시점에 LineItemFactory
의 MakeLineItem
메서드를 호출하여 LineItem
객체를 생성합니다.
- 생성된 LineItem
객체를 사용하여 비즈니스 로직을 처리합니다.
public interface LineItemFactory {
LineItem makeLineItem();
}
public class LineItem {
// LineItem 관련 필드 및 메서드
}
public class LineItemFactoryImpl implements LineItemFactory {
@Override
public LineItem makeLineItem() {
return new LineItem();
}
}
public class OrderProcessing {
private final LineItemFactory lineItemFactory;
public OrderProcessing(LineItemFactory lineItemFactory) {
this.lineItemFactory = lineItemFactory;
}
public void processOrder() {
// 비즈니스 로직 수행
LineItem item = lineItemFactory.makeLineItem();
// LineItem 사용
}
}
public class Main {
public static void main(String[] args) {
// LineItemFactory 구현체 생성
LineItemFactory factory = new LineItemFactoryImpl();
// OrderProcessing 객체 생성
OrderProcessing orderProcessing = new OrderProcessing(factory);
// 주문 처리
orderProcessing.processOrder();
}
}
Main
클래스에서는 LineItemFactory
의 구현체를 생성하여 OrderProcessing
객체에 전달합니다.
OrderProcessing
클래스에서는 LineItemFactory
인터페이스를 통해 LineItem
객체를 생성하고, 이를 비즈니스 로직에 사용합니다.
이렇게 함으로써, 객체 생성과 비즈니스 로직이 명확히 분리되어 코드의 유연성과 유지보수성이 향상됩니다.
이 예시를 통해 팩토리 패턴을 사용하여 시스템 생성과 사용을 분리하는 방법을 이해할 수 있습니다.
제어 역전(Inversion of Control, IoC) 기법을 의존성 관리에 적용한 것
제어 역전 (IoC) 개념
제어 역전이란?: 객체가 맡은 보조 책임을 새로운 객체에게 전적으로 떠넘겨, 새로운 객체는 단일 책임 원칙(Single Responsibility Principle, SRP)을 지키게 하는 기법
의존성 관리 맥락: 객체가 의존성을 직접 인스턴스로 만드는 대신, 이를 전담 메커니즘(main 루틴이나 특수 컨테이너 등)에 맡긴다.
시스템은 처음부터 완벽하게 설계되지 않기에 점진적으로 발전하고 확장 가능한 구조를 가지는 것이 중요
횡단 관심사는 여러 모듈에 걸쳐 공통적으로 나타나는 문제로, 이를 효과적으로 관리하는 것이 시스템의 복잡성을 줄이는 데 중요
횡단 관심사는 로깅, 보안, 트랜잭션 관리 등 여러 모듈에서 공통적으로 나타나는 문제이다. 이를 중앙에서 관리함으로써 코드 중복을 줄이고 일관성을 유지할 수 있다.
영속성 프레임워크는 데이터베이스와의 상호작용을 중앙에서 관리하여, 데이터 접근 로직을 단순화하고 일관성을 유지할 수 있다.
AOP는 횡단 관심사를 모듈 간에 분리하여, 코드의 중복을 줄이고 유지보수를 쉽게 하는 프로그래밍 기법이다. 이를 통해 시스템의 일관성과 유연성을 높일 수 있다.
자바 프록시는 동적 프록시를 사용하여 런타임에 객체의 동작을 변경하거나 확장할 수 있는 기능을 제공한다.
자바 프록시는 런타임에 객체의 동작을 가로채어 추가 기능을 수행하거나, 객체의 동작을 동적으로 변경할 수 있다.
JDK 동적 프록시와 바이트 코드 처리 라이브러리는 런타임에 객체의 동작을 동적으로 변경할 수 있는 강력한 도구를 제공한다. 이를 통해 객체의 동작을 유연하게 제어할 수 있다.