DRY 원리의 해석


10

지금은 코딩에서 DRY (Do n't Repeat Yourself)라는 개념으로 어려움을 겪고 있습니다. 너무 복잡해지기를 두려워하는이 기능을 만들고 있지만 DRY 원칙을 따르려고합니다.

createTrajectoryFromPoint(A a,B b,C c,boolean doesSomething,boolean doesSomething2)

내가 말을해야이 기능은 3 개 입력 매개 변수를 다음 함수는 부울 조합 주어진 약간 다른 것을 할 것 doesSomething등을 doesSomething2. 그러나 내가 겪고있는 문제는이 기능이 추가되는 모든 새로운 부울 매개 변수와 함께 복잡성이 크게 증가한다는 것입니다.

그래서 내 질문은, 동일한 논리 (따라서 DRY 원칙을 위반 함)를 공유하는 많은 다른 기능이나 여러 매개 변수가 약간 다르게 동작하지만 훨씬 더 복잡하게 만드는 하나의 기능을 갖는 것이 좋습니다 (그러나 건조 보존)?


3
공유 / 공통 로직을 다른 공용 createTrajectory...기능이 모두 호출 하는 개인 기능으로 분해 할 수 있습니까 ?
FrustratedWithFormsDesigner

그럴 수는 있지만 그 개인 함수는 여전히 부울 매개 변수를
가져와야합니다.

2
구체적인 예가 주어진다면 대답하기가 훨씬 쉬울 것입니다. 나의 즉각적인 반응은 당신이 묘사하고있는 이분법이 전적으로 실제적이지 않다는 것입니다. 옆으로, 나는 boolean매개 변수로 a를 사용하는 것이 최선 이라고 생각합니다 .
Jerry Coffin


어, 왜 조건적인 것들을 그들 자신의 기능으로 분해하지 않습니까?
Rig

답변:


19

단일 함수 / 메소드에서 다른 코드 경로를 트리거하는 부울 인수는 끔찍한 코드 냄새 입니다.

위반 한 사항은 느슨한 결합높은 응집력단일 책임 원칙 을 위반 하며 , 이는 우선적으로 DRY보다 훨씬 중요 합니다.

즉, 사물은 ( 커플 링 )해야 할 때만 다른 사물에 의존 해야하며 단 하나의 일만해야합니다 ( 연합 ).

자신의 누락으로, 이것은 너무 밀접하게 결합되어 있으며 (모든 부울 플래그는 상태 의존성 유형이며, 이는 최악의 것 중 하나입니다!) 너무 많은 개별 책임이 혼합되어 있습니다 (과도하게 복잡함).

당신이하고있는 일은 어쨌든 DRY의 정신에 있지 않습니다 . DRY는 반복에 대한 자세한 내용입니다 (의 R약자 REPEAT). 복사 및 붙여 넣기를 피하는 것이 가장 기본적인 형태입니다. 당신이하고있는 일은 반복과 관련이 없습니다.

문제는 코드 분해가 올바른 수준이 아니라는 것입니다. 중복 코드가 있다고 생각되면 복사 및 붙여 넣기가 아닌 적절한 매개 변수가 지정된 자체 기능 / 방법이어야하며 다른 코드는 설명 적으로 이름을 지정하고 핵심 기능 / 방법에 위임해야합니다.


좋아, 내가 쓰고있는 방식이별로 좋지 않다고 생각한다. (나의 초기 의심)이 '건조한 Sprit'가 어떻게되는지 잘 모르겠다
Albinoswordfish


4

함수가 다른 작업을 수행하도록 부울을 전달한다는 사실은 단일 책임 원칙을 위반하는 것입니다. 함수는 한 가지 일을해야합니다. 한 가지만해야하며 잘해야합니다.

설명이 포함 된 여러 개의 작은 함수로 나누고 부울 값에 해당하는 코드 경로를 분리해야하는 것처럼 냄새가납니다.

그렇게하면 결과 함수에서 공통 코드를 찾아서 자체 함수로 인수 분해해야합니다. 이 일이 얼마나 복잡한 지에 따라 한 두 클래스를 제외시킬 수도 있습니다.

물론 이것은 버전 제어 시스템을 사용하고 있고 테스트 스위트가 좋다고 가정하여 무언가를 깨뜨리지 않아도 리팩토링 할 수 있습니다.


3

무언가 또는 무언가를 수행하기로 결정한 후 다음과 같은 세 가지 함수를 갖기 전에 함수의 모든 논리를 포함하는 다른 함수를 작성하지 않는 이유는 무엇입니까?

createTrajectoryFromPoint(A a,B b,C c){...}

dosomething(A a, B b, C c){...}

dosomething2(A a, B b, C c){...}

이제 세 개의 동일한 매개 변수 유형을 세 개의 다른 함수에 전달하면 다시 반복되므로 A, B, C를 포함하는 구조체 또는 클래스를 정의해야합니다.

또는 매개 변수 A, B, C 및 수행 할 작업 목록이 포함 된 클래스를 만들 수 있습니다. 객체에 작업을 등록하여 이러한 매개 변수 (A, B, C)로 어떤 작업 (일부, 일)을 추가 할 것인지 추가하십시오. 그런 다음 객체에서 등록 된 모든 작업을 호출하는 방법이 있습니다.

public class MyComplexType
{
    public A a{get;set;}
    public B b{get;set;}
    public C c{get;set;}

    public delegate void Operation(A a, B b, C c);
    public List<Operation> Operations{get;set;}

    public MyComplexType(A a, B b, C c)
    {
        this.a = a;
        this.b = b;
        this.c = c   
        Operations = new List<Operation>();
    }

    public CallMyOperations()
    {
        foreach(var operation in Operations)
        {
            operation(a,b,c);
        }
    }
}

부울 dosomething 및 dosomething2의 가능한 값 조합을 설명하려면 기본 createTrajectoryFromPoint 함수 외에 2가 아닌 4 개의 함수가 필요합니다. 이 방법은 옵션의 수가 증가함에 따라 확장이 잘되지 않으며 심지어 함수의 이름도 지루합니다.
JGWeissman

2

DRY는 DRY와 함께 단일 책임 원칙 (SRP)을 사용하는 것이 가장 좋습니다. 동일한 코드의 약간 다른 버전을 수행하도록 함수에 bool 플래그를 추가하면 하나의 함수로 너무 많은 일을 할 수 있습니다. 이 경우 플래그가 나타내는 각 경우에 대해 별도의 함수를 만드는 것이 좋습니다. 각 함수를 작성할 때 모든 플래그를 전달하지 않고 개인 함수로 이동할 수있는 공통 섹션이 있는지 상당히 분명해야합니다. , 코드의 명백한 섹션이 없다면, 당신은 실제로 자신을 반복하지 않고 있습니다.


1

나는 보통이 문제로 여러 단계를 거쳐 더 나아가는 방법을 알 수 없을 때 멈춘다.

먼저, 당신이 한 일을하십시오. DRY로 열심히하십시오. 당신 큰 털이 엉망으로 끝나지 않으면 끝났습니다. 귀하의 경우와 같이 중복 코드가 없지만 각 부울 값이 20 곳의 다른 위치에서 확인 된 경우 다음 단계로 이동하십시오.

둘째, 코드를 블록으로 나눕니다. 부울은 올바른 블록으로 실행을 지시하기 위해 각각 한 번만 (잘, 때로는 두 번) 참조됩니다. 두 개의 부울을 사용하면 네 개의 블록으로 끝납니다. 각 블록은 거의 동일합니다. 건조가 사라졌습니다. 각 블록을 별도의 방법으로 만들지 마십시오 . 더 우아하지만 모든 코드를 하나의 방법으로 배치하면 유지 관리를 수행하는 모든 사람이 각 위치를 4 곳에서 변경해야한다는 것을 쉽게 알 수 있습니다. 잘 구성된 코드와 키가 큰 모니터를 사용하면 차이점과 실수가 거의 분명합니다. 이제 유지 보수 코드가 하고 더 빨리 원래의 얽힌 혼란보다 더 실행됩니다.

셋째, 각 블록에서 중복 코드 줄을 가져 와서 멋진 간단한 방법으로 만드십시오. 때로는 아무것도 할 수 없습니다. 때로는 많은 것을 할 수 없습니다. 그러나 조금만 수행하면 DRY로 돌아가고 코드를 조금 더 쉽고 안전하게 유지 관리 할 수 ​​있습니다. 이상적으로는 원래 방법에 중복 코드가 없을 수 있습니다. 이때 부울 매개 변수없이 여러 메소드로 분할하거나 그렇지 않을 수 있습니다. 호출 코드의 편리함이 이제 주요 관심사입니다.

두 번째 단계로 인해 이미 여기에 많은 수의 답변을 추가했습니다. 중복 코드가 싫지만 문제를 해결할 수있는 유일한 이해할 수있는 방법이라면 누구나 내가하는 일을 한눈에 알 수있는 방식으로 수행하십시오. 여러 블록과 하나의 방법 만 사용하십시오. 이름, 간격, 정렬 등 모든 것에서 블록을 가능한 동일하게 만드십시오. 그러면 차이점이 독자에게 튀어 나와야합니다. DRY 방식으로 다시 작성하는 방법을 분명히 할 수 있으며 그렇지 않은 경우 유지 관리하는 것이 합리적입니다.


0

다른 방법은 인터페이스 구현으로 리팩토링 된 다른 부울 값을 처리하는 코드를 사용하여 부울 매개 변수를 인터페이스 매개 변수로 바꾸는 것입니다. 그래서 당신은

createTrajectoryFromPoint(A a,B b,C c,IX x,IY y)

여기서 부울에 대한 다른 값을 나타내는 IX 및 IY 구현이 있습니다. 어디에 있든 함수 본문에서

if (doesSomething)
{
     ...
}
else
{
     ...
}

생략 된 코드 블록을 포함하는 구현으로 IX에 정의 된 메소드에 대한 호출로이를 대체합니다.

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