왜 드라이가 중요합니까?


81

아주 간단합니다. 왜 모든 경우에 적용 가능한 코드와 확장 가능한 데이터를 작성하고 싶을 때 약간의 미세 조정만으로 동일한 프로세스를 몇 번 반복하면 좋습니까?

조만간 다시 편집 할 필요는 없습니다.

그냥 갈 일이 훨씬 적은 것 같습니다 ...

function doStuff1(){/*.a.*/}
function doStuff2(){/*.b.*/}
function doStuff3(){/*.c.*/}

그리고 내가 뭔가를 추가해야한다면 ...

function doStuff4(){/*.d.*/}

제거해야 할 경우 제거합니다.

데이터를 공급하고 모든 경우를 처리 할 수있는 간단한 패턴으로 모든 것을 만드는 방법을 알아내는 것이 더 어렵습니다. 할 것.

빠른 잘라 내기 + 붙여 넣기가 훨씬 적은 작업으로 보일 때 왜 건조해야합니까?


11
당신이 그것을 올바르게 할 때 건조는 여전히 더 빠르기 때문에, 당신이 실수를 한 경우에도 마찬가지입니다. 효과가 다른 모든 것을
다니엘 리틀

97
"언제든지 다시 편집해야 할 필요는 없습니다" – 희망 할 수도 있지만, 아마도 여기에서 실수를 저지르고있을 것입니다. 그리고 만약 당신이 그 코드를 다시 작업하려고한다면, 그렇게 하지는 않을 것입니다. 복제본이 어디에 있는지 잊어 버리고 복제본은 미묘하지만 위험한 불일치를 갖게됩니다. 고전을 인용하기 위해 "코드를 관리 할 사람이 당신이 사는 곳을 아는 위험한 미치광이 인 것처럼 쓰십시오".
9000

14
나는 당신이 그것을 다음과 같이 요약 할 수 있다고 생각합니다 : 단일 변경 지점이 유지하기가 더 쉽습니다.
팔콘

17
이 질문에 스스로 대답 할 수 없다면 개발 및 유지 보수에 대한 실제 경험을 쌓아야합니다.
David Heffernan

15
@Wayne 수백만 명의 프로그래머가 갑자기 테러로 외치는 것처럼 소스에서 큰 혼란을 느꼈습니다.
시크릿

답변:


121

반복하면 유지 관리 성 문제가 발생할 수 있습니다. doStuff1-3이 모두 유사하게 구조화 된 코드를 가지고 있고 하나에서 문제를 해결하면 다른 곳에서 문제를 해결하는 것을 쉽게 잊을 수 있습니다. 또한 처리 할 새 사례를 추가해야하는 경우, 여러 곳에서 복사 붙여 넣기 대신 다른 매개 변수를 하나의 함수에 전달할 수 있습니다.

그러나 DRY는 종종 영리한 프로그래머들에 의해 극단적으로 진행됩니다. 때때로 자신을 반복하지 않으려면 추상화를 만들어야하므로 팀원이이를 따라갈 수 없게하십시오. 때로는 두 가지의 구조가 모호하게 유사하지만 충분히 다릅니다. doStuff1-4가 자신을 반복하지 않도록 리팩토링하면 부 자연스러운 코드를 작성해야하거나 영리한 코딩 백 플립이 발생하여 팀이 눈에 띄게 될 수 있으므로 반복해야합니다. 나는 부 자연스럽게 여러 번 반복하지 않기 위해 뒤로 구부러져 최종 제품을 후회했습니다.

나는 항상 DRY 측면에서 실수를한다. 드문 경우지만 가독성의 이점이 누군가가 여러 곳에서 버그를 수정하는 것을 잊어 버릴 위험이 있다고 생각할 때 반복된다.

그 조언을 고려하면, 당신의 경우처럼 들립니다.

약간의 조정만으로 동일한 과정을 몇 번 반복

나는 당신의 경우에 자신을 반복하지 않기 위해 확실히 노력할 것입니다. 최소한의 "비틀기"를 가정하면 동작에 영향을 미치는 다른 매개 변수로 처리하거나 다른 하위 작업을 수행하기 위해 종속성을 주입 할 수 있습니다.

빠른 잘라 내기 + 붙여 넣기가 훨씬 적은 작업으로 보일 때 왜 건조해야합니까?

유명한 마지막 말. 후배 엔지니어가 한 doStuff를 조정 / 수정 / 리팩터링하고 다른 엔지니어가 존재한다는 사실조차 깨닫지 못한다는 생각을 후회할 것입니다. 성대가 계속됩니다. 가슴 앓이는 거의 없습니다. 모든 코드 라인은 더 많은 비용이 듭니다. 반복되는 함수로 몇 개의 코드 경로를 테스트해야합니까? 하나의 기능이면 몇 가지 행동 수정으로 하나의 기본 경로를 테스트하면됩니다. 복사하여 붙여 넣은 경우 모든 doStuff를 별도로 테스트해야합니다. 이상하게도 한 가지를 놓치면 고객이 반갑지 않은 버그가 있고받은 편지함에 반갑지 않은 이메일이있을 수 있습니다.


