공통 코드를 중앙 집중화하기 위해 함수를 사용하는 것이 좋습니까?


20

나는이 문제를 많이 겪고있다. 예를 들어, 현재 읽기 기능과 쓰기 기능을 작성하고 buf있으며 NULL 포인터인지 mode변수가 특정 경계 내에 있는지 확인합니다.

이것은 코드 복제입니다. 자체 기능으로 이동하여 해결할 수 있습니다. 그러나 나는해야합니까? 이것은 빈약 한 기능 (많은 일을하지는 못함), 현지화 (일반 목적은 아님)이며 자체적으로 잘 견디지 못합니다 (어디가 있는지 알지 못하면 필요한 것을 파악할 수 없음) 익숙한). 또 다른 옵션은 매크로를 사용하는 것이지만이 게시물의 기능에 대해 이야기하고 싶습니다.

따라서 이와 같은 기능을 사용해야합니까? 장단점은 무엇입니까?


2
한 줄짜리 물건은 별도의 기능으로의 이동을 보증하기 위해 여러 곳에서 사용되는 것 이상이 필요합니다.

1
좋은 질문입니다. 내가 똑같이 궁금해하는 많은 시간이 있었다.
rdasxy

답변:


31

이것은이다 중대한 기능의 사용.

이것은 꽤 빈혈 기능이 될 것입니다 (별로하지는 않습니다) ...

잘 됐네요 함수는 한 가지만 수행해야합니다.

오히려 현지화 된 ...

OO 언어에서는 비공개로 설정하십시오.

(그래서 일반적인 목적은 아닙니다)

더 이상의 사건을 처리하는 경우 이다 범용. 또한 일반화 만이 함수의 유일한 사용은 아닙니다. 실제로 (1) 같은 코드를 두 번 이상 작성하지 않아도되지만 (2) 코드를 더 작은 청크로 나누면 더 읽기 쉽습니다. 이 경우 (1)과 (2)를 모두 달성하고 있습니다. 그러나 함수가 한 곳에서만 호출 되었더라도 여전히 (2)에 도움이 될 수 있습니다.

스스로 잘 견디지 못합니다 (어디에 사용되는지 알지 못하면 필요한 것을 파악할 수 없습니다).

그것에 대한 좋은 이름을 생각해 내십시오. "ValidateFileParameters"또는 무엇인가. 이제 자체적으로 괜찮습니다.


6
잘 상품 포인트. 한 곳에서 수정할 수있을 때 전체 코드베이스에서 복제 된 버그를 찾지 못하게 (3) 추가합니다. 코드 중복으로 인해 유지 관리가 매우 빠릅니다.
deadalnix

2
@ deadalnix : 좋은 지적입니다. 내가 글을 쓰는 동안 나는 나의 요점이 지나치게 지나치게 단순화되었다는 것을 깨달았다. 작성 및보다 쉬운 디버깅은 기능을 개별 함수를 단위 테스트하는 기능과 같이 기능으로 분할하는 이점이 있습니다.
Kramii Reinstate Monica

11

그것은 완전히 기능이어야합니다.

if (isBufferValid(buffer)) {
    // ...
}

훨씬 더 읽기 쉽고 유지 관리가 용이합니다 (체크 로직이 변경되면 한 곳에서만 변경하십시오).

게다가 이와 같은 것들이 쉽게 인라인되므로 함수 호출 오버 헤드에 대해 걱정할 필요조차 없습니다.

더 좋은 질문을하겠습니다. 어떻게 하면 좋은 습관 이되지 않습니까?

옳은 일을하십시오. :)


4
isBufferValid가 단순히 return buffer != null;그렇다면 가독성을 손상 시키고 있다고 생각합니다.
pdr

5
@ pdr :이 간단한 경우 제어 괴물의 사고 방식이 있고 실제로 실제로 버퍼가 유효한지 코드가 확인하는 방법을 알고 싶다면 가독성이 떨어집니다. 따라서 간단한 경우에 주관적입니다.
Spoike

