더 큰 객체 모델로 타사 라이브러리를 래핑하는 수동 노력을 어떻게 줄일 수 있습니까?


15

2012 년이 질문 의 저자 와 2013 년이 질문 의 저자와 마찬가지로, 응용 프로그램을 올바르게 테스트하기 위해 랩 해야하는 타사 라이브러리가 있습니다. 최고 답변은 다음과 같습니다.

항상 인터페이스 뒤에 타사 유형 및 메소드를 래핑하려고합니다. 이것은 지루하고 고통 스러울 수 있습니다. 때로는 코드 생성기를 작성하거나 도구를 사용하여이를 수행 할 수 있습니다.

필자의 경우 라이브러리는 객체 모델을위한 것이므로이 전략을 성공적으로 수행하기 위해 랩핑해야하는 많은 클래스와 메소드가 있습니다. 단순히 "지루하고 고통 스럽다"는 것 외에도 이것은 테스트에 대한 어려운 장벽이됩니다.

이 질문 이후 4 년 동안 격리 프레임 워크가 먼 길을 왔다는 것을 알고 있습니다. 내 질문은 : 이제 타사 라이브러리의 전체 배치 효과를 얻는 더 간단한 방법이 있습니까? 이 과정에서 어려움을 겪고 수동 노력을 줄이려면 어떻게해야합니까?


내 질문은 수동으로 줄 바꿈하는 노력을 줄이는 것이므로 처음에 연결 한 질문과 중복되지 않습니다. 다른 질문들은 포장이 합리적 일지 묻지 만 노력이 어떻게 작게 유지 될 수 있는지는 묻지 않습니다.


어떤 프로그래밍 언어와 어떤 종류의 라이브러리에 대해 이야기하고 있습니까?
— Doc Brown

@DocBrown C # 및 PDF 조작 라이브러리.
— Tom Wright

2
귀하의 질문을 다시 여는 데 도움이 되는 메타 에 대한 게시물을 시작했습니다 .
— Doc Brown

@DocBrown에게 감사드립니다-거기에 흥미로운 관점이 있기를 바랍니다.
— Tom Wright

1
다음 48 시간 동안 더 나은 답변을 얻지 못하면 이에 대해 현상금을 지급합니다.
— Doc Brown

답변:


4

매우 유비쿼터스하고 찾기가 쉽기 때문에 조롱 프레임 워크를 찾고 있지 않다고 가정하면 미리 주목할만한 몇 가지 사항이 있습니다.

  1. "항상"해야 할 일은 "아무것도"없습니다.
    항상 타사 라이브러리를 마무리하는 것이 가장 좋은 것은 아닙니다. 응용 프로그램이 본질적으로 라이브러리에 의존적이거나 문자 그대로 하나 또는 두 개의 핵심 라이브러리를 기반으로 구축 된 경우 시간을 낭비하지 마십시오. 라이브러리가 변경되면 응용 프로그램을 변경해야 합니다.
  2. 통합 테스트를 사용해도됩니다.
    이는 응용 프로그램에 내재되어 있거나 내재되어 있거나 쉽게 조롱 할 수없는 경계에서 특히 그렇습니다. 이러한 조건이 충족되면 포장 및 조롱 이 복잡하고 지루합니다. 이 경우 두 가지를 피할 수 있습니다. 포장하지 말고 조롱하지 마십시오. 통합 테스트 만 작성하십시오. (자동 테스트가 목표 인 경우)
  3. 도구와 프레임 워크는 논리적 복잡성을 제거 할 수 없습니다.
    원칙적으로 공구는 상용구 만 줄일 수 있습니다. 그러나 복잡한 인터페이스를 가져 와서 간단하게 만드는 자동화 가능한 알고리즘은 없습니다. 인터페이스 X를 가져와 사용자의 요구 에 맞게 조정하는 것은 물론 입니다. (당신은 그 알고리즘 을 알고 있습니다 !) 따라서, 얇은 래퍼를 생성 할 수있는 도구는 의심 할 여지 가 없지만, 결국에는 여전히 지능적으로 코딩해야하기 때문에 수동으로 랩퍼 뒤에 숨겨져 있어도 인터페이스에 대해

