본문 바로가기
C++ 200제/코딩 IT 정보

전역변수 문제점, 나쁜 객체지향 코드 (유지보수 망침)

by vicddory 2017. 7. 19.

전역변수 문제점, 나쁜 객체지향 코드 (유지보수 망침)


전역변수의 문제점


비지역성, 비국지성(Non-Locality)

각각의 오브젝트들은 구현 범위가 좁을수록 이용하기 쉽습니다. 전역변수 문제점은 이와 정반대라 이해하기도 힘들고 사용하기도 힘듭니다.


접근 제어, 제약 조건 확인(No Access Control or Constraint Checking)

전역변수 문제점은 프로그램 내부의 어느 곳에서나 설정할 수 있습니다(Get, Set). 그렇지만 그 모든 설정을 기억하기도 힘들뿐더러 워낙에 많은 곳에서 다루다 보니 잘못된 접근 방법을 사용하여 에러를 유발합니다. 결과적으론 가독성과 신뢰성이 떨어지며, OOP에서 중요한 요소 중 하나인 은폐성을 갖추지 못합니다.

내포된 커플링(Implicit coupling)

전역변수 문제점은 직간접적으로 내부의 다른 변수, 함수들과 연관성을 갖습니다. 프로그램의 규모가 커질수록 이 문제는 심각해집니다. 왜냐면, 전역변수의 인자는 다양하지 못하기에 함수 재사용을 못 하는 등의 여러 문제가 발생하기 때문입니다.


동시성 문제(Concurrency issues)

여러 스레드에서 사용할 경우 동기화가 필요합니다(매우 중요함에도 무시되는 경우가 많습니다). 그래서 동적으로 상호 연관을 지을 때, 스레드의 안정성이 떨어질 수 있습니다. (사실, 여러 함수와 전역변수의 상관관계를 모두 파악해 스레드의 성격을 일일이 정해주기란 힘듬)


Namespace 오염(Namespace pollution)

글로벌 변수가 사방팔방에서 사용되기에 사용자는 자신이 인지하지 못하는 사이 스펠링 오류, 같은 이름의 지역 변수, 전역변수 사용 등의 문제를 발생시킵니다. 운이 좋다면 에러가 뜨진 않겠지만, 그것조차 컴파일러의 오류일 가능성이 높습니다.


나쁜 코드 나쁜 개발자[OOP 객체지향 프로그래밍] Global 변수 나쁜점


메모리 할당 문제(Memory allocation issues)

어떤 환경에선 글로벌 변수를 위한 메모리 할당이 복잡합니다. 특히 생성자로 메모리를 할당할 경우 또 다른 문제가 생기는 것도 사실입니다. 그리고, 동적으로 모듈을 연결할 때, 다른 곳에서 참조한 라이브러리들과 충돌한다는 문제도 쉽게 해결하진 못합니다.


테스트의 어려움(Testing and Confinement)

전역변수를 사용하면 깨끗한 환경을 구축하기 힘들어 테스트는 좀 더 어려워집니다. 명시적으로 전역변수가 어떤 상황에서 어떤 형태를 띠는지 파악하기도 힘들고, 전역변수로 인해 지역, 전역의 변수, 함수를 모두 살펴봐야 하는 등의 어려움이 따릅니다.


전역변수를 사용하는 정말 나쁜 이유 (전역변수 문제점, 이런 의도로 사용하면 안 됩니다!!)

- 지역변수가 뭐지?

- 데이터 멤버가 뭐지?

- 전역변수는 저의 느린 타이핑 속도를 보완해요.

- 저는 복잡한 과정보다는 전역변수로 빨리 결과를 얻고 싶어요.

- 전 데이터들이 어느 클래스에 속하는지 몰라요, 그래서 전역변수를 써요.

전역변수 문제점, 대체 방법


ContextObject

어느 한 곳에 종속적이지 않게 추상적인 전역변수로 사용한다면, 오버라이드를 통해 쉽게 사용할 수 있습니다.


DependencyInjection(DI)

전역변수를 위한 별도의 의존 관계 클래스(메소드)를 생성해서 사용합니다. DI 패턴을 사용하면 전역변수 자체를 직접 사용하는 빈도가 줄어드는 장점이 있습니다.


숨기기

사용 범위를 제한합니다(예를 들면, 클래스 내부에선 static으로만 사용(전역변수의 static은 일반 변수의 static와 성격이 다름)). 그래도 다른 모듈과 연결되는 문제점은 여전합니다.


코딩을 발로 해도 전역 변수는 만들지 맙시다[OOP 객체지향 프로그래밍] Global 변수 나쁜점


상태 저장

전역변수를 위한 Setter, Getter 등 각종 상태를 변경, 저장하기 위한 별도의 설정들을 정의해 사용합니다.


SigletonPattern

전역변수를 사용하는 데 있어서 가장 효과적입니다. (전역변수 문제점 해결에 효율적)


데이터베이스, TurpleSpace, DataDistributionService 사용

