실수로 중복되는 코드를 방지하려면 어떻게해야합니까?


33

나는 다소 큰 코드 기반으로 작업합니다. 수백 개의 클래스, 수많은 다른 파일, 많은 기능, 새로운 사본을 풀다운하는 데 15 분 이상이 소요됩니다.

이러한 큰 코드 기반의 큰 문제는 상당히 많은 유틸리티 메소드가 있고 동일한 기능을 수행하거나 가능한 경우 이러한 유틸리티 메소드를 사용하지 않는 코드가 있다는 것입니다. 또한 유틸리티 메소드는 모두 한 클래스에있는 것이 아닙니다 (왜냐하면 엄청나게 혼란 스러울 수 있기 때문입니다).

저는 코드 기반에 익숙하지 않지만 수년 동안 코드를 개발해온 팀장도 같은 문제를 겪고 있습니다. 많은 코드와 작업 중복으로 이어 지므로 무언가가 깨지면 기본적으로 동일한 코드의 4 복사본으로 나뉩니다.

이 패턴을 어떻게 억제 할 수 있습니까? 대부분의 대규모 프로젝트와 마찬가지로 모든 코드가 문서화되어 있지는 않지만 모든 코드가 문서화되어 있지는 않습니다. 그러나 기본적으로 우리가 이와 관련하여 품질을 향상시킬 수 있다면 앞으로 코드 중복이 적고 유틸리티 기능과 같은 것들을 쉽게 발견 할 수 있다면 정말 좋을 것입니다.

또한 유틸리티 함수는 일반적으로 일부 정적 도우미 클래스, 단일 객체에서 작동하는 비 정적 도우미 클래스 또는 주로 "도움이되는"클래스의 정적 메서드입니다.

유틸리티 함수를 확장 메소드로 추가하는 한 가지 실험이 있습니다 (클래스 내부는 필요하지 않으며 매우 구체적인 시나리오에서만 필요했습니다). 이것은 기본 클래스 등을 혼란스럽게 만드는 효과가 있었지만 이미 알고 있지 않으면 더 이상 발견 할 수 없습니다.


답변:


30

간단한 대답은 실제로 코드 복제를 막을 수 없다는 것입니다. 그러나 어려운 연속 반복 증분 프로세스를 통해 "수정"하여 두 단계로 나눌 수 있습니다.

1 단계. 레거시 코드에서 테스트 작성 시작 (테스트 프레임 워크 사용)

2 단계. 테스트에서 배운 내용을 사용하여 복제 된 코드를 다시 작성 / 리팩터링

정적 분석 도구 를 사용하여 중복 된 코드를 감지 할 수 있으며 C #에는이를 수행 할 수있는 많은 도구가 있습니다.

이와 같은 도구를 사용하면 유사한 작업을 수행하는 코드에서 포인트를 찾는 데 도움이됩니다. 그들이 실제로하는지 결정하기 위해 테스트를 계속 작성하십시오. 동일한 테스트를 사용하여 중복 코드를보다 간단하게 사용할 수 있습니다. 이 "리팩토링"은 여러 가지 방법으로 수행 할 수 있으며이 목록을 사용하여 올바른 것을 결정할 수 있습니다.

또한이 주제에 관한 Michael C. Feathers의 레거시 코드로 효과적으로 작업하기 위한 전체 책도 있습니다 . 코드를 더 잘 변경하기 위해 취할 수있는 다양한 전략이 있습니다. 그는 위의 두 단계 프로세스에서 멀지 않은 "레거시 코드 변경 알고리즘"을 가지고 있습니다.

  1. 변경점 식별
  2. 테스트 포인트 찾기
  3. 의존성 중단
  4. 테스트 작성
  5. 변경 및 리팩터링

브라운 필드 개발, 즉 변경이 필요한 레거시 코드를 다루는 경우이 책을 잘 읽어보십시오.

이 경우

OP의 경우 테스트 할 수없는 코드가 여러 가지 형태의 "유틸리티 방법과 트릭"에 대한 허니팟으로 인해 발생했다고 상상할 수 있습니다.

이것들에는 아무런 문제가 없지만, 반면에 그것들은 일반적으로 유지하고 변경하기가 어렵습니다. .NET의 확장 메소드는 정적 메소드이지만 비교적 테스트하기도 쉽습니다.

리팩토링을 진행하기 전에 팀과상의하십시오. 작업을 진행하기 전에 동일한 페이지에 보관해야합니다. 무언가를 리팩토링하면 가능성이 높기 때문에 병합 충돌이 발생하기 때문입니다. 따라서 무언가를 재 작업하기 전에 조사하고, 조사 할 때까지 잠시 동안주의하여 해당 코드 포인트에서 작업하도록 팀에 지시하십시오.