즉, 수업을 직접 언급하지 않기 위해 여러 언어로 사용할 수 있는 전술 이 있습니다. 경우에 따라 실제로 존재하지 않는 인터페이스 또는 씬 래퍼를 "숨길"수 있습니다. 예를 들어 C #에서는 다음 두 경로 중 하나를 사용합니다.

  1. 팩토리 및 암시 적 타이핑을 사용하십시오 .

이 작은 콤보로 복잡한 클래스를 완전히 감싸는 노력을 피할 수 있습니다.

// "factory"
class PdfDocumentFactory {
  public static ExternalPDFLibraryDocument Build() {
    return new ExternalPDFLibraryDocument();
  }
}

// code that uses the factory.
class CoreBusinessEntity {
  public void DoImportantThings() {
    var doc = PdfDocumentFactory.Build();

    // ... i have no idea what your lib does, so, I'm making stuff but.
    // but, you can do whatever you want here without explicitly
    // referring to the library's actual types.
    doc.addHeader("Wee");
    doc.getAllText().makeBiggerBy(4).makeBold().makeItalic();
    return doc.exportBinaryStreamOrSomething();
  }
}

보다 "기능적인"접근 방식을 통해 또는 사전 (또는 무엇이든 ) 에 저장하여 이러한 객체를 멤버로 저장하는 것을 피할 수있는 경우이 접근 방식은 핵심 비즈니스 엔터티가 정확히 알 필요없이 컴파일 타임 유형 검사의 이점을 갖습니다. 그들이 어떤 수업을하고 있는지

컴파일 타임에 팩토리가 리턴 한 클래스에는 실제로 비즈니스 오브젝트가 사용하는 메소드가 있어야합니다.

  1. 동적 입력을 사용하십시오 .

이것은 암시 적 타이핑 을 사용하는 것과 같은 맥락 이지만 또 다른 트레이드 오프가 필요합니다. 컴파일 형식 검사를 잃고 익명으로 외부 종속성을 클래스 멤버로 추가하고 종속성을 주입 하는 기능을 얻을 수 있습니다.

class CoreBusinessEntity {
  dynamic Doc;

  public void InjectDoc(dynamic Doc) {
    Doc = doc;
  }

  public void DoImortantThings() {
    Doc.addHeader("Wee");
    Doc.getAllText().makeBiggerBy(4).makeBold().makeItalic();
    return Doc.exportBinaryStreamOrSomething();
  }
}

ExternalPDFLibraryDocument앞서 언급 했듯이이 두 가지 전술을 모두 사용하여 조롱 할 때가되면해야 할 일이 있지만 어쨌든 해야 할 일 입니다 . 그리고이 구성으로 100 개의 얇은 작은 래퍼 클래스를 정의하는 것을 피할 수있었습니다. 라이브러리를 직접 보지 않고 단순히 대부분을 사용 했습니다 .

이 모든 것을 말하면서, 여전히 타사 라이브러리를 명시 적으로 래핑하는 것을 고려할 세 가지 큰 이유가 있습니다.

  1. 특정 라이브러리는 응용 프로그램에 고유 하지 않습니다 .
  2. 그것을 감싸지 않고 교환하는 것은 매우 비쌉니다.
  3. API 자체가 마음에 들지 않습니다.

내가 그 지역의 세에서 어느 정도의 우려가없는 경우에는 하지 않는 어떤 만드는 중요한 을 마무리하기 위해 노력합니다. 그리고 세 가지 영역 모두에 관심이 있다면 자동 생성 된 얇은 래퍼는 실제로 도움이되지 않습니다.

라이브러리를 마무리하기로 결정했다면 가장 효율적이고 효과적인 시간 사용은 원하는 인터페이스에 대해 애플리케이션 을 빌드하는 것입니다 . 기존 API에 반대하지 않습니다.

