왜 글로벌 주가 그렇게 악한가?


328

시작하기 전에 추상화 및 종속성 주입의 개념을 잘 알고 있다고 가정하겠습니다. 눈을 뜨고있을 필요는 없습니다.

글쎄, 대부분의 사람들은 "글로벌 변수를 사용하지 마십시오"또는 "싱글 톤은 글로벌이기 때문에 악한 것"이라는 것을 실제로 이해하지 않고 여러 번 말합니다. 하지만 실제로는 무엇 이며 불길한 전역 상태에 대해 그렇게 나쁜?

내 응용 프로그램, 예를 들어 시스템 폴더 경로 또는 응용 프로그램 전체 데이터베이스 자격 증명에 대한 전역 구성이 필요하다고 가정 해 봅시다.

이 경우 전체 응용 프로그램에서 일반적으로 사용할 수있는 일종의 전역 공간에서 이러한 설정을 제공하는 것 외에 다른 좋은 해결책은 없습니다.

나는 나쁜 알고 학대 를하지만, 글로벌 공간은 정말 THAT 악? 그리고 그렇다면 어떤 좋은 대안이 있습니까?


5
해당 설정에 대한 액세스를 처리하는 구성 클래스를 사용할 수 있습니다. 다른 객체가 해당 구성 항목에서 가져 오는 동안 구성 파일 및 / 또는 데이터베이스에서 구성 설정을 읽는 유일한 장소를 갖는 것이 좋습니다. 디자인을보다 깨끗하고 예측 가능하게 만듭니다.
Hajo

25
글로벌을 사용하는 것과의 차이점은 어디에 있습니까? "단 하나의 장소"는 싱글 톤처럼 매우 의심스럽게 들립니다.
Madara Uchiha

130
유일한 악은 도그마입니다.
Pieter B

34
동료 프로그래머와 함께 전역 상태를 사용하는 것은 친구들과 동일한 칫솔을 사용하는 것과 같습니다.
ziGi

5
악마는 악하다. 세계 국가는 악하거나 선하지 않습니다. 사용 방법에 따라 매우 유용하거나 해로울 수 있습니다. 다른 사람의 말을 듣지 말고 올바르게 프로그래밍하는 법을 배우십시오.
annoying_squid

답변:


282

간단히 말해 프로그램 상태를 예측할 수 없게 만듭니다.

좀 더 정교하게 말하면, 동일한 전역 변수를 사용하는 두 개의 객체가 있다고 상상해보십시오. 어느 모듈 내에서 임의의 소스를 사용하지 않는다고 가정하면, 메소드를 실행하기 전에 시스템의 상태를 알면 특정 메소드의 출력을 예측 (및 테스트) 할 수 있습니다.

그러나 개체 중 하나의 메서드가 공유 전역 상태의 값을 변경하는 부작용을 유발하면 다른 개체에서 메서드를 실행할 때 시작 상태가 무엇인지 더 이상 알 수 없습니다. 더 이상 메소드를 실행할 때 얻을 수있는 결과를 예측할 수 없으므로 테스트 할 수 없습니다.

학업 수준에서 이것은 심각하게 들리지 않을 수도 있지만 테스트 코드를 단위로 구성 할 수있는 것이 정확성 (또는 최소한 목적에 적합 함)을 증명하는 과정에서 중요한 단계입니다.

현실에서 이것은 매우 심각한 결과를 초래할 수 있습니다. 글로벌 데이터 구조를 채우는 하나의 클래스와 해당 데이터 구조의 데이터를 소비하여 상태를 변경하거나 프로세스에서 데이터를 파괴하는 다른 클래스가 있다고 가정하십시오. populator 클래스가 완료되기 전에 프로세서 클래스가 메소드를 실행하면 프로세서 클래스가 처리 할 데이터가 불완전 할 수 있으며 populator 클래스가 작업중인 데이터 구조가 손상되거나 손상 될 수 있습니다. 이러한 상황에서 프로그램 동작은 완전히 예측할 수 없으며 서사시 손실로 이어질 수 있습니다.

또한 전역 상태는 코드의 가독성을 손상시킵니다. 코드에 코드에 명시 적으로 도입되지 않은 외부 종속성이있는 경우 코드 유지 관리 작업을 수행하는 사람은 코드의 출처를 찾아야합니다.

어떤 대안이 존재하는지에 관해서는 글로벌 상태를 전혀 가질 수 없지만 실제로는 일반적으로 전역 상태를 다른 모든 것을 감싸는 단일 객체로 제한 할 수 있으며 범위 지정 규칙에 따라 절대로 참조해서는 안됩니다 사용중인 언어의 특정 객체에 특정 상태가 필요한 경우 생성자 또는 setter 메서드에 인수로 전달하여 명시 적으로 요청해야합니다. 이것을 의존성 주입이라고합니다.

사용중인 언어의 범위 지정 규칙으로 인해 이미 액세스 할 수있는 상태로 어리석은 것처럼 보일 수 있지만 이점은 엄청납니다. 이제 누군가가 코드를 따로 살펴보면 필요한 상태와 코드의 출처를 알 수 있습니다. 또한 코드 모듈의 유연성과 다른 컨텍스트에서 재사용 할 수있는 기회와 관련하여 큰 이점이 있습니다. 상태가 전달되고 상태 변경이 코드 블록에 로컬 인 경우 원하는 상태 (올바른 데이터 유형 인 경우)를 전달하고 코드를 처리하도록 할 수 있습니다. 이 스타일로 작성된 코드는 쉽게 상호 교환 할 수있는 느슨하게 연결된 구성 요소 모음이 나타나는 경향이 있습니다. 모듈의 코드는 상태가 어디에서 왔는지, 처리 방법 만 신경 쓰지 않아야합니다.

