가능한 경우 지역 변수를 제거해야합니까?


95

예를 들어 Android에서 CPU를 켜려면 다음과 같은 코드를 사용할 수 있습니다.

PowerManager powerManager = (PowerManager)getSystemService(POWER_SERVICE);
WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "abc");
wakeLock.acquire();

하지만 지역 변수를 생각 powerManager하고 wakeLock제거 할 수있다 :

((PowerManager)getSystemService(POWER_SERVICE))
    .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyWakelockTag")
    .acquire();

iOS 경보보기에 유사한 장면이 나타납니다. 예 : from

UIAlertView *alert = [[UIAlertView alloc]
    initWithTitle:@"my title"
    message:@"my message"
    delegate:nil
    cancelButtonTitle:@"ok"
    otherButtonTitles:nil];
[alert show];

-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
    [alertView release];
}

에:

[[[UIAlertView alloc]
    initWithTitle:@"my title"
    message:@"my message"
    delegate:nil
    cancelButtonTitle:@"ok"
    otherButtonTitles:nil] show];

-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
    [alertView release];
}

범위에서 한 번만 사용 된 경우 로컬 변수를 제거하는 것이 좋습니다?


95
반드시 그런 것은 아닙니다. 때로는 코드가 일회성 변수를 사용하는 것이 더 명확 해지며 대부분의 프로그래밍 언어에서는 추가 변수에 대한 런타임 비용이 거의 없습니다.
Robert Harvey

75
또한 디버거가있는 코드를 단계별로 진행하는 것이 더 어렵습니다. 또한 언어에 따라 첫 번째 표현식이 NULL 또는 오류가 아닌지 확인해야합니다.
GrandmasterB

78
아닙니다. 실제로, 메소드 호출 체인을 끊기 위해 로컬 변수를 도입 하는 것이 좋습니다 . 생성 된 기계어 코드는 동일 할 가능성이 높으며 소스 코드는 거의 읽기 쉬워 지므로 더 좋습니다.
Kilian Foth

48
해봐 이제 디버거를 연결하고 PowerManager 또는 WakeLock 상태를 확인하십시오. 당신의 실수를 실현하십시오. 코드를 조사하는 데 대부분의 시간을 할애 할 때까지 나는 같은 생각을했습니다.
Faerindel

42
귀하의 예에서도 "제거 된"버전에는 스크롤 막대가 있으며 화면에 완전히 맞지 않아서 읽기가 더 어렵습니다.
Erik

답변:


235

코드는 쓰여진 것보다 훨씬 자주 읽히므로, 6 개월 후에 코드를 읽어야 할 불쌍한 영혼을 불쌍히 여겨야합니다. 제 생각에는 지역 변수가있는 첫 번째 형식이 훨씬 이해하기 쉽습니다. 한 줄에 세 가지 작업이 아니라 세 줄에 세 가지 작업이 표시됩니다.

그리고 지역 변수를 제거하여 무엇을 최적화한다고 생각하면 그렇지 않습니다. 최신 컴파일러는 로컬 변수 사용 여부에 상관없이 메소드 를 호출하기 powerManager위해 레지스터 1을 넣 습니다 newWakeLock. 의 경우도 마찬가지입니다 wakeLock. 따라서 두 경우 모두 동일한 컴파일 된 코드로 끝납니다.

1 선언과 로컬 변수 사용 사이에 많은 코드가 포함되어 있으면 스택에 들어갈 수 있지만 세부 사항은 아닙니다.


2
많은 코드가 있는지 여부를 알 수 없으며 옵티마이 저가 일부 기능을 인라인 할 수 있습니다. 그렇지 않으면 가독성을 위해 노력하는 것이 올바른 조치입니다.
Matthieu M.

2
글쎄, 나는 생각할 수있는 속성 대신에 사용되는 각 함수에서 로컬 변수가 여러 번 초기화되는 것을 보았을 때 그 이유를 검색 할 것입니다. 기본적으로 줄을 철저히 읽고 비교합니다. 그들이 같은지 확인하십시오. 그래서 시간을 낭비 할 것입니다.
Walfrat

15
@Walfrat 지역 변수가 여러 번 초기화됩니까? 아야. 아무도 지역 주민의 재사용을 제안하지
않았습니다