2
어, 분명하게 말하면, 나는 건조한 것을 제안하지 않는 것이 나쁘다.이 질문은 더 많은 악마를 옹호한다. 나는 cut + paste + tweak 코드가 좋다고 생각하는 사람들에게 연결할 수있는 논리적 인 반응을 찾고 있습니다.
시크릿

2
즉, DRY의 두 가지 실패, 즉 귀찮게하지 않고 선외로 나가는 것뿐만 아니라 영향을 설명하는 답변이 가장 좋습니다. -버그의 가독성을 떨어 뜨리는 것에 대한 한 가지 점에서, 반복적 인 코드는 당신이 지적한 것과 같은 이유로 읽기가 쉽지 않다고 주장합니다.
시크릿

16
DRY의 쉬운 함정에 밑줄을 긋습니다. 비슷한 코드가 병합되었습니다. 기능적으로 관련이 없지만 매우 유사한 코드가있는 두 가지 사용 사례가있는 경우 DRY가 좋기 때문에 두 가지 를 병합 하는 것이 쉽습니다 . 불행히도, 진화해야 할 때 , 기능을 다시 한 번 분할 하고 불쾌한 작업을 수행 한 다음 모든 콜 사이트를 거쳐 여기서 어느 것을 호출해야하는지 신중하게 고려해야합니다 ... 예 : LLVM 구조 타이핑 (유사한 모든 유형이 하나로 병합 됨) IR을 원래 코드에 다시 매핑하는 것이 거의 불가능합니다.
Matthieu M.

1
빙고. 둘 이상의 코드 조각이 변경되고 모든 조각에 대해 변경 사항이 항상 동일하면 병합되어야합니다. 다른 방식과 다른 방식으로 변경해야하는 조각은 병합하지 않아야합니다. 코드가 전혀 변경되지 않으면 병합 여부에 관계없이 중요하지 않습니다. 변경 사항을 잠 그거나 분리해야하는지에 대한 문제는 문제의 코드 크기보다 훨씬 중요합니다.
supercat

1
@MatthieuM 약간 불공평 한 예입니다. LLVM은 구조적 타이핑을 최적화 로 적용하고 있습니다 . 즉, LLVM 사람들은 이해하기 어려운 IR의 가격을 성능상의 이점으로 지불하기로 결정했습니다. DRY는 일반적으로 유지 보수성 문제이지만이 경우 유지 보수성을 줄이기위한 의도적 인 결정이었습니다.
벤자민 호지 슨

47

DRY는 나중에 덜 작동하기 때문입니다.


건조 : (반복하지 마십시오)

인수를 취하는 하나의 함수.

def log(arg):
    print(arg)

C & P : (복사 & 붙여 넣기)

26 개의 gazillion 함수는 본질적으로 동일한 기능을 수행하지만 2 자 차이가 있습니다.

def logA():
    print('a')

def logB():
    print('b')

...ad infinitum...

인쇄 내용을 정확히 지정하여 인쇄 내용을 업데이트하는 것은 어떻습니까?

마른:

def log(arg):
    print(arg + "Printed from process foo")

끝난.

C & P :

돌아가서 모든 단일 기능을 변경해야 합니다 .


어느 쪽이 디버그하기 쉽다고 생각합니까?


10
또한 중복 함수가있는만큼 유사한 테스트 스위트를 작성해야합니다.
9000

개념을 적절하게 설명했지만 실제로는 그런 식으로가 아니라 gazillion 함수로 설명 된 것을 수행하지 않습니다.
Robert Harvey

@Robert 나는 희망하지 않을 것이다! 나는 개념을 더 잘 설명하고 그것이 좋은 이유가 될 수있는 매우 간단한 작업을 선택했습니다.
John

11
@Robert은 - 당신의 기사 중 하나를 읽고 thedailywtf.com ) - 그냥 할 것입니다 누가 거기 몇 가지가 있습니다
HorusKol

1
@ 9000 테스트 스위트가없는 경우 : p (실제로 일부 프로젝트에서 실제로 발생하는 경우가 있습니다 ... 불행히도 ...)
Svish

16

이므로 귀하의 예에 적용됩니다.

  • + 가독성

    코드적을수록 잡음적습니다 . (항상 그런 것은 아닙니다 ...)

  • + 유연성

    의 행동을 바꿔야한다면 doStuffX자신을 죽이거나 누군가를 쓰려고 할 것입니다.

  • + 확장 성

    선택한 데이터 구조로 구별되는 부분을 추출한 다음 generic을 호출하여 반복 한 경우 doStuff데이터 구조에서 새 항목을 원하는 위치에 한 줄을 추가하거나 제거 할 수 있습니다. 행동을 바꾸는 것은 단지 편집을 의미 할 것이다 doStuff. 유지 관리가 더 쉽습니다 .

  • + 비용 효율성

    여기서 적은 코드는 다음을 의미합니다.

    • => 적은 개발 => 비용 절감
    • => 버그 낮은 확률 => 이하 지원 시간 => 저비용
  • + (가능) 관리 최적화

    언어에 따라 컴파일러 / 인터프리터는 generic doStuff이 항상 거의 똑같은 것을 호출하고 이를 인라인 하거나 최적화 하려고 시도 할 가능성이 더 높습니다 . 의 X 변형에 대해서는 아마 아닐 것입니다 doStuffX.

  • + 테스트 및 품질

    테스트가 더 쉽습니다. 테스트가 doStuff필요합니다. 정확히는 아니지만 이미 더 많은 내용 을 다루고 있습니다 . 만의 IO 기대는 다양하고 서로 다른 조건에서 시험 할 필요가 있지만, 아직 많은 테스트를 쉽게 하고 더 유지 보수 의 모든 변화보다 doStuffX.