국가를 통과하는 것이 세계 국가에 의존하는 것보다 훨씬 우수한 다른 많은 이유가 있습니다. 이 답변은 결코 포괄적이지 않습니다. 왜 세계 상태가 나쁜지에 대한 책을 쓸 수도 있습니다.


61
기본적으로 누구나 상태를 변경할 수 있으므로 의존 할 수 없습니다.
Oded

21
@Truth 대안? 의존성 주입 .
rdlowrey

12
기본 인수는 구성을 나타내는 객체와 같이 효과적으로 읽기 전용 인 항목에는 실제로 적용되지 않습니다.
frankc

53
그 중 어느 것도 읽기 전용 전역 변수가 나쁜 이유를 설명하지 않습니다 ... OP가 명시 적으로 묻습니다. 그렇지 않으면 좋은 대답입니다. OP가 다른 점을 명시 적으로 언급했을 때 OP가 "수락"으로 표시 한 것에 놀랐습니다.
Konrad Rudolph

20
being able to unit test code is a major step in the process of proving its correctness (or at least fitness for purpose). 아닙니다. "프로그램 테스트는 버그의 존재를 설득력있게 보여줄 수 있다고 지적 된 이래 이제 20 년이 지났지 만, 그 부재를 시연 할 수는 없습니다.이 잘 알려진 발언을 독창적으로 인용 한 후, 소프트웨어 엔지니어는 하루의 순서로 돌아가서 계속합니다 그의 chrysocosmic 정화를 계속 정제하는 yore의 연금술사와 마찬가지로 그의 테스트 전략을 개선하기 위해. " - Djikstra, 1988 (즉 ... 지금 4.5 년 수)
메이슨 윌러

135

변하기 쉬운 세계 상태는 여러 가지 이유로 악하다 :

  • 변경 가능한 전역 상태의 버그 -많은 까다로운 버그는 변경 가능성으로 인해 발생합니다. 프로그램의 어느 곳에서나 돌연변이로 인해 발생할 수있는 버그는 더 까다로울 수 있습니다. 정확한 원인을 찾기가 어렵습니다.
  • 불량한 테스트 가능성 -변경 가능한 글로벌 상태 인 경우 작성하는 모든 테스트에 대해 상태를 구성해야합니다. 이로 인해 테스트가 더 어려워집니다 (따라서 사람들이 사람들이 할 가능성이 적습니다!). 예를 들어 응용 프로그램 전체 데이터베이스 자격 증명의 경우 한 테스트에서 다른 테스트 데이터베이스와 다른 특정 테스트 데이터베이스에 액세스해야하는 경우에는 어떻게됩니까?
  • 비 유연성 -코드의 한 부분에 전역 상태에서 하나의 값이 필요하지만 다른 부분에 다른 값이 필요한 경우 (예 : 트랜잭션 중 임시 값)? 갑자기 손에 리팩토링이 심하게 있습니다
  • 함수 불순물 - "순수한"함수 (즉, 결과가 입력 매개 변수에만 의존하고 부작용이없는 함수)는 더 큰 프로그램을 추론하고 구성하기가 훨씬 쉽습니다. 변경 가능한 전역 상태를 읽거나 조작하는 기능은 본질적으로 불완전합니다.
  • 코드 이해 -많은 가변 글로벌 변수에 의존하는 코드 동작은 이해하기가 훨씬 어렵습니다. 코드 동작에 대해 추론하기 전에 글로벌 변수와의 가능한 상호 작용 범위를 이해해야합니다. 경우에 따라이 문제는 다루기 어려워 질 수 있습니다.
  • 동시성 문제 -변경 가능한 전역 상태는 일반적으로 동시 상황에서 사용될 때 어떤 형태의 잠금이 필요합니다. 이것은 올바르게 작성하기가 어렵고 (버그의 원인이 됨) 코드에 훨씬 더 복잡한 작업을 추가합니다 (유지하기가 어렵고 비용이 많이 듭니다).
  • 성능 -동일한 전역 상태에서 여러 스레드가 지속적으로 bash를 수행하면 캐시 경합이 발생하여 시스템 전체의 속도가 느려집니다.

