형식 매핑 및 확장 방법에 관한 모범 사례


15

C #에서 매핑 유형 및 확장 메서드 사용과 관련된 모범 사례에 대해 몇 가지 질문을하고 싶습니다. 이 주제에 대해 지난 몇 년 동안 여러 차례 논의되었지만, 많은 게시물을 읽었지만 여전히 의문이 있습니다.

내가 겪은 문제는 내가 소유 한 클래스를 "변환"기능으로 확장하는 것이 었습니다. 논리에 의해 사용될 객체를 나타내는 "Person"클래스가 있다고 가정 해 봅시다. 또한 외부 API의 응답을 나타내는 "고객"클래스가 있습니다 (실제로 둘 이상의 API가 있으므로 각 API의 응답을 공통 유형 : Person에 맵핑해야 함). 나는 클래스의 소스 코드에 액세스 할 수 있고 이론적으로 거기에 내 자신의 메소드를 구현할 수 있습니다. 고객을 Person으로 변환해야 데이터베이스에 저장할 수 있습니다. 이 프로젝트는 자동 매퍼를 사용하지 않습니다.

가능한 4 가지 해결책이 있습니다.

  1. Consumer 클래스의 .ToPerson () 메서드 간단하지만, Single Responsibility 패턴을 깨뜨리는 것처럼 보입니다. 특히 Consumer 클래스가 다른 클래스 (다른 외부 API에 필요함)에도 매핑되므로 여러 매핑 방법이 포함되어야합니다.

  2. Consumer를 인수로 사용하는 Person 클래스의 맵핑 생성자 또한 단일 책임 패턴을 위반하는 것처럼 쉽고 쉽습니다. 소비자와 동일한 데이터를 제공하지만 약간 다른 형식으로 다른 API의 클래스가 있으므로 여러 개의 매핑 생성자가 필요합니다.

  3. 확장 메소드가있는 변환기 클래스 이렇게하면 Consumer 클래스에 .ToPerson () 메서드를 작성할 수 있으며 다른 API가 자체 NewConsumer 클래스와 함께 도입되면 다른 확장 메서드를 작성하여 모두 동일한 파일에 유지할 수 있습니다. 확장 방법은 일반적으로 악의적이며 절대적으로 필요한 경우에만 사용해야하므로 의견이 나쁘다는 의견을 들었습니다. 그렇지 않으면이 솔루션을 좋아합니다

  4. 변환기 / 매퍼 클래스. 변환을 처리하고 소스 클래스 인스턴스를 인수로 사용하고 대상 클래스 인스턴스를 반환하는 메서드를 구현하는 별도의 클래스를 만듭니다.

요약하면, 내 문제는 질문의 수로 축소 될 수 있습니다 (위의 설명과 관련하여 모두).

  1. 컨슈머 클래스의 .ToPerson () 메소드와 같은 (POCO?) 오브젝트에 변환 메소드를 넣는 것이 단일 책임 패턴을 위반하는 것으로 간주됩니까?

  2. (DTO와 같은) 클래스에서 변환 생성자를 사용하는 것이 단일 책임 패턴을 깨는 것으로 간주됩니까? 특히 그러한 클래스가 여러 소스 유형에서 변환 될 수 있다면 여러 변환 생성자가 필요합니까?

  3. 원래 클래스 소스 코드에 액세스하는 동안 확장 메소드를 사용하는 것이 나쁜 습관으로 간주됩니까? 이러한 동작을 논리 분리를위한 실행 가능한 패턴으로 사용할 수 있습니까? 아니면 안티 패턴입니까?


는 IS PersonA 급 DTO는? 동작이 포함되어 있습니까?
Yacoub Massad

내가 아는 한 그것은 기술적으로 DTO입니다. 여기에는 확장 메소드로 "주입 된"로직이 포함되어 있지만이 로직은 "ConvertToThatClass"유형의 메소드로 제한됩니다. 이것들은 모두 레거시 방법입니다. 내 직업은 이러한 클래스 중 일부를 사용하는 새로운 기능을 구현하는 것이지만 일관성을 유지하는 것이 나쁜 경우 현재 접근 방식을 고수해서는 안된다는 말을 들었습니다. 나는 그것으로 스틱, 또는 다른 무언가를 시도해야하는 경우 확장 방법을 사용하여 변환이 기존의 접근 방식은 좋은 하나입니다 있는지 궁금 해요 그래서
하기 Emsi

답변:


11

컨슈머 클래스의 .ToPerson () 메소드와 같은 (POCO?) 오브젝트에 변환 메소드를 넣는 것이 단일 책임 패턴을 위반하는 것으로 간주됩니까?

그렇습니다. 전환은 또 다른 책임이기 때문입니다.

(DTO와 같은) 클래스에서 변환 생성자를 사용하는 것이 단일 책임 패턴을 깨는 것으로 간주됩니까? 특히 그러한 클래스가 여러 소스 유형에서 변환 될 수 있다면 여러 변환 생성자가 필요합니까?

그렇습니다. 전환은 또 다른 책임입니다. 생성자 또는 변환 방법 (예 :)을 통해 변경해도 차이가 없습니다 ToPerson.

원래 클래스 소스 코드에 액세스하는 동안 확장 메소드를 사용하는 것이 나쁜 습관으로 간주됩니까?

반드시 그런 것은 아닙니다. 확장하려는 클래스의 소스 코드가있는 경우에도 확장 메소드를 작성할 수 있습니다. 확장 방법을 만들지 여부는 그러한 방법의 특성에 따라 결정되어야한다고 생각합니다. 예를 들어 많은 논리가 포함되어 있습니까? 객체 자체의 멤버 이외의 다른 것에 의존합니까? 의존성이 작동하거나 복잡한 논리가 포함 된 확장 방법이 없어야한다고 말하고 싶습니다. 확장 방법에는 가장 간단한 논리 만 포함해야합니다.