전반적으로 이것은 유지 관리 가능한 코드와 팀의 개발 효율성 향상 을 설명하며 보다 강력하고 신뢰할 수있는 소프트웨어 생성 하는 데 도움이 되는 많은 모범 사례 중 하나입니다 .


13

다른 사람들이 중복 코드로 유지 관리 문제를 설명하는 데 큰 역할을 했으므로 다음과 같이 말하면됩니다.

프로그래밍의 대부분은 현재의 순간뿐만 아니라 미래에 대해서도 생각해야합니다. 복사 및 붙여 넣기가 더 쉬워 졌다는 것이 맞습니다. 그러나이 문장 은 곧 다시 편집 할 필요 는 없습니다. " 라고 생각하지 않습니다. 예, 빠르고 더러운 복사 / 붙여 넣기를하면 즉시 문제를보고 내일을 생각할 수 없다는 것을 보여주고 있습니다이 코드를 다시 방문 할 필요가 없다고 생각하십니까? 다음 기능 세트를 구현해야 할 때 다시 방문 할 필요가 없음을 100 % 보장 할 수 있습니까? 이러한 문제는 내일 문제가 될 수 있으며 오늘날 설계 할 때 고려해야합니다.

물론, 복사 / 붙여 넣기가 필요할 때가 있습니다. UI 개발자로서 DRY 원칙을 위반해야 할 때가 있다는 것을 알게되었습니다. 나는 그것이 일어날 때마다 울고, 고맙게도 드물다. 그러나 않습니다 발생합니다.

차이점은 DRY를 위반 할 때 매우 설득력있는 이유가 있어야한다는 것입니다. 모든 문장 을 하나의 간단한 패턴으로 만드는 방법 이 실제로는 하나가 아니라는 진술 이 어렵 습니다. 당신이 엄청난 시간 위기에 처해 있고 상사가 몇 시간 안에 무언가를 얻기 위해 비명을 지르거나 직업을 잃을 경우를 제외하고는 이것이 유효한 근거라고 생각하지 않습니다.

이것을 잘못된 길로 받아들이지 마십시오. 나는 당신을 징계하거나 쫓아 내려고하지 않고, 당신의 정신이 어디에서 잘못되었는지 보려고 노력합니다. 프로그래머는 미래의 게으름에 투자합니다. DRY는 그것을 달성하는 방법입니다. 어려운 설계 문제를 해결하는 오늘날의 작업은 내일 보상이 될 것입니다.


DRY를 위반 한 가장 큰 이유는 "완료 또는 해고"라고 말하는 상사와 동의한다는 것이 확실하지 않습니다. 기술 부채, 시간을 제대로 맞출 수 있다면 시간을 절약 할 수 있습니까?
시크릿

1
@ 시크릿, 나는 약간 아이러니하려고했습니다 :)
bedwyr

7

조만간 다시 편집 할 필요는 없습니다.

이것이 실제로 사실이라면, 그것을 피할 수는 있지만 유지 관리가 필요한 코드로 작업하는 것이 아닙니다. 이는 기능 확장, 버그 수정 및 기타 개선 사항을 의미합니다. 10 개의 다른 장소에서 동일한 코드의 작은 변형이 있고 언젠가 해당 코드로 돌아와서 변경해야하는 경우 10 개의 다른 장소에서 동일한 변경을 수행하는 오류가 발생하기 쉽습니다 (죄송합니다. 11 장소, 당신은 하나를 잊어 버렸고 지금은 버그가 있습니다).

해결하려는 문제를 일반화 할 수 있으면 버그가 발생하면 코드를 쉽게 확장하고 수정할 수 있습니다.


근사한 대답이지만, 내가 도망 갈지라도 내 뒤를 지키는 가난한 수액은 어떻습니까? ;).
시크릿

참조 : "보통 유지해야하는 코드 작업을하지 않을 것보다 더 자주":)
Alex

그럼에도 불구하고 복사하여 붙여 넣는 것이 100 % 버그가없는 완벽 성인 경우에만 해당됩니다. 그리고 그렇지 않을 수도 있습니다.
Dan Ray

나는 과거에 물건을 복사하여 붙여 넣었 음을 인정할 것이지만, 하루 이상 머물러서는 안될 일이 아닙니다. 때로는 빠르고 더러운 스크립트가 필요합니다.
Alex

6