변경 가능한 전역 상태의 대안 :

  • 함수 매개 변수 -간과 되기는하지만 전역 상태를 피하는 가장 좋은 방법은 함수를 더 잘 매개 변수화하는 것입니다. 중요한 개념적 질문을 해결하도록 강요합니다.이 기능이 작동하려면 어떤 정보가 필요합니까? "컨텍스트"라는 데이터 구조를 가지고 모든 관련 정보를 마무리하는 일련의 함수로 전달 될 수 있습니다.
  • 의존성 주입 -함수 매개 변수와 동일하며 조금 더 일찍 수행되었습니다 (함수 호출이 아닌 객체 구성에서). 의존성이 변경 가능한 객체 인 경우 조심하십시오. 이로 인해 변경 가능한 전역 상태와 동일한 문제가 빠르게 발생할 수 있습니다 .....
  • 불변의 글로벌 상태 는 대부분 무해합니다. 사실상 일정합니다. 그러나 이것이 실제로 일정하고 나중에 변경 가능한 전역 상태로 전환하려는 유혹을받지 않도록하십시오!
  • 불변의 싱글 톤 -불변의 전역 상태와 거의 동일하지만 필요할 때까지 인스턴스화를 연기 할 수 있다는 점이 다릅니다. 고가의 일회성 사전 계산이 필요한 대규모 고정 데이터 구조에 유용합니다. 가변 싱글 톤은 물론 가변 글로벌 상태와 동등하므로 악의적이다 :-)
  • 동적 바인딩 -Common Lisp / Clojure와 같은 일부 언어에서만 사용 가능하지만 다른 스레드에는 영향을 미치지 않는 제어 된 범위 (일반적으로 스레드 로컬 기준) 내에서 값을 효과적으로 바인딩 할 수 있습니다. 현재 실행 스레드 만 영향을 받는다는 것을 알기 때문에 이것은 전역 변수와 동일한 효과를 얻는 "안전한"방법입니다. 예를 들어 독립적 인 트랜잭션을 처리하는 스레드가 여러 개인 경우에 특히 유용합니다.

11
함수 매개 변수 또는 종속성 주입으로 컨텍스트 객체를 전달하면 컨텍스트가 변경 가능한 경우 가변 전역 상태를 사용하는 것과 동일한 문제가 발생할 수 있다고 생각합니다.
알프레도 오소리오

1
모든 좋은 물건과 아멘! 그러나 문제는 불변의 세계 국가에 관한 것입니다
MarkJ

@Alfredo-매우 사실입니다. 그렇지 비록 같은 나쁜 최소 범위는 어느 정도 제어 할 수 있기 때문이다. 그러나 일반적으로 컨텍스트와 종속성을 변경 불가능하게하여 문제를 해결하는 것이 더 쉽습니다.
mikera

2
변경 가능 / 변경 불가능한 경우 +1 불변의 글로벌은 괜찮습니다. 게으르지 만 결코 변하지 않는 것조차. 물론 전역 변수를 노출하지 말고 전역 인터페이스 또는 API를 노출하십시오.
Jess

1
@giorgio 문제는 해당 변수가 시작시 값을 가져오고 프로그램 실행 (시스템 폴더, 데이터베이스 자격 증명) 중에는 절대로 변경되지 않는다는 것을 분명히합니다. 즉, 불변이며, 일단 값이 주어지면 변경되지 않습니다. 개인적으로 나는 "상태"라는 단어를 사용하는데, 실행마다 다르거 나 다른 기계에서 다를 수 있기 때문입니다. 더 나은 단어가있을 수 있습니다.
MarkJ

62
  1. 빌어 먹을 앱 전체가 그것을 사용할 수 있기 때문에, 다시 다시 계산하는 것은 매우 어렵습니다. 글로벌과 관련하여 변경 사항이 있으면 모든 코드를 변경해야합니다. 이것은 단순히 grep유형 이름으로 어떤 기능을 사용하는지 알아내는 것보다 훨씬 어려운 문제입니다.
  2. 숨겨진 종속성을 도입하여 멀티 스레딩을 중단하여 점점 더 많은 응용 프로그램에 점점 더 중요해지기 때문에 나쁩니다.
  3. 모든 변수가 무언가를 할 수 있기 때문에 전역 변수의 상태는 항상 완전히 신뢰할 수 없습니다.
  4. 그들은 실제로 테스트하기가 어렵습니다.
  5. 그들은 API 호출을 어렵게 만듭니다. 그냥 "당신은 API를 호출하기 전에 () SET_MAGIC_VARIABLE 전화를 기억해야한다" 구걸 호출하는 것을 잊지 사람. 오류가 발생하기 쉬운 API를 사용하여 찾기 어려운 버그가 발생합니다. 일반 매개 변수로 사용하면 호출자가 강제로 값을 제공하게됩니다.

필요한 함수에 참조를 전달하십시오. 그렇게 어렵지 않습니다.


3
글쎄, 당신은 잠금을 캡슐화하고 가능한 한 언제든지 상태를 변경하도록 설계된 IS를 구성하는 전역 구성 클래스를 가질 수 있습니다. 코드에서 1000x 곳에서 구성 판독기를 인스턴스화하는 것 보다이 방법을 선택합니다. 그러나 예, 예측 불가능 성은 그들에 대해 절대적으로 최악입니다.
Coder

6
@Coder은 : 글로벌에 제정신 대안이 있지만, "코드의 1000 배 곳에서 구성 판독기"가 아닙니다 하나 (-> 의존성 주입) 방법을 매개 변수로 받아 들일 수있는 설정 객체를 생성 구성 판독기.
sleske

2
Nitpicking : 왜 전역보다 유형을 grep하는 것이 더 쉬운가요? 그리고 질문에 대한 읽기 전용 전역을 점 2와 3은 관련이없는, 그래서
MarkJ

2
@ MarkJ : 아무도 그 이름을 가리지 않기를 바랍니다.
DeadMG

