티스토리 뷰
클래스
클래스 체계
- 클래스를 정의하는 표준 자바 관례에 따르면, 가장 먼저 변수 목록이 나오고 변수 목록 다음에는 공개 함수가 나온다.
class Example {
변수들 ...
함수들 ...
}
- 변수: 정적 공개 상수 -> 정적 비공개 변수 -> 비공개 인스턴스 변수 -> 공개 변수
- 변수 목록 다음에는 공개 함수가 나온다. 즉, 추상화 단계가 순차적으로 내려간다.
클래스는 작아야 한다!
- 클래스를 설계할 때도, 함수와 마찬가지로 ‘작게’가 기본 규칙이다.
- 함수는 물리적인 행 수로 크기를 측정했다면, 클래스는 맡은 책임을 센다.
- 작명은 클래스 크기를 줄이는 첫 번째 관문이다. 간결한 이름이 떠오르지 않는다면 필경 클래스 크기가 너무 커서 그렇다.
단일 책임 원칙(Single Responsibility Principle)
- 클래스나 모듈을 변경할 이유가 하나뿐이어야 한다는 원칙이다.
- SRP는 객체 지향 설계에서 더욱 중요한 개념이다.
- 큰 클래스 몇 개가 아니라 작은 클래스 여럿으로 이뤄진 시스템이 더 바람직하다. 작은 클래스는 각자 맡은 책임이 하나며, 변경할 이유가 하나며, 다른 작은 클래스와 협력해 시스템에 필요한 동작을 수행한다.
- 역할, 책임, 협력
응집도
- 클래스는 인스턴스 변수가 작아야 하고, 각 클래스 메서드는 클래스 인스턴스 변수를 하나 이상 사용해야 한다.
- 일반적으로 메서드가 변수를 더 많이 사용할수록 메서드와 클래스는 응집도가 더 높다.
- 응집도가 높다는 말은 클래스에 속한 메서드와 변수가 서로 의존하며 논리적인 단위로 묶인다는 의미이다.
- 때때로 몇몇 메서드만이 사용하는 인스턴스 변수가 생기는데, 이는 새로운 클래스로 쪼개야 한다는 신호이다.
변경하기 쉬운 클래스
- 대다수 시스템은 지속적인 변경이 가해진다. 그리고 뭔가 변경할 때마다 시스템이 의도대로 동작하지 않을 위험이 따른다. 깨끗한 시스템은 클래스를 체계적으로 정리해 변경에 수반하는 위험을 낮춘다.
- 새 기능을 수정하거나 기존 기능을 변경할 때 건드릴 코드가 최소인 시스템 구조가 바람직하다. 이상적인 시스템이라면 새 기능을 추가할 때 시스템을 확장할 뿐 기존 코드를 변경하지 않는다.
변경이 필요해 '손대야' 하는 클래스
public class Sql{
public Sql(String table, Column[] columns)
public String create()
public String insert(Object[] fields)
public String selectAll()
}
닫힌 클래스 집합
abstract public class Sql{
ublic Sql(String table, Column[] colunns)
abstract public String generate();
}
public class CreateSql extends Sql{
public CreateSql(String table, Column[] columns)
@Override public String generate()
}
public class SelectSql extends Sql{
public SelectSql(String table, Column[] columns)
@Override public String generate()
}
public class InsertSql extends Sql{
public InsertSql(String table, Column[] columns, Object[] fields)
@Override public String generate()
private String valuesList(Object[] fields, final Column[] columns)
}
클래스가 서로 분리되었기 때문에, 함수 하나를 수정했다고 다른 함수가 망가질 위험이 사라졌다.
테스트 관점에서 모든 논리를 구석구석 증명하기도 쉬워졌다.
변경으로부터 격리
- 객체 지향 프로그래밍에는 구체적인 클래스와 추상 클래스가 있다. 구체적인 클래스는 상세한 구현(코드)을 포함하며 추상 클래스는 개념만 포함한다.
- 상세한 구현에 의존하는 클라이언트 클래스는 구현이 바뀌면 위혐에 빠진다. 그래서 인터페이스와 추상 클래스를 사용해 구현에 미치는 영향을 격리한다.
public interface StockExchange{
Money currentPrice(String symbol);
}
public Portfolio{
private StockExchange exchange;
public Portfolio(StockExchange exchange){
this.exchange = exchange;
}
}
public class PortfolioTest{
private FixedStockExchangeStub exchange;
private Portfolio portfolio;
@Before
protected void setUp() throws Exception{
exchange = new FixedStockExchangeStub();
exchange.fix("MSFT",100);
portfolio = new Portfolio(exchange)
}
@Test
public void GivenFiveMSFTTotalShouldBe500() throws Exception{
portfolio.add(5, "MSFT");
Assert.assertEquals(500, portfolio.value());
}
}
- StockExchange Interface는 주식 기호를 받아 현재 주식 가격을 반환한다는 추상적인 개념을 포함한다.
- 이와 같은 추상화로 실제 주가를 얻어오는 출처나 얻어오는 방식 등과 같은 구체적인 사실을 모두 숨긴다.
- 위와 같은 테스트가 가능할 정도로 시스템의 결합도를 낮추면 유연성과 재사용성도 더욱 높아진다. 결합도가 낮다는 소리는 각 시스템 요소가 다른 요소로부터 그리고 변경으로부터 잘 격리되어 있다는 의미다.
- 이렇게 결합도를 최소로 줄이면 자연스럽게 또 다른 클래스 설계 원칙인 DIP(Dependency Inversion Principle)를 따르는 클래스가 나온다.
- 본질적으로 DIP는 클래스가 상세한 구현이 아니라 추상화에 의존해야 한다는 원칙이다.
'Book > Clean Code' 카테고리의 다른 글
[Clean Code] 시스템 (0) | 2022.01.26 |
---|---|
[Clean Code] 오류처리, 경계 (0) | 2022.01.24 |
[Clean Code] 형식 맞추기, 객체와 자료구조 (0) | 2022.01.23 |
[Clean Code] 주석 (0) | 2022.01.18 |
[Clean Code] 깨끗한 코드, 의미있는 이름, 함수 (0) | 2022.01.18 |
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
링크
TAG
- 스프링부트
- 자료구조
- mysql 8.0
- 인프런
- 그리디
- 정렬
- 스프링
- 노마드
- 데이터베이스
- 파이썬
- 김영한
- 백준
- leetcode
- kotlin
- 코테
- 알고리즘
- Real MySQL
- Spring
- 스프링 부트
- 리팩토링
- 북클럽
- Algorithm
- 코틀린
- 노마드코더
- webflux
- MySQL
- 구현
- 문자열
- 릿코드
- spring boot
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
글 보관함