다른 질문에 대한 답변에서 언급했듯이 내 접근 방식은 다음과 같습니다.

  1. 특정 문제를 처음 해결하면 문제가 해결됩니다.
  2. 두 번째로 (즉, 비슷한 문제를 해결할 때) 생각합니다. 흠, 아마도 반복하고 있지만 지금은 빠른 복사하여 붙여 넣기로 갈 것입니다.
  3. 내가 생각하는 세 번째 시간 : 흠, 나는 나 자신을 반복하고 있습니다-> 그것을 일반으로 만드십시오!

즉, 2까지, 또 다른 원칙 (YAGNI)이 DRY를 이깁니다. 그러나 3 (또는 정말로 게으른 경우 4)부터 시작하면 필요할 것 같아서 DRY를 따릅니다.

최신 정보

최근 경험에서 얻은 몇 가지 추가 아이디어. 다른 팀에서 개발 한 두 가지 구성 요소 A와 B를 제품에 적용 / 통합해야했습니다. 첫째, 두 구성 요소 A와 B는 서로 매우 유사하므로 아키텍처가 약간 다르다는 사실 때문에 이미 혼란 스러웠습니다. 둘째 : 하위 클래스를 사용하고 실제로 필요한 것을 재정의하기 때문에 기꺼이 적응해야했습니다.

그래서이 두 가지 구성 요소 (각각 약 8 개의 C ++ 클래스로 구성)를 리팩토링하기 시작했습니다. A와 B에 공통 아키텍처를 구축하고 서브 클래스를 정의하여 필요한 기능을 추가하고 싶었습니다. 이런 식으로 우리의 두 가지 새로운 구성 요소 A '와 B'는 기존 구성 요소에서 파생되었을 것입니다.

2 주 동안 기존 코드에서 공통적이고 명확한 구조를 얻으려고 노력하고 일상적인 회의에서 원래 코드가 너무 지저분해서 진행이 거의 이루어지지 않았다고 설명해야했는데, 나는 상사에게 말했다. 우리는이 두 가지 새로운 구성 요소 A '와 B'보다 더 많은 것이 필요하지 않다는 것을 관찰했습니다.

좋아, 그렇게하십시오 : 나는 A와 B의 클래스를 대량으로 복사하고 이름을 바꾸고 코드 사본을 조정하기 시작했습니다. 2 주 안에 더 작동하도록했습니다 (여전히 버그 수정을하고 있습니다).

장점 : 이제 거의 기능이 완료되었으며 모든 버그를 수정하면 완료되었습니다. A와 B의 모든 리팩토링과 테스트를 저장했습니다.

단점 : 2 주 전에 다른 팀이 A와 B에서 사용하는 다른 구성 요소 C를 변경했습니다. A와 B를 수정했지만 A '와 B'도 고장 났고 우리는 스스로 변경해야했습니다. 이것은 우리가 고쳐야 할 새로운 버그를 소개했습니다. A '와 B'가 코드의 대부분을 A와 B와 공유했다면이 추가 작업은 불필요했을 것입니다.

따라서 코드 복제는 항상 위험합니다. 나는 그것이 항상 절충점을 찾는 문제라고 생각하며 종종 쉽지 않습니다.


5

다른 답변에서 이것을 찾지 못했기 때문에 명확히하기 위해 :

DRY모든 종류 의 정보 반복을 줄이기위한 소프트웨어 개발 원칙입니다 .

모든 지식은 시스템 내에서 하나의 명백하고 권위있는 표현을 가져야합니다.

Andy Hunt와 Dave Thomas가 언급 한 DRY 원칙은 코드 복제 방지에만 국한되지 않습니다. 또한 코드 생성 및 자동화 프로세스를 옹호 합니다. 아이러니하게도, 코드 생성 결과는 중복 코드 일 수도 있습니다 ...

다른 답변에서 이미 철저하게 설명 된 이유는 Falcon의 의견에 따르면 충분합니다 .IMHO :

단일 변경 지점을 유지 관리하기가 더 쉽습니다.


와우, 태그에 데이터가 있다고 생각했습니다. 거기에 정보를 넣겠습니다.
시크릿

3

DRY가 너무 많습니다. 이런 일이 발생하면 어느 시점에서 팩터링 코드 (1)를 보증하기에 충분히 유사한 두 가지 개념이 나중에 별도의 구현을 수행 할만큼 충분히 다른 것으로 판명 될 수 있습니다.

다시 말해, DRY와 느슨한 결합은 때때로 충돌합니다. doStuff1과 친구들이 소프트웨어의 모든 새로운 릴리스로 분기 될 것으로 예상되면, 코드를 복제해도됩니다.

내 경험상 소프트웨어가 앞으로 어디로 가고 있는지 판단하기가 어려울 수 있으며, 이러한 이유로 DRY는 종종 안전한 선택입니다.

지나치게 "건조 된"코드는 일반적으로 복잡한 제어 흐름과 너무 많은 매개 변수를 갖습니다. 초기에 간단한 기능은 추가 매개 변수로 제어되는 새로운 기능을 지원하도록 확장되었습니다. 2 ~ 3 회 반복 한 후에는 기능을 더 이상 유지 보수 할 수 없습니다. 설정에서 발생하는 버그를 수정하고 다른 설정에서 새로운 버그를 소개합니다.