OP는 코드에 새로운 것이기 때문에 무엇인가를하기 전에해야 할 일이 있습니다.

  • 코드베이스에서 배우는 데 시간을내어 "모든 것"을 깨고 "모든 것"을 테스트하고 되 돌리십시오.
  • 커밋하기 전에 팀 담당자에게 코드를 검토하도록 요청하십시오. ;-)

행운을 빕니다!


실제로 상당한 단위 및 통합 테스트가 있습니다. 100 % 적용 범위는 아니지만 코드 기반을 근본적으로 변경하지 않으면 단위 테스트를 수행하는 것이 거의 불가능합니다. 중복을 찾기 위해 정적 분석을 사용하는 것을 고려하지 않았습니다. 다음에 시도해야합니다.
Earlz

@Earlz : 정적 코드 분석이 대단합니다! ;-) 또한 변경을해야 할 때마다 변경을보다 쉽게 ​​수행 할 수있는 솔루션을 생각하십시오 (이에 대한 패턴 카탈로그 리 팩터 확인)
Spoike

+1 누군가가이 Q에 현상금을 걸면이 답변을 "추가 도움"으로 인정할 수 있습니다. Refactor to Patterns Catalog는 금이며 GuidanceExplorer.codeplex.com 과 같은 방식으로 훌륭한 프로그래밍 보조 도구입니다.
Jeremy Thompson

2

우리는 다른 각도에서 문제를 보려고 노력할 수도 있습니다. 문제가 코드 중복이라고 생각하는 대신 문제가 코드 재사용 정책의 부족에서 비롯된 것인지 고려할 수 있습니다.

최근 에 재사용 가능한 구성 요소가 포함 된 소프트웨어 엔지니어링 이라는 책을 읽었으며 실제로 조직 수준에서 코드 재사용 성을 향상시키는 방법에 대한 매우 흥미로운 아이디어가 있습니다.

이 책의 저자 인 Johannes Sametinger는 코드 재사용에 대한 일련의 장벽을 설명하며, 일부는 기술적 인 개념입니다. 예를 들어 :

개념 및 기술

  • 재사용 가능한 소프트웨어 찾기 어려움 : 소프트웨어를 찾을 수 없으면 소프트웨어를 재사용 할 수 없습니다. 리포지토리에 구성 요소에 대한 충분한 정보가 없거나 구성 요소가 제대로 분류되지 않은 경우 재사용이 발생하지 않습니다.
  • 발견 된 소프트웨어의 재사용 불가 : 기존 소프트웨어에 쉽게 액세스 할 수 있다고해서 반드시 소프트웨어 재사용이 증가하는 것은 아닙니다. 의도하지 않게 소프트웨어는 다른 사람이 재사용 할 수있는 방식으로 작성되지 않습니다. 다른 사람의 소프트웨어를 수정하고 수정하면 필요한 기능을 처음부터 프로그래밍하는 것보다 훨씬 비쌀 수 있습니다.
  • 재사용에 적합하지 않은 레거시 구성 요소 : 재사용을 위해 설계 및 개발되지 않은 한 구성 요소를 재사용하는 것은 어렵거나 불가능합니다. 다양한 레거시 소프트웨어 시스템에서 기존 구성 요소를 수집하고 새로운 개발에 재사용하는 것만으로는 체계적인 재사용에 충분하지 않습니다. 리엔지니어링은 재사용 가능한 구성 요소를 추출하는 데 도움이 될 수 있지만 상당한 노력이 필요합니다.
  • 객체 지향 기술 : 객체 지향 기술은 소프트웨어 재사용에 긍정적 인 영향을 미치는 것으로 널리 알려져 있습니다. 불행하게도 잘못, 많은 사람들은 재사용이이 기술에 의존하거나 객체 지향 기술을 채택하면 소프트웨어를 재사용하기에 충분하다고 생각합니다.
  • 수정 : 구성 요소가 항상 원하는 방식 인 것은 아닙니다. 수정이 필요한 경우 구성 요소 및 이전 검증 결과에 미치는 영향을 확인할 수 있어야합니다.
  • 쓰레기 재사용 : 재사용 가능한 구성 요소를 특정 품질 수준으로 인증하면 가능한 결함을 최소화하는 데 도움이됩니다. 열악한 품질 관리는 재사용의 주요 장애물 중 하나입니다. 필요한 기능이 구성 요소가 제공하는 기능과 일치하는지 판단 할 수있는 수단이 필요합니다.

