Clean Code that Works.

출처. CODE COMPLETE 5장

클래스를 작성 하는 이유

실세계의 객체를 모델링하라
 - 실세계의 객체를 모델링하는 것이 클래스를 생성하는 유일한 이유는 아니겠지만, 그래도 여전히 정당한 이유이다! 여러분의 프로그램이 모델링하는 실세계의 모든 객체 형에 대한 클래스를 생성하라. 객체에 필요한 데이터를 클래스에 입력하고, 객체의 행위를 모델링하는 서비스 루틴들을 구축한다.

추상 객체들을 모델링하라
 - 클래스를 생성하기 위한 또 다른 정당한 이유는 추상 객체, 즉 실체가 없는 실세계의 객체지만 다른 실질적인 객체의 추상화를 제공하는 객체를 모델링하기 위한 것이다. Shape 객체가 좋은 예이다. Circle과 Square는 실제로 존재하지만, Shape는 다른 구체적인 도형들에 대한 추상화이다.
   프로그래밍 프로젝트를 진행할 때, 추상화는 Shape에서처럼 미리 만들어져 있지 않기 때문에 분명한 추상화를 이끌어내기 위해서 더욱 열심히 일해야 한다. 실세계의 엔티티로부터 추상적인 개념을 만들어내는 과정은 결정저이지 않으며, 서로 다른 설계자들이 서로 다른 일반 법칙들을 추상화할 것이다. 예를 들어, 여러분이 원이나 정사각형, 삼각형과 같은 기하학적인 모양을 모른다면, 찌그러진 모양, 줄기 모양, 폰티악 아즈텍 모양과 같이 특이한 모양을 생각해낼 것이다. 적절한 추상화 객체를 생각해내는 것은 객체 지향 설계에서 가장 어려운 난제 중 하나이다.

복잡성을 줄여라
 - 정보를 감추기 위해서 클래스를 생성하면 그러한 정보에 대해 생각할 필요가 없을 것이다. 물론, 클래스를 작성할 때에는 정보에 대해서 생각해야 할 것이다. 하지만 클래스를 작성하고 난 후에는, 세부사항들을 잊고 내부 작동 방식에 대해서 몰라도 클래스를 사용할 수 있어야 한다. 클래스를 생성하는 또 다른 이유들에는 코드의 크기를 최소화하거나, 유지 보수성을 향상시키거나, 정확성을 향상시키기 위한 것들이 있다. 하지만 클래스의 추상적인 능력이 없다면, 복잡한 프로그램을 지적으로 관리하는 것이 불가능할 것이다.

복잡성을 고립시켜라
 - 복잡한 알고리즘이나 큰 데이터 집합, 뒤얽힌 통신 프로토콜 등 어떤 형태든지 간에 복잡성은 오류를 유발할 가능성이 있다. 오류가 발생했을 때, 오류가 코드 전체에 퍼져 있지 않고 클래스 내에 지역화되어 있다면 훨씬 찾기 쉬울 것이다. 오류를 수정하기위한 변경 사항들은 다른 코드에 영향을 미치지 않을 것이다. 왜냐하면 한 클래스만 변경되고 다른 코드는 건드리지 않을 것이기 때문이다. 만약 더 좋고, 간단하며, 신뢰할 수 있는 알고리즘을 발견했을 때, 해당 알고리즘이 클래스에 고립되어 있다면 오래된 알고리즘을 보다 쉽게 대체할 수 있을 것이다. 개발을 하는 도중에도 다양한 설계를 쉽게 시도해 볼 수 있고 가장 잘 작동하는 설계를 유지하기가 쉬울 것이다.

세부적인 정보를 숨겨라
 - 세부적인 정보를 감추고자 하는 바람은, 세부 사항이 복잡한 데이터베이스에 대한 접근처럼 복잡하거나 특정한 데이터 멤버에 숫자나 문자가 저장되는 것처럼 평범한지에 상관없이 클래스를 생성하기 위한 훌륭한 이유이다.

변경의 영향을 제한하라
 - 변경할 가능성이 있는 부분을 고립시켜서 변경의 영향을 단일 클래스나 적은 수의 클래스로 제한한다. 변경될 가능성이 제일 높은 부분을 가장 쉽게 변경할 수 있도록 설계하라. 변경할 가능성이 있는 부분에는 하드웨어에 의존적인 부분, 입력/출력, 복잡한 데이터 형, 비즈니스 규칙들이 있다.

