“가변은 가능한 한 가장 작은 범위 내에 있어야한다”는“가능한 경우 변수가 존재하지 않아야한다”는 사례를 포함합니까?


32

" 인스턴스 변수보다 로컬 변수를 선호하는 이유는 무엇입니까? " 에 대한 대답에 따르면 변수는 가능한 가장 작은 범위 내에 있어야합니다.
내 해석으로 문제를 단순화하면 다음과 같은 종류의 코드를 리팩터링해야합니다.

public class Main {
    private A a;
    private B b;

    public ABResult getResult() {
        getA();
        getB();
        return ABFactory.mix(a, b);
    }

    private getA() {
      a = SomeFactory.getA();
    }

    private getB() {
      b = SomeFactory.getB();
    }
}

이런 식으로 :

public class Main {
    public ABResult getResult() {
        A a = getA();
        B b = getB();
        return ABFactory.mix(a, b);
    }

    private getA() {
      return SomeFactory.getA();
    }

    private getB() {
      return SomeFactory.getB();
    }
}

그러나 "변수는 가능한 한 가장 작은 범위 내에 있어야한다"의 "정신"에 따르면, "변수가 없다"는 "변수가있는 것"보다 더 작은 범위를 가지지 않습니까? 따라서 위의 버전을 리팩터링해야한다고 생각합니다.

public class Main {
    public ABResult getResult() {
        return ABFactory.mix(getA(), getB());
    }

    private getA() {
      return SomeFactory.getA();
    }

    private getB() {
      return SomeFactory.getB();
    }
}

그 때문에 getResult()전혀 어떤 지역 변수가 없습니다. 그게 사실입니까?


72
명시 적 변수를 만들면 이름을 지정해야한다는 이점이 있습니다. 몇 가지 변수를 도입하면 불투명 한 방법을 빠르게 읽을 수있는 방법으로 바꿀 수 있습니다.
Jared Goguen

11
솔직히, 변수 이름 a와 b에 관한 예제는 지역 변수의 가치를 설명하기에는 너무 많이 고안되었습니다. 다행히도, 당신은 좋은 대답을 얻었습니다.
Doc Brown

3
두 번째 스 니펫에서 변수는 실제로 변수 가 아닙니다 . 그것들은 수정하지 않기 때문에 효과적으로 지역 상수입니다. Java 컴파일러는 로컬 범위에 있고 컴파일러 그 결과를 알고 있기 때문에 최종 정의하면 동일하게 취급 합니다. 실제로 final키워드를 사용해야하는지 여부는 스타일과 의견의 문제입니다 .
하이드

2
@JaredGoguen 명시 적 변수를 만들려면 변수 이름을 지정해야하는 부담과 이름을 지정할 수 있다는 이점이 있습니다.
15:56에 Bergi

2
"가변이 가능한 가장 작은 범위 내에 있어야합니다"와 같은 코드 스타일 규칙은 전혀 제로 없이 적용 할 수 있습니다. 따라서, 당신은 간단한 지침을 취하여 명확하지 않은 영역으로 확장하는이 질문의 폼의 추론에 기초한 코드 스타일 결정을 결코 원하지 않습니다 ( "가능한 경우 변수는 작은 범위를 가져야합니다"-> "변수는 가능하면 존재하지 않아야합니다"). 결론을 구체적으로 뒷받침 할만한 이유가 있거나 결론이 부적절한 확장입니다. 어느 쪽이든 원래 지침이 필요하지 않습니다.

답변:


110

아니요. 몇 가지 이유가 있습니다.

  1. 의미있는 이름을 가진 변수는 코드를 이해하기 쉽게 만들 수 있습니다.
  2. 복잡한 수식을 더 작은 단계로 나누면 코드를 더 쉽게 읽을 수 있습니다.
  3. 캐싱.
  4. 두 번 이상 사용할 수 있도록 객체에 대한 참조를 보유합니다.

등등.


22
또한 언급 할 가치가 있습니다. 값은 메모리에 관계없이 메모리에 저장되므로 실제로는 동일한 범위로 끝납니다. 로버트가 위에서 언급 한 이유로 이름을 붙일 수도 있습니다!
Maybe_Factor

5
@Maybe_Factor 가이드 라인은 실제로 성능상의 이유가 아닙니다. 코드를 유지 관리하는 것이 얼마나 쉬운 지에 관한 것입니다. 그러나 이는 단순히 이름이 잘 정해지고 디자인 된 기능을 구성하지 않는 한 (예 : 할 이유가 없음) 의미있는 지역이 하나의 거대한 표현보다 낫다는 것을 의미 var taxIndex = getTaxIndex();합니다.
Luaan