다른 기본 기술 문제는 다음과 같습니다.

  • 재사용 가능한 구성 요소가 무엇인지에 동의합니다.
  • 구성 요소의 기능 및 사용 방법 이해
  • 재사용 가능한 구성 요소를 나머지 설계에 인터페이스하는 방법 이해
  • 제어 가능한 방식으로 쉽게 적응하고 수정할 수 있도록 재사용 가능한 구성 요소를 설계합니다.
  • 프로그래머가 필요한 것을 찾고 사용할 수 있도록 저장소를 구성합니다.

저자에 따르면, 조직의 성숙도에 따라 다른 수준의 재사용 가능성이 발생합니다.

  • 응용 프로그램 그룹 간 임시 재사용 : 재사용에 대한 명시적인 약속이없는 경우 재사용은 비공식적이고 우연한 방식으로 발생할 수 있습니다. 재사용은 대부분 프로젝트 내에서 이루어집니다. 이것은 또한 코드 소거로 이어지고 코드 복제로 끝납니다.
  • 응용 프로그램 그룹 간 리포지토리 기반 재사용 : 구성 요소 리포지토리를 사용하고 다양한 응용 프로그램 그룹에서 액세스 할 수있는 상황이 약간 개선됩니다. 그러나 구성 요소를 저장소에 배치하기위한 명시 적 메커니즘은 없으며 저장소의 구성 요소 품질을 책임지는 사람은 없습니다. 이로 인해 많은 문제가 발생하고 소프트웨어 재사용이 방해받을 수 있습니다.
  • 구성 요소 그룹을 통한 중앙 재사용:이 시나리오에서는 구성 요소 그룹이 저장소를 명시 적으로 책임집니다. 그룹은 리포지토리에 저장할 구성 요소를 결정하고 이러한 구성 요소의 품질과 필요한 설명서의 가용성을 보장하며 특정 재사용 시나리오에서 적합한 구성 요소를 검색하는 데 도움을줍니다. 응용 프로그램 그룹은 구성 요소 그룹과 분리되어 각 응용 프로그램 그룹에 대한 일종의 하청 업체 역할을합니다. 구성 요소 그룹의 목적은 중복성을 최소화하는 것입니다. 일부 모델에서이 그룹의 구성원은 특정 프로젝트에서 작업 할 수도 있습니다. 프로젝트를 시작하는 동안 지식은 재사용을 촉진하는 데 유용하며 특정 프로젝트에 참여한 덕분에 리포지토리에 포함될 후보를 식별 할 수 있습니다.
  • 도메인 기반 재사용 : 컴포넌트 그룹의 전문화는 도메인 기반 재사용에 해당합니다. 각 도메인 그룹은 도메인의 구성 요소 (예 : 네트워크 구성 요소, 사용자 인터페이스 구성 요소, 데이터베이스 구성 요소)를 담당합니다.

따라서 다른 답변에 제공된 모든 제안 외에도 재사용 가능성 프로그램 설계, 관리, 도메인 분석을 통해 재사용 가능한 구성 요소 식별을 담당하는 구성 요소 그룹 구성 및 다른 개발자가 쉽게 사용할 수있는 재사용 가능한 구성 요소 저장소를 정의 할 수 있습니다. 그들의 문제에 대한 요리 된 해결책을 찾고 찾으십시오.


1

두 가지 가능한 솔루션이 있습니다.

예방 -가능한 한 좋은 문서를 작성하십시오. 모든 기능을 올바르게 문서화하고 전체 문서를 쉽게 검색 할 수 있습니다. 또한 코드를 작성할 때 코드가 어디로 가야하는지 명확하게해야합니다. "유틸리티"코드의 양을 제한하는 것이 이것의 핵심 포인트 중 하나입니다. "유틸리티 클래스를 만들자"라는 말을들을 때마다 머리카락이 올라가고 혈액이 얼어 붙습니다. 분명히 문제이기 때문입니다. 일부 기능이 이미 존재하는 경우 항상 사람들에게 코드베이스를 알려주도록 빠르고 쉬운 방법이 있습니다.

솔루션 -예방이 실패하면 문제가되는 코드를 빠르고 효율적으로 해결할 수 있어야합니다. 개발 과정에서 중복 코드를 신속하게 수정할 수 있습니다. 단위 테스트는 코드를 손상시키지 않고 효율적으로 코드를 수정할 수 있기 때문에 완벽합니다. 따라서 두 개의 유사한 코드 조각을 찾으면 약간의 리팩토링으로 함수 또는 클래스로 추상화하는 것이 쉽습니다.

