11장. 시스템

ssu_hyun·2023년 9월 20일
0

Clean Code

목록 보기
12/12

관심사 분리

  • 시스템 제작(construction)과 사용(use)을 분리하라. (생성 로직과 사용 로직을 분리하면 모듈성이 높아진다.)

1) Main 분리

  • 생성과 사용의 분리
    • Main에서 Application 호출 전 Builder에 생성 요청 > Builder는 Configured Object 생성해 Main에 리턴 > Main은 Application에 Builder에서 넘겨준 Configured Object를 넘겨주어 로직에서 사용하게 함
    • 시스템에 필요한 객체 생성과 관련한 일은 Main에서 하고 Application은 그저 Main으로부터 생성된 객체를 사용하는 방식 (즉, Application은 Main이나 객체가 생성되는 과정을 전혀 모름)


2) 팩토리

  • 객체 생성 시점 Application이 제어

    - main에서 FactoryImpl 구현 > Factory에서 Configured Object 제작 > Main에서 이 생성 시점을 제어 (Item 제작과 생성의 분리)
    • 의존성 주입 (DI, Dependency Injection)
      - 분리시킨 의존성을 메서드/생성자/환경변수 등으로 주입해주는 형태
      - 객체에 특정 변수/객체(의존성)를 매개변수로 주입한다는 의미
      ex) saveData(Data data) : saveData는 Data라는 객체에 의존성이 강한 함수
      - 디자인 패턴 중 하나로 객체 간의 의존성을 자신이 아닌 외부에서 받아 유연하고 느슨한 결합을 통해 종속성을 감소시켜 변경에 민감하지 않아 코드의 유연성, 재사용성, 테스트 용이성을 개선시킴
      // SpellChecker는 KoreanDictionary에 의존
      public class SpellChecker
      {
      	Dictionary dictionary = new Koreandictionary();
      }
      
      // DI 방식 1 : 생성자 주입 (Constructor Injection)
      public class SpellChecker
      {
      	private Dictionary dictionary;
       
       public SpellChecker(Dictionary dictionary)
       {
       	this.dictionary = dictionary;
       }
      }
      
      // DI 방식 2 : 세터 주입 (Setter Injection)
      public class SpellChecker
      {
      	private Dictionary dictionary;
       
       // SpellChecker 객체 생성하는 곳에서 아래 메서드 실행
       public void setDictionary(Dictionary dictionary)
       {
       	this.dictionary = dictionary;
       }
      }


3) 확장

  • 소프트웨어 시스템은 물리적인 시스템과 다르다. 관심사를 적절히 분리해 관리한다면 소프트웨어 아키텍처는 점진적으로 발전할 수 있다.



횡단 관심사(cross-cutting concern)

  • 핵심적인 기능이 아닌 중간중간 삽입되어야 할 기능 관심들(기능/모듈들)
  • 관심사들은 디자인과 구현 면에서 시스템의 나머지 부분으로부터 깨끗이 분해되지 못하는 경우가 있을 수 있으며 분산(코드 중복)되거나 얽히는(시스템 간의 상당한 의존성 존재) 일이 일어날 수 있다.

    AOP (Aspect Oriented Programming)

    • 관점 지향 프로그래밍
    • 횡단 관심사항의 기능을 모듈화하여 중복을 최소화하면서, 핵심관심사항에 집중하도록 하는 프로그래밍 기법
    • 소스코드 상에서 핵심적인 관점과 부가적인 관점으로 나누어보고 이를 기준으로 각각 분리하는 것 의미

    • Aspect (관심 모듈) : 횡단 관심사를 모듈화한 것 (Advice + PointCut)
    • Target : Aspect를 적용하는 곳, 부가 기능을 적용할 대상
    • Advice : Aspect에서 수행해야 하는 작업
    • JoinPoint : 코드 실행 중 Aspect가 적용될 수 있는 특정 지점
    • PointCut : 어느 부분에 Advice를 적용할지를 정의. 메서드 호출, 필드 엑세스 등의 특정 지점
    • Weaving : Aspect 코드를 어플리케이션 코드에 적용하는 프로세스