2
@DeadMG, Re "..는 항상 완전히 신뢰할 수 없습니다 ..", 의존성 주입도 마찬가지입니다. 매개 변수를 만들었다 고해서 obj가 고정 상태를 보장한다는 의미는 아닙니다. 함수 어딘가에서 상단에 주입 된 변수의 상태를 수정하는 다른 함수를 호출 할 수 있습니다.
Pacerier

34

"state"라고 말하면 일반적으로 "mutable state"를 의미합니다. 전역 변경 가능 상태는 프로그램의 어떤 부분이 (전역 상태를 변경하여) 다른 부분에 영향을 줄 수 있기 때문에 완전히 악 합니다.

알 수없는 프로그램 디버깅을 상상해보십시오. 함수 A가 특정 입력 매개 변수에 대해 특정 방식으로 작동하지만 동일한 매개 변수에 대해 다르게 작동하는 경우가 있습니다. 전역 변수 x를 사용한다는 것을 알았습니다 .

x 를 수정하는 장소를 찾아서 수정하는 장소가 5 개 있다는 것을 알게됩니다. 이제 어떤 경우에 함수 A가 어떤 역할을하는지 알아내는 행운을 빕니다.


그래서 불변의 세계 국가는 그렇게 악하지 않습니까?
FrustratedWithFormsDesigner

50
불변의 세계 국가는 '상수 (constants)'라고 알려진 잘 알려진 모범 사례라고 말할 수 있습니다.
Telastyn

6
불변의 글로벌 상태는 악이 아니며 단지 나쁘다 :-). 결합으로 인해 여전히 문제가 있지만 (변경, 재사용 및 단위 테스트가 더 어려워 짐) 문제가 훨씬 적기 때문에 간단한 경우 일반적으로 허용됩니다.
sleske

2
IFF는 전역 변수를 사용하므로 하나의 코드 만 수정해야합니다. 나머지는 자유롭게 읽을 수 있습니다. 다른 사람들이 그것을 바꾸는 문제는 캡슐화 및 액세스 기능으로 사라지지 않습니다. 그 구조가 무엇을위한 것이 아닙니다.
phkahler

1
@Pacerier : 예. 전역 변수로 사용되는지 로컬 변수로 사용되는지에 관계없이 널리 사용되는 인터페이스를 변경하는 것은 어렵습니다. 그러나 그것은 내 요점과 독립적입니다. 즉, 다른 코드 조각의 상호 작용 은 전역 변수와 이해하기 어렵습니다.
sleske

9

당신은 자신의 질문에 대답했습니다. '거품이있는'경우 관리하기는 어렵지만 제대로 사용하면 유용하고 예측할 수 있습니다. 전역의 유지 관리 및 변경은 일반적으로 악몽이며 응용 프로그램의 크기가 커질수록 악화됩니다.

글로벌 옵션이 유일한 옵션이고 차이점을 쉽게 해결할 있는 숙련 된 프로그래머 이를 사용하는 데 최소한의 문제가있을 있습니다. 그러나 사용하면서 발생할 수있는 끝없는 문제는 사용에 대한 조언이 필요합니다.

편집 : 내가 의미하는 바를 명확히하기 위해 글로벌은 본질적으로 예측할 수 없습니다. 예측할 수없는 작업과 마찬가지로 예측할 수없는 조치를 취할 수 있지만 수행 할 수있는 작업에는 항상 제한이 있습니다. 이것에 상대적으로 알려지지 않은 변수를 다루어야하는 프로젝트에 참여하는 새로운 개발자들의 번거 로움에, 글로벌 사용에 대한 권장 사항을 이해할 수 있어야합니다.


9

싱글 톤에는 많은 문제가 있습니다-여기 내 마음에 가장 큰 두 가지 문제가 있습니다.

  • 단위 테스트에 문제가 있습니다. 지구 상태는 한 테스트에서 다음 테스트로 오염 될 수 있습니다

  • "일대일"하드 규칙을 적용합니다.이 규칙은 변경 될 수는 없지만 갑자기 변경됩니다. 전역 적으로 액세스 가능한 객체를 사용한 전체 유틸리티 코드를 변경해야합니다.

그러나 대부분의 시스템에는 Big Global Objects가 필요합니다. 이는 크고 비싸거나 (예 : 데이터베이스 연결 관리자) 광범위한 상태 정보 (예 : 잠금 정보)를 보유한 항목입니다.

Singleton의 대안은 시작시 이러한 Big Global Object를 작성하고이 오브젝트에 액세스해야하는 모든 클래스 또는 메소드에 매개 변수로 전달하는 것입니다.

여기서 문제는 "패스 소포"의 큰 게임으로 끝납니다. 컴포넌트와 그 의존성에 대한 그래프가 있으며, 일부 클래스는 다른 클래스를 생성하며, 각각의 스폰 된 컴포넌트 (또는 스폰 된 컴포넌트의 컴포넌트)가 필요하기 때문에 각 클래스는 많은 의존성 컴포넌트를 보유해야합니다.

새로운 유지 관리 문제가 발생합니다. 예 : 갑자기 "WidgetFactory", 그래프의 깊이에있는 구성 요소에는 조롱하려는 타이머 객체가 필요합니다. 그러나 "WidgetFactory"는 "WidgetCreationManager"의 일부인 "WidgetBuilder"에 의해 작성되며 실제로 하나만 사용하더라도이 타이머 오브젝트에 대해 알고있는 세 개의 클래스가 필요합니다. Singletons를 포기하고 되돌리려는 경우,이 타이머 객체를 전역 적으로 액세스 할 수있게 만드십시오.

