8. 코드를 모듈화하라
모듈화의 주된 목적 중 하나는 코드를 변경하거나 재구성하기 쉽도록 만드는 것입니다. 미래에 코드가 어떻게 변경되거나 재구성될지 정확히 알지 못해도, 코드를 변경하거나 재구성하기 쉽도록 만들어야 합니다. 이를 위해 각각의 기능이 코드베이스의 서로 다른 부분에서 구현되어야 합니다. 이렇게 하면 요구 사항 중 하나가 변경되어도, 코드베이스에서 해당 요구 사항이나 기능과 관련한 부분만 수정하면 됩니다. 이렇게 모듈화를 잘 구성하면 코드 유지보수의 비용을 줄일 수 있으며, 코드를 효율적으로 관리할 수 있습니다.
1. 의존성 주입의 사용을 고려하세요
클래스 내의 하드코딩된 값에 의존하면 코드가 유연하지 못합니다. 의존성 주입으로 다형성을 활용합시다.
코드를 설계할 때 의존성 주입을 염두에 두세요. 코드를 작성하다 보면 의존성 주입을 사용하고 싶어도 사용이 거의 불가능한 코드가 만들어질 수 있기 때문입니다. 이후에 의존성 주입을 사용할 가능성이 있다면 이런 방식으로 코드를 작성하는 것은 피해야 합니다. → 인터페이스에 의존해야 함
2. 인터페이스에 의존하세요
구체적인 구현에 의존하면 적응성이 제한됩니다.
구체 클래스에 의존하면 동일한 인터페이스를 구현한 다른 구현체로 대체할 수 없습니다. 즉, 다형성을 활용할 수 없습니다.
클래스가 인터페이스를 구현하고 이 인터페이스가 필요한 동작을 정의한다면 이것은 곧 다른 개발자가 해당 인터페이스에 대해 다르게 구현한 클래스를 작성할 수 있다는 것을 강하게 시사합니다.
특정 클래스보다는 인터페이스에 의존함으로써 더 많은 노력을 기울일 필요 없이 코드를 상당히 모듈화시키고 적응성을 높일 수 있습니다.
3. 클래스 상속을 조심스럽게 사용하세요
두 가지 사물이 is-a 관계를 가진다면 상속이 적절할 수 있습니다. 그러나 일반적으로 조합을 사용하면 상속의 단점을 보완할 수 있습니다.
클래스 상속은 문제가 될 수 있습니다.
상속을 사용할 때 문제는 다음과 같습니다.
- 상속은 추상화 계층에 방해가 될 수 있습니다. 한 클래스가 다른 클래스를 확장하면 슈퍼클래스의 모든 기능을 상속합니다. 이는 원하는 것보다 더 많은 기능을 노출할 수도 있으며 이로 인해 추상화 계층이 복잡해지고 구현 세부 정보가 드러날 수 있습니다.
- 상속은 적응성 높은 코드의 작성을 어렵게 만들 수 있습니다. 하위 클래스의 상속 때문에 상위 클래스의 코드 수정이 쉽지 않습니다.
해결책) 조합을 사용하세요. 그 이점은 다음과 같습니다.
- 더 간결한 추상화 계층. 조합을 사용하면 원하지 않는 기능은 외부로 노출하지 않을 수 있습니다.
- 적응성이 높은 코드
진정한 is-a 관계는 어떤가요?
is-a 관계이더라도 상속은 여전히 문제가 될 수 있습니다.
- 취약한 베이스 클래스 문제. 슈퍼클래스가 수정되면 서브클래스가 작동하지 않을 수 있습니다. 따라서 코드를 변경할 때 그 변경이 문제가 없을지 판단하기 어려울 수 있습니다.
- 다이아몬드 문제. 일부 언어는 두 개 이상의 슈퍼클래스를 확장할 수 있는 다중 상속을 지원합니다. 여러 슈퍼클래스가 동일한 함수의 각각 다른 버전을 제공하는 경우 어떤 함수를 상속하는지 모호해집니다.
- 문제가 있는 계층 구조. 많은 언어에서는 다중 상속을 지원하지 않습니다. 단일 상속만 가능한 경우 클래스가 논리적으로 둘 이상의 클래스에 속할 때 문제가 발생할 수 있습니다.
4. 클래스는 자신의 기능에만 집중해야 합니다
다른 클래스와 지나치게 연관되어 있으면 문제가 될 수 있습니다. 하나의 개념이 여러 클래스에 분산되는 경우 해당 개념과 관련된 요구 사항을 변경하려면 관련된 클래스를 모두 수정해야 합니다.
자신의 기능에만 충실한 클래스를 만들어야 합니다. 코드 모듈화를 위한 핵심 목적 중 하나는 요구 사항이 변경되면 해당 요구 사항과 직접 관련된 코드만 변경하려는 것입니다.
클래스는 서로에 대한 어느 정도의 지식을 필요로 할 때도 있지만, 가능한 한 이것을 최소화하는 것이 좋습니다. 이를 통해 코드 모듈화를 유지할 수 있으며 적응성과 유지보수성을 크게 개선할 수 있습니다.
디미터 법칙 - 한 객체가 다른 객체의 내용이나 구조에 대해 가능한 한 최대한 가정하지 않아야 한다는 원칙입니다. 한 객체는 직접 관련된 객체와만 상호작용해야 합니다.
5. 관련 있는 데이터는 함께 캡슐화하세요
서로 다른 데이터가 서로 밀접하게 연관되어 항상 함께 움직여야 할 때가 있습니다. 이 경우 클래스로 그룹화하는 것이 합리적입니다. 이렇게 하면 코드는 여러 항목의 세부사항을 다루는 대신, 그 항목들이 묶여 있는 단일한 클래스가 제공하는 상위 수준의 개념을 다룰 수 있습니다.
6. 반환값에 구현 세부 정보가 노출되지 않도록 주의하세요.
코드를 간결하게 유지하기 위해서는, 각 계층의 구현 세부 정보가 외부로 노출되지 않아야 합니다. 만약 구현 세부 정보가 노출된다면, 코드의 하위 계층에 대한 정보가 노출될 수 있으며, 향후 수정이나 재설정이 어려워질 수 있습니다.
일반적으로 구현 세부 정보를 유출하는 방식 중 하나는 해당 정보와 밀접하게 연결된 유형을 반환하는 것입니다. 예를 들면, HTTP 요청으로 프로필 사진을 가져오는 클래스가 있다면, HTTP의 status와 같은 정보를 반환하면 가져온 데이터가 HTTP 요청임을 외부로 노출하는 것입니다. 이러한 경우, 외부로 노출할 개념을 최소화하는 유형을 새로 정의하여 사용하면 더 간결한 추상화 계층을 얻을 수 있습니다.
7. 예외 처리 시 구현 세부 사항이 노출되지 않도록 주의하세요.
예외를 발생시킬 때도 구현 세부 정보가 노출될 수 있는 경우가 있습니다. 이를 방지하기 위해서는 추상화 계층에 적절한 예외를 정의하여 사용하는 것이 좋습니다. 이를 통해 외부에 불필요한 정보가 노출되지 않도록 하고, 코드를 더 간결하게 유지할 수 있습니다.