진짜 개발자
본문 바로가기

Application Knowhow/Common

ApplicationKnowhow - 깔끔한 코드작성 방법 - Class편

728x90
깔끔한 코드 - Class

이번 포스팅에서는, Class를 깔끔하게 작성하는 방법에 대해 알아보도록 하겠습니다.

이 글은 로버트 C.마틴의 Clean Code라는 책을 정리한 글입니다.

 

 

1. Class 는 가능한 작게

Class를 Clean하게 작성하는 방법의 첫번째는 가능한 작게 Class를 설계하라 입니다. 작게라.. 너무 상대적인 말입니다. 그렇다면, 작게의 척도는 무엇일까요?

 

Class의 척도는 클래스가 맡은 책임입니다.

 

너무 많은 메소드를 가진 클래스

위의 예제 클래스는 위에 적힌 메소드 외에도 총 70개에 달하는 메소드를, 가지고 있습니다. 네 이 클래스는, 그 누가 보더라도 많이 복잡한 클래스임을 느낄수 있을 것입니다.

 

적은 수의 메소드를 가진 클래스

그렇다면 위의 예제클래스는 어떨까요? 상대적으로 메소드가 적기 때문에 한눈에 보이는 것은 사실입니다. 하지만 잘 짜여진 클래스일까요??

 

아닙니다. 앞서 말씀드렸듯이, 클래스를 작게 설계할 때의 작게의 단위는 클래스의 메소드 개수 및 클래스의 길이가 아닌, 클래스가 맡은 책임의 개수를 의미 합니다.

 

 

 

 

2. 클래스 작명

클래스를 Clean하게 작성하는 방법 두번째는, 클래스의 이름을 잘 만들자 입니다. 너무 뻔한얘기인가요?? 그렇지 않을것입니다.

 

 

클래스 이름에 Manager, Processor, Super 등이 들어가는 경우

저희 모두가 처음 프로그래밍을 시작했을때에, 다음과 같은 클래스 이름을 만들었던 적이 있을 것입니다. ...Manager, ...Processor, ...Super 이게 뭐가 잘못되었냐구요? 벌써, 방금 말씀드렸던 클래스를 가능한 작게 설계하라는 말씀을 잊으셨군요.

 

조금 더 쉽게 말씀드리자면, Manager, Processor, Super등의 이름은 이미 많은 책임을 가지는 클래스임을 내포하고 있습니다.

 

클래스 작명시 간결한 이름이 떠오르지 않는다면, 클래스가 필요이상으로 큰 경우이며, 클래스의 이름이 애매모호 하다면, 클래스가 너무 많은 책임을 가지고 있다는 의미입니다.

 

 

 

클래스 이름에 if, and, or, but 등의 접속사가 들어가는 경우

또한 클래스 이름에 접속사가 들어가는 것은, 개발자가 벌써 클래스가 여러가지 일을 하고있어요 라고, 말을하면서 클래스를 만들고 있는 격이 됩니다.

 

 

 

 

3. SRP (단일책임 원칙)

클래스를 가능한 작게만들고, 클래스의 이름을 2번에서 설명드린것과 같이 작성하는 이유는 바로, SRP를 위해서 입니다.

 

SRP는 다들 아시다시피, 클래스가 단하나의 책임만을 가져야하는 원칙을 의미합니다. 이는 클래스의 변경점을 1개로 만들어, 유지보수의 편의와 클래스의 사용의 편의를 도모합니다.

 

많은 책임을 가지는 클래스

위의 클래스는, Application의 version 정보와(getVersion), User 목록을 관리하며(getUserList), Application을 시작하기도(run) 합니다.

 

이렇게 되면, Application의 실행방식을 바꿀때에도 이 클래스를 변경하게 되며, user list를 가져오는 방법이 바뀔때, 그리고 version 정보를 가져오는 방법이 바뀔 때에도 이클래스를 변경해주어야 합니다.

 

 

많은 책임이 왜 안좋은가?

일상생활과 비교해 보면 쉽습니다. 다용도 상자에서 필요한 물건을 찾는것과, 잘 정돈된 작은 상자 여러개에서 필요한 물건을 찾는 것중 어느것이 쉬울까요?

 

