Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
Tags
- Adapater Pattern
- 11758번
- 11286번
- 2156번
- java
- 1300번
- 1043번
- 2206번
- Design Patterns
- SerialDate 리펙터링
- Dxerr.h
- 백준
- springboot
- 클린코드
- programmers
- 냄새와 휴리스틱
- 코딩테스트
- 코딩 테스트
- Spring
- DxTrace
- Design Pattern
- 9장
- 프로그래머스
- 가장 긴 증가하는 부분 수열2
- java의 정석
- 10830번
- 자바의 정석
- BOJ
- 17장
- 2166번
Archives
- Today
- Total
Don't give up!
[클린코드] 10장 클래스 본문
이 글은 로버트 마틴의 '클린 코드'를 읽고 TIL(Today I Learned)로써 정리한 내용을 기록하는 글입니다.
자세한 내용은 책을 통해 확인하실 수 있습니다.
알라딘: 클린 코드 Clean Code (aladin.co.kr)
클래스
클래스 체계
- 표준 자바 관례
- static public 상수
- static private 변수
- private 인스턴스 변수
- public 함수
- private 함수
캡슐
- 변수와 유틸리티 함수는 가능한 공개하지 않는 편이 낫지만 반드시 숨겨야 한다는 법칙도 없다.
- 같은 패키지 안에서 테스트 코드가 함수를 호출하거나 변수를 사용해야 한다면 그 함수나 변수를 protected로 선언하거나 패키지 전체로 공개할 수 있다. (캡슐화를 풀어주는 결정은 언제나 최후의 수단이다.)
클래스 크기
- 함수와 마찬가지로 클래스는 작아야 한다.
- 클래스는 클래스가 맡은 책임으로 크기를 측정한다.
- 클래스에 대해 간결한 이름이 떠오르지 않는다면 클래스가 맡은 책임이 많다는 뜻. (클래스 크기가 크다.)
- 클래스 설명은 if, and, but을 사용하지 않고서 25단어 내외로 가능해야 한다.
단일 책임 원칙
- 클래스나 모듈을 변경할 이유(책임)는 단 하나뿐이어야 한다.
- 변경할 이유(책임)를 파악하려 애쓰다 보면 코드를 추상화하기도 쉬워진다.
- 작은 클래스가 많은 시스템이든 큰 클래스가 몇 개뿐인 시스템이든 돌아가는 부품은 그 수가 비슷하다.
- 개발자가 무엇이 어디에 있는지 쉽게 찾기 위해서는 체계적인 정리가 필수다.
- 큼직한 다목적 클래스 몇 개로 이루어진 시스템은 변경을 가할 때 당장 알 필요가 없는 사실까지 들이밀어 독자를 방해한다.
//나쁜 코드 - Super와 같이 모호한 단어가 담긴 클래스명(함수가 갖는 책임이 많음.)
public class SuperDashboard extends JFrame implements MetaDataUser {
public Component getLastFocusedComponent() { ... }
public void setLastFocused(Component lastFocused) { ... }
public int getMajorVersionNumber() { ... }
public int getMinorVersionNumber() { ... }
public int getBuildNumber() { ... }
}
//개선 - 추상화된 클래스로 책임을 분리
public class Version {
public int getMajorVersionNumber() { ... }
public int getMinorVersionNumber() { ... }
public int getBuildNumber() { ... }
}
public class SuperDashboard extends JFrame implements MetaDataUser {
public Component getLastFocusedComponent() { ... }
public void setLastFocused(Component lastFocused) { ... }
}
응집도
- 클래스는 인스턴스 변수 수가 작아야 한다.
- 일반적으로 메서드가 변수를 더 많이 사용할 수록 메서드와 클래스는 응집도가 더 높다.
- 응집도가 높다는 말은 클래스에 속한 메서드와 변수가 서로 의존하며 논리적인 단위로 묶인다는 의미.
- 큰 함수를 작은 함수 여럿으로 쪼개다 보면 작은 클래스 여럿으로 쪼갤 기회가 생기고 구조가 투명해진다.
변경하기 쉬운 클래스
- 대다수 시스템은 지속적인 변경이 가해진다.
- 깨끗한 시스템은 클래스를 체계적으로 정리해 변경에 수반하는 위험을 낮춘다.
//나쁜 예시 - 단일 책임 원칙을 위반하는 Sql 클래스
public class Sql {
public Sql(String table, Column[] columns) { ... }
public String create() { ... }
public String insert(Object[] fields) { ... }
public String selectAll() { ... }
public String findByKey(String keyColumn, String keyValue) { ... }
public String select(Column column, String pattern) { ... }
public String select(Criteria criteria) { ... }
public String preparedInsert() { ... }
private String columnList(Column[] columns) { ... }
private String valuesList(Object[] fields, final Column[] columns) { ... }
private String selectWithCriteria(String criteria) { ... }
private String placeholderList(Column[] columns) { ... }
}
//개선 - 추상 클래스, 유틸리티 클래스 선언으로 단일 책임 원칙, 열림 닫힘 원칙을 준수
abstract public class Sql {
public Sql(String table, Column[] columns) { ... }
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 class SelectWithCriteriaSql extends Sql {
public SelectWithCriteriaSql(String table, Column[] columns, Criteria criteria) { ... }
@Override public String generate() { ... }
}
public class SelectWithMatchSql extends Sql {
public SelectWithMatchSql(String table, Column[] columns, Column column, String pattern) { ... }
@Override public String generate() { ... }
}
public class FindByKeySql extends Sql
public FindByKeySql(String table, Column[] columns, String keyColumn, String keyValue) { ... }
@Override public String generate() { ... }
}
public class PreparedInsertSql extends Sql {
public PreparedInsertSql(String table, Column[] columns) { ... }
@Override public String generate() { ... }
private String placeholderList(Column[] columns) { ... }
}
public class Where {
public Where(String criteria) { ... }
public String generate() { ... }
}
public class ColumnList {
public ColumnList(Column[] columns) { ... }
public String generate() { ... }
}
변경으로부터 격리
- 요구사항은 변하기 마련이다. 따라서 코드도 변하기 마련이다.
- 상세한 구현에 의존하는 클라이언트 클래스는 구현이 바뀌면 위험에 빠진다.
- 상세한 구현에 의존하는 코드는 테스트가 어렵다.
- 인터페이스와 추상 클래스를 사용해 구현이 미치는 영향을 격리할 수 있다.
- 결합도가 낮다는 것은 각 시스템 요소가 다른 요소로부터 그리고 변경으로부터 잘 격리되어 있다는 의미.
- 시스템 요소가 서로 잘 격리되어 있으면 각 요소를 이해하기도 쉬워진다.
//좋은 예시 - 추상화를 사용하여 구체적인 구현을 모두 숨기고 인터페이스에 의존.
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());
}
}
'개발서적 > 클린 코드' 카테고리의 다른 글
[클린코드] 11장 시스템 (0) | 2021.07.31 |
---|---|
[클린코드] 9장 단위테스트 (0) | 2021.07.27 |
[클린코드] 8장 경계 (0) | 2021.07.27 |