16
모두 중요한 요소입니다. 그것에 방식의 I 모양 : 간결은 목표; 선명도는 또 다른 것입니다. 견고성은 또 다른 것입니다. 성능은 또 다른 것입니다. 등등. 이 중 어느 것도 절대 목표가 아니며 때로는 충돌합니다. 따라서 우리의 임무는 이러한 목표를 가장 잘 균형 잡는 방법을 찾는 것입니다.
gidds

8
컴파일러는 3 & 4를 처리합니다.
Monica Harming

9
숫자 4는 정확성에 중요 할 수 있습니다. 함수 호출의 값이 변경되어 (예 : now()변수를 제거하고 메소드를 두 번 이상 호출하면 버그가 발생할 수 있습니다. 이로 인해 실제로 미묘하고 디버깅하기 어려운 상황이 발생할 수 있습니다. 명백한 것처럼 보이지만 변수를 제거하기 위해 맹목적인 리팩토링 임무를 수행하는 경우 결함이 발생하기 쉽습니다.
JimmyJames

16

필요하지 않고 코드의 가독성을 향상시키지 않는 변수는 피해야합니다. 코드의 특정 지점에서 범위 내에있는 변수가 많을수록 코드를 이해하기가 더 복잡합니다.

정말 변수의 이익을 볼 수 없습니다 ab나는 변수없이 버전을 작성합니다, 그래서 당신의 예를. 반면에 처음에는 기능이 너무 간단해서 중요하지 않다고 생각합니다.

함수가 길어질수록 더 많은 문제가 발생하고 범위에 더 많은 변수가 있습니다.

예를 들어

    a=getA();
    b=getB();
    m = ABFactory.mix(a,b);

더 큰 함수의 맨 위에는 하나가 아닌 세 개의 변수를 도입하여 나머지 코드를 이해해야하는 정신적 부담이 커집니다. 나머지 코드를 읽어야하는지 a또는 b다시 사용 되는지 확인해야 합니다. 필요 이상으로 범위가 긴 지역은 전체 가독성이 좋지 않습니다.

물론 변수가 필요한 경우 (예 : 임시 결과를 저장하기 위해) 또는 변수 코드의 가독성을 향상시키는 경우에는 변수를 유지해야합니다.


2
물론, 방법이 너무 길어 현지인의 수가 너무 많아서 유지 관리하기가 어려울 때는 어쨌든 해체하고 싶을 것입니다.
Luaan

4
"외장 적"변수의 이점 중 var result = getResult(...); return result;하나는 중단 점을 설정 return하고 정확히 무엇인지 배울 수 있다는 것 result입니다.
Joker_vD

5
@Joker_vD : 이름에 맞는 디버거는 어쨌든 그 모든 것을 수행 할 수 있어야합니다 (로컬 변수에 저장하지 않고 함수 종료시 중단 점을 설정하지 않고 함수 호출의 결과를 볼 수 있음).
Christian Hackl

2
@ChristianHackl 디버거가 거의 없어서 추가하는 데 시간이 걸리는 복잡한 시계를 설정하지 않고도 그렇게 할 수 있습니다. 요일마다 디버깅을 위해 with variables 버전을 사용하겠습니다.
Gabe Sechan

1
@ChristianHackl 그리고 디버거가 끔찍하게 설계되어 있기 때문에 디버거가 기본적으로 그렇게 할 수없는 경우에도 더 발전시키기 위해 결과를 저장하기 위해 컴퓨터의 로컬 코드를 수정 한 다음 디버거에서 확인할 수 있습니다 . 그런 목적으로 변수에 값을 저장할 이유가 없습니다 .
그레이트 오리

11

다른 답변 외에도 다른 것을 지적하고 싶습니다. 변수의 범위를 작게 유지하는 이점은 구문 적으로 변수에 액세스 할 수있는 코드의 양을 줄이는 것뿐만 아니라 변수에 새로운 값을 할당하거나 호출하여 변수를 수정할 수있는 가능한 제어 흐름 경로 수를 줄이는 것입니다 변수에 보유 된 기존 객체의 변경 방법).

클래스 범위 변수 (인스턴스 또는 정적)는 로컬 범위 변수보다 훨씬 많은 제어 흐름 경로를 갖습니다. 메소드에 의해 변경 될 수 있기 때문에 순서에 관계없이 여러 번 호출 할 수 있으며, 종종 클래스 외부의 코드에 의해 호출 될 수 있습니다 .

