package examples;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import java.util.*;
public class JavaBeansAreDangerousDontgDoThis {
static class Transaction{
//USES SOME MUTABLES!
int id;
int amount = 0;
Date date = new Date();
String desc = "";
public Transaction(int id, int amount, Date date, String desc) {
this.id = id;
this.amount = amount;
this.date = date;
this.desc = desc;
}
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public int getAmount() {
return amount;
}
public void setAmount(int amount) {
this.amount = amount;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
@Override
public String toString() {
return "Transaction{" +
"amount=" + amount +
", date=" + date +
", desc='" + desc + '\'' +
'}';
}
}
static class Account{
//VULNARABLE BECAUSE IT USES MUTABLES
private Map<Integer, Transaction> transactions = new HashMap<>();
public void addOrUpdateTransaction(Transaction t) {
this.transactions.put(t.id, t);
}
public Collection<Transaction> getTransactions() {
return transactions.values();
}
public String toString(){
return transactions.toString();
}
//..
}
interface AccountChangedListener {
void accountEdited(Account a);
}
static class AccountEditor {
private Account a;
private List<AccountChangedListener> listeners;
public AccountEditor(Account a, List<AccountChangedListener> listeners){
this.a = a;
this.listeners = listeners;
}
public void addTransaction(Transaction t){
a.addOrUpdateTransaction(t);
listeners.forEach(l -> l.accountEdited(a));
}
public void updateTransaction(Transaction t){
a.addOrUpdateTransaction(t);
listeners.forEach(l -> l.accountEdited(a));
}
}
static class PNLReport implements AccountChangedListener{
//VULNARABLE BECAUSE IT USES MUTABLES
private List<Account> accounts;
private int totalFunds = 0;
public PNLReport(List<Account> accounts){
this.accounts = accounts;
computeTotals();
}
public void computeTotals(){
totalFunds = 0;
for(Account a : accounts){
for(Transaction t: a.getTransactions()){
totalFunds += t.getAmount();
}
}
}
public void print(){
System.out.println("Accounts:" + accounts);
System.out.println("Total:" + totalFunds);
}
@Override
public void accountEdited(Account a) {
computeTotals();
}
}
static class BalanceSheet implements AccountChangedListener{
//VULNARABLE BECAUSE IT USES MUTABLES
private List<Account> accounts;
public BalanceSheet(List<Account> a){
this.accounts = a;
}
public void computeTotals(){
//...
}
public void print(){
//..
}
@Override
public void accountEdited(Account a) {
computeTotals();
}
}
public static void main(String[] argc){
//First you initialize everyhing
Account a = new Account();
List<Account> chartOfAccounts = Arrays.asList(new Account[]{a});
PNLReport pnl = new PNLReport(chartOfAccounts);
BalanceSheet balances = new BalanceSheet(chartOfAccounts);
AccountEditor editor = new AccountEditor(a, Arrays.asList(pnl, balances));
editor.addTransaction(new Transaction(1, 100, new Date(), "First Transaction"));
pnl.print();
//Now you edit some transactions. This would already break things if done from another thread
Transaction t2 = new Transaction(2, 100, new Date(), "Second Transaction");
editor.addTransaction(t2);
editor.updateTransaction(new Transaction(1, 50, new Date(), "Modified First Transaction"));
//This worked because listeners were called
pnl.print();
//This will break things even from the same thread because listeners were not called so totals didn't get recalculated
t2.setAmount(1000);
pnl.print();
//This really won't be reliable because mutability makes it thread-unsafe
new Thread(new Runnable() {
@Override
public void run() {
pnl.print();
}
}).start();
}
}