32
@Walfrat 회원은 부작용을 번식 시키며 특별히 공개 회원과의 통화 간 상태를 유지하려는 경우에만 사용해야합니다. 이 질문은 중간 계산의 임시 저장을위한 로컬 사용을 다루는 것으로 보입니다.
Gusdor

4
Q : 가능하다면 지역 변수를 제거해야합니까? A : 아니요.
Simon

94

고도로 찬란한 의견이 이것을 말했지만, 내가 본 대답은 없었으므로 대답으로 추가 할 것입니다. 이 문제를 결정하는 주요 요인은 다음과 같습니다.

디버깅 가능성

종종 개발자는 코드 작성보다 훨씬 많은 시간과 노력을 씁니다.

지역 변수, 당신은 할 수 있습니다 :

  • 지정된 줄의 중단 점 (조건부 중단 점 등과 같은 다른 모든 중단 점 장식 포함)

  • 지역 변수의 검사 / 감시 / 인쇄 / 값 변경

  • 캐스트로 인해 발생하는 문제를 파악하십시오.

  • 읽을 수있는 스택 추적이 있습니다 (라인 XYZ에는 10이 아닌 하나의 작업이 있음)

로컬 변수가 없으면 디버거에 따라 위의 모든 작업이 훨씬 어렵거나 매우 어렵거나 완전히 불가능합니다.

따라서, 악명 높은 격언에 따라 (방법으로 코드를 작성하면 하듯이 다음 개발자는 자신이 살고있는 곳을 알고 미친 미치광이 후 그것을 유지하는 경우)의 측면에 잘못을 쉽게 debuggability 의미, 사용 지역 변수 .


20
한 줄에 많은 호출이있을 때 스택 추적이 훨씬 유용하지 않다고 덧붙입니다. 50 행에 널 포인터 예외가 있고 해당 회선에 10 개의 호출이있는 경우에는 가운이 많이 좁아지지 않습니다. 프로덕션 응용 프로그램에서 이는 종종 결함 보고서에서 얻은 정보의 대부분입니다.
JimmyJames

3
코드를 단계별로 실행하는 것도 훨씬 어렵습니다. call ()-> call ()-> call ()-> call ()이 있으면 세 번째 메소드 호출로 들어가기가 어렵습니다. 네 개의 지역 변수가 있으면 훨씬 더 쉽습니다
gnasher729

3
@ FrankPuffer-우리는 현실 세계를 다루어야합니다. 디버거가 제안한 것을 수행하지 않는 곳.
DVK

2
@ DVV : 실제로 생각하거나 식을 볼 수있는 것보다 많은 디버거가 있습니다. MS Visual Studio (버전 2013 이후)에는 C ++ 및 C #에이 기능이 있습니다. 이클립스는 자바를 위해있다. 다른 의견에서 Faerindel은 IE11에 대해 JS를 언급했습니다.
Frank Puffer

4
@ DVV : 예, 많은 실제 세계 디버거는 내가 제안한 것을하지 않습니다. 그러나 그것은 내 의견의 첫 부분과 관련이 없습니다. 내 코드를 유지 관리하는 사람이 주로 디버거를 통해 코드와 상호 작용한다고 가정하는 것은 미친 짓입니다. 디버거는 특정 목적에 적합하지만 코드 분석을위한 주요 도구를 얻는다면 뭔가 잘못되었다고 말하고 코드를 더 디버깅 가능하게 만드는 대신 수정하려고합니다.
Frank Puffer

47

코드를 이해하기 쉽게 만드는 경우에만 가능합니다. 귀하의 예에서는 읽기가 더 어렵다고 생각합니다.

컴파일 된 코드에서 변수를 제거하는 것은 존경 할만한 컴파일러에게는 사소한 작업입니다. 출력을 직접 검사하여 확인할 수 있습니다.


1
그것은 변수를 사용하는 것만 큼 스타일링과 관련이 있습니다. 질문이 수정되었습니다.
Jodrell

29

귀하의 질문은 "범위에서 한 번만 사용 된 경우 로컬 변수를 제거하는 것이 좋습니다?" 잘못된 기준을 테스트하고 있습니다. 지역 변수의 유틸리티는 사용 횟수가 아니라 코드를 더 명확하게 만드는지 여부에 달려 있습니다. 중간 값을 의미있는 이름으로 레이블하면 코드가 더 작고 더 소화하기 쉬운 청크로 분할되므로 현재와 같은 상황에서 명확성을 향상시킬 수 있습니다.

