티스토리 뷰



[프로그래밍 방법론] 효율적 클래스 설계 방법 - Clean Code


이 글은 Clean Code 책의 내용을 정리한 것입니다.


코드의 표현력과 코드로 이루어진 함수에 아무리 신경을 쓸지라도 좀 더 차원 높은 단계까지 신경 쓰지 않으면 깨끗한 코드를 얻기는 어렵습니다. 이 글에서는 깨끗한 클래스에 대해서 정리하겠습니다.


1. 클래스 체계와 캡슐화


표준 자바 관례에 따르면 변수 목록, 함수 목록순으로 나옵니다. 변수는 정적 공개 상수, 정적 비공개 상수, 비공개 인스턴스가 나오고 공개변수가 필요한 경우는 거의 없습니다.


변수와 유틸리티 함수는 가능한 공개하지 않는 편이 낫지만 반드시 숨겨야 한다는 법칙도 없습니다. 하지만 비공개 상태를 유지할 온갖 방법을 강구해야 하며, 캡슐화를 풀어주는 결정은 언제나 최후의 수단이어야 합니다. 


‘캡슐화란?’

캡슐화란 ‘실제 기능’은 숨기고 ‘접근할 방법’만 노출하는 것을 말한다. 


2. 클래스는 작아야 한다.


클래스를 만들 때 첫번째 규칙도, 두번째 규칙도 작아야 한다는 것입니다. 클래스를 설계할 때도 함수와 마찬가지로 ‘작게’가 기본 규칙입니다. 얼마나 작아야 하는가?에 대한 답은 클래스가 맡은 책임이 무엇인가로 생각할 수 있습니다. 


클래스의 이름은 해당 클래스의 책임을 기술해야 하는데, 이는 만일, 그리고, ~하며, 하지만을 사용하지 않고 25단어 내외로 가능해야 합니다. 


단일 책임 원칙(Single Responsibility Principle, SRR)

클래스나 모듈을 변경할 이유가 하나, 단 하나뿐이어야 한다는 원칙을 말합니다. SRP는 객체지향 설계에서 가장 중요한 개념입니다. 또한 이해하고 지키기도 가장 쉬운 개념이기도 합니다. 프로그램을 돌아만 가게 만들자는 생각이 많아서 가장 무시하는 규칙이기도 합니다. 


큰 클래스 몇 개가 아니라 작은 클래스 여럿으로 이뤄진 시스템이 더 바람직합니다. 


응집도(Cohesion)

응집도가 가장 높은 클래스는 구현이 가능하지도, 바람직하지도 않지만 일반적으로 응집도가 높은 클래스를 선호해야 합니다. 


응집도가 높다는 말은 클래스에 속한 메소드와 변수가 서로 의존하며 논리적인 단위로 묶인다는 의미이기 때문입니다. 


Public class Statck 

{

List<Integer> elements = new LinkedList<Integer>();

private int size() 

return elements.Length; 

}


public void push(int element)

{

elements.add(element);

}

}


위의 코드는 응집도가 높은 코드입니다. 모든 함수에서 elements를 사용하고 있습니다. 


3. 변경하기 쉬운 클래스 



위의 SqlManager는 SQL 구문을 처리하기 위해 만들어진 클래스입니다. 하지만 현재 Update에 대한 기능이 구현되지 않았다고 가정을 했을 때, 언젠가는 이 Update 함수도 추가 구현이 되어야 할 것입니다. 


이 클래스에서 Update 함수를 추가 구현하게 되면 다른 함수와의 충돌 위험이 있고, SqlManager를 사용하고 있는 다른 클래스에서 문제가 발생할 수도 있어 새 기능을 추가 구현하면 새로이 테스트를 진행해야 합니다. 이는 유지보수에 많은 비용을 들이게 하는 단점이 됩니다. 이는 앞에서 말한 SRP에 대한 규칙을 어기게 됩니다.


이러한 문제를 해결하기 위해서는 각 기능을 클래스로 위임하는 방법이 있습니다.


 


위의 같이 Sql 인터페이스 아래 각 기능에 대한 클래스가 구현이 되어 있으면, Update에 대한 기능을 추가 구현해야 할 때는 Update 클래스를 추가로 만들어 주면 됩니다. 이미 클래스 단위로 기능이 구분이 되어 있기 때문에 기존에 사용했던 코드가 문제가 될 일은 없습니다. 


또한 코드에 대한 가독성이 이전보다 훨씬 높아졌습니다. 이는 클래스가 서로 분리가 되었기 때문에 가능한 일입니다. 위의 코드는 SRP를 만족하고, OCP(Open Closed Principle)도 지원합니다.


OCP란?

클래스는 확장에 개방적이고 수정에 폐쇄적이어야 한다는 원칙입니다. 다른 말로 하면 기존 코드를 변경하지 않으면서 코드의 수정을 허용하게 하는 원리를 말합니다.


4. 변경으로부터의 격리


요구사항은 항상 변하기 마련입니다. 따라서 코드도 항상 변하게 됩니다. 객체지향 입문에서 추상클래스와 구현클래스로 구분이 되고, 구현 클래스에서 상세한 코드를 작성한다고 배웠습니다. 하지만 상세한 구현에 의존하는 클라이언트 클래스는 구현이 바뀌면 위험에 빠질 가능성이 있습니다. 


이를 해결하기 위해서 인터페이스와 추상클래스를 사용하여 구현이 미치는 영향을 격리해야 합니다. 격리를 하게 되면 결합도가 낮은 코드가 작성이 됩니다. 결합도가 낮다는 이야기는 각 시스템 요소가 다른 요소로부터 그리고 변경으로부터 잘 격리되어 있다는 의미가 됩니다. 


위의 그림에서 알 수 있듯이 Update 코드를 변경하였을 때, 앞의 경우는 다른 코드부분에 영향을 미치지만 뒤의 코드는 Update 클래스 하나만 추가하면 되므로 앞의 코드에 비해 결합도가 상당히 낮다고 볼 수 있습니다. 


결합도를 줄이면 DIP(Dependency Inversion Principle)을 따르는 클래스가 나오게 됩니다. DIP는 클래스가 상세한 구현이 아니라 추상화에 의존해야 한다는 원칙입니다. 


이 글이 도움이 되셨나요?

그렇다면 아래의 그림을 클릭해주세요.




댓글