초기 getResult방법을 살펴 보겠습니다 .

public ABResult getResult() {
    getA();
    getB();
    return ABFactory.mix(this.a, this.b);
}

이제 이름 getAgetB수 있습니다 제안 그들이에 할당 할 것을 this.a하고 this.b, 우리는 알 수 없다 확실히 그냥보고에서 getResult. 따라서 메소드에 전달 된 this.athis.b값이 이전 에 호출 된 이전 mixthis오브젝트 상태에서 온 것일 수 있으며 , getResult클라이언트가 메소드 호출 방법 및시기를 제어하므로 예측할 수 없습니다.

지역 변수 ab변수가 있는 수정 된 코드에서는 변수가 사용되기 직전에 선언 되었기 때문에 각 변수의 할당에서 사용에 이르기까지 정확히 하나의 (예외가없는) 제어 흐름이 있음이 분명합니다.

따라서 제어 흐름 추론을 단순화한다는 점에서 클래스 범위에서 로컬 범위로 (수정 가능한) 변수를 이동하고 루프 외부에서 내부로 (수정 가능한) 변수를 이동하는 것에는 상당한 이점이 있습니다.

반면, 마지막 예제에서와 같이 변수를 제거하면 실제로 제어 흐름 추론에 영향을 미치지 않으므로 이점이 적습니다. 또한 값에 주어진 이름을 잃어 버립니다. 단순히 변수를 내부 범위로 옮길 때 발생하지 않습니다. 이것은 고려해야 할 절충 사항이므로 변수를 제거하는 것이 더 나은 경우가 있고 다른 경우에는 더 나쁜 경우가 있습니다.

변수 이름을 잃고 싶지는 않지만 변수의 범위를 줄이려면 (더 큰 함수 내에서 사용되는 경우) 변수와 그 용도를 블록 문으로 감싸는 것을 고려할 수 있습니다 ( 또는 자신의 기능으로 이동 ).


2

이것은 언어에 따라 다소 다르지만, 함수형 프로그래밍의 덜 명백한 이점 중 하나는 프로그래머와 코드 리더가이를 필요로하지 않도록 장려한다는 것입니다. 치다:

(reduce (fn [map string] (assoc map string (inc (map string 0))) 

또는 일부 LINQ :

var query2 = mydb.MyEntity.Select(x => x.SomeProp).AsEnumerable().Where(x => x == "Prop");

또는 Node.js :

 return visionFetchLabels(storageUri)
    .then(any(isCat))
    .then(notifySlack(secrets.slackWebhook, `A cat was posted: ${storageUri}`))
    .then(logSuccess, logError)
    .then(callback)

마지막은 중간 변수없이 이전 함수의 결과에서 함수를 호출하는 체인입니다. 그것들을 소개하면 훨씬 덜 명확해질 것입니다.

그러나 첫 번째 예와 다른 두 예의 차이점 은 작동 순서를 암시하는 것 입니다. 이것은 실제로 계산 된 순서와 같지 않을 수 있지만 독자가 생각해야하는 순서입니다. 두 번째는 왼쪽에서 오른쪽입니다. Lisp / Clojure 예제의 경우 오른쪽에서 왼쪽으로 더 비슷합니다. 언어의 "기본 방향"이 아닌 코드를 작성하는 데 약간의주의를 기울여야하며이 둘을 혼합하는 "중간"표현은 피해야합니다.

F #의 파이프 연산자 |>는 오른쪽에서 왼쪽이어야하는 왼쪽에서 오른쪽으로 쓸 수 있기 때문에 부분적으로 유용합니다.


2
간단한 LINQ 표현식 또는 람다에서 변수 이름으로 밑줄을 사용했습니다. myCollection.Select(_ => _.SomeProp).Where(_ => _.Size > 4);
Graham

이것이 조금이라도 OP의 질문에 답하는 것은 확실치 않다.
AC

1
왜 다운 보트인가? 질문에 직접 답변하지 않더라도 여전히 유용한 정보입니다.
reggaeguitar

1

"가능한 가장 작은 범위"를 "기존 범위 또는 추가하기에 적합한 범위"로 읽어야하므로 아니요라고 대답합니다. 그렇지 않으면 {}변수의 범위가 마지막 의도 된 사용을 넘어 확장되지 않도록 인공 범위 (예 : C와 같은 언어의 무차별 블록)를 작성해야하며 이미 존재하지 않는 한 일반적으로 난독 화 / 클러 터로 찌그러 질 것입니다. 범위가 독립적으로 존재하는 좋은 이유입니다.


2
많은 언어에서 범위 지정의 한 가지 문제점은 마지막 사용이 다른 변수의 초기 값을 계산할 때 일부 변수를 갖는 것이 매우 일반적이라는 것입니다. 언어가 범위 지정 목적으로 명시 적으로 구성을 가지고 있으면, 범위 내 변수를 사용하여 계산 된 값을 범위 외부 초기화에 사용할 수있게한다면 그러한 범위는 많은 언어에서 요구하는 엄격하게 필요한 범위보다 더 유용 할 수 있습니다.
supercat

1

함수 ( methods )를 고려하십시오 . 가능한 가장 작은 서브 태스크에서 코드를 분할하거나 가장 큰 단일 코드 조각도 코드를 분할하지 않습니다.

논리적 작업을 한정하여 소모품으로 바꾸는 한계입니다.

변수도 마찬가지 입니다. 논리적 데이터 구조를 이해할 수있는 부분으로 지적 또는 단순히 매개 변수의 이름을 지정 (상태 지정)하십시오.

boolean automatic = true;
importFile(file, automatic);

그러나 물론 상단에 선언이 있고 2 백 줄 더 나아가 첫 번째 사용법은 오늘날 나쁜 스타일로 받아 들여지고 있습니다. 이것은 "가변이 가능한 가장 작은 범위 내에서 살아야한다" 고 말하는 것입니다. 매우 가까운 "변수를 재사용하지 마십시오."


1

NO의 이유가 디버깅 / 준비 상태이므로 다소 누락되었습니다. 코드는이를 위해 최적화되어야하며 명확하고 간결한 이름은 예를 들어

if (frobnicate(x) && (get_age(x) > 2000 || calculate_duration(x) < 100 )

이 줄은 짧지 만 이미 읽기가 어렵습니다. 몇 개의 매개 변수를 추가하고 여러 행에 걸쳐있는 경우 추가하십시오.

can_be_frobnicated = frobnicate(x)
is_short_lived_or_ancient = get_age(x) > 2000 || calculate_duration(x) < 100
if (can_be_frobnicated || is_short_lived_or_ancient )

나는이 방법을 읽고 이해하기가 더 쉽다는 것을 알았습니다. 따라서 중간 변수에는 아무런 문제가 없습니다.

또 다른 예는 R과 같은 언어이며 마지막 줄은 자동으로 반환 값입니다.

some_func <- function(x) {
    compute(x)
}

이것은 위험합니다. 반품이 만료되었거나 필요합니까? 이것은 더 명확하다 :

some_func <- function(x) {
   rv <- compute(x)
   return(rv)
}

항상 그렇듯이, 이것은 판단 호출입니다-중개 변수가 독서를 향상시키지 않으면 제거하고, 그렇지 않으면 유지하거나 소개하십시오.

또 다른 요점은 디버깅 가능성 일 수 있습니다. 중개 결과가 관심있는 경우 위의 R 예 에서처럼 중개자를 소개하는 것이 가장 좋습니다. 이것이 얼마나 자주 요구되는지는 상상하기 어렵고 너무 많은 디버깅 변수가 혼란스러워서 다시 판단 판단에주의해야합니다.


0

제목 만 참조 : 변수가 불필요하면 삭제해야합니다.

그러나“필요하지 않다”는 것은 변수를 사용하지 않고 동등한 프로그램을 작성할 수 있다는 것을 의미하지는 않습니다. 그렇지 않으면 모든 것을 이진수로 작성해야한다는 메시지가 나타납니다.

가장 일반적인 종류의 불필요한 변수는 사용되지 않는 변수이며, 변수의 범위가 작을수록 불필요한 것으로 판단하기가 더 쉽습니다. 중간 변수가 불필요한 지 여부는 이진 상황이 아니기 때문에 상황에 따라 결정하기가 더 어렵습니다. 실제로 두 가지 방법으로 동일한 소스 코드를 사용하면 주변 코드의 문제를 해결 한 과거 경험에 따라 동일한 사용자가 다른 답변을 얻을 수 있습니다.

예제 코드가 정확하게 표현 된 경우 두 개의 개인 메소드를 제거하는 것이 좋지만 팩토리 호출의 결과를 로컬 변수에 저장했는지 또는 믹스의 인수로 사용했는지에 대해서는 거의 관심이 없습니다. 방법.

코드 가독성은 올바르게 작동하는 것을 제외하고 모든 것보다 우선합니다 (올바른 "가능한 한 빨리"는 허용 가능한 성능 기준을 포함합니다).

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