Splitwise LLD | workat.tech | Review Appreciated

For people asking about question here is the link:
https://workat.tech/machine-coding/editorial/how-to-design-splitwise-machine-coding-ayvnfo1tfst6

App.java

package io.binactivate;

import java.util.ArrayList;
import java.util.List;

import io.binactivate.model.ExpenseGroup;
import io.binactivate.model.User;
import io.binactivate.service.ExpenseManager;

/**
 * Hello world!
 *
 */
public class App 
{
    public static void main( String[] args )
    {
        
        //creating users
        User u1 = new User("u1", "Kundan", "kundan@gmail.com");
        User u2 = new User("u2", "Swaraj", "swaraj@gmail.com");
        User u3 = new User("u3", "Abhinav", "abhinav@gmail.com");
        User u4 = new User("u4", "Anshil", "anshil@gmail.com");
        User u5 = new User("u5", "Saket", "saket@gmail.com");


        // Equal splittype
        ExpenseGroup eg1 = new ExpenseGroup();
        List<User> takers1 = new ArrayList<>();
        takers1.add(u1);
        takers1.add(u2);
        takers1.add(u3);
        takers1.add(u4);
        

        eg1.setTakers(takers1);
        eg1.setGiver(u1);
        eg1.setSplittype("EQUAL");
        eg1.setTransactionAmount(1000);

        
        ExpenseManager expenseManager1 = new ExpenseManager(eg1);
        expenseManager1.updateBalanceForUser();

        printBalance(eg1);


        System.out.println("========================================");

        
        
        // exact splittype
        ExpenseGroup eg3 = new ExpenseGroup();
        List<User> takers3 = new ArrayList<>();
        takers3.add(u2);
        takers3.add(u3);
        eg3.setTakers(takers3);
        
        eg3.setGiver(u1);
        eg3.setSplittype("EXACT");
        eg3.setTransactionAmount(1250);

        ExpenseManager expenseManager3 = new ExpenseManager(eg3);
        expenseManager3.updateBalanceForUser();

        printBalance(eg3);    

        System.out.println("========================================");
        // percent splittype
        
        ExpenseGroup eg2 = new ExpenseGroup();
        List<User> takers2 = new ArrayList<>();
        takers2.add(u1);
        takers2.add(u2);
        takers2.add(u3);
        takers2.add(u4);
        eg2.setTakers(takers2);
        
        eg2.setGiver(u4);
        eg2.setSplittype("PERCENT");
        eg2.setTransactionAmount(1200);

        ExpenseManager expenseManager2 = new ExpenseManager(eg2);
        expenseManager2.updateBalanceForUser();

        printBalance(eg2);    
        

    }


    static void printBalance(ExpenseGroup eg)
    {
        for(User u : eg.getTakers())
        {
            System.out.println("Printing for: " + u.getName() );
            System.out.println(u.getBalances());
        }
    }

    
}

User.java

package io.binactivate.model;

import java.util.HashMap;

public class User{
    
    private String userId;
    private String name;
    private String email;
    private HashMap<User,Double> balances;

    
    public HashMap<User, Double> getBalances() {
        return balances;
    }


    public User(String name, String email) {
        this.name = name;
        this.email = email;
    }


    public void setUserId(String userId) {
        this.userId = userId;
    }


    public String getUserId() {
        return userId;
    }


    public String getName() {
        return name;
    }


    public User(String userId, String name, String email) {
        this.userId = userId;
        this.name = name;
        this.email = email;
        this.balances = new HashMap<>();
    }


    public void setName(String name) {
        this.name = name;
    }


    public String getEmail() {
        return email;
    }


    public void setEmail(String email) {
        this.email = email;
    }


    


    @Override
    public String toString() {
        return "User [name=" + name + "]";
    }


    @Override
    public boolean equals(Object obj) {
        if(obj instanceof User)
        {
            User givenUser = (User)obj;
            return userId.equals(givenUser.getUserId());
        }
        return false;
    }


}

ExpenseGroup.java

package io.binactivate.model;

import java.util.HashMap;
import java.util.List;
import java.util.Scanner;

import io.binactivate.service.EqualSplitType;
import io.binactivate.service.ExactSplitType;
import io.binactivate.service.PercentSplitType;
import io.binactivate.service.SplitType;

public class ExpenseGroup {
    
    private double transactionAmount;
    private User giver;
    private List<User> takers;
    private SplitType splittype;

    private HashMap<User,Double> exactSplit;
    private HashMap<User,Integer> percentSplit;

    public double getTransactionAmount() {
        return transactionAmount;
    }
    public ExpenseGroup() {
        this.exactSplit = new HashMap<>();
        this.percentSplit = new HashMap<>();
    }
    public void setTransactionAmount(double transactionAmount) {
        this.transactionAmount = transactionAmount;
    }
    public User getGiver() {
        return giver;
    }
    public void setGiver(User giver) {
        this.giver = giver;
    }
    public List<User> getTakers() {
        return takers;
    }
    public void setTakers(List<User> takers) {
        this.takers = takers;
    }
    public SplitType getSplittype() {
        return splittype;
    }
    public void setSplittype(String splittype) {

        if(splittype.equals("EXACT"))
        {
            this.splittype = new ExactSplitType(this);
        }else if(splittype.equals("PERCENT"))
        {
            this.splittype = new PercentSplitType(this);
        }else if(splittype.equals("EQUAL")) {
            this.splittype = new EqualSplitType(this);
        }else{
            throw new IllegalArgumentException("Invalid splittype given");
        }

        
        Scanner sc = new Scanner(System.in);
        if(this.splittype instanceof ExactSplitType)
        {
            for (User user : takers) {
                System.out.println("Enter exact amount for " + user.getName());
                double nextDoubleInp = sc.nextDouble();
                exactSplit.put(user,nextDoubleInp );
            }
        }else if(this.splittype instanceof PercentSplitType)
        {
            for (User user : takers) {
                System.out.println("Enter percent for " + user.getName());
                int nextIntInp = sc.nextInt();
                
                percentSplit.put(user,nextIntInp);
            }
        }
        // sc.close();
    }

