C++ - 헐리우드 원칙 (The Hollywood Principle)
Architecture & Design Pattern 전체 링크
참고
헐리우드 원칙 (The Hollywood Principle)
- Don't call us, we'll call you
- 높은 수준의 모듈 간의 상호작용을 관리하기 위해 사용되는 원칙
- 상위 모듈이 하위 모듈에 의존할 때, 상위 모듈은 직접적으로 하위 모듈에 의존하지 말아야 한다.
- 두 모듈 모두 추상화에 의존해야 한다.
- 하위 모듈이 상위 모듈을 호출하지 않아야 하며, 상위 모듈이 추상화된 인터페이스를 통해 하위 모듈을 호출해야 한다.
의존성 부패 (Dependency Rot)
- 여러 구성 요소 간의 의존성이 증가하는 현상
- 상위 모듈이 하위 모듈에, 하위 모듈이 상위 모듈에 서로 의존적이면 의존성 부패가 나타나게 된다.
먼저 헐리우드 원칙 위반 사례를 보자.
STL 클래스(하위 모듈)에서는 quickSort를 사용할 수 있도록 메서드를 구현하였다.
class STL // 하위 모듈
{
public:
static void stlQuickSort(int* arr, int size);
};
void STL::stlQuickSort(int* arr, int size)
{
cout << "STL Quick Sort 0 ~ " << size - 1 << endl;
}
그리고 상위 모듈인 MyAlgorithm 클래스에서 배열을 정렬한 후, 특정 작업을 더 하려고 한다.
class MyAlgorithm // 상위 모듈
{
public:
void mySort()
{
int arr[] = { 1, 2, 3, 4, 5 };
int size = sizeof(arr) / sizeof(int);
STL::stlQuickSort(arr, size);
// another job...
}
};
예시 코드는 다음과 같다.
#include <iostream>
using namespace std;
class STL // 하위 모듈
{
public:
static void stlQuickSort(int* arr, int size);
};
class MyAlgorithm // 상위 모듈
{
public:
void mySort()
{
int arr[] = { 1, 2, 3, 4, 5 };
int size = sizeof(arr) / sizeof(int);
STL::stlQuickSort(arr, size);
cout << "and another job..." << endl;
}
};
void STL::stlQuickSort(int* arr, int size)
{
cout << "STL Quick Sort 0 ~ " << size - 1 << endl;
}
int main()
{
MyAlgorithm* al = new MyAlgorithm();
al->mySort();
return 0;
}
여기서 요구사항을 추가해 보자.
MyAlgorithm 내에서 특정 위치(start)부터만 정렬을 하고 싶은 상황이 주어진다.
그래서 start를 멤버로 추가하고 get/set 메서드를 추가하였다.
class MyAlgorithm // 상위 모듈
{
public:
void mySort() { ... }
void setStart(int s) { start = s; }
int getStart() { return start; }
private:
int start;
};
만약 두 번째 원소부터 정렬하고 싶다면, 아래와 같이 setStart로 start = 2로 설정한다.
그리고 MyAlgorithm 객체(this)를 stlQuickSort에 넘겨준다.
void mySort()
{
int arr[] = { 1, 2, 3, 4, 5 };
int size = sizeof(arr) / sizeof(int);
setStart(2);
STL::stlQuickSort(arr, size, this);
cout << "and another job..." << endl;
}
stlQuickSort는 start 값을 get 메서드로 얻은 후, 정렬을 하도록 수정하였다.
void STL::stlQuickSort(int* arr, int size, MyAlgorithm* al)
{
int start = al->getStart();
cout << "STL Quick Sort " << start << " ~ " << size << endl;
}
요구사항을 만족한 코드는 아래와 같다.
#include <iostream>
using namespace std;
class STL // 하위 모듈
{
public:
static void stlQuickSort(int* arr, int size, MyAlgorithm* al);
};
class MyAlgorithm // 상위 모듈
{
public:
void mySort()
{
int arr[] = { 1, 2, 3, 4, 5 };
int size = sizeof(arr) / sizeof(int);
setStart(2);
STL::stlQuickSort(arr, size, this);
cout << "and another job..." << endl;
}
void setStart(int s) { start = s; }
int getStart() { return start; }
private:
int start;
};
void STL::stlQuickSort(int* arr, int size, MyAlgorithm* al)
{
int start = al->getStart();
cout << "STL Quick Sort " << start << " ~ " << size << endl;
}
int main()
{
MyAlgorithm* al = new MyAlgorithm();
al->mySort();
return 0;
}
헐리우드 원칙 적용
위의 코드는 헐리우드 원칙을 위배한다.
하위 모듈인 STL 클래스에서 상위 모듈인 MyAlgorithm의 메서드를 호출하기 때문이다.
고수준 모듈이 저수준 모듈에 의존하고, 저수준이 다시 고수준 모듈에 의존하는 것을 의존성 부패라고 한다.
void STL::stlQuickSort(int* arr, int size, MyAlgorithm* al)
{
int start = al->getStart();
cout << "STL Quick Sort " << start << " ~ " << size << endl;
}
실제 올바른 방법은 stlQuickSort를 수정하지 않고, sort 내부에서 문제를 해결해야 한다.
이렇게 수정하면 하위 모듈인 stlQuickSort에서 MyAlgorithm의 메서드를 호출할 필요가 없어진다.
void mySort()
{
int arr[] = { 1, 2, 3, 4, 5 };
int size = sizeof(arr) / sizeof(int);
setStart(2);
int temp[5] = { 0 };
int index = 0;
for (int i = start; i < size; i++)
temp[index++] = arr[i]; // start ~ size까지 배열 복사
STL::stlQuickSort(temp, size - start); // 복사한 배열만 정렬
index = 0;
for (int i = start; i < size; i++)
arr[i] = temp[index++]; // 정렬된 배열을 다시 arr로 복사
cout << "and another job..." << endl;
}
만약 상위 모듈이 하위 모듈을 호출하려면 추상화된 인터페이스를 이용해야 한다. (템플릿 메서드 패턴)