본문 바로가기
C#/Effective C#

[Effective C# Item 34] 함수를 매개변수로 사용하여 결합도를 낮춰라

by 코모's 2023. 1. 10.
반응형

보통의 경우엔 클래스 내의 메서드를 정의하기 위해서 베이스 클래스나 인터페이스를 정의하고 이렇게 정의된 내용을 기반으로 코딩한다. 하지만 이 외에도 '함수를 매개변수로 취하는 방식'을 활용한다면 기존의 컴포넌트나 라이브러리와 함께 사용해야 하는 코드를 개발할 때 상당히 큰 도움이 된다.

 

왜 함수를 매개변수로 취하는가?

함수를 매개변수로 취하게 되면 우선 개발자가 더 이상 구항 타입(concreate type)을 작성할 필요가 없다. 오히려 추상화된 정의를 통해 종속성을 다루는 것을 의미한다.

그리고 API를 좀 더 단순하게 만들 수 있다. 실제로 델리게이트를 사용하여 컴포넌트의 계약을 기술하면 클라이언트 측에서 코드를 사용하기가 쉬워진다.

또한 의존성을 낮춰 단위 테스트를 수행하기 쉬워지고 다른 환경에서 코드를 재사용하기 쉬워진다.

 

단점은?

함수를 매개변수로 사용하게 되면서 컴포넌트를 사용하는 측과 구현하는 측으로 나뉘게 된다. 그리고 이 과정에서 추가 작업을 할 수 밖에 없고 코드의 명확성도 떨어진다.

따라서 코드를 분리해서 사용자들이 얻을 수 있는 잠재적인 이득과 코드의 복잡도를 증가라는 단점 사이에서 적절한 균형을 가져야한다.

또한 클라이언트가 중간에 작업을 중단할 수 없기 때문에 CreateSequence() 메서드는 항상 요청된 개수만큼 요소를 생성한다.

 

예시) List.RemoveAll() 메서드

void List<T>.RemoveAll(Predicate<T> match);

 

.NET Framework를 설계한 개발자들은 이 메서드를 활용할 때 델리게이트가 아니라 인터페이스를 이용해서도 사용할 수 있도록 구현해뒀다.

// 추가적으로 부적절한 연관 관계가 생김
public interface IPredicate<T> 
{ 
	bool Match(T soughtObject) 
} 

public class List<T> 
{
    public void RemoveAll(IPredicate<T> match) 
    { 
    	// 생략
    }

    // 이하 생략

} 

// 이 인터페이스를 사용하려면 추가적인 작업이 필요하다. 
public class MyPredicate : IPredicate<int> 
{
	public bool Match(int target) => target < 100;
}

위 코드를 보면 그냥 델리게이트를 사용한 REmoveAll() 메서드를 하용하는 것이 훨씬 간편하다는 것을 알 수 있다. 인터페이스를 사용하는 것보다 델리케이트를 사용하는 편이 훨씬 낫다.

 

IPredicate<T>와 같은 인터페이스는 설사 이를 구현한다고 해도 이 타입이 무엇을 지원하는지가 명확하지 않다.

타입의 성격을 드러내기 위한 경우가 아니라면 인터페이스 보다는 델리게이트를 사용하는 것이 좋은 대안이 될 수 있다.

 

 

 

 

참조 - Effective C# <강력한 C# 코드를 구현하는 50가지 전략과 기법, 이펙티브>, 빌 와그너, 김명신, 한빛미디어

반응형