필자가 제시 한 수정되지 않은 코드는 수정 된 코드보다 명확하므로 변경하지 않은 채로 두어야합니다. 사실, 명확성을 높이기 위해 수정 된 코드에서 로컬 변수를 가져 오는 것이 좋습니다.

로컬 변수의 성능 영향을 기대하지 않으며, 변수가 있더라도 프로그램의 속도 결정 부분에서 코드가 매우 빡빡하지 않으면 고려할만한 가치가 없습니다.


나는 그것이 스타일링과 공백을 다른 것으로 사용하는 것과 관련이 있다고 생각합니다. 질문이 수정되었습니다.
Jodrell

15

아마도.

개인적으로 타입 캐스트가 관련된 경우 로컬 변수를 제거하는 것을 망설입니다. 나는 괄호의 수가 내가 가진 정신적 한계에 다 다르기 시작함에 따라 압축 버전이 읽기 쉽지 않다는 것을 알았습니다. 심지어 로컬 변수를 사용하는 약간 더 장황한 버전에서는 필요하지 않은 새로운 괄호 세트도 소개합니다.

코드 편집기에서 제공하는 구문 강조는 이러한 문제를 어느 정도 완화 할 수 있습니다.

내 눈에 이것은 더 나은 타협입니다.

PowerManager powerManager = (PowerManager)getSystemService(POWER_SERVICE);
powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "abc").acquire();

따라서 하나의 지역 변수를 유지하고 다른 지역 변수를 버립니다. C #에서는 typecast가 이미 유형 정보를 제공하기 때문에 첫 번째 줄에 var 키워드를 사용합니다.


13

현지 변수에 유리한 답변의 타당성을 인정하지만, 악마의 옹호자를 대적하고 반대되는 견해를 제시 할 것입니다.

나는 개인적으로 로컬 변수를 순전히 문서화하는 목적으로 로컬 변수를 사용하는 것에 반대하지만, 그 이유 자체는 실천에 가치가 있음을 나타냅니다. 로컬 변수는 효과적으로 코드를 의사 문서화합니다.

귀하의 경우 주요 문제는 변수가 아닌 들여 쓰기입니다. 코드를 다음과 같이 포맷 할 수 있습니다.

((PowerManager)getSystemService(POWER_SERVICE))
        .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,"MyWakelockTag")
        .acquire();

지역 변수가있는 버전과 비교하십시오.

PowerManager powerManager=(PowerManager)getSystemService(POWER_SERVICE);
WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,"abc");
wakeLock.acquire();

지역 변수를 사용하면 이름이 독자에게 정보를 추가하고 유형이 명확하게 지정되지만 실제 메소드 호출은 덜 눈에 띄고 명확하게 읽히지 않습니다.

로컬 변수를 사용하지 않으면 : 더 간결하고 메소드 호출이 더 잘 보이지만 리턴되는 내용에 대한 자세한 정보를 요청할 수 있습니다. 그러나 일부 IDE에서는이를 보여줄 수 있습니다.

세 가지 추가 의견 :

  1. 제 생각에, 기능적으로 사용되지 않는 코드를 추가하는 것은 독자가 실제로 기능적으로 사용되지 않고 단순히 "문서화"를 위해 있다는 것을 인식해야하기 때문에 때때로 혼란 스러울 수 있습니다. 예를 들어,이 메소드의 길이가 100 줄인 경우 전체 메소드를 읽지 않으면 나중에 로컬 변수 (및 메소드 호출의 결과)가 필요하지 않은 것은 분명하지 않습니다.

  2. 지역 변수를 추가하면 해당 유형을 지정해야하며, 그렇지 않으면 코드에 의존성이 발생하지 않습니다. 메소드의 리턴 유형이 사소하게 변경된 경우 (예 : 이름이 바뀐 경우) 로컬 변수가 없으면 코드를 업데이트해야합니다.

  3. 디버거에 메서드에서 반환 된 값이 표시되지 않으면 로컬 변수를 사용하여 디버깅하기가 더 쉬울 수 있습니다. 그러나 그 해결책은 코드를 변경하지 않고 디버거의 결함을 수정하는 것입니다.