4
@ pdr 나는 그것이 표준에 의해 가독성을 어떻게 손상시키는 지 모른다. 당신은에 대한 배려에서 다른 개발자를 면제하고 어떻게 당신이 뭔가를하고있는, 그리고에 초점을 맞추고 어떤 일을하는지. 보다 분명하게 의사 소통을하기 때문에 isBufferValid(내 책에서)보다 더 읽기 buffer != null쉽습니다. 그리고 다시 말하지만, 여기에서도 중복을 방지 할 수 있습니다. 더 필요한 게 뭐야?
얌 마르코비치

5

IMO, 코드 스 니펫은 함수가 매우 짧거나 한 번만 사용되는지 여부에 관계없이 코드를 더 읽기 쉽게 만들 때마다 자체 함수로 이동하는 것이 좋습니다.

물론 상식에 의해 규정 된 한계가 있습니다. 예를 들어 WriteToConsole(text)본문 이있는 메소드를 원치 않습니다 Console.WriteLine(text). 그러나 가독성 측면에서 잘못하는 것이 좋습니다.


2

일반적으로 함수를 사용하여 코드에서 중복을 제거하는 것이 좋습니다.

그러나 너무 멀리 걸릴 수 있습니다. 이것은 판단 요청입니다.

null 버퍼 검사의 예를 들어, 다음 코드가 명확하고 동일한 패턴이 몇 군데에서 사용 되더라도 별도의 함수로 추출해서는 안된다고 말할 수 있습니다.

if (buf==null) throw new BufferException("Null read buffer!");

오류 메시지를 일반 널 점검 함수에 매개 변수로 포함하고 함수를 정의하는 데 필요한 코드도 고려하면이를 대체 할 순 LOC가 아닙니다.

checkForNullBuffer(buf, "Null read buffer!");

또한, 디버깅 할 때 수행중인 작업을 확인하기 위해 함수에 들어가야한다는 것은 함수 호출이 사용자에게 "투명"하지 않으므로 읽기 쉽고 유지 관리하기가 어렵다는 것을 의미합니다.


의심 할 여지없이, 귀하의 예는 실제 복제가 필요한 것보다 언어의 계약 지원 부족에 대해 더 많이 말합니다. 그러나 어쨌든 좋은 지적입니다.
deadalnix

1
내가 보는 방식으로 요점을 놓쳤습니다. 아니 이상 단계 또는 논리 때마다 디버그 고려해야 할 필요 것은 무엇을 내가 해요 찾고. 어떻게되는지 알고 싶다면 한 번 확인하십시오. 매번 그것을 해야하는 것은 단순히 바보입니다.
얌 마르코비치

오류 메시지 ( "널 읽기 버퍼")가 반복되면 해당 중복을 제거해야합니다.
케빈 클라인

이것은 단지 예일 뿐이지 만 각 함수 호출 사이트에서 예를 들어 "Null read buffer"와 "Null write buffer"와 같은 다른 메시지가있을 것입니다. 각 상황에 대해 동일한 로그 / 오류 메시지를 원하지 않는 한 중복 제거 할 수 없습니다 .......
mikera

-1 Preconditions.checkNotNull은 나쁜 습관이 아닌 좋은 습관입니다. 문자열 메시지를 포함 할 필요는 없습니다. google-collections.googlecode.com/svn/trunk/javadoc/com/google/…
ripper234

2

일반적으로 코드를 중앙 집중화하는 것이 좋습니다. 코드를 가능한 많이 재사용해야합니다.

그러나 그렇게하는 방법에 주목 하는 것이 중요 합니다. 예를 들어, compute_prime_number () 또는 check_if_packet_is_bad ()를 수행하는 코드가 있으면 좋습니다. 기능 알고리즘 자체가 발전하여 이익을 얻게 될 가능성이 있습니다.

그러나 산문으로 반복되는 코드는 즉시 중앙 집중화 할 수 없습니다. 이것은 나쁘다. 응용 프로그램의 여러 부분이 사용되기 시작하면 시간이 지남에 따라 코드를 숨기려면 함수 안에 임의의 코드 줄을 숨길 수 있습니다.