다행히도 이것은 Dependency Injection 프레임 워크로 해결되는 문제입니다. 프레임 워크에 어떤 클래스를 생성해야하는지 알려주고 리플렉션을 사용하여 종속성 그래프를 파악하고 필요할 때 각 객체를 자동으로 구성합니다.

요약하자면, 싱글 톤은 좋지 않으며, 대안은 Dependency Injection 프레임 워크를 사용하는 것입니다.

Castle Windsor를 사용하지만 선택의 여지가 있습니다. 사용 가능한 프레임 워크 목록은 2008 년부터이 페이지 를 참조하십시오 .


8

우선 의존성 주입이 "상태 저장"이 되려면 싱글 톤을 사용해야하므로 사람들은 이것이 대체로 잘못되었다고 말하는 사람들입니다. 사람들은 항상 전역 컨텍스트 객체를 사용합니다. 예를 들어 세션 상태도 본질적으로 전역 변수입니다. 의존성 주입 여부에 관계없이 모든 것을 전달하는 것이 항상 최상의 솔루션은 아닙니다. 나는 현재 많은 글로벌 컨텍스트 객체 (IoC 컨테이너를 통해 주입 된 싱글 톤)를 사용하는 매우 큰 응용 프로그램을 작업 중이며 디버그하는 데 결코 문제가되지 않았습니다. 특히 이벤트 중심 아키텍처의 경우 전역 컨텍스트 개체를 사용하고 변경된 내용을 전달하는 것이 좋습니다. 당신이 묻는 사람에 따라 다릅니다.

무엇이든 남용 될 수 있으며 응용 프로그램 유형에 따라 다릅니다. 예를 들어 웹 앱에서 정적 변수를 사용하는 것은 데스크톱 앱과 완전히 다릅니다. 전역 변수를 피할 수 있다면 그렇게하지만 때로는 그 용도가 있습니다. 최소한 글로벌 데이터가 명확한 맥락에 있는지 확인하십시오. 디버깅까지는 호출 스택과 일부 중단 점으로 해결할 수 없습니다.

나는 맹목적으로 전역 변수를 사용하는 것이 좋지 않다는 것을 강조하고 싶습니다. 함수는 재사용이 가능해야하고 데이터의 출처를 신경 쓰지 않아야합니다. 전역 변수를 참조하면 함수가 특정 데이터 입력과 연결됩니다. 이것이 단일 컨텍스트를 통해 중앙 집중식 컨텍스트 저장소를 처리하고 있지만 이것이 전달되어야하는 이유와 종속성 주입이 도움이되는 이유입니다.

Btw ... 어떤 사람들은 Linq의 제작자를 포함하여 의존성 주입이 나쁘다고 생각하지만, 나 자신을 포함하여 사람들이 그것을 사용하지 못하게하지는 않습니다. 궁극적으로 경험은 최고의 선생님이 될 것입니다. 규칙을 따르는 시간과 규칙을 어기는 시간이 있습니다.


4

여기에 다른 답변이 변경 가능 하고 불변의 전역 상태를 구별 하기 때문에 불변의 전역 변수 / 설정 조차도 종종 성가신 것으로 생각하고 싶습니다 .

질문을 고려하십시오.

... 내 응용 프로그램, 예를 들어 시스템 폴더 경로 또는 응용 프로그램 전체 데이터베이스 자격 증명에 대한 전역 구성이 필요하다고 가정 해 봅시다. ...

작은 프로그램의 경우 문제가되지 않지만 약간 더 큰 시스템의 구성 요소로이 작업을 수행하면 모든 테스트 (~ 같은 프로세스 내에서 실행)가 작동해야하므로 자동 테스트 작성이 갑자기 어려워집니다. 동일한 전역 구성 값

모든 구성 데이터가 명시 적으로 전달되면 구성 요소를 훨씬 쉽게 테스트 할 수 있으며 여러 테스트에 대해 전역 구성 값을 병렬로 부트 스트랩하는 방법을 걱정할 필요가 없습니다.


3

우선, 싱글 톤으로 할 수있는 것과 정확히 같은 문제가 발생할 수 있습니다. 오늘날 "내가 필요로하는 세계적인 것"처럼 보이는 것은 갑자기 더 많은 것을 필요로하는 것으로 바뀔 것입니다.

예를 들어, 오늘 전체 시스템에 대해 하나의 전역 구성을 원하기 때문에이 전역 구성 시스템을 작성합니다. 몇 년이 지났을 때 다른 시스템으로 포팅하고 누군가 "이러한 일반적인 글로벌 구성과 하나의 플랫폼 특정 구성이 있다면 더 잘 작동 할 것입니다."라고 말합니다. 갑자기 글로벌 구조가 아닌 글로벌 구조를 만들기 위해이 모든 작업을 수행 했으므로 여러 구조를 가질 수 있습니다.

(이것은 임의의 예가 아닙니다 ... 이것은 현재 프로젝트의 구성 시스템에서 발생했습니다.)

비전 역적 인 것을 만드는 데 드는 비용이 일반적으로 사소하다는 것을 고려하면, 어리석은 일입니다. 미래의 문제를 만들고 있습니다.


