From 65adc09007249a2cabbac68df4ebc34353db5ce2 Mon Sep 17 00:00:00 2001 From: Narendra Pathai Date: Sun, 7 Aug 2016 17:05:22 +0530 Subject: [PATCH] Work on #381, added draft implementation of service stub pattern. Doing this to help @jdoetricksy get the hang of abstractions --- service-stub/pom.xml | 52 ++++++++++++++ .../com/iluwatar/servicestub/service/App.java | 34 ++++++++++ .../service/BuyAtCurrentPriceCommand.java | 31 +++++++++ .../service/BuyAtLimitPriceCommand.java | 51 ++++++++++++++ .../servicestub/service/Portfolio.java | 44 ++++++++++++ .../servicestub/service/SellCommand.java | 6 ++ .../iluwatar/servicestub/service/Stock.java | 48 +++++++++++++ .../servicestub/service/StockInformation.java | 39 +++++++++++ .../servicestub/service/StockQuote.java | 23 +++++++ .../servicestub/service/StockService.java | 6 ++ .../servicestub/service/StockServiceStub.java | 20 ++++++ .../service/YahooStockService.java | 14 ++++ .../service/BuyAtLimitPriceTest.java | 67 +++++++++++++++++++ 13 files changed, 435 insertions(+) create mode 100644 service-stub/pom.xml create mode 100644 service-stub/src/main/java/com/iluwatar/servicestub/service/App.java create mode 100644 service-stub/src/main/java/com/iluwatar/servicestub/service/BuyAtCurrentPriceCommand.java create mode 100644 service-stub/src/main/java/com/iluwatar/servicestub/service/BuyAtLimitPriceCommand.java create mode 100644 service-stub/src/main/java/com/iluwatar/servicestub/service/Portfolio.java create mode 100644 service-stub/src/main/java/com/iluwatar/servicestub/service/SellCommand.java create mode 100644 service-stub/src/main/java/com/iluwatar/servicestub/service/Stock.java create mode 100644 service-stub/src/main/java/com/iluwatar/servicestub/service/StockInformation.java create mode 100644 service-stub/src/main/java/com/iluwatar/servicestub/service/StockQuote.java create mode 100644 service-stub/src/main/java/com/iluwatar/servicestub/service/StockService.java create mode 100644 service-stub/src/main/java/com/iluwatar/servicestub/service/StockServiceStub.java create mode 100644 service-stub/src/main/java/com/iluwatar/servicestub/service/YahooStockService.java create mode 100644 service-stub/src/test/java/com/iluwatar/servicestub/service/BuyAtLimitPriceTest.java diff --git a/service-stub/pom.xml b/service-stub/pom.xml new file mode 100644 index 00000000000..e3009618d88 --- /dev/null +++ b/service-stub/pom.xml @@ -0,0 +1,52 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.13.0-SNAPSHOT + + service-stub + + + junit + junit + test + + + org.mockito + mockito-core + test + + + com.yahoofinance-api + YahooFinanceAPI + 3.1.0 + + + diff --git a/service-stub/src/main/java/com/iluwatar/servicestub/service/App.java b/service-stub/src/main/java/com/iluwatar/servicestub/service/App.java new file mode 100644 index 00000000000..3e0a6ff4539 --- /dev/null +++ b/service-stub/src/main/java/com/iluwatar/servicestub/service/App.java @@ -0,0 +1,34 @@ +package com.iluwatar.servicestub.service; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +public class App { + + public static void main(String[] args) throws Exception { + ExecutorService executor = Executors.newSingleThreadExecutor(); + + try { + YahooStockService service = new YahooStockService(); + + Portfolio portfolio = new Portfolio("iluwatar"); + BuyAtCurrentPriceCommand googleCurrentPriceBuy = new BuyAtCurrentPriceCommand(service, portfolio, + new Stock("GOOG", "Google"), 100); + googleCurrentPriceBuy.execute(); + + BuyAtCurrentPriceCommand yahooCurrentPriceBuy = new BuyAtCurrentPriceCommand(service, portfolio, + new Stock("YHOO", "Yahoo"), 100); + yahooCurrentPriceBuy.execute(); + + BuyAtLimitPriceCommand yahooLimitPriceBuy = new BuyAtLimitPriceCommand(service, portfolio, + new Stock("YHOO", "Yahoo"), 100,service.getQuote(new Stock("YHOO", "Yahoo")).getCurrentPrice(), + executor); + + yahooLimitPriceBuy.execute().get(); + + System.out.println(portfolio); + } finally { + executor.shutdownNow(); + } + } +} diff --git a/service-stub/src/main/java/com/iluwatar/servicestub/service/BuyAtCurrentPriceCommand.java b/service-stub/src/main/java/com/iluwatar/servicestub/service/BuyAtCurrentPriceCommand.java new file mode 100644 index 00000000000..18e576383e9 --- /dev/null +++ b/service-stub/src/main/java/com/iluwatar/servicestub/service/BuyAtCurrentPriceCommand.java @@ -0,0 +1,31 @@ +package com.iluwatar.servicestub.service; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Future; + +public class BuyAtCurrentPriceCommand { + + private StockService service; + private Portfolio portfolio; + private Stock stock; + private int quantity; + + public BuyAtCurrentPriceCommand(StockService service, Portfolio portfolio, Stock stock, int quantity) { + this.service = service; + this.portfolio = portfolio; + this.stock = stock; + this.quantity = quantity; + } + + public Future execute() { + try { + StockQuote stockQuote = service.getQuote(stock); + portfolio.add(stock, quantity, stockQuote.getCurrentPrice()); + return CompletableFuture.completedFuture(null); + } catch (Exception e) { + CompletableFuture future = new CompletableFuture<>(); + future.completeExceptionally(e); + return future; + } + } +} diff --git a/service-stub/src/main/java/com/iluwatar/servicestub/service/BuyAtLimitPriceCommand.java b/service-stub/src/main/java/com/iluwatar/servicestub/service/BuyAtLimitPriceCommand.java new file mode 100644 index 00000000000..9d9f6f5ae14 --- /dev/null +++ b/service-stub/src/main/java/com/iluwatar/servicestub/service/BuyAtLimitPriceCommand.java @@ -0,0 +1,51 @@ +package com.iluwatar.servicestub.service; + +import java.math.BigDecimal; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +public class BuyAtLimitPriceCommand { + private StockService service; + private Portfolio portfolio; + private Stock stock; + private int quantity; + private BigDecimal priceLimit; + private Executor executor; + + public BuyAtLimitPriceCommand(StockService service, Portfolio portfolio, Stock stock, + int quantity, BigDecimal priceLimit, + Executor executor) { + this.service = service; + this.portfolio = portfolio; + this.stock = stock; + this.quantity = quantity; + this.priceLimit = priceLimit; + this.executor = executor; + } + + public Future execute() { + CompletableFuture future = new CompletableFuture<>(); + executor.execute(() -> { + try { + while (true) { + StockQuote stockQuote = service.getQuote(stock); + + if (stockQuote.getCurrentPrice().compareTo(priceLimit) <= 0) { + portfolio.add(stock, quantity, stockQuote.getCurrentPrice()); + future.complete(null); + break; + } else { + Thread.sleep(TimeUnit.SECONDS.toMillis(1)); + } + } + + } catch (Exception e) { + future.completeExceptionally(e); + } + + }); + return future; + } +} diff --git a/service-stub/src/main/java/com/iluwatar/servicestub/service/Portfolio.java b/service-stub/src/main/java/com/iluwatar/servicestub/service/Portfolio.java new file mode 100644 index 00000000000..823b9ed53a5 --- /dev/null +++ b/service-stub/src/main/java/com/iluwatar/servicestub/service/Portfolio.java @@ -0,0 +1,44 @@ +package com.iluwatar.servicestub.service; + +import java.math.BigDecimal; +import java.util.HashMap; +import java.util.Map; + +public class Portfolio { + + private final String username; + private final Map stockToStockInformation = new HashMap<>(); + private BigDecimal totalAssets; + + public Portfolio(String username) { + this.username = username; + this.totalAssets = new BigDecimal(0); + } + + public void add(Stock stock, int quantity, BigDecimal price) { + StockInformation stockInformation = stockToStockInformation.get(stock); + if (stockInformation == null) { + stockInformation = new StockInformation(stock, price, quantity); + stockToStockInformation.put(stock, stockInformation); + } else { + stockInformation.add(quantity, price); + } + totalAssets = totalAssets.add(price.multiply(new BigDecimal(quantity))); + } + + @Override + public String toString() { + + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append(String.format("User: %s | Total assets worth: %f%n", username, totalAssets.floatValue())); + for (StockInformation stockInformation : stockToStockInformation.values()) { + stringBuilder.append(stockInformation.toString()).append("\n"); + } + + return stringBuilder.toString(); + } + + public StockInformation get(Stock stock) { + return stockToStockInformation.get(stock); + } +} diff --git a/service-stub/src/main/java/com/iluwatar/servicestub/service/SellCommand.java b/service-stub/src/main/java/com/iluwatar/servicestub/service/SellCommand.java new file mode 100644 index 00000000000..a2d8a7ddd17 --- /dev/null +++ b/service-stub/src/main/java/com/iluwatar/servicestub/service/SellCommand.java @@ -0,0 +1,6 @@ +package com.iluwatar.servicestub.service; + +public class SellCommand { + + +} diff --git a/service-stub/src/main/java/com/iluwatar/servicestub/service/Stock.java b/service-stub/src/main/java/com/iluwatar/servicestub/service/Stock.java new file mode 100644 index 00000000000..4ed2a589fdb --- /dev/null +++ b/service-stub/src/main/java/com/iluwatar/servicestub/service/Stock.java @@ -0,0 +1,48 @@ +package com.iluwatar.servicestub.service; + +public class Stock { + + private final String symbol; + private final String name; + + public Stock(String symbol, String name) { + this.symbol = symbol; + this.name = name; + } + + public String getSymbol() { + return symbol; + } + + public String getName() { + return name; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((symbol == null) ? 0 : symbol.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Stock other = (Stock) obj; + if (symbol == null) { + if (other.symbol != null) + return false; + } else if (!symbol.equals(other.symbol)) + return false; + return true; + } + + + +} diff --git a/service-stub/src/main/java/com/iluwatar/servicestub/service/StockInformation.java b/service-stub/src/main/java/com/iluwatar/servicestub/service/StockInformation.java new file mode 100644 index 00000000000..0a83978ab76 --- /dev/null +++ b/service-stub/src/main/java/com/iluwatar/servicestub/service/StockInformation.java @@ -0,0 +1,39 @@ +package com.iluwatar.servicestub.service; + +import java.math.BigDecimal; + +public class StockInformation { + + private final Stock stock; + private BigDecimal totalStockPrice; + private int quantity; + + + public StockInformation(Stock stock, BigDecimal buyPrice, int quantity) { + this.stock = stock; + this.totalStockPrice = buyPrice.multiply(new BigDecimal(quantity)); + this.quantity = quantity; + } + + public Stock getStock() { + return stock; + } + + public BigDecimal getAverageStockPrice() { + return totalStockPrice.divide(new BigDecimal(quantity)); + } + + public int getQuantity() { + return quantity; + } + + public void add(int quantity, BigDecimal price) { + this.quantity += quantity; + this.totalStockPrice = totalStockPrice.add(price.multiply(new BigDecimal(quantity))); + } + + @Override + public String toString() { + return String.format("Stock: %s | Quantity: %d | Avg.Price: %f", stock.getName(), quantity, getAverageStockPrice().floatValue()); + } +} diff --git a/service-stub/src/main/java/com/iluwatar/servicestub/service/StockQuote.java b/service-stub/src/main/java/com/iluwatar/servicestub/service/StockQuote.java new file mode 100644 index 00000000000..c9e52b50ffd --- /dev/null +++ b/service-stub/src/main/java/com/iluwatar/servicestub/service/StockQuote.java @@ -0,0 +1,23 @@ +package com.iluwatar.servicestub.service; + +import java.math.BigDecimal; + +public class StockQuote { + + private final BigDecimal currentPrice; + + public StockQuote(BigDecimal currentPrice) { + this.currentPrice = currentPrice; + } + + + public BigDecimal getCurrentPrice() { + return currentPrice; + } + + @Override + public String toString() { + return String.format("Current trading price: %f", currentPrice.floatValue()); + } + +} diff --git a/service-stub/src/main/java/com/iluwatar/servicestub/service/StockService.java b/service-stub/src/main/java/com/iluwatar/servicestub/service/StockService.java new file mode 100644 index 00000000000..e1d3596cda3 --- /dev/null +++ b/service-stub/src/main/java/com/iluwatar/servicestub/service/StockService.java @@ -0,0 +1,6 @@ +package com.iluwatar.servicestub.service; + +public interface StockService { + + StockQuote getQuote(Stock stock) throws Exception; +} diff --git a/service-stub/src/main/java/com/iluwatar/servicestub/service/StockServiceStub.java b/service-stub/src/main/java/com/iluwatar/servicestub/service/StockServiceStub.java new file mode 100644 index 00000000000..c852bc751be --- /dev/null +++ b/service-stub/src/main/java/com/iluwatar/servicestub/service/StockServiceStub.java @@ -0,0 +1,20 @@ +package com.iluwatar.servicestub.service; + +import java.math.BigDecimal; + +public class StockServiceStub implements StockService { + private StockQuote quote; + + public StockServiceStub(BigDecimal quotePrice) { + this.quote = new StockQuote(quotePrice); + } + + @Override + public StockQuote getQuote(Stock stock) throws Exception { + return quote; + } + + public void setQuotePrice(BigDecimal quotePrice) { + this.quote = new StockQuote(quotePrice); + } +} diff --git a/service-stub/src/main/java/com/iluwatar/servicestub/service/YahooStockService.java b/service-stub/src/main/java/com/iluwatar/servicestub/service/YahooStockService.java new file mode 100644 index 00000000000..d9a8ba33cc1 --- /dev/null +++ b/service-stub/src/main/java/com/iluwatar/servicestub/service/YahooStockService.java @@ -0,0 +1,14 @@ +package com.iluwatar.servicestub.service; + +import yahoofinance.Stock; +import yahoofinance.YahooFinance; + +public class YahooStockService implements StockService { + + @Override + public StockQuote getQuote(com.iluwatar.servicestub.service.Stock stock) throws Exception { + Stock yahooStock = YahooFinance.get(stock.getSymbol()); + return new StockQuote(yahooStock.getQuote().getPrice()); + } + +} diff --git a/service-stub/src/test/java/com/iluwatar/servicestub/service/BuyAtLimitPriceTest.java b/service-stub/src/test/java/com/iluwatar/servicestub/service/BuyAtLimitPriceTest.java new file mode 100644 index 00000000000..d90217d9048 --- /dev/null +++ b/service-stub/src/test/java/com/iluwatar/servicestub/service/BuyAtLimitPriceTest.java @@ -0,0 +1,67 @@ +package com.iluwatar.servicestub.service; + +import java.math.BigDecimal; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Test; + +public class BuyAtLimitPriceTest { + + + private StockServiceStub stub = new StockServiceStub(new BigDecimal(101)); + private Portfolio portfolio = new Portfolio("iluwatar"); + private Stock stock = new Stock("GOOG", "Google"); + private BuyAtLimitPriceCommand buyAtLimitPriceCommand; + private Future future; + private ExecutorService executor = Executors.newSingleThreadExecutor(); + + @Test + public void test() throws InterruptedException, ExecutionException { + stockIsTradingAt(101); + + whenBuyingAtLimitPriceOf(100); + + tradeDoesNotOccur(); + + afterAWhile(); + + stockIsTradingAt(99); + + tradeOccurs(); + } + + private void afterAWhile() throws InterruptedException { + Thread.sleep(1000); + } + + private void tradeOccurs() throws InterruptedException, ExecutionException { + future.get(); + + Assert.assertEquals(100, portfolio.get(stock).getQuantity()); + } + + private void tradeDoesNotOccur() { + Assert.assertFalse(future.isDone()); + Assert.assertNull(portfolio.get(stock)); + } + + private void whenBuyingAtLimitPriceOf(int limitPrice) { + buyAtLimitPriceCommand = new BuyAtLimitPriceCommand(stub, portfolio, + stock, 100, new BigDecimal(limitPrice), executor); + future = buyAtLimitPriceCommand.execute(); + } + + private void stockIsTradingAt(int currentPrice) { + stub.setQuotePrice(new BigDecimal(currentPrice)); + } + + @After + public void tearDown() { + executor.shutdownNow(); + } +}