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


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.