귀하의 예는 OP가 의미하는 것이 아닙니다. 구성 관리자 클래스의 여러 인스턴스에 프로그램의 실행중인 인스턴스에서 모든 키를 분할하려는 경우 추상 구성 (응용 프로그램 별 기본 설정 키가없는 구성 관리자 데이터 구조 만)을 여러 번 인스턴스화하는 것에 대해 분명히 말하고 있습니다. 예를 들어, 이러한 각 인스턴스는 하나의 구성 파일을 처리 할 수 ​​있습니다.
Jo So

3

흥미롭게도 다른 문제는 응용 프로그램이 "전역 적으로"충분하지 않기 때문에 응용 프로그램을 확장하기가 어렵다는 것입니다. 전역 변수의 범위는 프로세스입니다.

여러 프로세스를 사용하거나 여러 서버에서 실행하여 응용 프로그램을 확장하려는 경우 할 수 없습니다. 적어도 모든 전역을 제외하고 다른 메커니즘으로 대체 할 때까지는 안됩니다.


3

왜 글로벌 주가 그렇게 악한가?

우리의 두뇌가 한 번에 몇 가지 이상의 매개 변수를 고려하고 타이밍 관점과 가치 관점에서 결합하여 무언가에 영향을 미치는 방법을 알아내는 것이 매우 어렵 기 때문에 변하기 쉬운 세계 상태는 악의입니다.

따라서 프로그램 실행 중에 동작이 변경되어야하는 외부 이유가 몇 가지 이상인 객체를 디버깅하거나 테스트하는 데 매우 나쁩니다. 우리가 수십 가지의 물건을 모아서 추론해야 할 때는 물론입니다.


3

안전하고 강력한 시스템 설계를 위해 설계된 언어는 종종 전역 가변 상태를 제거 합니다. (불변의 객체는 상태 전이가 없기 때문에 실제로 상태가 아니기 때문에 전역이 없음을 의미합니다.)

Joe-E 가 한 예이며 David Wagner는 다음과 같이 결정을 설명합니다.

개체에 액세스 할 수있는 사람과 최소 권한의 원칙에 대한 분석은 기능이 전역 변수에 저장되어 프로그램의 모든 부분에서 잠재적으로 읽을 수있는 경우 무시됩니다. 개체를 전체적으로 사용할 수있게되면 더 이상 분석 범위를 제한 할 수 없습니다. 개체에 대한 액세스는 프로그램의 모든 코드에서 보류 할 수없는 권한입니다. Joe-E는 전역 범위에 기능이없고 변경 불가능한 데이터 만 포함되어 있는지 확인하여 이러한 문제를 방지합니다.

생각하는 한 가지 방법은

  1. 프로그래밍은 분산 추론 문제입니다. 대규모 프로젝트의 프로그래머는 프로그램을 개별적으로 추론 할 수있는 부분으로 나눌 필요가 있습니다.
  2. 범위가 작을수록 추론하기가 더 쉽습니다. 이는 시스템의 특성을 입증하려고하는 개인 및 정적 분석 도구와 시스템의 특성을 테스트해야하는 테스트 모두에 해당됩니다.
  3. 전 세계적으로 사용 가능한 중요한 권한 소스는 시스템의 특성을 추론하기 어렵게 만듭니다.

따라서 전역 적으로 변경 가능한 상태는 더 어려워집니다.

  1. 강력한 시스템 설계
  2. 시스템의 속성을 증명하기가 더 어렵고
  3. 테스트가 프로덕션 환경과 유사한 범위에서 테스트되고 있는지 확인하기가 더 어렵습니다.

전역 변경 가능 상태는 DLL hell 과 유사합니다 . 시간이 지남에 따라 큰 시스템의 다른 부분은 공유 가능한 변경 가능한 상태와는 다른 미묘한 동작이 필요합니다. DLL 지옥과 공유 가능한 변경 가능한 상태 불일치를 해결하려면 서로 다른 팀 간의 대규모 조정이 필요합니다. 이러한 문제는 글로벌 상태의 시작 범위가 적절하다면 발생하지 않았습니다.


2

글로벌은 그렇게 나쁘지 않습니다. 몇 가지 다른 답변에서 언급했듯이, 실제 문제는 현재 글로벌 폴더 경로가 내일, 수백 또는 수백 중 하나 일 수 있다는 것입니다. 빠른 일회성 프로그램을 작성하는 경우 더 쉬운 경우 전역을 사용하십시오. 그러나 일반적으로 하나만 필요하다고 생각할 때에도 배수를 허용하는 것이 좋습니다. 갑자기 두 개의 데이터베이스 와 통신해야하는 복잡한 대규모 프로그램을 재구성해야하는 것은 좋지 않습니다 .

그러나 그들은 신뢰성을 손상시키지 않습니다. 모든 이 예기치 않게 변경되면 프로그램의 여러 곳에서 참조 데이터는 문제가 발생할 수 있습니다. 열거하는 컬렉션이 열거 중간에 변경되면 열거자가 질식합니다. 이벤트 큐 이벤트는 서로 트릭을 재생할 수 있습니다. 스레드는 항상 문제를 일으킬 수 있습니다. 지역 변수 또는 변경 불가능한 필드가 아닌 것은 문제입니다. 글로벌은 이런 종류의 문제이지만, 비전 역으로 만들어서 고칠 수는 없습니다.