이러한 동작을 논리 분리를위한 실행 가능한 패턴으로 사용할 수 있습니까? 아니면 안티 패턴입니까?

논리가 복잡하다면 확장 방법을 사용하지 않아야한다고 생각합니다. 앞에서 언급했듯이 가장 간단한 것에 만 확장 메소드를 사용해야합니다. 나는 전환을 간단하게 고려하지 않을 것입니다.

전환 서비스를 만드는 것이 좋습니다. 다음과 같이 단일 일반 인터페이스를 가질 수 있습니다.

public interface IConverter<TSource,TDestination>
{
    TDestination Convert(TSource source_object);
}

그리고 당신은 다음과 같은 변환기를 가질 수 있습니다 :

public class PersonToCustomerConverter : IConverter<Person,Customer>
{
    public Customer Convert(Person source_object)
    {
        //Do the conversion here. Note that you can have dependencies injected to this class
    }
}

또한 Dependency Injection 을 사용하여 및 IConverter<Person,Customer>사이의 변환 기능이 필요한 모든 클래스에 변환기 (예 :) 를 주입 할 수 있습니다 .PersonCustomer


내가 실수하지 않으면 IConverter프레임 워크에 이미 구현 대기 중입니다.
RubberDuck

@RubberDuck, 어디에 존재합니까?
Yacoub Massad

나는 우리가 여기서 찾고 IConvertable있는 것이 아닙니다 . 내 실수.
RubberDuck

아하! @YacoubMassad에 대한 생각을 찾았습니다. ConverterList호출 할 때 사용됩니다 ConvertAll. msdn.microsoft.com/ko-kr/library/kt456a2y(v=vs.110).aspx OP가 얼마나 유용한 지 잘 모르겠습니다.
RubberDuck

또한 관련이 있습니다. 다른 사람이 여기에서 제안한 접근 방식을 취했습니다. codereview.stackexchange.com/q/51889/41243
RubberDuck 2018

5

컨소시엄 .ToPerson()클래스의 메소드 와 같은 (POCO?) 오브젝트 안에 변환 메소드를 넣는 것이 단일 책임 패턴을 위반하는 것으로 간주됩니까?

예. Consumer클래스는 소비자와 관련된 데이터를 보유 (그리고 아마도 일부 작업 수행) 및 변환에 대해 책임을지지 않습니다해야 할 책임이 자신을 또 다른, 관련이없는 유형으로.

(DTO와 같은) 클래스에서 변환 생성자를 사용하는 것이 단일 책임 패턴을 깨는 것으로 간주됩니까? 특히 그러한 클래스가 여러 소스 유형에서 변환 될 수 있다면 여러 변환 생성자가 필요합니까?

아마. 일반적으로 도메인 및 DTO 개체 외부에 변환을 수행하는 메서드가 있습니다. 나는 종종 도메인 객체를 가져 와서 데이터베이스, 메모리, 파일 등으로 직렬화하는 저장소를 사용합니다. 이 논리를 도메인 클래스에 넣으면 테스트와 다른 것들에 나쁜 하나의 특정 직렬화 형식에 연결했습니다. 논리를 DTO 클래스에 넣으면 다시 도메인에 연결하여 테스트를 제한합니다.

원래 클래스 소스 코드에 액세스하는 동안 확장 메소드를 사용하는 것이 나쁜 습관으로 간주됩니까?

과용 될 수는 있지만 일반적으로 아닙니다 . 확장 메서드를 사용 하면 코드를보다 쉽게 ​​읽을 수있는 선택적 확장을 만듭니다. 그들은 단점이 있습니다-컴파일러가 해결 해야하는 모호성을 쉽게 만들 수 있습니다 (때로는 조용히). 확장 방법의 출처가 명확하지 않기 때문에 코드를 디버그하기가 더 어려울 수 있습니다.

귀하의 경우에는 변환기 또는 매퍼 클래스가 가장 간단하고 가장 간단한 접근법처럼 보이지만 확장 방법을 사용하여 잘못된 것을 생각하지는 않습니다 .


2

AutoMapper 또는 사용자 정의 매퍼를 사용하는 방법은 다음과 같습니다.

MyMapper
   .CreateMap<Person>()
   .To<PersonViewModel>()
   .Map(p => p.Name, vm => vm.FirstName)
   .To<SomeDTO>()
   .Map(...);

도메인에서

 db.Persons
   .ToListAsync()
   .Map<PersonViewModel>();

후드 아래에서 AutoMapper를 추상화하거나 자신의 매퍼를 굴릴 수 있습니다.


2

나는 이것이 오래되었다는 것을 알고 있지만 여전히 유쾌합니다. 이렇게하면 두 가지 방법으로 모두 변환 할 수 있습니다. Entity Framework로 작업하고 뷰 모델 (DTO)을 만들 때이 정보가 도움이됩니다.

public interface IConverter<TSource, TDestination>
{
    TDestination Convert(TSource source_object);
    TSource Convert(TDestination source_object);
}

public class PersonCustomerConverter : IConverter<Person, Customer>
{
    public Customer Convert(Person source_object)
    {
        //Do the conversion here. Note that you can have dependencies injected to this class
    }
    public Person Convert(Customer source_object)
    {
        //Do the conversion here. Note that you can have dependencies injected to this class
    }
}

AutoMapper가 실제로 간단한 매핑 작업을 수행하는 데 약간 도움이된다는 것을 알았습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.