다른 방법으로, 고전적인 조언에 유의하십시오 . 가능한 모든 결정을 연기하십시오. 먼저 응용 프로그램의 "핵심"을 작성하십시오. 것이다 인터페이스에 대한 코드 결국 것이다 당신이 원하는 것을, 결국 아직 존재하지 않는 "주변 물건"에 의해 성취 될 수있다. 필요에 따라 간격을 메우십시오.

이러한 노력은 시간을 절약하는 것처럼 느껴 지지 않을 수 있습니다 . 그러나 랩퍼가 필요하다고 생각되면 이것이 가장 효율적인 방법 입니다.

이런 식으로 생각하십시오.

이 라이브러리에 대해 코드의 어두운 구석에서 코드를 작성해야 합니다. 테스트하는 동안 라이브러리를 조롱하면 포장이 끝난 경우라도 피할 수없는 수동 작업이 필요 합니다. 그렇다고 해서 대부분의 응용 프로그램에서 해당 라이브러리 를 이름 으로 직접 인식해야한다는 의미는 아닙니다 .

TLDR

라이브러리를 래핑 할 가치가있는 경우, 타사 라이브러리에 대한 광범위한 직접 참조를 피하기 위해 전술 을 사용 하지만 얇은 래퍼를 생성하기위한 바로 가기는 사용하지 마십시오. 비즈니스 로직을 먼저 구축하고, 인터페이스를 신중히 고려한 다음 필요에 따라 유기적으로 어댑터를 등장 시키십시오 .

또한 통합 테스트를 두려워하지 마십시오. 그것들은 약간 어지럽지만 여전히 작동 코드의 증거를 제공하며 회귀를 막기 위해 쉽게 만들 수 있습니다.


2
명시 적이었다 - 이것은 또 다른 질문에 대답하지 게시물입니다 하지 "경우 포장을", 그러나 "어떻게 수동 노력을 줄일 방법".
— Doc Brown

1
죄송하지만 질문의 핵심 요점을 놓친 것 같습니다. 귀하의 제안이 어떻게 포장을 수동으로 줄일 수 있는지 모르겠습니다 . 스테이크에있는 lib에 수십 개의 클래스와 수백 개의 메소드가있는 복잡한 오브젝트 모델이 API라고 가정하십시오. API는 표준 사용법과 마찬가지로 훌륭하지만 고통이나 노력이 적은 단위 테스트를 위해 랩핑하는 방법은 무엇입니까?
— Doc Brown

1
TLDR; 보너스 포인트를 원한다면 우리가 아직 모르는 것을 말하십시오 ;-)
— Doc Brown

1
@DocBrown 나는 요점을 놓치지 않았다. 그러나 나는 당신 이 내 대답의 요점 을 놓쳤다 고 생각 합니다 . 올바른 노력에 투자하면 많은 작업이 절약됩니다. 테스트에 영향이 있지만 부작용 일뿐입니다. 하는 -에 도구를 사용하면 자동적으로 아직도 당신이 다른 사람의 API 주변의 핵심 라이브러리를 구축하고 모의 객체를 구축 잎 도서관 주위에 얇은 래퍼를 생성하는 것입니다 많은 노력을 할 수없는 라이브러리 아웃 떠날 때 당신이 만약에게있는 거 고집을 피할 수는 당신의 테스트.
— svidgen

1
@DocBrown 이것은 터무니없는입니다. "이러한 문제에는 도구 나 접근 방법이 있습니까?" 예. 물론 그렇습니다. 방어 적 인 코딩 과 단위 테스트에 대한 독단적 인 견해 ... 내 대답처럼. 당신이 경우 한 자동적으로 생성 된 얇은 래퍼를 가지고, 무슨 가치를 제공 할 것!? ... 테스트를 위해 의존성을 주입 할 수는 없지만 여전히 수동으로해야합니다. 그리고 여전히 라이브러리의 API에 대해 코딩 하고 있기 때문에 라이브러리를 교체 할 수는 없습니다 ... 지금은 간접적입니다.
— svidgen

10

해당 코드를 단위 테스트하지 마십시오. 대신 통합 테스트를 작성하십시오. 경우에 따라 단위 테스트, 조롱 은 지루하고 고통 스럽습니다. 단위 테스트를 버리고 실제로 벤더를 불러내는 통합 테스트를 작성하십시오.