횡단 관심사 해결을 위한 자바에서 사용하는 Aspect or Aspect와 유사한 메커니즘 3가지

  1. 자바 프록시

    • 단순한 상황에 적합
    • 프록시를 사용하면 깨끗한 코드를 작성하기 어려우며 시스템 단위로 실행 지점을 명시하는 매커니즘도 제공하지 않는다.

    프록시 (Proxy)

    • 어떤 다른 객체나 서비스에 대한 접근을 제어하거나 대리하는 객체
    • 클라이언트와 실제 객체 사이에 중간 역할을 수행하며, 클라이언트가 실제 객체에 직접 접근하지 않고 프록시를 통해 상호 작용하게 함
    • 보안 및 권한 관리, 로깅 및 모니터링, 캐싱, 원격 호출, 지연 로딩 등의 용도로 사용
    • 객체 간의 상호작용을 보다 유연하게 제어하고 확장성을 높이는데 도움
    • 프록시의 단점 : 코드의 양과 크기 (프록시를 사용할 경우 깨끗한 코드를 작성하기 어렵다.)
    using System;
    
    // 실제 서비스를 나타내는 인터페이스
    public interface IService
    {
    	void DoSomething();
    }
    
    // 실제 서비스를 구현한 클래스
    public class RealService : IService
    {
    	public void DoSomething()
        {
        	Console.WriteLine("RealService is doing something.");
        }
    }
    
    // 프록시 클래스 (POJO)
    public class ProxyService : IService
    {
    	private IService realService;
        
        public ProxyService()
        {
        	// 실제 서비스 객체 생성
            realService = new RealService();
        }
        
        public void DoSomething()
        {
        	// 프록시에서 추가적인 작업 수행 가능
            Console.WriteLine("ProxyService is doing something before calling the real service");
            
            // 실제 서비스 메서드 호출
            realService.DoSomething();
            
            // 프록시에서 추가적인 작업 수행 가능
            Console.WriteLine("ProxyService is doing something after calling the real service");
        }
    }
    
    class Program
    {
    	static void Main()
        {
        	// 클라이언트 코드는 프록시를 사용해 서비스 호출
            IService service = new ProxyService();
            service.DoSomething();
        }
    }
  2. 순수 자바 AOP 프레임워크

  • AOP 언어인 AspectJ를 사용하지 않는 방법
  • 다른 라이브러리나 프레임워크에 종속되지 않은 POJO(Plain Old Java Object) 객체만 남음
  • 테스트 하기 쉬움
  1. AspectJ
  • AOP를 구현하는 방법을 담은 언어 (스프링AOP보다 많은 기능들을 제공함)



    테스트 주도 시스템 아키텍처 구축

  • 코드 수준에서 아키텍처 관심사를 구분할 수 있다면, 진정한 테스트 주도 아키텍처 구축이 가능

  • 좋은 API는 걸리적거리지 않아야 한다.

  • 최선의 시스템 구조는 각기 POJO(또는 다른) 객체로 구현되는 모듈화된 관심사 영역(도메인)으로 구성된다. 이렇게 서로 다른 영역은 해당 영역 코드에 최소한의 영향을 미치는 관점이나 유사한 도구를 사용해 통합한다.

    의사 결정을 최적화하라

    모듈을 나누고 관심사를 분리하면 지엽적인 관리와 결정이 가능해진다.

    명백한 가치가 있을 때 표준을 현명하게 사용하라.

    표준 을 사용하면 아이디어와 컴포넌트를 재사용하기 쉽고, 적절한 경험을 가진 사람을 구하기 쉬우며, 좋은 아이디어를 캡슐화하기 쉽고, 컴포넌트를 엮기 쉽다. 하지만 때로는 표준을 만드는 시간이 너무 오래 걸려 업계가 기다리지 못한다. 어떤 표준은 원래 표준을 제정한 목적을 잊어버리기도 한다.

    시스템은 도메인 특화 언어가 필요하다.

    도메인 특화 언어(Domain-Specific Language, DSL)를 사용하면 고차원 정책에서 저차원 세부사항에 이르기까지 모든 추상화 수준과 모든 도메인을 POJO로 표현할 수 있다.



    결론

  • 시스템 역시 깨끗해야 함

  • 모든 추상화 단계에서 의도는 명확히 표현해야 한다. 그러려면 POJO를 작성하고 관점 혹은 관점과 유사한 메커니즘을 사용해 각 구현 관심사를 분리해야 한다.

  • 설계시 시스템이든 개별 모듈이든, 실제로 돌아가는 가장 단순한 수단을 사용해야 한다.




Reference

[클린 코드] 11장 - 시스템 (Systems)

0개의 댓글