    public HashMap<User, Double> getExactSplit() {
        return exactSplit;
    }
    public void setExactSplit(HashMap<User, Double> exactSplit) {
        this.exactSplit = exactSplit;
    }
    public HashMap<User, Integer> getPercentSplit() {
        return percentSplit;
    }
    public void setPercentSplit(HashMap<User, Integer> percentSplit) {
        this.percentSplit = percentSplit;
    }
    public int getTotalUserInvolvedforExpense()
    {
        return takers.size();
    }

}

SplitType.java

package io.binactivate.service;

import io.binactivate.exception.IllegalExactSplitGiven;
import io.binactivate.exception.IllegalPercentSplitGiven;
import io.binactivate.model.User;

public interface SplitType {

    double getPart(User user) throws IllegalExactSplitGiven, IllegalPercentSplitGiven ;
}

EqualSplitType.java


package io.binactivate.service;

import io.binactivate.model.ExpenseGroup;
import io.binactivate.model.User;

public class EqualSplitType implements SplitType{

    ExpenseGroup expenseGroup;

    @Override
    public double getPart(User user) {
        
        int totalPeople = expenseGroup.getTotalUserInvolvedforExpense();
        double txAmount = expenseGroup.getTransactionAmount();

        return txAmount/totalPeople;
    }

    public EqualSplitType(ExpenseGroup expenseGroup) {
        this.expenseGroup = expenseGroup;
    }

    
    
}

ExactSplitType.java

package io.binactivate.service;

import java.util.Map.Entry;

import io.binactivate.exception.IllegalExactSplitGiven;
import io.binactivate.model.ExpenseGroup;
import io.binactivate.model.User;

public class ExactSplitType implements SplitType{

    ExpenseGroup expenseGroup;
    @Override
    public double getPart(User user) throws IllegalExactSplitGiven{
        
        if(getExactSplitSum() != expenseGroup.getTransactionAmount())
        {
            throw new IllegalExactSplitGiven("sum of participants not equal to total tx amount");
        }

        return expenseGroup.getExactSplit().get(user);
    }
    public ExactSplitType(ExpenseGroup expenseGroup) {
        this.expenseGroup = expenseGroup;
    }

    private double getExactSplitSum()
    {
        double givenByUser = 0;
        for (Entry<User,Double> entry: expenseGroup.getExactSplit().entrySet()) {
            givenByUser += entry.getValue();
        }
        return givenByUser;
    }
}

PercentSplitType.java

package io.binactivate.service;

import java.util.Map.Entry;

import io.binactivate.exception.IllegalPercentSplitGiven;
import io.binactivate.model.ExpenseGroup;
import io.binactivate.model.User;

public class PercentSplitType implements SplitType{

    ExpenseGroup expenseGroup;
    @Override
    public double getPart(User user) throws IllegalPercentSplitGiven {
        
        if(!isPercentSplitSumEqualTo100())
        {
            throw new IllegalPercentSplitGiven("total sum of percent does not equal to 100");
        }

        return expenseGroup.getTransactionAmount()*expenseGroup.getPercentSplit().get(user)/100;
    }

    public PercentSplitType(ExpenseGroup expenseGroup) {
        this.expenseGroup = expenseGroup;
    }
    
    private boolean isPercentSplitSumEqualTo100()
    {
        int total = 0;
        for (Entry<User,Integer>entry : expenseGroup.getPercentSplit().entrySet()) {
            total += entry.getValue();
        }

        return total == 100;
    }
}

ExpenseManager.java

package io.binactivate.service;

import io.binactivate.exception.IllegalExactSplitGiven;
import io.binactivate.exception.IllegalPercentSplitGiven;
import io.binactivate.model.ExpenseGroup;
import io.binactivate.model.User;

public class ExpenseManager {
    private ExpenseGroup expenseGroup;

    public ExpenseManager(ExpenseGroup expenseGroup) {
        this.expenseGroup = expenseGroup;
    }


    public void updateBalanceForUser() 
    {
        User giver = expenseGroup.getGiver();
        for (User taker : expenseGroup.getTakers()) {
            try {
                // update taker
                double part = expenseGroup.getSplittype().getPart(taker);
                if(!giver.equals(taker))
                {
                    if(taker.getBalances().containsKey(giver))
                    {
                        double prevAmt = taker.getBalances().get(giver);
                        taker.getBalances().put(giver, prevAmt-part);
                    }else{
                        taker.getBalances().put(giver, -1*part);
                    }
                

                    // update giver
                    if(giver.getBalances().containsKey(taker))
                    {
                        double prevAmt = giver.getBalances().get(taker);
                        giver.getBalances().put(taker, prevAmt + part);
                    }else{
                        giver.getBalances().put(taker, part);
                    }
                }
                
                
            } catch (IllegalExactSplitGiven | IllegalPercentSplitGiven e) {
                
                System.out.println(e.getClass().getName() + " : " + e.getMessage());
            }
        }
    }
    
}

IllegalExactSplitGiven.java

package io.binactivate.exception;

public class IllegalExactSplitGiven extends Exception{

    public IllegalExactSplitGiven(String message) {
        super(message);
    }
    
}

IllegalPercentSplitGiven.java

package io.binactivate.exception;

public class IllegalPercentSplitGiven extends Exception{

    public IllegalPercentSplitGiven(String message) {
        super(message);
    }
    
}
Comments (6)