Study/클린코더스 강의

클린 코더스 1, 2강 - OOP

voider 2021. 2. 27. 15:07

💡백명석 님의 클린 코더스 강의를 듣고 요약한 자료입니다.

목차

1, 2강 OOP
3, 4강 Function
5강 Function Structure
6강 Form

Why Clean Code

소프트웨어는 한 번 작성되면 최소 10번 이상 읽힌다고 한다. 내가 작성한 코드도 2주만 지나면 까먹기가 일쑤다. 실제 업무에서는 내 코드를 나만 보는 것도 아니다. 내가 아닌 누군가가 내 코드를 읽어야 할 수도 있다. 그래서 '돌아만 가는 코드' 말고 '읽기 쉬운 코드'를 작성해야 한다.

절차지향과 객체지향

절차지향은 프로시저가 같은 데이터에 의존하고, 프로시저가 프로시저를 의존하는 형태다.

https://github.com/msbaek/clean-coders-2013/blob/master/1.OOP.png

이런 구조이기 처음엔 쉽게 만들 수 있지만 시간이 지날수록 수정이 어렵다.

그렇다면 객체지향적인 구조는 어떨까?

https://github.com/msbaek/clean-coders-2013/blob/master/1.OOP.png

객체지향은 데이터를 공유하지 않는다. 데이터를 사용하는 기능을 객체로 매핑한다(응집). 데이터에 변경이 있다면 그 객체만 수정하면 된다. 객체가 변경될 때 다른 객체에 영향을 끼치지 않도록 하는 것이 객체지향 설계다. 이런 설계를 위해서 객체와 객체는 서로의 구체적인 구현을 알아선 안 된다. 협업에 필요한 것만 노출해야 한다(인터페이스). 객체지향의 장점은 데이터와 코드가 캡슐화되어 있어서, 인터페이스를 변경하는 것이 아니라면 다른 객체에 영향을 끼치지 않고 변경할 수 있다는 것이다. (높은 응집도와 낮은 결합도)

이런 객체지향의 이점에도 불구하고 자꾸 절차지향적 코드를 작성하는 것은 그런 사고 방식에 익숙하기 때문이다. 반면 객체지향적으로 사고하는 것은 어렵다. 처음부터 객체지향적으로 코드를 작성할 수 없다면 아래와 같은 절차를 따른다.

  1. 절차지향적 코드 작성
  2. 객체지향적으로 리팩토링

이런 순서 대로 개발할 수도 있다. 원래 돌아가던 코드가 리팩토링 뒤에도 똑같이 작동한다는 것을 검증하기 위해 테스트 코드가 필요하다.

객체, 역할, 책임

객체/클래스 이름을 '무엇'을 할 것인지에 초점을 둬야 한다. 이를 테면 게시판을 만든다고 했을 때 비즈니스 레이어로 ArticleService를 두는 경우가 많다. 하지만 이는 객체지향적인 이름이 아니다. 무엇을 할 것인지에 초점을 둔 이름이라면 WriteArticleService 같은 이름이 되어야 한다. RequestParser는 좋은 이름이지만, JsonRequestParser는 좋은 이름이 아니다. JSON이 아닌 다른 파일일 경우 사용할 수 없기 때문.

특정 요구사항을 만족시키기 위한 행위를 책임(responsibility)이라고 한다. 책임과 역할을 구분해야 한다. 역할은 책임의 집합이다. 그리고 객체는 역할/책임을 갖는다.

Tell, Don't Ask

묻지 말고 시켜라. 객체에게 데이터를 요청해서 변경하고 저장하지 않는다. 객체에게 그 작업을 수행하라고 메세지를 전송해야 한다. 그래야만 캡슐화가 유지되어 변경에 영향을 받지 않는다.

Command-Query Separation(CQS)

커맨드는 객체 내부의 상태를 변경하는 메서드를 말한다. 쿼리는 상태변경 없이 결과 값만 주는 메서드다. Command와 Query가 한 군데 응집되어 있으면 안 된다. 커맨드도 하나의 상태만 변경해야지, 두 개 이상을 변경하려고 하면 힘들어진다. 또한 이것은 원칙이기 때문에 메서드 이름이 Command 또는 Query일 때 개발자는 그것이 상태를 변경하거나 결과만 값을 주는 메서드라고 기대한다. 따라서 원칙에 따라 개발하는 것이 좋다.

다형성(Polymorphism)

한 객체가 여러가지(poly) 모습/타입(morph)을 가질 수 있다. 자바에서 다형성을 구현하는 방법은 두 가지가 있다.

  1. 상속(inheritance)
  2. 구현(implements)

그런데 인터페이스를 왜 사용할까? 인터페이스는 구현을 강제하는 반면 상속은 상위 클래스에 있는 구현의 제약없이 사용할 수 있는데.

하지만 상속을 무분별하게 사용했다간 아무것도 건드리지 못하는 상황에 빠질 수 있다. 인터페이스를 사용하는 것이 재사용이다.

구현체가 하나여도 인터페이스를 쓰는 이유는, 소프트웨어는 반드시 변경되기 때문이다. 다른 구현체가 생긴다. 다른 이유로는 테스트가 쉽고, 부가적인 기능을 넣기 쉽다.

가장 중요한 것은 재사용이다. 인터페이스를 사용하는 코드는 재사용된다. 클라이언트가 B를 바라볼 때 인터페이스를 통해서 바라보도록 하는 것이다. 클라이언트가 인터페이스를 바라보고 있다면 구현이 B에서 C로 변경하더라도 클라이언트는 영향을 받지 않고 그대로 인터페이스를 재사용한다.

계속해서 사용되는 코드를 라이브러리처럼 만들어서 A에서도 쓰고 B에서도 쓰는 것이 재사용이 아니다. 인터페이스를 사용하는 로직은 비즈니스 로직이다. 이 비즈니스 로직이 사용하는 디테일한 로직. 이를테면 데이터를 읽거나 쓰는 로직의 변경이 있을 때(여기서 변경은 새로운 코드, 새로운 객체가 추가되는 경우를 말한다.) 그 인터페이스를 바라보고 있는 객체는 변경이 일어나지 않는 것을 재사용이라고 한다. 이처럼 객체지향의 핵심은 의존성 관리를 통해 고수준 로직을 디테일한 저수준 로직으로부터 보호하는 것이다.

반드시 처음부터 인터페이스를 둬야 한다는 것이 아니다. 처음엔 인터페이스를 두지 않고 설계하고 코드를 작성하더라도, 새로운 객체가 추가되어야 할 때는 인터페이스를 고려해야 한다.

'Study > 클린코더스 강의' 카테고리의 다른 글

클린코더스 6강 - Form  (0) 2021.03.30
클린코더스 5강 Function Structure #1  (0) 2021.03.10
클린코더스 2강 - Function  (0) 2021.02.28