개인적으로는 예방이 불가능하다고 생각합니다. 더 많이 시도할수록 기존 기능을 찾기가 더 어려워집니다.


0

나는 이런 종류의 문제가 일반적인 해결책을 가지고 있다고 생각하지 않습니다. 개발자가 기존 코드를 검색 할 의사가있는 경우 중복 코드가 생성되지 않습니다. 또한 개발자는 원하는 경우 문제를 바로 해결할 수 있습니다.

언어가 C / C ++ 복제 인 경우 연결의 유연성으로 인해 병합이 더 쉬워집니다 ( extern사전 정보없이 함수를 호출 할 수 있음 ). Java 또는 .NET의 경우 헬퍼 클래스 및 / 또는 유틸리티 구성 요소를 고안해야 할 수도 있습니다.

중복 된 부분에서 큰 오류가 발생하는 경우에만 기존 코드의 중복 제거를 시작합니다.


0

이것은 많은 프로그래머가 처리하고 때로는 많은 동료 압력하에 기여한 더 큰 프로젝트의 전형적인 문제입니다. 수업의 사본을 만들어 특정 수업에 적응하는 것은 매우 유혹적입니다. 그러나 시작 클래스에서 문제가 발견되면 종종 잊혀지는 악의적 인 문제도 해결해야합니다.

이에 대한 해결책이 있으며 Java 6에 도입 된 Generics라고합니다. 이는 Template이라는 C ++과 같습니다. 정확한 클래스가 아직 일반 클래스에 알려지지 않은 코드입니다. Java Generics를 확인하면 수많은 문서를 찾을 수 있습니다.

좋은 접근 방법은 특정 버그로 인해 수정해야하는 첫 번째 코드를 다시 작성하여 여러 곳에 복사 / 붙여 넣은 것처럼 보이는 코드를 다시 작성하는 것입니다. Generics를 사용하도록 다시 작성하고 매우 엄격한 테스트 코드를 작성하십시오.

Generic 클래스의 모든 메소드가 호출되었는지 확인하십시오. 코드 커버리지 도구를 도입 할 수도 있습니다. 일반 코드는 여러 위치에서 사용되므로 전체 코드 커버리지 여야합니다.

또한 일반 코드와 함께 사용될 첫 번째 지정된 클래스에 대해 JUnit 등을 사용하여 테스트 코드를 작성하십시오.

앞의 모든 코드가 작동하고 완전히 테스트되면 두 번째 (대부분) 복사 된 버전의 일반 코드를 사용하십시오. 지정된 클래스에 특정한 코드 줄이 있음을 알 수 있습니다. Generic 기본 클래스를 사용하는 파생 클래스에서 구현해야하는 추상 보호 메소드에서 이러한 코드 라인을 호출 할 수 있습니다.

예, 지루한 작업이지만, 계속 진행하면서 비슷한 클래스를 찢어 내고 매우 깨끗하고 잘 작성되었으며 유지 관리하기가 훨씬 쉬운 클래스로 대체하는 것이 더 나아질 것입니다.

나는 일반 클래스에서 6 또는 7 개의 거의 동일한 클래스를 대체했지만 거의 동일한 클래스이지만 여러 프로그래머가 복사하고 붙여 넣은 비슷한 상황을 가졌습니다.

그리고 네, 코드의 자동 테스트를 선호합니다. 처음에는 비용이 많이 들지만 전체적으로 엄청난 시간을 절약 할 수 있습니다. 그리고 일반 코드의 경우 최소 80 % 및 100 %의 코드 적용 범위를 달성하십시오.

이것이 도움이되고 행운이 있기를 바랍니다.


0

실제로 여기와 측면에서 가장 인기있는 의견을 Gangnus제시하고 코드 복제가 항상 유해하지는 않으며 때로는 덜 악할 수 있다고 제안합니다.

예를 들어, 당신은 나에게 다음과 같은 옵션을 사용할 수 있습니다 :

A) 잘 테스트 된 안정적이고 (변경되지 않은) 작은 이미지 라이브러리로, 도트 제품 및 lerps 및 클램프와 같은 벡터 수학에 대한 수십 줄의 사소한 수학 코드를 복제하지만 다른 것과 완전히 분리되어 일부에서 생성됩니다. 두 번째.

B) 위에서 언급 한 수십 줄의 코드를 피하기 위해 서사시 수학 라이브러리에 의존하는 불안정한 (빠르게 변화하는) 이미지 라이브러리, 수학 라이브러리가 불안정하고 지속적으로 새로운 업데이트 및 변경 사항을 수신하므로 이미지 라이브러리도 완전히 변경되지 않은 경우 다시 작성하십시오. 전체를 정리하는 데 15 분이 걸립니다.