추가 의견과 관련하여 : 1. 지역 변수가 사용되는 위치에 최대한 가깝게 선언되고 메소드의 길이를 합리적으로 유지한다는 규칙을 따르는 경우 문제가되지 않습니다. 2. 형식 유추가있는 프로그래밍 언어가 아닙니다. 3. 잘 작성된 코드에는 종종 디버거가 필요하지 않습니다.
Robert Harvey

2
첫 번째 코드 예제는 유창한 인터페이스 또는 "빌더"를 사용하여 객체를 제작하는 경우에만 작동합니다.이 패턴은 코드의 어느 곳에서나 사용하지 않고 선택적으로 만 사용합니다.
Robert Harvey

1
문제는 로컬 변수가 기능적으로 중복되므로 인터페이스 유형이 케이스에 필요하다고 가정합니다. 다양한 변형을 통해 지역 주민을 제거 할 수는 있지만 간단한 사례를보고 있음을 명심했습니다.
rghome

1
가독성으로 인해 변형이 훨씬 더 나쁩니다. VB.net에 너무 많이 노출되었을 수 있지만 샘플을 볼 때 "With 문은 어디로 갔습니까? 실수로 삭제 했습니까?" 무슨 일이 일어나고 있는지 두 번 봐야하며 몇 달 후에 코드를 다시 방문해야 할 때 좋지 않습니다.
Tonny

1
@Tonny 나를 위해, 더 읽기 쉽습니다. 지역 주민들은 문맥에서 분명하지 않은 것을 추가하지 않습니다. Java 헤드가 있으므로 아무 with진술 도 없습니다 . Java에서는 일반적인 형식 지정 규칙입니다.
rghome

6

동기 부여가 여기에 있습니다. 임시 변수를 도입하면 코드를보다 쉽게 ​​읽을 수 있습니다. 그러나 또한 Extract Method , Temp를 Query로 바꾸기 와 같은 다른 가능한 리팩토링을 방지하고보기 어렵게 만들 수 있습니다 . 후자의 리팩토링 유형은 적절한 경우 임시 변수보다 훨씬 큰 이점을 제공합니다.

Fowler는 후자의 리팩토링에 대한 동기에 대해 다음과 같이 씁니다 .

"임시의 문제점은 임시적이고 지역적이라는 것입니다. 사용되는 방법의 맥락에서만 볼 수 있기 때문에 온도는 더 긴 방법을 권장하는 경향이 있습니다. 왜냐하면 그것이 당신이 온도에 도달 할 수있는 유일한 방법이기 때문입니다." temp를 쿼리 메소드로 대체하면 클래스의 모든 메소드가 정보를 얻을 수 있습니다. 이는 클래스에 대한보다 명확한 코드를 만드는 데 도움이됩니다. "

따라서 코드를 더 읽기 쉽게 만들려면 특히 temps를 사용하십시오. 특히 사용자와 팀의 로컬 표준 인 경우. 그러나 그렇게하면 때때로 더 큰 대체 개선 사항을 찾기가 더 어려워 질 수 있다는 점에 유의하십시오. 그러한 온도없이 갈 가치가있을 때를 감지하고 그렇게 할 때 더 편안해질 수있는 능력을 개발할 수 있다면 그것은 아마도 좋은 일일 것입니다.

FWIW 나는 개인적으로 Fowler의 책 "Refactoring"을 10 년 동안 읽지 않았습니다. 왜냐하면 비교적 간단한 주제에 대해서는 말할 것도 많지 않기 때문입니다. 나는 완전히 틀렸다. 결국 읽을 때 매일 코딩 방식이 바뀌 었습니다.


1
좋은 대답입니다. 내가 작성한 (그리고 다른 사람들에게서 본) 최고의 코드에는 종종 그러한 임시 변수를 대신 할 수있는 작은 (2, 3 줄) 메소드가 많이있었습니다. 개인적인 방법이 악취가 나는 논란의 여지가 있습니다 .... 잘 생각하고 종종 그것을 발견했습니다.
Stijn de Witt

이 질문의 온도는 이미 기능을 참조 하므로이 답변은 OP 질문과 관련이 없습니다.
user949300

@ user949300 나는 그것이 관련이 없다고 생각하지 않습니다. 예, OP의 특정 예제에서 temps는 함수 또는 메서드의 반환 값입니다. 그러나 OP는 분명한 시나리오 일뿐입니다. 실제 질문은 훨씬 더 일반적인 것입니다. "가능한 경우 임시 변수를 제거해야합니까?"
Jonathan Hartley