전역 데이터를 숨겨라
 - 만약 전역 데이터를 사용할 필요가 잇다면, 세부적인 구현 사항을 클래스 인터페이스 뒤에 숨길 수 있다. 접근 루틴들을 통해서 전역 데이터를 다루면, 적연 데이터를 직접적으로 다루는 것에 비해서 여러 가지 이점을 제공한다. 여러분은 프로그램을 변경하지 않고 데이터의 구조를 변경할 수 있다. 여러분은 데이터에 대한 접근을 감시할 수 있다. 또한, 접근 루틴을 사용하는 원칙을 지키면 그 데이터가 정말로 전역인지에 대해서 생각하게 된다. 실제로 "전역 데이터"가 단순한 객체 데이터일 뿐이라는 점이 명백해지는 경우가 종종 있다.

매개변수의 전달을 간소화하라
 - 만약 어떤 매개변수를 여러 루틴들 사이에서 전달하고 있다면, 매개변수를 객체 데이터로 공유하는 클래스에 이 루틴을 포함시켜야 할 필요성이 있다는 것을 암시한다. 매개변수 전달을 간소화하는 것 자체가 목표가 아니지만, 수많은 데이터를 전달한다는 것은 다른 클래스 구성이 더 잘 작동할 것임을 말해준다.

중앙 집중 관리하라
 - 한 곳에서 작업을 처리하는 것은 좋은 아이디어이다. 제어에는 많은 형식이 있다. 테이블에 있는 엔트리들의 수에 대한 정보도 한 형식이고, 디바이스들(파일, 데이터베이스 연결, 프린터 등)의 관리도 또 다른 형식이다. 그리고 데이터베이스로부터 읽고 쓰는 클래스를 작성하는 것도 집중화된 관리의 또 다른 형식이다. 만약 데이터베이스가 파일이나 메모리 내의 데이터로 변환되어야 한다면, 그러한 변경 사항이 한 클래스에만 영향을 미칠 것이다.
   집중화된 관리의 기본 개념은 정보 은닉과 유사하지만, 여러분의 프로그래밍 도구 상자에 추가할 가치가 있는 유일한 경험적인 힘을 갖고 있다.

코드의 재사용을 도와라
 - 코드를 잘 분리된 클래스에 입력하면 하나의 큰 클래스에 포함되어 있는 것보다 다른 프로그램에서 쉽게 재사용될 수 있다. 비록 어떤 코드 섹션이 프로그램내의 한 곳에서만 호출되고 큰 클래스의 일부로써 이해하ㅣㄱ 쉽다고 하더라도, 해당 코드 섹션이 다른 프로그램에서 사용될 것 같다면 별도의 클래스에 입력 하는 것이 좋다.

프로그램군(family)을 위한 계획을 작성하라
 - 만약 프로그램이 변경될 것으로 예상하고 있다면, 그러한 부분들을 별도의 클래스로 입력하여 고립시키근 것이 좋다. 그러면 프로그램의 나머지 부분에 영향을 미치지 않고 클래스를 변경할 수 있거나, 대신 완전히 새로운 클래스를 입력할 수 있다. 단순히 한 프로그램의 모습만이 아니라 전체 프로그램군의 모습을 생각하는 것이 전체적인 변경 내용을 예상하는 데 크게 도움이 된다.

연관된 기능을 패키지화하라
 - 만약 여러분이 정보를 숨기거나, 데이터를 공유하거나, 유연한 계획을 세울 수 없는 경우에는, 관련된 기능들을 삼각법 함수들, 통계 함수들, 문자열 처리 루틴들, 비트 처리 루틴들, 그래픽 루틴들과 같이 적당한 그룹으로 패키지화할 수 있다. 클래스는 연관된 기능들을 결합하는 한 가지 수단이다. 여러분은 언어에 따라서 패키지나 네임 스페이스, 또는 헤더 파일을 사용할 수 있다.

특정한 리팩토링을 수행하라
 - 특정한 리팩토링을 수행하면 하나의 클래스를 두 개의 클래스로 변환하고, 델리게이트를 감추고, 미들 맨을 제거 하고, 확장 클래스를 만다는 방법으로 ㅅ로운 클래스들이 만등러진다. 이 섹션을 통해서 소개된 임무를 더 잘 달성하고자 한다면 이러한 새로운 클래스를 만들어 볼 수 있다.