... 그리고 분명히 A가 실제로 작은 코드 복제로 인해 바람직하다는 것은 대부분의 사람들에게 쉬운 일이 아닙니다. 내가 강조해야 할 핵심은 잘 테스트 된 부분입니다. 분명히 코드가 중복되어 처음부터 작동하지 않는 버그보다 더 나쁜 것은 없습니다.이 시점에서 버그를 복제합니다.

그러나 생각해야 할 결합과 안정성이 있으며, 여기에 약간의 복제가 있으며 패키지의 안정성 (변하지 않는 성질)을 증가시키는 분리 메커니즘으로 작용할 수 있습니다.

따라서 제 제안은 실제로 테스트에 중점을두고 실제로 변경되지 않는 것처럼 (미래에 변경해야 할 몇 가지 이유를 찾음) 외부 소스에 대한 종속성이있는 신뢰할 수있는 무언가를 생각해내는 것입니다. 코드베이스에서 모든 형태의 복제를 제거하려고 시도하는 것보다 매우 안정적입니다. 대규모 팀 환경에서 후자는 코드베이스에있는 불안정한 코드의 커플 링과 양을 증가시킬 수 있다는 점은 말할 것도없이 비실용적 인 경향이 있습니다.


-2

코드 복제가 항상 유해하지는 않다는 것을 잊지 마십시오. 상상해보십시오. 이제 프로젝트의 다른 모듈에서 해결해야 할 작업이 있습니다. 바로 지금 같은 작업입니다.

세 가지 이유가있을 수 있습니다.

  1. 이 작업과 관련된 일부 테마는 두 모듈에서 동일합니다. 이 경우 코드 중복이 잘못되어 청산되어야합니다. 이 테마를 지원하고 두 모듈에서 해당 메소드를 사용하기 위해 클래스 또는 모듈을 작성하는 것은 영리합니다.

  2. 과제는 프로젝트 측면에서 이론적입니다. 예를 들어, 물리 또는 수학 등에서 나옵니다. 작업은 프로젝트와 독립적으로 존재합니다. 이 경우 코드 중복이 나쁘고 청산되어야합니다. 그런 기능을 위해 특별한 클래스를 만들었습니다. 필요한 모든 모듈에서 이러한 기능을 사용하십시오.

  3. 그러나 다른 경우에는 작업의 우연의 일치가 일시적인 우연의 일치 일뿐입니다. 리팩토링 및 디버깅으로 인해 프로젝트를 변경하는 동안 이러한 작업이 동일하게 유지된다고 믿는 것은 위험합니다. 이 경우 서로 다른 위치에 두 개의 동일한 함수 / 코드 조각을 작성하는 것이 좋습니다 . 그리고 그들 중 하나의 미래 변화는 다른 하나를 건드리지 않을 것입니다.

그리고이 세 번째 경우는 매우 자주 발생합니다. "무의식적으로"복제하는 경우, 대부분이 이유 때문입니다. 실제 복제는 아닙니다!

따라서 실제로 필요할 때 깨끗하게 유지하고 꼭 필요한 것이 아니라면 복제를 두려워하지 마십시오.


2
code duplication is not always harmful하나의 나쁜 조언입니다.
Tulains Córdova

1
내가 당신의 권위에 절해야합니까? 나는 나의 이유를 여기에 두었다. 내가 착각하는 경우 실수가 어디 있는지 보여주십시오. 이제는 토론을 계속하는 능력이 부족한 것 같습니다.
Gangnus

3
코드 복제는 소프트웨어 개발의 핵심 문제 중 하나이며 많은 컴퓨팅 과학자와 이론가들은 소프트웨어 개발에서 유지 관리 성 문제의 주요 원인 인 코드 복제를 피하기 위해 패러다임과 방법론을 개발했습니다. "불량한 코드를 작성하는 것이 항상 나쁘지는 않다"고 말하는 것과 비슷합니다. 어쩌면 당신이 옳을 수도 있지만 코드 중복을 피하는 것은 반대를 장려하기에는 너무 좋은 원칙입니다.
Tulains Córdova

나는 여기에 인수를했습니다. 당신은하지 않았습니다. 당국에 대한 언급은 16 세기 이후에는 효과가 없습니다. 당신은 당신이 그것들을 올바르게 이해했고 그들이 저에게 권위라는 것을 보장 할 수 없습니다.
Gangnus

코드 복제 소프트웨어 개발의 핵심 문제 중 하나 가 아니며 이를 피하기위한 패러다임과 방법론이 개발되지 않았습니다.
Tulains Córdova
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.