1
알았어, 팔렸 어. ** 투표를 바꾸려고 했어. 그러나 종종 OP 질문의 제한된 범위를 넘어갈 때 나는 쇠약해진다. 그래서 변덕 스럽습니다 ... :-). 편집까지 어, 내 투표를 변경할 수 없습니다, 어떤 ...
user949300

@ user949300 성스러운 소! 문제를 신중하게 고려하여 마음을 바꾸는 사람! 당신은 매우 드문 일입니다.
Jonathan Hartley

3

코드를 더 읽기 쉽게 만들 수 있다면 지역 변수를 제거하는 것이 좋습니다 . 당신의 그 한 줄짜리 라이너는 읽을 수 없지만, 그것은 꽤 장황한 OO 스타일을 따릅니다. 당신이 그것을 같은 것으로 줄일 수 있다면

acquireWakeLock(POWER_SERVICE, PARTIAL, "abc");

아마 그것이 좋은 생각이라고 말할 것입니다. 아마도 도우미 메소드를 도입하여 비슷한 방식으로 정리할 수 있습니다. 이와 같은 코드가 여러 번 나타나면 가치가 있습니다.


2

여기서 Demeter법칙을 고려해 봅시다 . LoD에 관한 Wikipedia 기사에는 다음이 명시되어 있습니다.

함수에 대한 Demeter of Law는 객체 O의 메소드 m이 다음과 같은 객체의 메소드 만 호출 할 수 있어야합니다. [2]

  1. O 자체
  2. m의 매개 변수
  3. m 내에서 생성 / 인스턴스화 된 모든 객체
  4. O의 직접 컴포넌트 객체
  5. m 범위에서 O로 액세스 할 수있는 전역 변수

이 법칙을 따른 결과 중 하나는 위의 두 번째 예에 표시된 것처럼 긴 점으로 묶인 문자열로 다른 방법으로 반환 된 객체의 메서드를 호출하지 않아야한다는 것입니다.

((PowerManager)getSystemService(POWER_SERVICE)).newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,"MyWakelockTag").acquire();

이것이 무엇을하고 있는지 알아내는 것은 정말로 어렵습니다. 이를 이해하려면 각 프로 시저의 기능을 이해 한 다음 어떤 객체에서 어떤 함수가 어떤 객체에서 호출되는지를 해독해야합니다.

PowerManager powerManager=(PowerManager)getSystemService(POWER_SERVICE);
WakeLock wakeLock=powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,"abc");
wakeLock.acquire();

당신이하려는 일을 명확하게 보여줍니다-당신은 PowerManager 객체를 얻고 PowerManager로부터 WakeLock 객체를 얻은 다음 wakeLock을 얻습니다. 위의 규칙은 LoD의 규칙 # 3을 따릅니다. 코드 내에서 이러한 객체를 인스턴스화하여 필요한 것을 수행하는 데 사용할 수 있습니다.

아마도 또 다른 생각은 소프트웨어를 만들 때 간결함이 아니라 명확성을 위해 작성해야한다는 것을 기억하는 것입니다. 모든 소프트웨어 개발의 90 %가 유지 보수입니다. 기쁘지 않은 코드를 작성하지 마십시오.

행운을 빕니다.


3
유동적 구문이이 답변에 어떻게 적용되는지에 관심이 있습니다. 올바른 형식을 사용하면 유동적 인 구문을 쉽게 읽을 수 있습니다.
Gusdor

12
"계량법"이 의미하는 것은 아닙니다. 데메테르 법칙은 도트 카운팅 연습이 아닙니다. 본질적으로 "직접 친구에게만 이야기하십시오"를 의미합니다.
Robert Harvey

5
중간 변수를 통해 동일한 방법과 멤버에 액세스하는 것은 여전히 ​​Demeter의 법칙을 위반하는 것입니다. 당신은 그것을 수정하지 않고 방금 가렸다.
Jonathan Hartley

2
"계량기 법칙"의 관점에서, 두 버전은 모두 "나쁜"것입니다.
Hulk

@Gusdor는 질문 예제의 스타일에 대한 의견이 더 많았습니다. 질문이 수정되었습니다.
Jodrell

2

