티스토리 뷰

Book/Clean Code

[Clean Code] 클래스

hyuuny 2022. 1. 25. 12:26

클래스


클래스 체계

  • 클래스를 정의하는 표준 자바 관례에 따르면, 가장 먼저 변수 목록이 나오고 변수 목록 다음에는 공개 함수가 나온다.
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는 클래스가 상세한 구현이 아니라 추상화에 의존해야 한다는 원칙이다.
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/01   »
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
글 보관함