이러한 테스트는 배치 후 배치 후 자동화 활동으로 실행해야합니다. 단위 테스트 또는 빌드 프로세스의 일부로 실행되지 않습니다.

때로는 통합 테스트가 응용 프로그램의 특정 부분에 대한 단위 테스트보다 더 적합합니다. 코드를 "테스트 가능"하게하기 위해 진행해야하는 후프는 때로는 해로울 수 있습니다.


2
귀하의 답변을 읽을 때 가장 먼저 생각한 것은 "이진 수준의 파일을 비교하면 변경된 내용이나 변경 사항에 문제가 없는지 여부를 알 수 없으므로 PDF에 비현실적입니다." 그런 다음 이 오래된 SO post를 찾았 습니다. 실제로 작동 할 수 있음을 나타냅니다 (+1).
— Doc Brown

@DocBrown SO 링크의 도구는 PDF를 이진 수준으로 비교하지 않습니다. 페이지의 구조와 내용을 비교합니다. PDF 내부 구조 : safaribooksonline.com/library/view/pdf-explained/9781449321581/…
— linuxunil

1
@linuxunil : 그렇습니다, 내가 썼던 것처럼, 나는 먼저 생각했습니다 ... 그러나 그때 위에서 언급 한 해결책을 찾았습니다.
— Doc Brown

오 .. 알 겠어. 아마 당신의 대답의 마지막에 '실제로 작동 할지도 모릅니다.'
— linuxunil

1

내가 이해하는 것처럼,이 토론은 아이디어와 구현 지침을 래핑하는 대신 래퍼 생성 자동화 기회에 중점을 둡니다 . 여기에 이미 많은 것이 있기 때문에 아이디어에서 추상화하려고합니다.

우리는 .NET 기술을 사용하고 있기 때문에 강력한 반사 기능을 가지고 있습니다. 다음을 고려할 수 있습니다.

  1. .NET Wrapper Class Generator 와 같은 도구 . 나는 그 도구를 사용하지 않았으며 기존 기술 스택에서 작동한다는 것을 알고 있지만 귀하의 경우에는 적합 할 것입니다. 물론, 코드 품질, 종속성 반전 및 인터페이스 분리에 대한 지원은 별도로 조사해야합니다. 어쩌면 그와 같은 다른 도구가 있지만 많이 검색하지 않았습니다.
  2. 공용 메소드 / 인터페이스에 대한 참조 어셈블리에서 검색하고 매핑 및 코드 생성을 수행하는 자체 도구 작성. 지역 사회에 기여하는 것은 환영 이상입니다!
  3. .NET이 사실이 아닌 경우 여기를 참조하십시오 .

나는 그러한 자동화가 기준이 될 수 있지만 최종 솔루션은 아니라고 생각합니다. 수동 코드 리팩토링이 필요합니다 ... 또는 최악의 경우 Svidgen이 작성한 것에 완전히 동의하면 재 설계가 필요합니다.

원하는 인터페이스에 맞게 애플리케이션을 빌드하십시오. 타사 API에 반대하지 않습니다.


적어도 문제 가 어떻게 처리 될지에 대한 아이디어 입니다. 그러나이 유스 케이스에 대한 자신의 경험을 바탕으로하지는 않습니다.
— Doc Brown

질문은 소프트웨어 엔지니어링 도메인 (래퍼, 리플렉션, 디)에 관한 내 자신의 경험을 바탕으로합니다! 도구에 관해서-나는 asnwer에 명시된대로 그것을 사용하지 않았습니다.
— tom3k

0

랩퍼 라이브러리를 작성할 때 다음 지침을 따르십시오.

  • 현재 필요한 타사 라이브러리의 작은 하위 집합 만 노출 (요청시 확장)
  • 래퍼를 가능한 한 얇게 유지하십시오 (논리가 없음).

1
왜 질문의 요점을 놓치는 게시물에 대해 3 개의 공감대를 얻었는지 궁금합니다.
— Doc Brown
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.