한 가지주의 할 점은 코드를 자주 "다른 깊이"로 읽는다는 것입니다. 이 코드는 :

PowerManager powerManager = (PowerManager)getSystemService(POWER_SERVICE);
WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "abc");
wakeLock.acquire();

"탈지"하기 쉽습니다. 3 개의 진술입니다. 먼저 우리는 PowerManager. 그런 다음 우리는 WakeLock. 그럼 우리 . 각 줄의 시작 부분을 보면 아주 쉽게 알 수 있습니다. 간단한 변수 할당은 "Type varName = ..."으로 부분적으로 인식하기 쉽고 "..."에 대해 정신적으로 넘어갑니다. 마찬가지로 마지막 진술은 분명히 과제의 형태가 아니지만 두 개의 이름 만 포함하므로 "주 요 요점"은 즉시 명백합니다. 종종 "이 코드의 기능은 무엇입니까?" 높은 수준에서.acquirewakeLock

내가 여기 있다고 생각하는 미묘한 버그를 쫓고 있다면 분명히 더 자세히 설명하고 실제로 "..."를 기억할 것입니다. 그러나 별도의 진술 구조는 여전히 한 번에 하나의 진술을 수행하는 데 도움이됩니다 (특히 각 진술에서 호출되는 것들의 구현에 대해 더 깊이 뛰어들 필요가있는 경우 특히 유용합니다. 다음 문장으로 넘어갈 수 있습니다).

((PowerManager)getSystemService(POWER_SERVICE))
    .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyWakelockTag")
    .acquire();

이제는 모두 하나의 진술입니다. 최상위 구조는 쉽게 읽을 수 없습니다. OP의 원래 버전에서 줄 바꿈과 들여 쓰기없이 구조를 시각적으로 전달하려면 괄호를 세어 시퀀스를 3 단계 시퀀스로 디코딩해야했습니다. 다중 부분 표현식 중 일부가 메소드 호출 체인으로 배열되지 않고 서로 중첩되어 있으면 여전히 이와 유사하게 나타날 수 있으므로 괄호를 세지 않고 신뢰해야합니다. 그리고 들여 쓰기를 신뢰 하고이 모든 것으로 추정되는 마지막 점으로 넘어 가면 .acquire()스스로 나에게 무엇을 말합니까?

때때로, 그것은 당신이 원하는 것일 수 있습니다. 내가 당신의 변환을 반쯤 적용하고 썼다면 :

WakeLock wakeLock =
     ((PowerManeger)getSystemService(POWER_SERVICE))
    .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyWakeLockTage");
wakeLock.acquire();

이제 이것은 빠른 탈지 "get a WakeLock, then acquireit"과 통신합니다. 첫 번째 버전보다 훨씬 간단합니다. 획득 한 것은이라는 것이 즉시 명백합니다 WakeLock. 를 얻는 PowerManager것이이 코드의 요점에 상당히 중요하지 않은 하위 세부 사항이지만 wakeLock중요한 것은 실제로 PowerManager물건 을 묻어 두는 것이 도움이 될 수 있습니다. 이 코드의 기능에 대한 아이디어. 그리고 그것은 것을 통신 명명되지 되어 한 번만 사용하고, 때로는 그건 중요한 사항 (나머지 범위가 매우 길면 다시 사용되는지 여부를 알기 위해 모든 것을 읽어야하지만 명시 적 하위 범위를 사용하면 언어를 지원하는 다른 방법이 될 수 있습니다).

상황과 의사 소통에 따라 달라집니다. 자연어로 산문 을 작성하는 것과 같이, 정보 내용의 관점에서 기본적으로 동일한 주어진 코드를 작성하는 방법 은 항상 많습니다. 당신은 일반적으로해야 그들 사이에서 선택하는 방법을 자연 언어로 산문, 쓰기와 마찬가지로 하지 같은 기계적인 규칙을 적용하는 "를 한 번만 발생하는 모든 로컬 변수를 제거". 오히려 어떻게코드를 작성하면 특정 사항을 강조하고 다른 사항을 강조하지 않습니다. 실제로 강조하고 싶은 내용을 바탕으로 (선택적으로 기술적 인 이유로 읽기 어려운 코드를 작성하는 선택을 포함하여) 선택적으로 의식적으로 선택하도록 노력해야합니다. 특히 코드의 "장점"(다양한 수준에서)을 가져와야하는 독자에게 무엇을 제공해야하는지 생각해보십시오. 이는 매우 근접한 표현 식별 읽기보다 훨씬 더 자주 발생하기 때문입니다.


