2009-11-25 10 views
3

이것은 이론적 인 질문입니다. 로깅이 주 목적 인 클래스 내에 로깅이 있어야합니까?로깅이 아닌 주 목적의 클래스 내에 로깅이 있어야합니까?

다음은 숫자에 대한 계산을 수행 할 간단한 인터페이스입니다.

public interface ICalculation { 
    public int calculate(int number); 
} 

다음은 계산을 수행하고 일부 로깅을 수행하는 ICalculation 인터페이스의 구현입니다. 나는 이것이 매우 실용적인 접근이라고 생각한다. 계산 영역에서 일반적으로 볼 수없는 것을 받아들이는 생성자를 제외하고, 인라인 로깅은 논란의 여지가없는 관입입니다.

public class ReallyIntenseCalculation : ICalculation { 
    private readonly ILogger log; 

    public ReallyIntenseCalculation() : this(new DefaultLogger()) { 
    } 

    public ReallyIntenseCalculation(ILogger log) { 
     this.log = log; 
     log.Debug("Instantiated a ReallyIntenseCalculation."); 
    } 

    public int calculate(int number) { 
     log.Debug("Some debug logging.") 
     var answer = DoTheDirtyWork(number); 
     log.Info(number + " resulted in " + answer); 
     return answer; 
    } 

    private int DoTheDirtyWork(int number) { 
     // crazy math happens here 
     log.Debug("A little bit of granular logging sprinkled in here."); 
    } 
} 

ReallyIntenseCalculation에서 모든 로깅 코드를 제거한 후, 코드는 이제 명확 하나의 책임 것으로 보이는 있습니다.

public class ReallyIntenseCalculation : ICalculation { 

    public int calculate(int number) { 
     return DoTheDirtyWork(number); 
    } 

    private int DoTheDirtyWork(int number) { 
     // crazy math happens here 
    } 
} 

그래, ReallyIntenseCalculation의 내부 기능을 제거했습니다. 어떻게 그 기능을 외부화 할 수있는 방법을 찾을 수 있을까요? 데코레이터 패턴을 입력하십시오.

ICalculation을 꾸미는 클래스를 작성하면 Login을 믹스에 다시 추가 할 수 있지만 ReallyIntenseCalculation의 개인 메소드 내에서 발생하는보다 세분화 된 로깅의 일부가 손상 될 수 있습니다.

public class CalculationLoggingDecorator : ICalculation { 
    private readonly ICalculation calculation; 
    private readonly ILogger log; 

    public CalculationLoggingDecorator(ICalculation calculation, ILogger log) { 
     this.calculation = calculation; 
     this.log = log; 
     log.Debug("Instantiated a CalculationLoggingDecorator using " + calculation.ToString()); 
    } 

    public int calculate(int number) { 
     log.Debug("Some debug logging.") 
     var answer = calculation.calculate(number); 
     log.Info(number + " resulted in " + answer); 
    } 
} 

로깅 데코레이터를 사용하는 다른 장단점은 무엇입니까?

답변

3

나는 그것이 논쟁의 여지가 있다고 생각한다. 로깅이 단일 책임의 일부인 경우를 만들 수 있습니다.

그렇다면 나는 당신이 cross-cutting concerns의 관점에서 생각하고 있다고 생각합니다. 이것은 aspect-oriented programming과 관련이 있습니다. 사실, 로깅 코드는 AOP를위한 표준적인 예이다. aspect#과 같은 AOP 프레임 워크를 고려할 수 있습니다.

이와 같은 작업을 수행하는 장점은 물론 분해 가능성, 재사용 및 우려 사항 분리입니다.

0

몇 개의 로거가 있습니까? 계산기의 생성자를 구현하여 로거 인스턴스를 개인적으로 인스턴스화하거나 로거 싱글 톤 또는 로거 팩터 리 메서드를 사용하여 로깅 또는 로깅이 계산기의 공용 API에 속하지 않지만 계산기의 구현 부분에 포함되도록 할 수 있습니다.

+0

구현의 일부인 경우에는 생성자 매개 변수 여야합니다. 그렇지 않으면, API를 보는 사람은 어떻게 알 수 있습니까? 사용자가 다른 로거를 사용하고 싶지 않은 경우 어떻게됩니까? –

3

제이슨은 이것이 크로스 커팅 관심사라는 점에 동의합니다.

함수는 실제로 코드를 읽기 쉽도록 만들기 때문에 하나의 함수 만 수행해야하며 테스트하기가 더 쉽습니다. 예를 들어, 로그 파일이 꽉 차서 내 단위 테스트가 실패하면 내 메서드 내에서 로깅을 테스트하지 않기 때문에 혼란 스럽습니다.

여기서는 AOP가 최선의 선택이며 .NET PostSharp의 경우 최상의 옵션 일 수 있습니다.

AOP가 좋지 않은 경우 DI를 사용하고 로깅이있는 클래스 버전을 삽입하여 흐름을 테스트 할 수 있지만이 작업을 수행하려면 다음을 수행해야합니다. 삽입 된 클래스가 로깅을 수행 한 다음 로깅이없는 동일한 함수를 호출하므로 클래스 주위에 래퍼가 추가됩니다.

public class ReallyIntenseCalculation : ICalculation { 

    public int calculate(int number) { 
     return DoTheDirtyWork(number); 
    } 

    private int DoTheDirtyWork(int number) { 
     // crazy math happens here 
    } 
} 

public class CalculationLoggingDecorator : ICalculation { 
    ICalculation calculation; 
    ILogger log; 
    public CalculationLogging() { 
     this.calculation = new ReallyIntenseCalculation() ; 
     this.log = SomeLogger(...); 
     log.Debug("Initialized a CalculationLoggingDecorator using " + calculation.ToString()); 
    } 

    public int calculate(int number) { 
     log.Debug("Some debug logging.") 
     var answer = calculation.calculate(number); 
     log.Info(number + " resulted in " + answer); 
    } 
} 

이것은 당신의 장식과 비슷하지만, 당신이 비 로깅 버전에 대한 로깅 버전을 바꿀 때 모든 여분의 코드를 떨어 뜨, 당신은 ReallyIntenseCalculation가 사용되는 것을 보장하는 로깅 버전을 테스트하여 한 메소드는 한 번만 정의됩니다.

이것은 더 많은 작업이며 AOP가 바람직하지만 DI가 대안 일 수 있습니다.

업데이트 : 댓글에 기반.

이 인터페이스를 확장하는 클래스가 여러 개있는 경우 클래스가 폭발적으로 늘어날 수 있습니다.

AOP가 가장 좋은 방법이지만 개념은 DI가 아닌 일부 회사에게 어렵습니다.

각 구현마다 2 개의 클래스가 생길 수 있지만 로깅 정보는 여전히 다른 클래스에서 제거되고 각 클래스는 DI를 통해 주입되므로 두 개의 app.config 파일을 가질 수 있습니다. 하나는 모든 당신의 삶을 단순화하기 위해 로깅 클래스를 설정하고 프로덕션을 위해 하나를 설정합니다. 두 번째 클래스는 로깅 및 설정 정보 만 가지고 있으므로 너무 많은 추가 작업이 아니라고 생각합니다. 그러나 로깅에 대한 세세한 부분을 잃어 버렸습니다.

+0

예를 들어, ReallySimpleCalculation 및 AbsurdlyComplexCalculation과 같은 ICalculation을 구현하는 클래스를 더 소개하려면 어떻게해야합니까? 위의 코드가 클래스 폭발에 취약하지 않습니까? – JaredCacurak