주식(stock)을 거래(trade)를 하는 주문(order) 기능을 Java로 구현
public class Order {
private String customer;
private List<Trade> trades = new ArrayList<>();
public void addTrade(Trade trade) {
trades.add(trade);
}
public String getCustomer() {
return customer;
}
public void setCustomer(final String customer) {
this.customer = customer;
}
public double getValue() {
return trades.stream().mapToDouble(Trade::getValue).sum();
}
}
public class Trade {
public enum Type {BUY, SELL}
private Type type;
private Stock stock;
private int quantity;
private double price;
public Type getType() {
return type;
}
public void setType(final Type type) {
this.type = type;
}
public Stock getStock() {
return stock;
}
public void setStock(final Stock stock) {
this.stock = stock;
}
public int getQuantity() {
return quantity;
}
public void setQuantity(final int quantity) {
this.quantity = quantity;
}
public double getPrice() {
return price;
}
public void setPrice(final double price) {
this.price = price;
}
public double getValue() {
return quantity * price;
}
}
public class Stock {
private String symbol;
private String market;
public String getSymbol() {
return symbol;
}
public void setSymbol(final String symbol) {
this.symbol = symbol;
}
public String getMarket() {
return market;
}
public void setMarket(final String market) {
this.market = market;
}
}
public class TradeStockTest {
@Test
void 도메인객체를이용한주식거래() {
Order order = new Order();
order.setCustomer("myCustomer");
Stock stock1 = new Stock();
stock1.setSymbol("IBM");
stock1.setMarket("NYSE");
Trade trade1 = new Trade();
trade1.setType(Trade.Type.BUY);
trade1.setStock(stock1);
trade1.setPrice(125.00);
trade1.setQuantity(80);
order.addTrade(trade1);
Stock stock2 = new Stock();
stock2.setSymbol("GOOGLE");
stock2.setMarket("NASDAQ");
Trade trade2 = new Trade();
trade2.setType(Trade.Type.BUY);
trade2.setStock(stock2);
trade2.setPrice(375.00);
trade2.setQuantity(50);
order.addTrade(trade2);
}
}
- 위 코드는 장황하며, 도메인 전문가(비개발자, 주식 전문가)가 위 코드를 이해하고 검증하기가 어렵다.
- 도메인 전문가도 코드를 쉽게 이해할 수 있도록 DSL(Domain Specific Languages)을 제공하면 비개발자도 코드를 검증하기 쉬워진다.
메서드 체인 패턴을 이용한 DSL 구현
- orderBuilder의 forCustomer 메서드를 시작으로, Buy||Sell -> stock -> on -> at 으로 메서드가 체이닝 되어 있다.
- 체인의 연결은 각 Builder 클래스를 통해 연결된다.
- 각 체인은 최초의 빌더인 orderBuilder를 파라미터 참조를 통해 전달한다.
- 체인의 끝에서는 파라미터 참조된 orderBuilder의 최종 메서드로 연결된다.
- 체인의 중간 단계에서는 값설정 또는 처리되어야 하는 로직이 포함된다.
- 메서드 체인 패턴의 장점은 호출의 순서를 강제하기 때문에 메서드 호출이 잘못될 가능성을 없앨 수 있다.
메서드 체인 패턴으로 개발된 DSL을 사용한 주식거래
public class MethodChainingTest {
@Test
void 메서드체인_트레이드_코스() {
Order order = MethodChainingOrderBuilder.forCustomer("BigBank")
.buy(80)
.stock("IBM")
.on("NYSE")
.at(125.00)
.sell(50)
.stock("GOOGLE")
.on("NASDAQ")
.at(375.00)
.end();
System.out.println(order.getValue());
}
}
public class MethodChainingOrderBuilder {
public final Order order = new Order();
private MethodChainingOrderBuilder(String customer) {
order.setCustomer(customer);
}
// 빌더 패턴
public static MethodChainingOrderBuilder forCustomer(String customer) {
return new MethodChainingOrderBuilder(customer);
}
public TradeBuilder buy(int quantity) {
return new TradeBuilder(this, Trade.Type.BUY, quantity);
}
public TradeBuilder sell(int quantity) {
return new TradeBuilder(this, Trade.Type.SELL, quantity);
}
// 빌더 패턴
public MethodChainingOrderBuilder addTrade(Trade trade) {
order.addTrade(trade);
return this;
}
// 주문 만들기를 종료하고 반환
public Order end() {
return order;
}
}
public class TradeBuilder {
private final MethodChainingOrderBuilder builder;
public final Trade trade = new Trade();
public TradeBuilder(MethodChainingOrderBuilder builder, Trade.Type type, int quantity) {
this.builder = builder;
trade.setType(type);
trade.setQuantity(quantity);
}
public StockBuilder stock(String symbol) {
return new StockBuilder(builder, trade, symbol);
}
}
public class StockBuilder {
private final MethodChainingOrderBuilder builder;
private final Trade trade;
private final Stock stock = new Stock();
public StockBuilder(MethodChainingOrderBuilder builder, Trade trade, String symbol) {
this.builder = builder;
this.trade = trade;
stock.setSymbol(symbol);
}
public TradeBuilderWithStock on(String market) {
stock.setMarket(market);
trade.setStock(stock);
return new TradeBuilderWithStock(builder, trade);
}
}
public class TradeBuilderWithStock {
private final MethodChainingOrderBuilder builder;
private final Trade trade;
public TradeBuilderWithStock(MethodChainingOrderBuilder builder, Trade trade) {
this.builder = builder;
this.trade = trade;
}
public MethodChainingOrderBuilder at(double price) {
trade.setPrice(price);
return builder.addTrade(trade);
}
}
[단점]
- 빌더를 구현해야 한다는 것이 메서드 체인의 단점이다. 상위 빌더를 하위 빌더와 연결하기 위한 코드가 많이 필요하다.
- 도메인 객체의 구조에 맞춰 들여쓰기를 제공하지 못하는 것도 단점이다.
//아래 코드는 이해를 돕기위해 인위적으로 들여쓰기를 했을 뿐 제공된 DSL은 들여쓰기의 기준을 제시하지 못함
public class MethodChainingTest {
@Test
void 메서드체인_트레이드_코스() {
Order order = MethodChainingOrderBuilder.forCustomer("BigBank")
.buy(80)
.stock("IBM")
.on("NYSE")
.at(125.00)
.sell(50)
.stock("GOOGLE")
.on("NASDAQ")
.at(375.00)
.end();
System.out.println(order.getValue());
}
}
'프로그래밍 > OOP_Pattern_TDD' 카테고리의 다른 글
자바 DSL - 중첩된 함수(NestedFunction) + Lambda (0) | 2023.03.08 |
---|---|
자바 DSL - 중첩된 함수(NestedFunction) 패턴 (0) | 2023.03.08 |
람다 - 팩토리 패턴 (0) | 2023.03.03 |
람다 - 의무체인 패턴 (0) | 2023.03.03 |
람다 - 옵저버 패턴 (0) | 2023.03.03 |