종종 전역변수를 전역 map, 전역 hashtable, 전역 list로 사용하여 프로그램을 더 단순화하여 좋은 성능을 갖게 할 수 있습니다.


간단한 전역변수의 예)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
list<string> myList;  // global
 
void read_strings() {  -- read strings into myList -- }
void print_strings() { -- write contents of myList to stdout -- }
void sort_strings() {  -- sort contents of myList -- }
void sort_strings_reverse() { -- reverse-sort contents of myList -- }
void unique_strings() {-- remove duplicate strings -- }
 
int main()
{
    read_strings();
    sort_strings();
    print_strings();
    sort_strings_reverse();
    print_strings();
    unique_strings();
    print_strings();
 
    return 0;
}
cs


위와 같이 단순히 전역변수를 사용할 수 있습니다.

아래는 불필요한 과정을 거친 사용 예입니다. (아래처럼 사용할 필요는 없습니다)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class String_List {
private:
    list<string> m_List; // member
 
public:
    void read_strings() {  -- read strings into myList -- }
    void print_strings() { -- write contents of myList to stdout -- }
    void sort_strings() {  -- sort contents of myList -- }
    void sort_strings_reverse() {  -- reverse-sort contents of myList -- }
    void unique_strings() { -- remove duplicate strings -- }
};
 
int main() {
    String_List myList;  // local
    myList.read_strings();
    myList.sort_strings();
    myList.print_strings();
    myList.sort_strings_reverse();    
    myList.print_strings();
    myList.unique_strings();
    myList.print_strings();
 
    return 0;
}
cs


만약, 꽤 많은 곳에서 전역변수가 사용된다면 좀 더 관계를 명확히 해서 개념적으로 코딩할 필요가 있습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class String_List {
 
 public:
    list<string> m_List;  // member
 }
 
 class String_List_Manager {
 
 private:
    list<string> m_List;  // member
 
 public:
    String_List_Manager ( String_List m_List ) {
        this.m_List = m_List;
     }
    void read_strings() {
         -- read strings into m_List --
     }
    void print_strings() {        -- write contents of m_List to stdout -- }
    void sort_strings() {        -- sort contents of m_List -- }
    void sort_strings_reverse() {    -- reverse-sort contents of m_List -- }
    void unique_strings() {        -- remove duplicate strings -- }
 };
 
 int main() {
    String_List myList;  // local
    String_ListManager myListManager = new myListManager(myList);  // local
 
    myListManager.read_strings();
    myListManager.sort_strings();
    myListManager.print_strings();
    myListManager.sort_strings_reverse();
    myListManager.print_strings();
    myListManager.unique_strings();
    myListManager.print_strings();
 
    return 0;
 }
cs


위와 같이 사용하면 기존 리스트를 새로운 리스트로 갱신하는 것이 어렵지 않습니다.


Bad code smells - Global Variables[OOP 객체지향 프로그래밍] Global 변수 나쁜점


그러나, 계속해서 함수를 호출하기에 지역 변수는 영향을 받을 수 있습니다. 그래서 아래처럼 소스를 수정합니다.


1
2
3
4
String_List myListTestValues;  // local
 
String_ListManagerPerfected myListManager =
    new myListManagerPerfected(myList);  // local
cs


이렇게 소스를 수정하다 보면 어느 시점엔 꽤 괜찮은 소스가 됩니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
String_List myList; // local
 
if (condition("avaluators"// filled from a file
    myList = listFactory.getAvaluatorList();  
else if (condition("teachers"// filled from a file
    myList = listFactory.getTeacherList();    
else if (condition("pupils"// filled from a file
    myList = listFactory.getPupilList();    
else if (condition("DB"// pull data from DB
    myList = listFactory.getList(myDB);    
else if (condition("again"// passed by argument
    myList = formerList;            
else // hardcoded inside the class
    myList = listFactory.defaultList;     
 
String_ListManager myListManager = 
    new myListManager(myList);  // local
 
etc...
cs


If you use global variables. You gonna have a bad time[OOP 객체지향 프로그래밍] Global 변수 나쁜점


때로는, 잠재적인 문제를 안고 있는 전역변수가 왜 편리한가? (전역변수 문제점이 때로는 좋은 이유)


- 전체 시스템에서 작은 오브젝트나 단순한 작업의 반복을 위한 경우 간단히 구현할 수 있습니다.

- 프로그램의 중심으로서 제대로 사용이 된다면 코드가 단순화됩니다.

- 어떤 프로그래밍 언어는 전역변수를 위해서 특별한 지원을 합니다.

- 어떤 사람들은 전역변수를 사용해 복잡한 문제를 해결하라고 합니다.


이렇게 전역변수를 사용하면 더 빠르고 단순하게 코드의 테스트와 나중에 발생할 문제점들을 해결할 수 있습니다 (정말로 안전한 미래가 보장되진 않지만).


전역변수 문제점, 나쁜 객체지향 코드 (유지보수 망침)

출처가 기억나지 않아요