코드가 발전함에 따라 코드 품질이 저하되는 경우가 있지만, 신체에 다른 스파게티가 포함 된 다중 매개 변수 기능이 의미가 있지만 제대로 수행되지 않은 리팩토링 노력의 결과 인 경우를 보았습니다.

(1) "code"라는 단어를 사용하고 있지만 디자인에도 적용됩니다.


스펙트럼이 거의 보이지 않기 때문에 "너무 건조하다"는 예를 들어 보면 도움이 될 것입니다.
시크릿

@ 시크릿 : 답변을 편집했습니다. 구체적인 예는 없지만 내가 의미하는 바는 충분히 분명합니다.
Joh

2

관계형 데이터베이스 세계에서 DRY의 문제점을 언급해야합니다. 데이터베이스는 집합 기반 논리를 사용하고 Sargable 쿼리를 통해 빠르고 효과적으로 수행하도록 설계되었습니다. DRY 원칙으로 인해 개발자는 종종 Sargable 쿼리를 작성하거나 Row-by-agonizing-Row 논리를 사용하여 여러 상황에서 기존 코드를 활용할 수 있습니다. DRY 및 성능 최적화는 종종 상반되며 데이터베이스 세계에서는 일반적으로 성능이 유지 관리 능력보다 훨씬 중요합니다. 이것은 DRY 원칙을 전혀 사용해서는 안된다는 것을 의미하지 않으며, 데이터베이스의 전반적인 유용성에 어떤 영향을 미치는지 알아야합니다. 응용 프로그램 개발자는 먼저, 성능은 두 번째로, 데이터베이스 개발자는 데이터 무결성을 먼저, 성능은 두 번째로, 데이터의 보안을 세 번째로 생각합니다 (일부 시스템에서는 성능 및 보안이 장소를 바꿀 수 있음).

일반적으로 데이터베이스 쿼리에 추상화 계층이 많을수록 속도가 느려집니다. 나는 datbase 프로그램을 스스로 디자인하는 사람들이 개발자가 데이터베이스 성능에 영향을 미치지 않고 DRY를 사용하도록 더 나은 일을하지 않기를 원하지는 않지만 그 수준에서 데이터베이스 소프트웨어를 디자인하지는 않습니다. 따라서 데이터베이스에서 추상화와 성능의 충돌은 내가 생각하는 것보다 수정하기가 더 어렵습니다. 그러나 현재 구축 된 시스템으로 작업해야합니다. 향후 릴리스에서 성능을 저하 시키지는 않지만 (수년에 걸쳐 개선되었지만 여전히 문제가있는) DRY 원칙의 더 나은 구현을 요청할 수 있지만 그 동안 DRY가이 데이터베이스에 적합한 조치인지 고려해야합니다. 현재

그러나 종종 DRY 원칙을 충족시키기 위해 사용하려는 기능은 데이터베이스에 엄청난 문제를 일으키는 기능입니다. 나는 DRY를 사용하지 말라고 말하지 말고 그것을 넘어서는 안됩니다.

내가 말하는 것의 예. 한 달에 한 번 백만 레코드의 데이터 가져 오기를 수행해야합니다. 저장된 proc을 호출하는 사용자 인터페이스를 통해 레코드를 이미 수동으로 추가 할 수 있습니다. 이 proc은 단일 레코드 가져 오기 용으로 설계되었으므로 한 번에 하나의 레코드 만 추가합니다. DRY를 사용하여 두 위치에 삽입 코드를 사용하지 않으면 필요한 세트 기반 가져 오기를 작성하지 않고 proc을 반복적으로 호출하는 커서를 작성합니다. 가져 오기 시간은 세트 기반 논리를 사용하는 데 걸리는 30 분에서 18 시간으로 단축됩니다. 이제이 경우 DRY를 준수하는 올바른 방법은 여러 레코드 가져 오기를 처리하도록 proc을 수정하는 것입니다. 불행하게도, 배열을 proc에 전송하는 것은 불가능하거나 매우 어렵습니다 (db 백엔드에 따라) proc을 변경하면 응용 프로그램이 중단됩니다.

스칼라 함수 및 테이블 반환 함수는 DRY 원칙을 구현하는 데에도 사용되며 인덱스가 유용하지 못하게하는 방식으로 사용해야하는 경우 특히 성능에 심각한 영향을 줄 수 있습니다.

뷰는 DRY를 구현하는데도 좋습니다. 그러나 다른 뷰를 호출하는 뷰를 호출하는 뷰를 사용하여 DRY를 구현하면로드시 쿼리가 시간 초과되는 시점에 빠르게 도달 할 수 있습니다. 실제로 결국 세 개만 필요할 때 수백만 레코드의 데이터 세트를 생성해야 할 수도 있습니다. 따라서 DRY를 구현하기위한 복잡한 조인 집합에 대한 한 수준의 뷰가 우수 할 수 있습니다 (모든 재무보고에서 동일한 기본 테이블 집합과 특정 항목의 계산을 사용하도록하는 데 사용합니다). 성능 문제를 일으키는 지 고려해야합니다.