다음은 질문하기 전에 물어보아야 할 몇 가지 질문입니다.

  1. 함수가 고유 한 의미를 생성 했습니까? 아니면 여러 줄입니까?

  2. 다른 기능 중 동일한 기능을 사용해야하는 컨텍스트는 무엇입니까? 이것을 사용하기 전에 API를 약간 일반화해야 할 가능성이 있습니까?

  3. 예외를 던질 때 응용 프로그램의 다른 부분은 무엇입니까?

  4. 기능이 발전 할 것이라는 시나리오는 무엇입니까?

또한 이와 같은 것이 이미 존재하는지 확인해야합니다. 나는 많은 사람들이 이미 존재하는 것을 찾는 것이 아니라 항상 매크로 MIN, MAX를 재정의하려는 경향을 보았습니다.

본질적으로 문제는 "이 새로운 기능이 실제로 재사용 할 가치가 있는가 , 아니면 단지 복사-붙여 넣기 인가?"입니다. 처음이라면가는 것이 좋습니다.


1
복제 된 코드에 고유 한 의미가 없으면 리팩토링이 필요하다는 코드가 표시됩니다. 복제가 발생하는 장소는 아마도 그 자체의 의미가 없기 때문입니다.
deadalnix

1

코드 복제는 피해야합니다. 예상 할 때마다 코드 중복을 피해야합니다. 예상하지 못한 경우 동일한 코드 조각이 3 번 복제되기 전에 3 : 리팩토링 규칙을 적용하고 2 번 중복 된 경우 쿼크에 주석을 답니다.

코드 중복이란 무엇입니까?

  • 코드베이스의 여러 곳에서 반복되는 루틴. 이 Tourine은 간단한 함수 호출보다 복잡해야합니다 (그렇지 않으면 인수 분해를 통해 아무것도 얻지 못함).
  • 매우 일반적인 작업, 심지어 사소한 작업 (종종 테스트) 이것은 코드의 캡슐화와 의미를 향상시킵니다.

아래의 예를 고려하십시오.

if(user.getPrileges().contains("admin")) {
    // Do something
}

된다

if(user.isAdmin()) {
    // Do something
}

캡슐화 (이제 관리자가되도록 조건을 변경할 수 있음)와 코드의 의미를 개선했습니다. 사용자가 관리자인지 확인하는 방식으로 버그가 발견되면 전체 코드베이스를 버리고 어디에서나 고칠 필요가 없습니다 (단 하나를 잊어 버려 애플리케이션에 보안 결함이있는 경우).


1
Btw 예는 데메테르의 법칙을 보여줍니다.
MaR

1

DRY 는 코드 조작을 단순화하는 것입니다. 당신은 바로이 원리에 대한 좋은 점에 감동 한 : 그것은 하지 코드에서 토큰의 수를 최소화하는 것이 아니라 만드는 방법에 대해 의미 론적으로 해당 코드에 대한 수정의 단일 지점 . 수표는 항상 동일한 의미를 갖는 것처럼 들리므로 수정해야 할 경우에 대비하여 기능에 넣어야합니다.


0

중복 된 것으로 보이면이를 중앙 집중화하는 방법을 찾아야합니다.

함수는 좋은 방법입니다 (아마도 최선은 아니지만 언어에 따라 다름). 함수가 빈혈이더라도 그런 식으로 유지된다는 의미는 아닙니다.

다른 것을 확인해야한다면 어떻게해야합니까?

추가 검사를 추가하거나 기능을 변경 해야하는 모든 장소를 찾으십니까?


다른 한편으로, 절대로 무언가를 추가하지 않을 가능성이 매우 높다면 어떻게해야합니까? 이 경우에도 귀하의 제안이 여전히 최선의 조치입니까?
EpsilonVector

1
@EpsilonVector 내 경험칙은 한 번 변경하면 리팩토링 할 수 있다는 것입니다. 따라서 귀하의 경우에는 그대로두고 변경해야하면 기능이됩니다.
Thanos Papathanasiou

0

다음 조건이 충족되면 거의 항상 좋습니다.

  • 가독성 향상
  • 제한된 범위 내에서 사용

더 큰 범위에서는 중복 대 종속성 트레이드 오프를 신중하게 평가해야합니다. 범위 제한 기술의 예 : 개인 섹션이나 모듈을 공개하지 않고 숨기십시오.

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