태그 보관물: scope

scope

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

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

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()전혀 어떤 지역 변수가 없습니다. 그게 사실입니까?



답변

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

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

등등.


답변

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

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

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

예를 들어

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

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

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


답변

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

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

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

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

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

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


답변

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

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


답변

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


답변

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

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

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

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

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


답변

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 예 에서처럼 중개자를 소개하는 것이 가장 좋습니다. 이것이 얼마나 자주 요구되는지는 상상하기 어렵고 너무 많은 디버깅 변수가 혼란스러워서 다시 판단 판단에주의해야합니다.