1

위의 답변의 요점을 볼 수 없으므로 여기에갑니다. 원칙적으로 너무 많은 DRY 보지 말라 에 대하여뭔가를하고. 그것은 그렇게 표현 될 수 있지만 실제로는 상당히 다르고 긍정적 인 목적을 제공 할 수 있습니다. 더 나은 대답을 찾고, 생각하고, 찾는 신호입니다. 더 나은 솔루션을 설계 할 수있는 기회를 찾아야합니다. 내 코드에서 나쁜 냄새의 좋은면이있어 내 디자인을 다시 생각하게하고 훨씬 더 나아지게합니다. DRY는 단순한 가려운 구문 위반이 아닙니다. 모듈화하는 데 어려움이 있습니다. 구성 요소를 구성해야합니다. 그것은 무차별적인 힘과 무지 대신 템플릿과 코드 생성을 사용하는 것에 대해 생각 나게하는 반복을 나타냅니다. 자동화를 자동화 할 시간을 찾아야합니다. 그것은 당신을 parsimonious 생활로 연결합니다! 그것은 오래된 지루한 세부 사항보다는 더 시원하고 새로운 것을하는 데 더 많은 시간을 소비하는 데 도움이됩니다. 그리고 그것은 당신에게 좋은 매너, 좋은 호흡 및 건강한 라이프 스타일을 제공합니다! 글쎄, 아마 나는 약간의 길을 잃었다 ....


DRY는 저에게 매우 다른 영향을 미칩니다. 그러나 이것이 여러분에게 미치는 영향 인 경우, 나는 "정지하고 생각하고 더 나은 답을 찾을 수있는 신호"라는 철학과 도전을 좋아합니다.
n611x007

1

이전 개발자 중 일부는 DRY에 전혀 신경 쓰지 않은 오래된 레거시 프로젝트를 가지고 있습니다. 따라서 전체 코드베이스는 GetSystemTimeAsString (), LogToFile () 및 기타 많은 것들과 같은 도우미 메서드로 복잡해졌습니다. 일부 방법은 특수한 요구에 맞게 약간 사용자 정의되었지만 대부분은 복사하여 붙여 넣기 만했습니다.

불행히도 일부 메소드에는 strcpy () 등과 같은 안전하지 않은 것을 사용하여 char 배열과 같은 미묘한 버그가있는 경우가 있습니다.

따라서 모든 코드 조각을 찾아 조화시키고 버그를 수정하는 것이 실제 PITA였습니다. 그리고 우리는 여전히 화목하고 조화를 이루고 있습니다.

첫 번째 방법에서 실수를 한 다음 방금 복사했기 때문에 여러 번 수정 해야하는 경우 결코 알 수 없습니다. 그리고 나중에 몇 가지 방법을 사용하려면 코드베이스의 5 가지 방법 중 현재 어떤 방법을 사용하고 있는지 어떻게 알 수 있습니까? 그래서 당신은 하나를 복사하고 그것을 사용자 정의하고 여기에서 다시 시작합니다 ...



1

"자신을 반복하지 마십시오"라는 문구는 약간 지나치게 단순합니다. 중요한 것은 "두 개의 독립적 인 장소에 캡슐화 된 잠재적으로 변경 가능한 정보 한 조각을 피하는 것" 입니다.

프로그램이 각각 3 개의 woozles를 가진 위젯을 처리하고 여러 루프 형태 인 경우

for (i=0; i<3; i++)
  thisWidget.processWoozle(i);

그러면 위젯에 3 개의 woozles이 포함될 것으로 예상되는 경우 각 루프에 캡슐화되며 위젯 당 다른 수의 woozles를 수용하도록 코드를 업데이트하는 것이 어려울 수 있습니다. 반대로 말하면

#define WOOZLES_PER_WIDGET 3

그리고 각 루프는 다시 작성되었습니다

for (i=0; i<WOOZLES_PER_WIDGET; i++) ...

이러한 디자인 위젯 당 woozles 수를 매우 쉽게 변경할 수 있습니다.

그러나 위젯 당 woozles 수와 같은 정보를 단일 지점으로 통합하는 것이 바람직하지만 항상 실용적이지는 않다는 점에 유의해야합니다. 때로는 특정 크기의 경우에만 작동하는 논리를 하드 코딩해야 할 수도 있습니다. 예를 들어, 각 woozle에 값이 있고 특정 위젯과 연관된 중간 값을 찾으려면 값을 정렬하고 중간 값을 취할 수 있으며 이러한 접근 방식은 여러 woozles과 함께 작동하지만 논리는 세 항목의 중간 값을 찾기 위해 특별히 손으로 쓴 것보다 훨씬 빠릅니다.

WOOZLES_PER_WIDGET 상수를 사용하면 코드를 더 읽기 쉽게 만들 수 있지만 프로그램 논리에 대한 다른 조정없이 값을 변경할 수 없음을 분명히해야합니다. 이 경우, 세 항목에 대해 하드 코딩 된 논리와 상수 WOOZLES_PER_WIDGET은 "각 위젯에는 세 개의 woozles가 있습니다"라는 정보를 복제하지만 이러한 복제의 이점 (더 높은 실행 속도)은 비용을 능가 할 수 있습니다.


