Untitled
unknown
java
3 years ago
9.5 kB
10
Indexable
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) {
}
}Editor is loading...