파일에 쓰려고하고 폴더 경로가 변경되면 변경 내용과 쓰기를 동기화해야합니다. (잘못 될 수있는 수천 가지 중 하나 인 경우 경로를 잡고 해당 디렉토리가 삭제 된 다음 폴더 경로가 좋은 디렉토리로 변경된 다음 삭제 된 디렉토리에 쓰려고 시도합니다.) 폴더 경로가 전역이거나 프로그램에서 현재 사용중인 천 중 하나입니다.

큐의 다른 이벤트, 다른 수준의 재귀 또는 다른 스레드에서 액세스 할 수있는 필드에는 실제 문제가 있습니다. 간단하고 간단하게 만들려면 지역 변수가 좋고 필드가 나쁩니다. 그러나 전 세계 지구 는 여전히 현장이 될 것이기 때문에이 문제 (중요하지만 중요한) 는 전 세계 분야의 양호 또는 악 상태에 적용 되지 않습니다 .

추가 : 멀티 스레딩 문제 :

이벤트 큐 또는 재귀 호출과 비슷한 문제가 발생할 수 있지만 멀티 스레딩은 최악입니다. 다음 코드를 고려하십시오.

if (filePath != null)  text = filePath.getName();

경우 filePath지역 변수 또는 상수의 일종이며, 프로그램이되어 있지 때문에 실행하는 경우 실패 할 것 filePathnull입니다. 수표는 항상 작동합니다. 다른 스레드는 그 값을 변경할 수 없습니다. 그렇지 않으면 보장 할 수 없습니다. Java로 멀티 스레드 프로그램을 작성하기 시작했을 때 항상 이와 같은 줄에 NullPointerException이 발생했습니다. 어떤다른 스레드는 언제든지 값을 변경할 수 있으며 종종 변경됩니다. 다른 답변이 지적했듯이 테스트에 심각한 문제가 발생합니다. 위의 내용은 10 억 번 작동하여 광범위하고 포괄적 인 테스트를 거친 다음 생산 과정에서 한 번 폭파 될 수 있습니다. 사용자는 문제를 재현 할 수 없으며 자신이 물건을보고 잊어 버렸다고 확신 할 때까지 다시 발생하지 않습니다.

전역은 분명히이 문제가 있으며 완전히 제거하거나 상수 또는 지역 변수로 대체 할 수 있다면 매우 좋습니다. 웹 서버에서 상태 비 저장 코드가 실행중인 경우 가능합니다. 일반적으로 데이터베이스에서 모든 멀티 스레딩 문제를 해결할 수 있습니다.

그러나 프로그램이 한 사용자 작업에서 다음 사용자 작업까지 기억해야하는 경우 실행중인 스레드에서 필드에 액세스 할 수 있습니다. 비전 역 필드로 전역을 전환해도 안정성에 도움이되지 않습니다.


1
당신이? 무슨 뜻인지 명확히 할 수 "로컬 변수 또는 unchangable 필드가 아닌 것은 문제입니다. 전역이 문제가 이런 종류의,하지만 당신은 그들에게 비전하여 그 문제를 해결하지 않을거야."
Andres F.

1
@AndresF .: 나는 대답을 확장했다. 이 페이지의 대부분의 사람들이 더 많은 데이터베이스 코드를 사용하는 데스크탑 방식을 사용하고 있다고 생각합니다. "글로벌"은 이러한 경우에 다른 것을 의미 할 수 있습니다.
RalphChapin

2

실제 응용 프로그램에서는 상태를 피할 수 없습니다. 원하는 방식으로 마무리 할 수 ​​있지만 스프레드 시트에는 셀의 데이터가 포함되어야합니다. 인터페이스로만 기능을 사용하여 셀 객체를 만들 수 있지만 셀에서 메서드를 호출하고 데이터를 변경할 수있는 위치의 수를 제한하지는 않습니다. 코드의 다른 부분이 할 수 없도록 인터페이스를 숨기려고 전체 객체 계층을 구축합니다.기본적으로 데이터를 변경하십시오. 포함 객체에 대한 참조가 임의로 전달되는 것을 막을 수는 없습니다. 그 어느 것도 동시성 문제 자체를 제거하지도 않습니다. 데이터에 대한 액세스를 늘리기가 더 어려워 지지만 실제로는 글로벌에서 인식되는 문제를 제거하지는 않습니다. 누군가가 한 조각의 상태를 수정하려면 전역 또는 복잡한 API를 통해 날씨를 조정하려고합니다 (나중에 막는 것이 아니라 막지 않습니다).

글로벌 스토리지를 사용하지 않는 진정한 이유는 이름 충돌을 피하기위한 것입니다. 동일한 전역 이름을 선언하는 여러 모듈을로드하는 경우 정의되지 않은 동작 (단위 테스트가 통과되기 때문에 디버그하기가 매우 어렵다) 또는 링커 오류 (C-링커가 경고하거나 실패한다고 생각합니까?)가 있습니다.

코드를 재사용하려면 다른 곳에서 모듈을 가져 와서 같은 이름의 모듈을 사용했기 때문에 실수로 전역을 밟지 않아야합니다. 운이 좋으면 오류가 발생하면 충돌을 방지하기 위해 한 섹션의 코드에서 모든 참조를 변경하지 않아도됩니다.


1