0

나는 실제로 유지 보수성 등에 대한 다른 포스터 의견에 동의하지만 모두 유효합니다.

토론에 약간의 반대 의견을 말하고 싶습니다.

  • 프로그래머에게만 중요합니다. 당신의 임금을 지불하는 사람들은 소프트웨어가 UAT를 통과하는 한 덜 신경 쓰지 못했습니다.
  • 중요성 측면에서 올바른 요구 사항을 얻는 것, 프로젝트 스폰서의 말을 듣고 시간을 보내는 것 등의 항목보다 순위가 낮습니다.

이 사이트는 "프로그래머"를위한 것이므로 질문이 "프로그래머의 관점"에 관한 것이라고 말하는 것이 안전하다고 생각합니다. 임금 지불 자, UAT 및 중요도에 대한 귀하의 진술은 물론 유효하지만이 특정 질문과 관련이 없습니다.
ozz

1
나는 완전히 동의하지 않습니다. 훌륭한 경영진은 원칙과 그 이유를 자세히 설명하면 그 이유를 이해할 것입니다. 이 대화는 5 분이 아니라 심층적 인 대화가되어야합니다.
Michael Durrant

2
두 번째 글 머리 기호는 완전히 맞습니다.
Jim G.

1
@Ozz, 당신의 기술에 대한 자부심은 훌륭한 프로그래머에게도 중요하지만 아마도 "프로그래머의 관점"에는 "고객 만족"에 대한 관심이 포함되어야합니다.
제임스 앤더슨

0

<tl;dr>

나는 반복되는 모든 대답을 읽을 수 없었으므로 무언가를 놓쳤을 수도 있습니다 (그리고 내가 여기서 무엇을했는지 보시겠습니까?).

다음은 코드 복제 방지에 대한 멋진 목록입니다!

  1. 손쉬운 테스트 : 코드의 '복사본'만 테스트하면됩니다.
  2. 손쉬운 수정 : 코드의 '복사본'에서 버그를 찾아 한 번만 수정하면됩니다.
  3. 보다 쉬운 업데이트 (위와 동일) : 코드를 올바르게 재사용하는 데 시간이 걸리고 소스의 수백 또는 수천 개의 다른 위치에 동일한 행을 복사하지 않았기 때문에 필요한 변경 사항은 매우 적은 장소에서 코드를 수정하여 처리 할 수 ​​있습니다.
  4. 재사용이 더 쉬움 : (몇 곳에서 복제되지 않은) 일반적인 이름이 붙여진 일반적인 방법으로 유지되는 경우, 쉽게 작성하고 직접 작성하는 대신 쉽게 사용할 수 있습니다.
  5. 더 읽기 쉽다 : 중복 된 코드는 불필요하게 장황하기 때문에 읽기 어렵다. 여기에는 논리 및 특정 의도 된 기능의 일부가 아닌 많은 행이 포함됩니다 (예 : 조치를 수행하기위한 단계를 설정하는 데 사용되는 일반 명령 또는 여러 위치에서 필요한 일반 단순 반복 태스크). 코드 공간을 낭비하는 반복이 없기 때문에 코드를 정리하면 논리와 기능이 튀어 나옵니다.
  6. (1) 및 (5)로 인해 디버그하기가 더 쉽습니다.
  7. 시간과 돈을 절약하고 앞으로 더 재미있는 일을합니다. 보다 강력한 코드를 만들 수 있습니다. 이것은 결론이며 위의 거의 모든 것을 요약 한 것입니다. 많은 사람들이 동일한 기능을 사용하는 경우, 많은 doFoo1(a, b)성가신 결함 및 엣지 사례가 발견되고 해결 될 가능성이 더 높습니다. 모두가 코드를 복사하고 생성하면 doFoo2(specialA)... doFuu2^n(a, b, c)문제를 복제 doFoo1하고 구체적으로 더 많은 작업을 만들었습니다.

</tl;dr>

긴 버전 :

코드 복제의 문제는 코드를 복제 할 때 다른 사람에게 무의식적으로 다른 사람에게 권한을 부여하는 원인으로 "지수 적으로 증가"(즉, 빠르게 확장 됨)하고, 그들도 그렇게하도록 격려합니다. 소스에 혼란스러운 중복 반복이 많은 경우 유용한 코드를 찾아서 재사용하기가 어렵 기 때문에 어렵지 않게 만듭니다. 특히 코드가 이미 적절한 이름의 함수로 추출되지 않은 경우. 따라서 문제를 해결하기 위해 일반적인 간단한 문제에 직면하면 문제를 해결하는 코드를 직접 작성하게 될 것입니다. 그리고 아마도 테스트되지 않은 더 많은 코드를 추가하여 몇 가지 엣지 사례를 확인하지 못할 것입니다.

