public class Stack {
private int topOfStack = 0;
private List<Integer> elements = new LinedList<Integer>();
public List<Integer> getElements() {
return elements;
}
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;
}
}
클래스를 개발할 때 기본적으로 구현을 감추고, 외부 객체와 상호작용하는 부분만 노출한다.
외부의 잘못된 사용을 방지한다.
경계에서 배웠던 부분! Map
Stack 예제
책임
의 수로 크기를 측정한다.public class SuperDashboard extends JFrame implements MetaDataUser {
public Component getLastFocusedComponent()
public void setLastFocused(Component lastFocused)
public int getMajorVersionNumber()
public int getMinorVersionNumber()
public int getBuildNumber()
}
'focus, version 두 가지 책임이 보인다..'
별도의 클래스로 분리한다.
public class Version {
public int getMajorVersionNumber()
public int getMinorVersionNumber()
public int getBuildNumber()
}
💡 자잘한 단일 클래스가 많아지면 큰 그림을 이해하기 어렵다고 우려한다.
하지만 작은 클래스가 많은 시스템이든 큰 클래스가 몇 개뿐인 시스템이든
돌아가는 부품은 그 수가 비슷하다.
💡 큼직한 다목적 클래스 몇 개로 이뤄진 시스템은 (변경을 가할 때)
당장 알 필요가 없는 사실까지 들이밀어 독자를 방해한다.
class PaymentService {
public void pay();
public void cancel();
public void getAccount();
public void getAccountHistory();
}
💡 작은 클래스는 각자 맡은 책임이 하나며, 변경할 이유가 하나며,
다른 작은 클래스와 협력해 시스템에 필요한 동작을 수행한다.
public class TokyoStockExchange {
public Money currentPrice(String symbol);
}
public class Portfolio {
private TokyoStockExchange tokyoStockExchange;
public Portfolio(TokyoStockExchange exchange) {
this.tokyoStockExchange = tokyoStockExchange;
}
}
'Portfolio 클래스 테스트 코드를 짜야하는데..
TokyoStockExchange
함수는 API는 5분마다 값이 달라진다..
public interface StockExchange {
Money currentPrice(String symbol);
}
public class TokyoStockExchange implements StockExchange {
public Money currentPrice(String symbol) {
// call API...
}
}
public class Portfolio {
private StockExchange exchange;
public Portfolio(StockExchange exchange) {
this.exchange = exchange;
}
}
StockExchange
인터페이스를 통해 Portfolio
와 TokyoStockExchange
의 결합도를 끊어준다.
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());
}
}
테스트 결과가 늘 같도록 한다.
그러나.. 확장될 가능성이 적다면 일단은 결합하고, 나중에 추상화해도 좋다.
객체를 Mockking 하면 변경되는 클래스도 테스트할 수 있다.
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)
}
Update 문을 추가해야 한다면?
새로운 SQL을 추가할 때도 수정이 발생하고, 기존 SQL문을 수정할 때도 수정이 발생하므로 OCP 위반된다.
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 Where {
public Where(String criteria) public String generate()
}
public clas ColumnList {
public ColumnList(Column[] columns) public String generate()
}
해당 포스팅은 제로 베이스 클린코드 한달한권을 수강 후 정리한 내용입니다.