본문 바로가기

Develop/.NET 가이드

[C#] 리플렉션 Reflection (4) : 복잡한 if 문 대신 리플렉션으로 생성 함수 호출하기

반응형

Refleciton
Reflection

복잡한 if 문 대신 리플렉션으로 객체 생성하기

프로젝트를 진행하면서 아래 예제 코드와 같이 형타입에 따라 다른 객체를 생성해야 하는 경우가 있었습니다.

if( data is DataTypeA a)
{
    var model = new DataModelA(a);
    list.Add(model);
}
else if( data is DataTypeB b)
{
    var model = new DataModelB(b);
    list.Add(model);
}
// 엄청 긴 if 문~

위 예제 코드와 같은 구조로 개발을 하게 되면 데이터 타입이 추가될 때마다, if 문은 계속 길어질 수 밖에 없고 데이터 타입에 따라 DataModel을 생성하는 논리가 달라지면서 if 문 내부도 복잡해져갔습니다. 이러한 구조로 인해 점점 코드는 더러워지게 됩니다. 그래서 저는 이를 해결하기 위해 아래와 같이 리플렉션을 활용해서 DataModel을 생성하는 논리를 분리하였습니다.

// DataType에 따라 DataModel을 생성해주는 Factory 클래스
static class DataFactory
{
    // DataModel 을 생성하는 함수를 캐싱하기 위한 속성이다.
    private static IDictionary<MethodInfo, ParameterInfo> _createDataMethods;
    private static IDictionary<MethodInfo, ParameterInfo> CreateDataMethods
    {
        get
        {
            // Singleton 패턴을 활용한다.
            if(_createDataMethods is null)
            {
                _createDataMethods = new Dictionary<MethodInfo, ParameterInfo>();
                // DataFactory 클래스에서 static 이면서 private 또는 protected 인 함수의 methodInfo를 수집한다.
                var methodInfos = typeof(DataFactory).GetMethods(BindingFlags.Static | BindingFlags.NonPublic);
                foreach (var methodInfo in methodInfos)
                {
                    // 수집한 methodInfo 중에 반환 타입이 DataType인 함수만 수집한다.
                    if (methodInfo.ReturnType == typeof(DataModel))
                    {
                        var parametersInfos = methodInfo.GetParameters();
                        // 수집한 함수의 변수가 1개이면서 DataType을 상속할 수 있는 타입인 함수만 수집한다.
                        if(parametersInfos.Length == 1 
                           && typeof(DataType).IsAssignableFrom(parametersInfos[0].ParameterType)
                        {
                            _createModelMethods.Add(methodInfo, parametersInfos[0]);
                        }
                    }
                }
            }
            return _createDataMethods;
        }
    }

    public static DataModel CreateDataModel(DataType dataType)
    {
        foreach(var pair in CreateDataMethods)
        {
            // dataType 를 DataModel로 변환하는 함수인지 확인한다.
            if(pair.Value.ParameterType == dataType.GetType())
            {
                return (DataModel)pair.Key.Invoke(null, new object[] { dataType });
            }
        }

        throw new InvalidCastException($"{dataType.GetType()}을 Model로 변환하는 함수가 없습니다.");
    }

    #region 변환 함수 목록
    private static DataModel CreateDataModel(DataTypeA dataType)
    {
        return new DataModelA(a);
    }

    private static DataModel CreateDataModel(DataTypeB dataType)
    {
        return new DataModelA(b);
    }

    #endregion

}

이제 if 문 대신 아래와 같이 쓰면 됩니다.

list.Add(DataFactory.CreateDataModel(data));
반응형