[C++] 누구나 쉽게, 리팩토링(클린코드)-② 인터페이스
- 컴퓨터공학과/Programming
- 2020. 4. 7.
리팩토링
리팩토링은 내부의 코드를 개선하는 하나의 방법론이자 솔루션을 뜻합니다.
리팩토링을 한마디로 말하자면, 나쁜 냄새가 나는 코드를 최적화하는 것입니다. 여기서 주의할 점은 런타임 시의 최적화가 아니라 프로그래밍을 할 때 버그 없는 프로그램을 만드는 것입니다. 외부 인터페이스는 그대로 두고 내부의 코드만을 개선하여 재사용과 가독성을 높이는 데 목적이 있습니다.
리팩토링 시리즈
[C++] 누구나 쉽게, 리팩토링(클린코드)-① 순수가상함수/추상클래스
[C++] 누구나 쉽게, 리팩토링(클린코드)-② 인터페이스(이번글)
[C++] 누구나 쉽게, 리팩토링(클린코드)-③ 캐스팅(static·reinterpret·const·dynamic_cast)/타입체크 기능
이번시간에는 리팩토링을 진행하면서 인터페이스까지 함께 알아보겠습니다
2. 인터페이스
문제발생
인터페이스 설명을 위해 이번에는 핸드폰 예제를 이용합니다.
삼성의 자랑 애니콜(Anycall) 핸드폰의 통화(call)기능을 구현합니다. 사람(Person)은 use_phone()을 통해 통화 기능을 사용합니다.
싸이언(Cyon) 폰을 하나 더 구매했다고 합시다. 싸이언은 통화 기능을 애니콜과 달리 send 함수를 사용합니다. 역시 통화를 하기위해서 Person 클래스에 싸이언용 use_phone()을 구현합니다.
이때 애니콜과 싸이언은 다른 타입으로, 다른 타입의 포인터끼리는 호환이 되지 않기 때문에 void use_phone(Cyon *p, const char* n) { p -> send(n); } 을 하나 더 만들어주어야 합니다.
싸이언 폰을 하나 더 사용하므로 메인함수는 아래와 같이 싸이언 클래스를 정의하고 함수를 호출하는 코드가 추가됩니다.
int main()
{
Person p;
Anycall a;
p.use_phone(&a, "000-0000-0000");
// 새롭게 추가된 Cyon 클래스 사용
Cyon c;
p.use_phone(&c, "000-1111-1111");
}
그러나 이렇게 구현하게 되면 OCP에 위배됩니다.
♥ OCP(Open Close Principle, 개방폐쇄원칙)
개방-폐쇄 원칙은 시스템의 구조를 올바르게 재조직(리팩토링)하여 나중에 이와 같은 유형의 변경이 더 이상의 수정을 유발하지 않도록 하는 것이다. 개방-폐쇄 원칙이 잘 적용되면, 기능을 추가하거나 변경해야 할 때 이미 제대로 동작하고 있던 원래 코드를 변경하지 않아도, 기존의 코드에 새로운 코드를 추가함으로써 기능의 추가나 변경이 가능하다. [출처-위키피디아]
이는 강한 결합Tightly Coupling 되어 있기 때문입니다. 다른 기종의 핸드폰을 사용한다면 핸드폰 클래스를 또 추가해주고, Person 클래스에는 타입에 맞게 use_phone을 구현해주어야하는 번거로움이 생기게 됩니다.
문제 해결
그렇다면 OCP원칙에 위배되지 않고, 핸드폰 하나가 더 추가되더라도 내부 코드는 변화 없이 구현할 수 있을까요?
정답은, 느슨한 결합Loosely Coupling을 만들면 됩니다!
모든 기종의 핸드폰을 같은 이름의 통화 기능(call)으로 반드시 구현하도록 강제화합니다.
→ 이전에 배웠듯이 추상 클래스를 도입하면 되겠지요?
우선 애니콜과 사이언 모두 "Phone 클래스"라는 상위 클래스를 상속받아 call 기능을 무조건적으로 구현하도록 합니다.
class Phone
{
public :
// 순수 가상 함수는 자식에게 특정 기능을 강제화하기 위해 사용
virtual void call(const char *n) = 0;
};
♥ 여기서 인터페이스 개념이 등장합니다. 인터페이스는 서로 다른 클래스를 연결하는 연결장치라고 볼 수 있습니다. 여기서는 Anycall, Cyon 클래스를 Person 클래스와 연결하는 Phone 클래스를 인터페이스라고 할 수 있습니다.
그러나 97번째 줄의 Person클래스에서 보면 use_phone 함수의 첫 번째 파라미터가 다르기 때문에 (Anycall, Cyon 포인터 타입이 다르기 때문에) 2개의 함수를 재정의하게 됩니다.
♥ 상속을 받았으니 부모타입 * = &자식객체; 가 가능하게 됩니다. 내부적으로 부모의 코드를 가지고 있기 때문에 업캐스팅Upcasting이 가능합니다.
따라서 애니콜과 사이언의 부모 클래스인 Phone 클래스를 이용하여 void use_phone(Phone* p, const char* n) { p -> call(n); } 라고 구현할 수 있게 됩니다. 와우!
내부의 코드는 변화 없이 스카이(Sky) 핸드폰도 추가 할 수 있습니다.
172~176줄에 SKY 클래스를 추가했지만 다른 코드는 변함이 없이 사용될 수 있습니다. 이렇게 인터페이스를 사용하면 코드 수정을 국지화할 수 있고 폰클래스와 사람클래스 모두 인터페이스에만 맞춰서 개발하면 되므로 개발 향상성이 2배 빨라진다고 할 수 있습니다.
정리
o 인터페이스는 서로 다른 클래스를 연결하는 연결장치라고 볼 수 있습니다.
o 인터페이스를 사용하면 코드 수정을 국지화할 수 있고, 개발 향상성을 높일 수 있습니다.
* 자바에서의 인터페이스
자바에서는 인터페이스를 알아보기 위해 interface 키워드를 사용하는데, interface 키워드가 없는 C++에서는 관례적으로 심볼 I를 클래스 이름 앞에 붙여줍니다.
class IPhone
{
public :
virtual void call(const char *n) = 0;
};
아이폰이 되어버렸네요(ㅋㅋㅋㅋㅋㅋ)
추가로 인터페이스를 나타내기 위해 I를 그냥 쓰기보다 define을 사용하여 클래스를 인터페이스로 정의해주고 사용하면 가독성 높은 클린 코드가 됩니다.
#define interface class // 클래스를 인터페이스처럼 사용. 가독성을 위해서
interface Phone
{
public :
virtual void call(const char *n) = 0;
};
끝.
'컴퓨터공학과 > Programming' 카테고리의 다른 글
[C++] 누구나 쉽게, 리팩토링(클린코드)-④ 상속 (개념과 특징) (0) | 2020.04.13 |
---|---|
[C++] 누구나 쉽게, 리팩토링(클린코드)-③ 캐스팅/형변환 종류 및 방법 (0) | 2020.04.08 |
[C++] 함수에 관한 모든것(함수 특징/Call by value/call by reference/지역변수/전역변수/함수 오버로딩) (0) | 2020.04.05 |
[C++] 누구나 쉽게, 리팩토링(클린코드)-① 순수가상함수/추상클래스 (0) | 2020.03.25 |
[C++]정렬 알고리즘 프로그래밍-②정렬할 준비하기 (0) | 2020.03.25 |