나에게 API에 대한 지식이 없지만 변수가 없어도 코드가 무엇을하는지 매우 분명합니다. getSystemService(POWER_SERVICE)전원 관리자를 가져옵니다. .newWakeLock깨우기 잠금을 얻습니다. .acquire그것을 얻습니다. 좋아, 모든 유형을 모르지만 필요한 경우 IDE에서 찾을 수 있습니다.
rghome

코드 무엇을해야하는지 분명 할 수 있지만 실제로 어떤 역할을하는지 전혀 알 수 없습니다. 내가 버그를 찾고 있다면, 내가 아는 전부는 어딘가에 있는 코드 가 수행 해야하는 작업을 수행하지 않으며 어떤 코드 를 찾아야한다는 것입니다.
gnasher729

@ gnasher729 예. 버그 헌팅은 모든 세부 사항을주의 깊게 읽어야 할 때 제시 한 예입니다. 그리고해야 할 일을하지 않는 코드를 찾는 데 실제로 도움이되는 것 중 하나는 원래 저자의 의도가 무엇인지 쉽게 볼 수 있다는 것입니다.
Ben

내가 돌아 왔을 때 나는 "한 단위"를 완전히 이해했고 다음 진술로 넘어갈 수 있습니다. 사실은 아니야 실제로 지역 변수는 그러한 완전한 이해를 방해합니다. 그들이 여전히 남아 있기 때문에 ... 정신 공간을 차지하고 ... 다음 20 번 진술에서 그들에게 일어날 일 ... 드럼 롤 ... 지역 주민이 없으면 물건이 사라져서 아무것도 할 수 없다고 확신 할 것입니다 다음 줄에 그들과 함께. 그것들을 기억할 필요가 없습니다. return같은 이유로 가능한 빨리 방법 을 사용 하고 싶습니다 .
Stijn de Witt

2

이것은 자체 이름을 가진 반 패턴입니다 : Train Wreck . 교체를 피해야하는 몇 가지 이유가 이미 언급되었습니다.

  • 읽기 어렵다
  • 디버깅이 더 어려워 짐 (변수를보고 예외 위치를 탐지)
  • 데메테르 법칙 위반 (LoD)

이 방법이 다른 객체로부터 너무 많은 것을 알고 있는지 고려하십시오. 메소드 체인 은 커플 링을 줄이는 데 도움이되는 대안입니다.

또한 객체에 대한 참조가 실제로 저렴하다는 것을 기억하십시오.


흠 수정 된 코드가 메소드 체인을 수행하지 않습니까? 수정 된 링크를 읽으십시오. 그래서 차이점을 알 수 있습니다. 꽤 좋은 기사 감사합니다!
Stijn de Witt

검토해 주셔서 감사합니다. 어쨌든 메소드 체인은 경우에 따라 작동하지만 변수를 떠나는 것이 적절한 결정 일 수 있습니다. 사람들이 왜 당신의 대답에 동의하지 않는지 아는 것이 항상 좋습니다.
Borjab

"난파선"의 반대편은 "로컬 라인"이라고 생각합니다. 그것은 대부분의 날이 아무도 없더라도 누군가가 승하차하기를 원하는 경우를 대비하여 모든 시골 마을에서 정차하는 두 개의 주요 도시 사이의 기차입니다.
rghome

1

언급 된 질문은 "가능한 경우 지역 변수를 제거해야 하는가?"입니다.

단지 당신이 할 수 있기 때문에 제거해서는 안됩니다.

상점의 코딩 지침을 따라야합니다.

대부분의 지역 변수는 코드를보다 쉽게 ​​읽고 디버깅 할 수 있도록합니다.

나는 당신이 한 일을 좋아합니다 PowerManager powerManager. 저에게 클래스의 소문자는 한 번만 사용한다는 것을 의미합니다.

변수가 값 비싼 자원을 보유 할 것인지 여부는 아닙니다. 많은 언어에는 지역 변수에 대한 구문이있어 정리 / 해제해야합니다. C #에서는 사용 중입니다.

using(SQLconnection conn = new SQLconnnection())
{
    using(SQLcommand cmd = SQLconnnection.CreateCommand())
    {
    }
}