모든 글로벌 상태를 쉽게보고 액세스 할 수있게되면 프로그래머는 항상 그렇게합니다. 당신이 얻는 것은 무언의 의존성을 추적하기가 어렵습니다 (int blahblah는 배열 foo가 무엇이든 유효하다는 것을 의미합니다). 본질적으로 모든 것이 독립적으로 뒤 틀릴 수 있기 때문에 프로그램 불변을 유지하는 것이 거의 불가능합니다. someInt는 otherInt와 관계가 있습니다. 관리하기가 어렵고 언제든지 직접 변경할 수 있는지 증명하기가 어렵습니다.

즉, (일부 시스템에서 유일한 방법이었을 때 되돌아 갈 수 있음) 할 수는 있지만 그러한 기술은 상실됩니다. 그들은 주로 코딩 및 명명 규칙을 중심으로 진행됩니다. 컴파일러와 링커는 마스터 계획을 읽고 소스를 읽는 데 사람을 의존하는 것보다 클래스 / 모듈의 보호 / 개인 데이터에서 불변을 확인하는 작업을 더 잘 수행합니다.


1
"그러나 그 기술들은 잃어 버렸습니다"... 아직은 아닙니다. 나는 최근에 "클라리온 (Clarion)"이라는 소프트웨어 하우스에서 일했다. 코드 생성기 도구는 서브 루틴에 인수를 전달하는 것과 같은 기능이없는 기본 언어와 같은 언어를 가지고있다. "변화"또는 "현대화"는 마침내 나의 발언에 싫증이 났고 나를 부족하고 무능한 것으로 묘사했다. 난 떠나야했는데 ...
Louis Somers

1

전역 변수가 좋은지 나쁜지는 알 수 없지만 토론에 추가 할 것은 전역 상태를 사용하지 않으면 많은 메모리를 낭비한다는 사실을 알리는 것입니다. 특히 클래스를 사용하여 필드에 종속성을 저장하는 경우.

세계 국가의 경우 그러한 문제가 없으며 모든 것이 세계적입니다.

예를 들어, 다음 시나리오를 상상해보십시오. "보드"및 "타일"클래스로 구성된 10x10 그리드가 있습니다.

OOP 방식으로 수행하려면 "보드"개체를 각 "타일"에 전달합니다. "Tile"에 좌표를 저장하는 2 개의 "byte"유형 필드가 있다고 가정 해 봅시다. 한 타일에 대해 32 비트 시스템에서 차지하는 총 메모리는 (1 + 1 + 4 = 6) 바이트입니다. x 좌표의 경우 1, y 좌표의 경우 1, 보드에 대한 포인터의 경우 4입니다. 이것은 10x10 타일 설정에 총 600 바이트를 제공합니다

이제 보드가 전역 범위에있는 경우 각 타일에서 액세스 할 수있는 단일 객체는 각 타일 당 2 바이트의 메모리 만 가져야합니다. 즉 x 및 y 좌표 바이트입니다. 이것은 단지 200 바이트를 줄 것입니다.

따라서이 경우 전역 상태 만 사용하면 메모리 사용량의 1/3이됩니다.

이것은 다른 것들 외에도 전역 범위가 여전히 C ++과 같은 (상대적으로) 저수준 언어로 남아있는 이유라고 생각합니다.


1
그것은 언어에 크게 의존합니다. 프로그램이 지속적으로 실행되는 언어에서는 많은 클래스를 인스턴스화하는 대신 전역 공간과 싱글 톤을 사용하여 메모리를 절약 할 수 있지만 그 주장조차도 흔들릴 수 있습니다. 우선 순위에 관한 것입니다. PHP와 같은 언어 (요청 당 한 번 실행되고 객체가 지속되지 않음)에서 해당 인수조차도 모순입니다.
Madara Uchiha

1
@MadaraUchiha 아니요,이 주장은 어떤 식 으로든 흔들리지 않습니다. 일부 VM 또는 다른 컴파일 언어가 이러한 종류의 문제로 일부 하드 코어 코드 최적화를 수행하지 않는 한 객관적인 사실입니다. 그렇지 않으면 여전히 관련이 있습니다. "원샷"으로 사용되는 서버 측 프로그램에서도 메모리는 실행 시간을 통해 예약됩니다. 부하가 많은 서버에서는 이것이 중요한 지점 일 수 있습니다.
luke1985

0

글로벌 상태에는 고려해야 할 몇 가지 요소가 있습니다.

  1. 프로그래머 메모리 공간
  2. 불변의 전역 / 한 번의 전역 쓰기.
  3. 가변 글로벌
  4. 글로벌에 대한 의존성.

전역이 많을수록 복제본을 도입 할 가능성이 높아 지므로 복제본이 동기화되지 않을 때 문제가 발생합니다. 모든 지구촌을 인간의 기억에 남게하는 것이 필요하고 고통이됩니다.

불변 / 한 번 쓰기는 일반적으로 정상이지만 초기화 시퀀스 오류를 조심하십시오.

가변 글로벌은 종종 불변 글로벌로 오인됩니다 ...

전역을 효과적으로 사용하는 함수에는 추가 "숨김"매개 변수가있어 리팩토링이 더 어렵습니다.

전지구 적 국가는 사악하지는 않지만 확실한 비용으로 발생합니다. 혜택이 비용보다 클 때 사용하십시오.

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