피해야 할 클래스

만능(GOD) 클래스를 생성하지 말라
 - 모든 것을 알고 있고 모든 것을 할 수 있는 전지전능한 클래스를 생성하지 말라. 만약 어떤 클래스가 Get()과 Set() 루틴을 사용하여 다른 클래스로 부터 데이터를 얻는 데 시간을 보내고 있다면(즉, 다른 사람의 일에 끼어들어서, 무엇을 해야 하는지 말하고 있다면), 그 기능은 만능(god) 클래스보다 다른 클래스로 구성하는 것이 더 좋지 않은지 물어 보라

관련이 없는 클새스들을 제거하라
 - 만약 클래스가 행위는 없이 데이터로만 구성된다면, 그 클래스가 정말로 클래스인지 확인해 보고, 그 클래스를 강등시켜 클래스의 멤버 데이터가 하나 이상의 다른 클래스들의 특성이 될 수 있는지 골해 본다.

동사가 따라오는 클래스를 피하라
 - 데이터는 없이 행위로만 구성된 클래스는 일반적으로 클래스가 아니다. DatabaseInitialization()이나 StringBuilder()와 같은 클래스를 다른 클래스의 루틴으로 변환할 것을 고려해 본다.


체크 리스트 : 클래스의 품질

추상 데이터 형
 - 프로그램에 있는 클래스들을 추상 데이터 형으로 생각하고, 그러한 관점에서 클래스의 인터페이스들을 평가해 보앗는가?

추상화
 - 클래스가 핵심적인 목적을 갖고 있는가?
 - 클래스의 이름이 잘 지어졌고, 클래스의 이름이 핵심적인 목적을 설명하고 있는가?
 - 클래스의 인터페이스가 일관성 있는 추상화를 제공하는가?
 - 클래스의 인터페이스가 클래스의 사용 방법을 분명히 하고 있는가?
 - 여러분이 클래스의 서비스가 어떻게 구현되었는지에 대해서 생각할 필요가 없을 정도로 클래스의 인터페이스가 추상적인가? 클래스를 블랙박스로 취급할 수 있는가?
 - 다른 클래스들이 클래스의 내부 데이터를 쓸데없이 참견할 필요가 없을 만큼 클래스 서비스가 완벽한가?
 - 관련 없는 정보를 클래스에서 제거 하였는가?
 - 클래스를 컴포넌트 클래스로 분할하는 것에 대해서 생각해 보았는가? 그리고 최대한 분할하였는가?
 - 클래스를 수정할 때, 클래스 인터페이스의 무결성을 유지하고 있는가?

캡슐화
 - 클래스가 멤버에 대한 접근성을 최소화하고 있는가?
 - 클래스가 멤버 데이터의 노출을 피하고 있는가?
 - 클래스가 프로그래밍 언어가 허용하는 한 다른 클래스들로부터 세부적인 구현 사항들을 감추고 있는가?
 - 클래스가 파생 클래스와 사용자들에 대한 가정을 피하고 있는가?
 - 클래스가 다른 클래스에 독립적인가? 느슨하게 결합되어 있는가?
 
상속
 - 상속이 "is a"관계를 모델링 하기 위해서만 사용되었는가?
 - 클래스의 설명 문서가 상속 전략을 기술하고 있는가?
 - 파생 클래스가 오버라이드 불가능한 루틴에 대해서 "오버라이딩"을 피하고 있는가?
 - 공통적인 인터페이스, 데이터, 행위가 상속 트리에서 최대한 높은 곳에 있는가?
 - 상속 트리가 적절하게 얕은가?
 - 기본 클래스에 잇는 모든 데이터 멤버들이 protexted 대신 private 인가?

구현에 대한 다른 문제들
 - 클래스가 7개 이하의 데이터 멤버들을 포함하고 있는가?
 - 클래스가 다른 클래스에 대한 직접적이고 간접적인 루틴 호출을 줄였는가?
 - 클래스가 반드시 필요한 경우에만 다른 클래스와 협력하는가?
 - 모든 멤버 데이터가 생성자에서 초기화 되었는가?
 - 만약 얕은 복사를 생성해야 하는 합당한 이유가 없다면, 클래스가 얕은 복사 대신 깊은 복사로 사용되도록 설계되었는가?

언어에 따른 문제
 - 여러분이 사용하고 있는 프로그래밍 언어에서 클래스에 대한 이슈들을 조사하였는가?