티스토리 뷰

반응형


| 10. 클래스



|| 클래스 체계


- 클래스를 정의하는 표준 자바 관례 (순차적인 추상화 단계)

1. 변수 목록

- static public

- static private

- 비공개 인스턴스 변수

2. 공개 함수

3. 비공개 함수


||| 캡슐화


- 때로 변수나 유틸리티 함수를 protected로 선언하여 테스트 코드에 접근을 허용하는 방법도 있다.

- 하지만, 그 전에 비공개 상태를 유지할 온갖 방법을 강구하고 캡슐화를 풀어주는 결정은 언제나 최후의 수단으로!



|| 클래스는 작아야 한다!



- 클래스를 만들 때 첫 번째 규칙은 크기, 클래스는 작아야 한다. (두 세 번째 규칙도 크기다...)

- 함수와 마찬가지로 '작게'가 기본 규칙

- 함수는 물리적인 행 수로 크기를 측정했다면,

  클래스는 맡은 책임을 센다.


* 클래스 이름은 해당 클래스 책임을 기술해야 한다.


||| 단일 책임 원칙 (Single Responsibility Principle, SRP)


- 클래스나 모듈을 변경할 이유가 단 하나뿐이어야 한다.


> Before

1
2
3
4
5
6
7
public class SuperDashboard extends JFrame implements MetaDataUser {
    public Component getLastFocusedComponent()
    public void setLastFocused(Component lastFocused)
    public int getMajorVersionNumber()
    public int getMinorVersionNumber()
    public int getBuildNumber() 
}
cs

- SuperDashboard는 소프트웨어 버전 정보를 추적 (버전 정보는 달라진다)

SuperDashboard는 Swing 컴포넌트를 관리 (버전 정보는 달라진다)

- Version을 관리하는 독자적인 클래스를 만들 필요가 있다.


> After

1
2
3
4
5
public class Version {
    public int getMajorVersionNumber() 
    public int getMinorVersionNumber() 
    public int getBuildNumber()
}
cs


* 규모가 있는 수준의 시스템이 가지는 복잡성을 다루려면 체계적인 정리가 필수

  큰 클래스 몇 개가 아니라 작은 클래스 여럿으로 이뤄진 시스템이 더 바람직하다 !


||| 응집도 (Cohesion)


- 클래스는 인스턴스 변수가 작아야 한다.

- 각 클래스 메서드는 클래스 인스턴스 변수를 하나 이상 사용해야 한다.

  (모든 인스턴스 변수를 메서드마다 사용하는 클래스는 응집도가 가장 높다)


> 응집도가 높은 Stack.java Class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Stack {
    private int topOfStack = 0;
    List<Integer> elements = new LinkedList<Integer>();
 
    public int size() { 
        return topOfStack;
    }
 
    public void push(int element) { 
        topOfStack++
        elements.add(element);
    }
    
    public int pop() throws PoppedWhenEmpty { 
        if (topOfStack == 0)
            throw new PoppedWhenEmpty();
        int element = elements.get(--topOfStack); 
        elements.remove(topOfStack);
        return element;
    }
}
cs


||| 응집도를 유지하면 작은 클래스 여럿이 나온다


- 큰 함수를 작은 함수 여럿으로 나누기만 해도 클래스 수가 많아진다.

- 큰 함수 일부를 작은 함수로 빼내면서 

-> 큰 함수에 정의된 변수를 사용? -> 인수로 넘긴다? -> 인스턴스 변수로 승격시켜주자.

-> 클래스가 응집력을 잃는다? -> 독자적인 클래스로 분리하자.



|| 변경하기 쉬운 클래스


-

- 깨끗한 시스템은 클래스를 체계적으로 정리해 변경에 수반하는 위험을 낮춘다.

  (어떤 변경이든 클래스에 손대면 다른 코드를 망가뜨릴 잠정적인 위험이 존재)


> Before

1
2
3
4
5
6
7
8
9
10
11
12
13
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)
}
cs


> After

- 비공개 메서드는 해당하는 파생 클래스로

- 모든 파생 클래스가 공통으로 사용하는 비공개 메서드는 유틸리티 클래스에

..

- update 문을 추가한다 하더라도 기존 클래스를 변경할 필요가 전혀 없다.

- SRP와 OCP(Open-Closed-Principle)를 지원

- 새 기능에 개방적인 동시에 다른 클래스를 닫아놓는 방식으로 수정에 폐쇄적

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
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()
    }

cs


* 기능을 수정하거나 기존 기능을 변경할 때 건드릴 코드가 최소인 시스템 구조가 바람직!



|| 변경으로부터 격리



- 시스템의 결합도를 낮추면 유연성과 재사용성도 더욱 높아진다.

- 결합도가 낮다는 뜻은 각 시스템 요소가 다른 요소로부터 그리고 변경으로부터 잘 격리되어 있다는 의미.

- 결합도를 최소로 줄이면 DIP(Dependency Inversion Principle)를 따르게 된다.

  (클래스가 상세한 구현이 아니라 추상화에 의존해야 한다는 원칙)


> Example

- TokyoStockExchange라는 상세한 구현 클래스가 아닌 StockExchange Interface에 의존

StockExchange Interface는 주식 기호를 받아 현재 주식 가격을 반환한다는 추상적인 개념을 포함

- 이와 같은 추상화로 실제 주가를 얻어오는 출처나 얻어오는 방식 등과 같은 구체적인 사실을 모두 숨긴다.

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
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()); 
    }
}
cs






출처 클린 코드 (Robert C. Martin)

반응형
댓글
최근에 올라온 글
최근에 달린 댓글
링크
Total
Today
Yesterday