또 다른 것은 초보자에게는 이것이 대기업에만 영향을 미치는 문제처럼 들릴 수 있지만 작은 서버 (10000 줄의 중복 된 서버 측 코드)와 같은 작은 신생 기업에 악영향을 미친다는 것을 알았습니다. 마음의 상태입니다. 당신은 DRY를 마스터 할뿐만 아니라 다른 사람들도 그렇게하도록 격려하기 위해 노력해야합니다. 그렇지 않으면 당신은 대부분 코드를 복제하는 자신을 파멸시킬 것이기 때문입니다. DRY의 수단이 준비되어 있고 적용되면 훨씬 쉽게 적용 할 수 있습니다. 중복 된 코드가 많으면 복사 붙여 넣기 솔루션을 적용하는 것이 훨씬 쉽습니다.

코드 복제에서 해로운 것들 :

  1. 이 기능을 사용할 수 있습니까? 기능을 수행하는 (또는 필요한 것을 수행하는 것처럼 보이는) 함수가 올바르게 작동해야하는지 또는 중복되고 버려진 코드인지를 어떻게 알 수 있다고 가정 해 봅시다.
  2. 중복 코드. 때때로 사람들은 코드를 복제하여 사용하고 잊어 버립니다 (향후에는 항상 다시 복제 할 수 있음). 어느 시점에서 누군가 리팩토링을 시도하기 위해 일부 위치에서 복제 된 함수에 대한 호출을 제거하지만 사용되지 않는 함수는 사용되지 않더라도 남아 있습니다.
  3. 원하는 것을 찾기가 어렵습니다. 중복 된 코드는 공간을 차지하고 grep와 같은 도구를 사용하여 유용하고 필요한 것을 찾는 것이 수십 또는 수천 개의 결과를 얻는 것보다 훨씬 어려운 작업입니다.
  4. (이전에 언급 한 적이 있음) : 유지 관리가 어렵지만 유지 관리 및 회귀 목적으로 사용하기도 어렵습니다. 테스트 코드가 복제되고 함수로 제대로 추출되지 않으면 다른 코드가 복제됩니다. 누구나 QoL을 개선하기 위해 사용하기 쉽고 읽기 쉬운 API를 작성해야합니까? 내 경험상, 아니요, 사람들이 손을 until 때까지 더 시급한 것으로 여겨지는 일이 종종 있습니다.
  5. 코드 중복이 의도하지 않은 기능에 대한 정보를 추가하지 않는 경우 (예 : [over and over]에 사용되는 일반 메소드 호출과 같이) 필요하지 않은 곳에서 코드를 자세하게 표시하기 때문에 읽기가 더 어렵습니다. 여러 종류의 의도 된 기능에 대한 근거를 설정하고이 실제 기능이 튀어 나오는 것을 어렵게 만듭니다.
  6. 이것은 많이 언급되었습니다. 코드가 틀리면 일부 가난한 여자 또는 남자는 해당 코드의 각 사용을 검색하고 변경해야합니다. 예를 들어 누군가가 필요한 클래스의 매우 적은 장소에서 mysql_query에 대해 SQL 주입 안전하지 않은 호출을 사용한 경우, 수정하고 PHP PDO를 대신 사용하는 것이 쉽지만, 호출을 복사하는 곳이 수천 곳 이상인 경우 그리고 그것을 수정하는 것은 실제로 아웃소싱이 필요하거나 때로는 더 위험한 경우 코드를 처음부터 다시 작성해야합니다.
  7. 코드 복제는 나쁜 습관입니다. 무언가를 연습하면 천천히 두 번째 성격이되어 주변 사람들에게 영향을 미칩니다. 주니어 개발자는 당신이 그것을하고 또한 참조하십시오. 전파하는 것을 실천하고 올바른 일을하는 습관을들이십시오. 더 배우십시오. 중복되지 않은 코드를 작성하는 것은 어렵고 어렵습니다. 보람있는 습관입니다.

지나친 코드 중복 방지 및 요약에 대한 마지막 참고 사항 :

이것은 또한 이전에 언급되었지만 때로는 중복을 피하면 "뒤로 구부리 게"되고 다른 사람들이 이해하기에는 너무 정교하거나 중요하지 않은 일을합니다. 읽을 수없는 코드 (또는 농담으로 "작업 보존"코드라고 함)를 작성하는 것은 코드 복제를 방지하지 않는 경우에도 그 자체로 문제가됩니다. 그러나 올바른 인프라와 모범 사례가 처음부터 주입되면 코드 복제를 방지하는 것이 훨씬 쉬우 며 사람들은 직관적이지 않은 일을 피할 수 있으므로 향후 코드 중복 방지를 위해 불필요하고 읽을 수없는 작업을 방지 할 수 있습니다. 당신은 그 시작부터 바로 일을합니다.

무슨 일을 잘하고 있습니까? 대답하기 어려운 질문이지만 한 가지 프로젝트에 필요한 방법을 정의하고 회사 내부 및 외부의 다른 사람들이 이미 구현 한 것을 확인하고 가능한 경우 재사용하는 것입니다. 코드베이스에 추가 한 모든 것을 문서화하고 필요 이상으로 한 단계 더 일반화하려고 시도합니다. 필요하지 않은 곳에서 코드를 유연하게 만들기 위해 디자인 패턴을 과도하게 사용하지 마십시오.

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