[우아한테크코스] 3월 23일 TIL
3 minute read
오늘 배운 것
- 엘레강트 오브젝트 뽀개기1
- 엘레강트 오브젝트 뽀개기2
엘레강트 오브젝트1
- 인자의 값으로 null을 절대 허용하지 마라
String이라는 객체도 객체니까 무시하지 말자.
원시 값과 문자열을 포장하는 것이 null을 허용하지 않는 방법 중 하나일 수 있다.
- final이거나 abstract이거나
상속을 막을 수 있는 final 선언을 통해 상속을 막고 상속이 필요하면 abstract로 상속하도록 한다.
메서드에 final을 선언해 재할당을 막는 것처럼 class에도 final을 선언하도록 한다.
interface를 이용해 final + test + overriding 까지
추상클래스는 상태, 이 인스턴스 변수도 자식이 사용할 수 있게 protected로 하지 말고 private으로 하도록 하라
- -er 로 끝나는 이름을 사용하지 마라
클래스는 객체의 팩토리이다. 객체의 라이프사이클을 관리하는 것이다.
클래스는 객체의 능동적인 관리자로 저장소로 불리운다.
클래스의 이름은 무엇인지(what he is)
에 기반해 짓도록 한다.
무엇을 하는지는 controller와 같은 이름으로 옳지 않다.
객체는 캡슐화된 데이터의 대표자이다. 스스로 결정을 내리고 행동할 수 있는 자립적인 존재로 보자.
- 메서드 이름을 신중하게 선택하자
빌더(뭔가를 만들고 새로운 객체를 반환하는 메서드, void 절대 x) 이름은 명사
조정자(객체를 추상화한 실세계 엔티티를 수정하는 메서드, void) 이름은 동사
객체는 존중받고 싶은 살아있는 유기체다.
boolean의 경우에는 형용사로 짓는 것도 좋다.
get은 너무 name이 안에 있다는걸 확신하고 달라는거니까 그냥 name()으로 작성하도록 하자.
- 생성자 하나를 주 생성자로 만들자
클래스를 잘 설계한다면 많은 수의 생성자(5-10), 적은 수의 메서드(2-3)를 포함하는 것이 좋다.
생성자가 많아지면 유연성이 향상되는 반면 메서드가 많아지면 클래스의 초점이 흐려지고, 단일책임원칙이 위반된다.
입력을 string으로 받아서 파싱하는 것이 뷰에 의존하는 것이라고 생각할 수 는 없다. 사용자 입장에서 편리하기 때문에
주 생성자는 마지막, 부 생성자는 앞에 존재하도록 한다.
- 문서를 작성하는 대신 테스트를 만들자
단위 테스트는 클래스의 일부이지 독립적인 개체가 아니다.
- 생성자에 코드를 넣지 말자
객체의 변환 작업을 연기해서 요청 받기 전에는 어떠한 일도 하지 않도록 한다.
오직 할당문만 생성자가 포함하도록 한다.
성능 최적화가 쉬워서 실행 속도도 빨라진다.
객체의 변환과 관련된 실행 여부를 제어할 수 없어서 별로다.
파싱이 여러번 실행되지 않도록 데코레이터를 추가해 파싱 결과를 캐싱할 수 있다.
new는 생성자에서만 사용하도록 하자. 다른 값을 전달하지 않도록 한다.
객체를 인스턴스화하고 객체가 우리를 위해 작업하게 만들자.
엘레강트 오브젝트2
- 테스트하기 힘든 코드를 테스트 가능한 구조로 리팩토링하기
car.move(new RandomNumber())
와 같이 인자로 객체를 생성해 전달하면 직접적인 의존관계라서 tightly coupling이 생겨버려서 테스트하기 힘들어진다.
이를 테스트하기 위해서 다양하게 생성자를 추가하고 객체를 더욱 분리하면 각 테스트에서 관련한 메서드를 오버라이딩해서 테스트할 수 있다. 예를 들어 RandomNumbers를 직접 생성하지 않고 이 또한 다양한 생성자로 오버라이딩할 수 있도록 하면 테스트 가능해진다.
의존성을 외부에서 주입해주는 것을 DI라고 한다. DI 중에서 생성자를 이용한 의존성 주입을 하면 테스트하기 용이한 코드를 구현할 수 있다.
테스트하기 쉬운 코드를 만들면 코드가 유연해져서 요구사항이 변하는 때에 대응하기 쉽다.
메서드에서, 생성자에서 둘 다 의존성을 주입할 수 있다. 메서드에서 주입을 여러 곳에서 받게 되면 멤버변수로 올려주면 좋을 수 있다.
메서드 호출할 때마다 인자를 생성해주는게 재사용 측면에서 불리할 수 있다.
인스턴스 변수는 4~5개가 적정하다.
spring에서는 setter 메서드를 통해 의존성 주입을 하기도 한다. 상태를 가진 경우에는 변경할 수 없도록 setter를 사용하지 않는게 좋다. 상태를 가지지 않는 객체는 매번 인스턴스를 생성하는게 메모리 낭비이므로 인스턴스가 하나만 존재하도록 싱글턴 패턴을 적용하는데 이의 단점을 보완하고자 setter로 사용하기도 한다.
- 5개 이하의 public 메서드만 노출하자.
응집력이 높고, 테스트하기 용이한 객체는 작은 객체이고 그래야 유지보수가 쉽다.
- 불변 객체로 만들자
함수형 프로그래밍을 위해 불변 객체로 만들면 인자를 많이 전달해야해서 성능이 떨어진다는 단점이 있다.
하지만 하드웨어는 빠르게 발전하므로 성능 문제가 생길 때까지는 불변 객체로 만드는 연습을 하고 캐싱, 그래도 안되면 가변 객체로 만들도록 하자.
불변 객체를 위한 객체를 반환하는 것은 명사형으로 메서드명을 가지는 것이 규칙이다.
- 불변 객체와 상수 객체의 차이?
- public 상수를 사용하지 말자
우리는 상수라고 공유하지만 객체 사이에 어떠한 것도 공유해서는 안된다.
객체는 독립적이어야 하고 닫혀 있어야 하므로 public 상수조차 공유하지 않도록 한다.
객체 간의 결합도가 높아지고 응집도가 낮아지는 문제가 있다고 한다. 아무리 사소한 상수라도 작은 클래스를 이용해 대체하는 것을 추천한다.
enum 또한 상수이다. 이 또한 사용하지 말아보도록 하자.
이건 근데 저자의 극단적인 주장
- 가능하면 적게 캡슐화하자
4개 또는 그 이하의 필드 변수를 가진 객체를 캡슐화하자
3개 이상의 인스턴스 변수를 가진 클래스를 쓰지 않는다.
불필요한, 중복된 데이터를 가지는 변수를 만들지 말자.(예를 들어 List를 가지고 있을 때 size)
- 데코레이터 필드는 허용된다?
- 항상 인터페이스를 사용하자.
외부에서 사용하지 않는 클래스는 default로 접근 지정자 선언해주고 굳이 interface를 만들 필요가 없다.
그러지 않고서는 웬만하면 인터페이스를 만들도록 하자.
tightly coupling 의존 관계를 줄이는 역할을 spring framwork이 factory로 해준다.
- 왜?
객체지향 5원칙 (SOLID)
- SRP 단일 책임 원칙
작성된 클래스는 하나의 기능만 가지며 클래스가 제공하는 모든 서비스는 그 하나의 책임을 수행하는 데 집중되어 있어야 한다.
- OCP 개방폐쇄의 원칙
소프트웨어의 구성요소(컴포넌트, 클래스, 모듈, 함수)는 확장에는 열려있고, 변경에는 닫혀있어야 한다.
- LSP 리스코브 치환의 원칙
서브 타입은 언제나 기반 타입으로 교체할 수 있어야 한다. 즉, 서브 타입은 언제나 기반 타입과 호환될 수 있어야 한다.
- ISP 인터페이스 분리의 원칙
한 클래스는 자신이 사용하지 않는 인터페이스는 구현하지 말아야 한다.
- DIP 의존성 역전의 원칙
구조적 디자인에서 발생하던 하위 레벨 모듈의 변경이 상위 레벨 모듈의 변경을 요구하는 위계관계를 끊는 의미의 역전 원칙이다.