또, 이렇게 큰 단위(ApplicationManager) 등으로 이루어진 어플리케이션은 특정 부분만을 수정하기를 원할때에, 개발자에게 너무 많은 정보를 주어 헷갈리도록 합니다.

 

 

 

 

4. 응집도 - 인스턴스 변수를 적게하라

응집도와 결합도는 소프트웨어 공학에서 자주 들어볼 수 있는 말입니다. 이번 포스팅에서 감히 쉽게 설명드려보고자 합니다.

 

우선, 응집도란 클래스 안의 변수와, 메소드가 서로 의존하며 논리적인 단위로 묶인다는 의미입니다. 다르게 말하여, 클래스 안의 메소드가 클래스 인스턴스 변수를 많이 사용할 수록, 응집도가 높다고 할 수 있습니다.

 

클래스를 작성할 때에, 함수를 깔끔하게 작성하기 위한 규칙인, 함수를 작게 만들고, 매개변수 목록을 적게 만들어야 하는 규칙을 따르다보면, 몇몇 메소드에서만 사용하게되는 인스턴스 변수가 여러개가 생기기 마련입니다. 이는 클래스에서 맡은 역할이 많음을 의미하고, 따라서 클래스를 쪼개야 함을 암시합니다.

 

따라서 클래스에는 인스턴스 변수가 적어야합니다.

 

 

 

 

5. 결합도 - 변경으로부터의 격리

소프트웨어에 대한 요구사항은 변경되기 마련입니다. 이에따라 코드가 변하는 것은 당연합니다.

 

OOP에서는 구체적 클래스(Concreate Class)추상 클래스(Abstract Class)가 있습니다. 구체적 클래스는 구현에 대한 상세한 코드를 포함하며, 추상클래스는 클래스에 필요한 개념만을 포함합니다.

 

당연한 말이지만, 코드가 구체적인 클래스에 의존하게 된다면, 코드에 변경이 어려워집니다. 아래의 예에서 조금더 자세히 설명드리겠습니다.

 

구체적인 구현에 의존하는 경우

위의 예제는 CellPhone에서 음악을 듣기 위해, Earphones의 sound() 메소드를 직접 사용하고 있습니다. 이때, 만약 CellPhone의 내장 스피커를 이용해 음악을 듣고싶다면, CellPhone의 코드를 직접 변경해야 합니다. "구체적인 구현에 의존"이란 말이 어렵다면, 반대의 의미를 생각해보면 됩니다. "구체적인 구현에 의존"의 반대는, "추상적인 개념에 의존"입니다. 아직도 어려우시다면 아래의 예제를 보시죠.


 

인터페이스 또는 추상클래스에 의존하는 경우

sound(Music music) 를 메소드로 가지는, Speaker interface를 생성합니다. 즉, "소리를 내다"라는 추상적인 개념을 나타네는 인터페이스입니다.

 

earphone에서 Speaker를 구현하도록 합니다.

 

마찬가지로 BUiltInSpeaker(내장스피커)에서도 Speaker를 구현하도록 합니다.

 

CellPhone에서 출력장치를 변경한다면 setSpeaker()를 통해 원하는 출력장치를 주입해주면 됩니다.

 

이처럼 인터페이스 또는 추상클래스에 의존하여, 구체적인 클래스 코드가 아닌 개념에 의존하도록 코드를 변경하면, CellPhone의 출력장치를 변경한다고 하더라도 CellPhone의 코드에는 변화가 생기지 않습니다.

 

이렇게 클래스를 변화하다보면, OOP의 또다른 원칙중 하나인 DIP를 따르는 클래스를 작성하게 됩니다. 위 예제에서 DIP가 어떻게 되었는지를 간단히 설명드리면, interface를 사용하기 전에는 CellPhone에서 => Earphones로 의존성을 가지고 있었습니다.

하지만 interface를 사용한 다음코드에서는, CellPhone에서 interface를 통해 sound() 메소드를 사용하기 때문에, CellPhone은 더 이상 Earphones에 의존하지 않게 되었고, 반대로 Earphones에서 => Speaker로 의존하게 되었습니다. 이처럼 의존성을 뒤집는다고하여 DIP라고 불릐웁니다.