이것은 실제로 지역 변수와 관련이 없지만 리소스 정리와 관련이 있습니다 ... C #에 정통하지는 않지만 using(new SQLConnection()) { /* ... */ }합법적이지 않으면 놀랍 습니다 (유용한 지 여부는 다른 문제입니다).
Stijn de Witt

1

다른 답변에는 한 가지 중요한 측면이 언급되지 않았습니다. 변수를 추가 할 때마다 변경 가능한 상태가 발생합니다. 이것은 코드를 더 복잡하게 만들고 이해하고 테스트하기가 어렵 기 때문에 일반적으로 나쁜 것입니다. 물론 변수의 범위가 작을수록 문제는 더 작아집니다.

실제로 원하는 것은 값을 수정할 수있는 변수가 아니라 임시 상수입니다. 따라서 언어에서 허용하는 경우 코드로 이것을 표현하십시오. Java에서는 사용할 수 final있고 C ++에서는 사용할 수 있습니다 const. 대부분의 기능적 언어에서는 이것이 기본 동작입니다.

지역 변수가 가장 유해한 유형의 변경 가능한 상태라는 것은 사실입니다. 멤버 변수는 훨씬 더 많은 문제를 일으킬 수 있으며 정적 변수는 훨씬 더 나쁩니다. 여전히 코드에서 가능한 한 정확하게 표현하는 것이 중요하다는 것을 알았습니다. 그리고 나중에 수정 될 수있는 변수와 중간 결과의 단순한 이름 사이에는 큰 차이가 있습니다. 따라서 귀하의 언어로 이러한 차이를 표현할 수 있다면 그렇게하십시오.


2
나는 당신의 대답을 무시하지 않았지만, 일종의 장기 실행 방법에 대해 이야기하지 않는 한 지역 변수가 일반적으로 명령형 언어에서 '상태'로 간주되지 않는다는 사실과 관련이 있다고 생각합니다. 필자는 final / const를 모범 사례로 사용하는 것에 동의하지만 체인 호출을 간단하게 끊기 위해 변수를 도입하면 변경 가능한 상태로 인한 문제가 발생할 가능성은 거의 없습니다. 실제로 지역 변수를 사용하면 변경 가능한 상태를 처리하는 데 도움이됩니다. 메소드가 다른 시간에 다른 값을 리턴 할 수 있다면 그렇지 않으면 정말 흥미로운 버그가 생길 수 있습니다.
JimmyJames

@JimmyJames : 내 답변에 설명을 추가했습니다. 그러나 내가 이해하지 못한 의견 중 하나가 있습니다. "지역 변수를 사용하면 변경 가능한 상태를 처리하는 데 도움이됩니다." 이것을 설명해 주시겠습니까?
Frank Puffer

1
예를 들어, 값을 확인해야하고 10을 초과하면 경보를 발령한다고 가정하겠습니다. 이 값이 method에서 온다고 가정하십시오 getFoo(). 로컬 선언을 피하면 결국 if(getFoo() > 10) alert(getFoo());getFoo ()가 두 개의 다른 호출에서 다른 값을 반환 할 수 있습니다. 10 이하의 값으로 경고를 보내면 혼란스럽고 결함으로 다시 돌아옵니다. 이런 종류의 일은 동시성에 의해 더욱 중요해집니다. 로컬 할당은 원자 적입니다.
JimmyJames

아주 좋은 지적입니다. 어쩌면 나는이 지역 주민을 좋아하지 않는 이유는 .... 당신은 건가요 그 무언가 ,이 메소드를 호출 한 후 인스턴스를? 나머지 방법을 확인하겠습니다 .... mmm ok no. 그리고 당신 이 가진 일 ... 당신은 그것으로 무엇을하고 있습니까? 아, 네 더 읽을 수 있어야하는데 ...하지만 그들없이 나는 확실히 당신이 더 이상 그래서 방법의 나머지 부분을 읽을 필요가 없습니다를 사용하지 마십시오. PowerManagerWakeLock
Stijn de Witt

최근 발표에서 발표자는 변수가 최종 변수임을 보여주는 가장 좋은 방법 은 변수가 변하지 않는 짧은 방법을 작성 하는 것이라고 주장했습니다 . final메소드의 로컬 변수에 필요한 경우 메소드가 너무 깁니다.
user949300
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.