Untitled
unknown
java
2 years ago
9.5 kB
4
Indexable
Never
package mwolczecki; /* The idea of this coding challenge, is to implement a service able to subscribe to upstream balance change notifications from the GeneralBalanceService, keep an accumulated running balance for each customer, and send downstream balance update notifications for subscribed customers. Steps: 1. Subscribe to balance changes for all customers by providing a GeneralBalanceUpdateCallback to the GeneralBalanceService instance received on the constructor. 2. Update internal data-structure to hold balances for each customer when the GeneralBalanceUpdateCallback::onBalanceUpdate method is called. 3. If the customer has a registered callback provided via the CustomerBalanceService::subscribe method, call the CustomerBalanceUpdateCallback::onBalanceUpdate method with an instance of CustomerBalanceUpdate. +-----------+ +-------------------------+ +-----------------------+ | Customer | | CustomerBalanceService | | GeneralBalanceService | +-----------+ +-------------------------+ +-----------------------+ | | -------------------------------\ | | |-| ::new(GeneralBalanceService) | | | | |------------------------------| | | | | | | subscribe(GeneralBalanceUpdateCallback) | | |---------------------------------------------------->| | | | | subscribe(CustomerBalanceUpdateCallback) | | |------------------------------------------------------>| | | | | | | | | | | | | -----------\ | | | | customer |-| | | | buy/sell | | | | | crypto | | | | |----------| | | | | | | GeneralBalanceUpdateCallback | | | ::onBalanceUpdate(GeneralBalanceUpdate) | | |<----------------------------------------------------| | | ------------------\ | | |-| update internal | | | | | balances data | | | | | structure | | | | |-----------------| | | | | | CustomerBalanceUpdateCallback | | | ::onBalanceUpdate(CustomerBalanceUpdate) | | |<------------------------------------------------------| | | | | */ import java.util.*; import java.util.stream.Collectors; enum Token { USD, EUR, BTC, ETH; } /** * Message/Event received from the upstream GeneralBalanceService, * for any change in the balance of any token for any customer * Balance Increases if getChange() > 0 * Balance Decreases if getChange() < 0 */ interface GeneralBalanceUpdate { long getCustomer(); Token getToken(); double getChange(); } /** * A callback provided to the upstream GeneralBalanceService, to be called * whenever there is a change in the balance of a customer for any token */ interface GeneralBalanceUpdateCallback { void onBalanceUpdate(GeneralBalanceUpdate update); } /** * Service to subscribe for updates in the balances of all customers * (provided) */ interface GeneralBalanceService { void subscribe(GeneralBalanceUpdateCallback callback); } /** * A callback provided to the CustomerBalanceService, to be called * whenever there is a change in the balance of a token for the subscribed customer */ interface CustomerBalanceUpdateCallback { void onBalanceUpdate(CustomerBalanceUpdate update); } /** * Message/Event sent to the downstream customer balance subscription, whenever * the balance for a token for that customer changes */ class CustomerBalanceUpdate { private final Token token; private double amount; private double change; public CustomerBalanceUpdate(Token token, double amount, double change) { this.token = token; this.amount = amount; this.change = change; } public Token getToken() { return token; } public double getAmount() { return amount; } public double getChange() { return change; } } /** * A Service used by customers to get notifications of changes * to their own specific balances, for each {@link Token} * (implement) */ class CustomerBalanceService implements GeneralBalanceUpdateCallback { private final GeneralBalanceService balanceService; private final Map<Long, CustomerDetails> customers; public CustomerBalanceService(GeneralBalanceService balanceService) { this.balanceService = balanceService; customers = new HashMap<>(); balanceService.subscribe(this::onBalanceUpdate); // TODO: subscribe to GeneralBalanceService notifications } private void updateBalance(long customer, Token token, double change) { // TODO: update in-memory data structure to reflect the received balance change } public void subscribe(long customer, CustomerBalanceUpdateCallback callback) { if (!customers.containsKey(customer)) { Map<Token, Double> collect = Arrays.stream(Token.values()).collect(Collectors.toMap(i -> i, i -> 0d)); customers.put(customer, new CustomerDetails(List.of(callback), collect)); } else { CustomerDetails customerDetails = customers.get(customer); List<CustomerBalanceUpdateCallback> customerBalanceUpdateCallbacks = new ArrayList<>(customerDetails.callbacks); customerBalanceUpdateCallbacks.add(callback); customers.put(customer, new CustomerDetails(customerBalanceUpdateCallbacks, customerDetails.currencyData)); } // TODO: store the callback provided by the customer, so it can be triggered when a change is received } @Override public void onBalanceUpdate(GeneralBalanceUpdate update) { long customer = update.getCustomer(); if (!customers.containsKey(customer)) { Map<Token, Double> collect = Arrays.stream(Token.values()).collect(Collectors.toMap(i -> i, i -> 0d)); customers.put(customer, new CustomerDetails(Collections.emptyList(), collect)); } CustomerDetails customerDetails = customers.get(customer); Map<Token, Double> currencyData = customerDetails.currencyData; currencyData.put(update.getToken(), currencyData.get(update.getToken()) + update.getChange()); CustomerBalanceUpdate customerBalanceUpdate = new CustomerBalanceUpdate(update.getToken(), currencyData.get(update.getToken()), update.getChange()); customerDetails.callbacks.forEach(i -> i.onBalanceUpdate(customerBalanceUpdate)); } record CustomerDetails(List<CustomerBalanceUpdateCallback> callbacks, Map<Token, Double> currencyData) { } }