├── .DS_Store ├── .idea ├── .gitignore ├── misc.xml ├── modules.xml └── vcs.xml ├── DesignPatterns ├── .DS_Store ├── .gitignore ├── .idea │ ├── misc.xml │ ├── modules.xml │ ├── vcs.xml │ └── workspace.xml ├── BehavioralPatterns │ ├── ChainOfResponsibilityPattern.java │ ├── CommandPattern.java │ ├── IteratorPattern.java │ ├── MediatorPattern.java │ ├── MementoPattern.java │ ├── ObserverPattern.java │ ├── StatePattern.java │ ├── StrategyPattern.java │ ├── TemplatePattern.java │ └── VisitorPattern.java ├── CreationalPatterns │ ├── .DS_Store │ ├── AbstractFactoryPattern.java │ ├── BuilderPattern.java │ ├── FactoryPattern.java │ ├── PrototypePattern.java │ └── SingletonPattern.java ├── DesignPatterns.iml └── StructuralPatterns │ ├── .DS_Store │ ├── AdapterPattern.java │ ├── BridgePattern.java │ ├── CompositePattern.java │ ├── DecoratorPattern.java │ ├── FacadePattern.java │ ├── FlyweightPattern.java │ └── ProxyPattern.java ├── HLD ├── .DS_Store ├── ChatApp │ ├── ChatApp.md │ ├── ChatService.drawio │ └── ChatService.png ├── ECommerceStore │ ├── ECommerceStore.md │ ├── ecommerce.drawio │ └── ecommerce.drawio.png ├── GoogleDocs │ ├── GoogleDoc.drawio │ ├── GoogleDoc.png │ └── GoogleDocs.md ├── RateLimiter │ └── RateLimiter.md ├── Twitter │ ├── Twitter.md │ ├── TwitterHLD.drawio │ └── TwitterHLD.drawio.png ├── URLShortener │ ├── URLShortener.md │ ├── URLshortener.drawio │ └── URLshortener.png ├── Uber │ ├── Uber.drawio │ ├── Uber.md │ └── Uber.png ├── WebHook │ ├── WebHook.md │ ├── Webhook.drawio │ └── Webhook.png └── Youtube │ ├── Youtube.md │ ├── youtube.drawio │ └── youtube.png ├── LICENSE ├── LLD ├── ATM │ ├── .gitignore │ ├── .idea │ │ ├── .gitignore │ │ ├── gradle.xml │ │ ├── misc.xml │ │ ├── uiDesigner.xml │ │ └── vcs.xml │ ├── README.md │ ├── build.gradle.kts │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── settings.gradle.kts │ └── src │ │ └── main │ │ └── java │ │ └── org │ │ └── anuva04 │ │ ├── Controllers │ │ ├── ATM.java │ │ └── Transaction.java │ │ ├── Enums │ │ ├── Denomination.java │ │ └── PaymentNetwork.java │ │ ├── Main.java │ │ ├── Models │ │ ├── AtmCards │ │ │ ├── AtmCard.java │ │ │ ├── AxisVisaCard.java │ │ │ └── SbiRupayCard.java │ │ ├── Bank.java │ │ ├── CashAvailabilityDataResponseModel.java │ │ ├── CashReserve.java │ │ └── Currency.java │ │ └── Strategies │ │ ├── CashDispenserStrategy.java │ │ └── SimpleCashDispenseStrategy.java ├── ElevatorSystem │ ├── .gitignore │ ├── .idea │ │ ├── .gitignore │ │ ├── encodings.xml │ │ ├── misc.xml │ │ └── vcs.xml │ ├── README.md │ ├── pom.xml │ └── src │ │ └── main │ │ └── java │ │ └── org │ │ └── anuva04 │ │ ├── Controllers │ │ └── ElevatorSystem.java │ │ ├── Enums │ │ ├── ElevatorDirection.java │ │ ├── ElevatorStatus.java │ │ └── ElevatorType.java │ │ ├── Main.java │ │ ├── Models │ │ ├── AccelElevator.java │ │ ├── ClassicElevator.java │ │ ├── Elevator.java │ │ └── ServiceElevator.java │ │ ├── Strategies │ │ ├── ElevatorAllocationStrategy.java │ │ └── SimpleElevatorAllocationStrategy.java │ │ └── Utils │ │ └── Constants.java ├── MeetingScheduler │ ├── .gitignore │ ├── .idea │ │ ├── .gitignore │ │ ├── gradle.xml │ │ ├── misc.xml │ │ ├── uiDesigner.xml │ │ └── vcs.xml │ ├── README.md │ ├── build.gradle.kts │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── settings.gradle.kts │ └── src │ │ └── main │ │ ├── java │ │ └── org │ │ │ └── anuva04 │ │ │ ├── Controllers │ │ │ ├── CommandLineParser.java │ │ │ └── MeetingScheduler.java │ │ │ ├── Main.java │ │ │ ├── Models │ │ │ ├── Meeting.java │ │ │ └── MeetingDb.java │ │ │ └── Utils │ │ │ ├── Constants.java │ │ │ ├── Helper.java │ │ │ └── Parsers │ │ │ ├── AbstractCommandParser.java │ │ │ ├── ChangeMeetingLocationCommandParser.java │ │ │ ├── ChangeMeetingTimeCommandParser.java │ │ │ ├── CreateMeetingCommandParser.java │ │ │ ├── DeleteMeetingCommandParser.java │ │ │ ├── ForwardMeetingCommandParser.java │ │ │ ├── GetMeetingsForUsernameForDateCommandParser.java │ │ │ └── ShowCalendarCommandParser.java │ │ └── resources │ │ ├── Requirements.txt │ │ └── SampleInput.txt └── VendingMachine │ ├── .gitignore │ ├── .idea │ ├── .gitignore │ ├── encodings.xml │ ├── misc.xml │ └── vcs.xml │ ├── README.md │ ├── pom.xml │ └── src │ └── main │ ├── java │ └── org │ │ └── anuva04 │ │ ├── Constants.java │ │ ├── Controllers │ │ ├── AdminOperations.java │ │ ├── UserOperations.java │ │ └── VendingMachine.java │ │ ├── Main.java │ │ └── Models │ │ ├── Item.java │ │ └── Tray.java │ └── resources │ └── Requirements.txt └── README.md /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anuva04/SystemDesign/2338ea19d4f60812e6e37206f7b13a150e4ca78a/.DS_Store -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /DesignPatterns/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anuva04/SystemDesign/2338ea19d4f60812e6e37206f7b13a150e4ca78a/DesignPatterns/.DS_Store -------------------------------------------------------------------------------- /DesignPatterns/.gitignore: -------------------------------------------------------------------------------- 1 | *.class -------------------------------------------------------------------------------- /DesignPatterns/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /DesignPatterns/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /DesignPatterns/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /DesignPatterns/.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 13 | 14 | 16 | 17 | 18 | 21 | 27 | 28 | 29 | 30 | 31 | 1708695433845 32 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /DesignPatterns/BehavioralPatterns/ChainOfResponsibilityPattern.java: -------------------------------------------------------------------------------- 1 | /** 2 | * This pattern lets you pass requests along a chain of processors until it reaches an appropriate processor which can handle it. 3 | * User doesn't need to know about all the available processors. 4 | * Only the head of the chain needs to be known to the users, and each processor can know its succeeding one. 5 | * In this example, user is provided the head of the chain that is Leave Request processor. 6 | * If the request provided by user is a leave request, it is handled immediately, else it is passed down the chain until an appropriate processor is found. 7 | * If no processor can process it, an appropriate message is returned to the user. 8 | * If new processors come in, they can be attached to the chain without any change required on the user side. 9 | */ 10 | 11 | class ChainOfResponsibilityPattern { 12 | public static void main(String[] args) { 13 | RequestProcessor requestProcessor = getRequestProcessor(); 14 | 15 | Request request = new LeaveRequest("123", "test leave request", "01/01/1970", "02/01/1970"); 16 | System.out.println("Got request: " + request.description); 17 | requestProcessor.handle(request); 18 | 19 | Request request1 = new DatabaseAccessRequest("123", "test db request", "test_db", "read"); 20 | System.out.println("Got request: " + request1.description); 21 | requestProcessor.handle(request1); 22 | 23 | Request request2 = new OtherRequest("123", "test other request"); 24 | System.out.println("Got request: " + request2.description); 25 | requestProcessor.handle(request2); 26 | } 27 | 28 | public static RequestProcessor getRequestProcessor() { 29 | LeaveRequestProcessor leaveRequestProcessor = new LeaveRequestProcessor(); 30 | RelocationRequestProcessor relocationRequestProcessor = new RelocationRequestProcessor(); 31 | DatabaseAccessRequestProcessor databaseAccessRequestProcessor = new DatabaseAccessRequestProcessor(); 32 | 33 | leaveRequestProcessor.setNextProcessor(relocationRequestProcessor); 34 | relocationRequestProcessor.setNextProcessor(databaseAccessRequestProcessor); 35 | databaseAccessRequestProcessor.setNextProcessor(null); 36 | 37 | return leaveRequestProcessor; 38 | } 39 | } 40 | 41 | abstract class RequestProcessor { 42 | protected RequestProcessor next; 43 | 44 | public void setNextProcessor(RequestProcessor next) { 45 | this.next = next; 46 | } 47 | 48 | public abstract void handle(Request request); 49 | } 50 | 51 | public class LeaveRequestProcessor extends RequestProcessor { 52 | @Override 53 | public void handle(Request request) { 54 | if(request instanceof LeaveRequest) { 55 | System.out.println("[LeaveRequestProcessor] Processing request..."); 56 | } else { 57 | System.out.println("[LeaveRequestProcessor] Can't process request, handing over to next processor."); 58 | if(this.next != null) { 59 | this.next.handle(request); 60 | } else { 61 | System.out.println("[LeaveRequestProcessor] No available processor to handle the request."); 62 | } 63 | } 64 | } 65 | } 66 | 67 | public class RelocationRequestProcessor extends RequestProcessor { 68 | @Override 69 | public void handle(Request request) { 70 | if(request instanceof RelocationRequest) { 71 | System.out.println("[RelocationRequestProcessor] Processing request..."); 72 | } else { 73 | System.out.println("[RelocationRequestProcessor] Can't process request, handing over to next processor."); 74 | if(this.next != null) { 75 | this.next.handle(request); 76 | } else { 77 | System.out.println("[RelocationRequestProcessor] No available processor to handle the request."); 78 | } 79 | } 80 | } 81 | } 82 | 83 | public class DatabaseAccessRequestProcessor extends RequestProcessor { 84 | @Override 85 | public void handle(Request request) { 86 | if(request instanceof DatabaseAccessRequest) { 87 | System.out.println("[DatabaseAccessRequestProcessor] Processing request..."); 88 | } else { 89 | System.out.println("[DatabaseAccessRequestProcessor] Can't process request, handing over to next processor."); 90 | if(this.next != null) { 91 | this.next.handle(request); 92 | } else { 93 | System.out.println("[DatabaseAccessRequestProcessor] No available processor to handle the request."); 94 | } 95 | } 96 | } 97 | } 98 | 99 | class Request { 100 | String employeeId; 101 | String description; 102 | 103 | public Request(String employeeId, String description) { 104 | this.employeeId = employeeId; 105 | this.description = description; 106 | } 107 | } 108 | 109 | class LeaveRequest extends Request { 110 | public String startdate; 111 | public String endDate; 112 | 113 | public LeaveRequest(String employeeId, String description, String startdate, String endDate) { 114 | super(employeeId, description); 115 | this.startdate = startdate; 116 | this.endDate = endDate; 117 | } 118 | } 119 | 120 | class RelocationRequest extends Request { 121 | public String location; 122 | public String startDate; 123 | 124 | public RelocationRequest(String employeeId, String description, String location, String startDate) { 125 | super(employeeId, description); 126 | this.location = location; 127 | this.startDate = startDate; 128 | } 129 | } 130 | 131 | class DatabaseAccessRequest extends Request { 132 | public String dbName; 133 | public String accessType; 134 | 135 | public DatabaseAccessRequest(String employeeId, String description, String dbName, String accessType) { 136 | super(employeeId, description); 137 | this.dbName = dbName; 138 | this.accessType = accessType; 139 | } 140 | } 141 | 142 | class OtherRequest extends Request { 143 | public OtherRequest(String employeeId, String description) { 144 | super(employeeId, description); 145 | } 146 | } -------------------------------------------------------------------------------- /DesignPatterns/BehavioralPatterns/CommandPattern.java: -------------------------------------------------------------------------------- 1 | /** 2 | * This pattern is used when we want to include all data in an object required to perform an action. 3 | * Hence, the executor need not know the details of how to perform the action. It can simply call the execute method of the object. 4 | * In this example, multiple ArithmeticOperation objects are created and each of them contains all details required to perform the action. 5 | * The executor simply picks up an object and execute it using the operate() method. 6 | * This is useful while building an architecture of message queues with multiple worker threads which can pick up tasks when possible and execute them without needing to know how to perform it. 7 | */ 8 | 9 | import java.util.concurrent.*; 10 | class CommandPattern { 11 | public static void main(String[] args) { 12 | ExecutorService ex = Executors.newFixedThreadPool(3); 13 | 14 | BlockingQueue queue = new LinkedBlockingQueue<>(); 15 | queue.offer(new AdditionOperation(1, 2, 5)); 16 | queue.offer(new AdditionOperation(2, 3, 6)); 17 | queue.offer(new SubtractionOperation(3, 4, 1)); 18 | queue.offer(new MultiplicationOperation(4, 4, 4)); 19 | queue.offer(new MultiplicationOperation(5, 2, 2)); 20 | queue.offer(new DivisionOperation(6, 6, 3)); 21 | 22 | for(int i = 0; i < 3; i++) { 23 | ex.submit(new ArithmeticOperationExecutor(queue)); 24 | } 25 | 26 | ex.shutdown(); 27 | } 28 | } 29 | 30 | abstract class ArithmeticOperation { 31 | protected int operand1; 32 | protected int operand2; 33 | protected int timeTaken; 34 | protected int id; 35 | 36 | public ArithmeticOperation(int id, int operand1, int operand2, int timeTaken) { 37 | this.operand1 = operand1; 38 | this.operand2 = operand2; 39 | this.timeTaken = timeTaken; 40 | this.id = id; 41 | } 42 | 43 | abstract void operate(); 44 | } 45 | 46 | class AdditionOperation extends ArithmeticOperation { 47 | public AdditionOperation(int id, int operand1, int operand2) { 48 | super(id, operand1, operand2, 2); 49 | } 50 | 51 | @Override 52 | public void operate() { 53 | try { 54 | System.out.println("#" + this.id + " Sleeping for " + this.timeTaken + " seconds..."); 55 | Thread.sleep(this.timeTaken); 56 | System.out.println("#" + this.id + " " + (this.operand1 + this.operand2)); 57 | } catch (Exception e) { 58 | System.out.println(e.getMessage()); 59 | System.out.println("#" + this.id + " " + Integer.MAX_VALUE); 60 | } 61 | } 62 | } 63 | 64 | class SubtractionOperation extends ArithmeticOperation { 65 | public SubtractionOperation(int id, int operand1, int operand2) { 66 | super(id, operand1, operand2, 3); 67 | } 68 | 69 | @Override 70 | public void operate() { 71 | try { 72 | System.out.println("#" + this.id + " Sleeping for " + this.timeTaken + " seconds..."); 73 | Thread.sleep(this.timeTaken); 74 | System.out.println("#" + this.id + " " + (this.operand1 - this.operand2)); 75 | } catch (Exception e) { 76 | System.out.println(e.getMessage()); 77 | System.out.println("#" + this.id + " " + Integer.MAX_VALUE); 78 | } 79 | } 80 | } 81 | 82 | class MultiplicationOperation extends ArithmeticOperation { 83 | public MultiplicationOperation(int id, int operand1, int operand2) { 84 | super(id, operand1, operand2, 4); 85 | } 86 | 87 | @Override 88 | public void operate() { 89 | try { 90 | System.out.println("#" + this.id + " Sleeping for " + this.timeTaken + " seconds..."); 91 | Thread.sleep(this.timeTaken); 92 | System.out.println("#" + this.id + " " + (this.operand1 * this.operand2)); 93 | } catch (Exception e) { 94 | System.out.println(e.getMessage()); 95 | System.out.println("#" + this.id + " " + Integer.MAX_VALUE); 96 | } 97 | } 98 | } 99 | 100 | class DivisionOperation extends ArithmeticOperation { 101 | public DivisionOperation(int id, int operand1, int operand2) { 102 | super(id, operand1, operand2, 5); 103 | } 104 | 105 | @Override 106 | public void operate() { 107 | try { 108 | System.out.println("#" + this.id + " Sleeping for " + this.timeTaken + " seconds..."); 109 | Thread.sleep(this.timeTaken); 110 | System.out.println("#" + this.id + " " + (this.operand1 / this.operand2)); 111 | } catch (Exception e) { 112 | System.out.println(e.getMessage()); 113 | System.out.println("#" + this.id + " " + Integer.MAX_VALUE); 114 | } 115 | } 116 | } 117 | 118 | class ArithmeticOperationExecutor implements Runnable { 119 | private final BlockingQueue queue; 120 | 121 | public ArithmeticOperationExecutor(BlockingQueue queue) { 122 | this.queue = queue; 123 | } 124 | 125 | @Override 126 | public void run() { 127 | while(!Thread.currentThread().isInterrupted()) { 128 | try { 129 | ArithmeticOperation operation = queue.poll(); 130 | if(operation == null) break; 131 | operation.operate(); 132 | } catch(Exception e) { 133 | Thread.currentThread().interrupt(); 134 | } 135 | } 136 | } 137 | } -------------------------------------------------------------------------------- /DesignPatterns/BehavioralPatterns/IteratorPattern.java: -------------------------------------------------------------------------------- 1 | /** 2 | * This pattern is used to abstract traversal logic of a collection. 3 | * By doing that, we take care of separation of concerns. The collection itself stores information about the data and their connectivity only. 4 | * And the iterator class takes care of traversal. 5 | * In this example, a TreeNode is an element of a collection. It is only concerned with value and left and right children 6 | * Traversal is taken care of by BfsTree class which implements TreeIterable interface and implements the iteration logic. 7 | */ 8 | 9 | import java.util.LinkedList; 10 | import java.util.Queue; 11 | class IteratorPattern { 12 | public static void main(String[] args) { 13 | TreeNode root = new TreeNode(1); 14 | root.left = new TreeNode(2); 15 | root.right = new TreeNode(3); 16 | root.left.left = new TreeNode(4); 17 | root.left.right = new TreeNode(5); 18 | root.right.right = new TreeNode(6); 19 | root.left.left.left = new TreeNode(8); 20 | 21 | BfsTree tree = new BfsTree(root); 22 | 23 | while(tree.hasNext()) { 24 | System.out.println(tree.next().value); 25 | } 26 | } 27 | } 28 | 29 | interface TreeIterable { 30 | public boolean hasNext(); 31 | public T next(); 32 | } 33 | 34 | public class TreeNode { 35 | public int value; 36 | public TreeNode left; 37 | public TreeNode right; 38 | 39 | public TreeNode(int value) { 40 | this.value = value; 41 | left = right = null; 42 | } 43 | } 44 | 45 | public class BfsTree implements TreeIterable { 46 | Queue queue; 47 | 48 | public BfsTree(TreeNode root) { 49 | queue = new LinkedList<>(); 50 | queue.offer(root); 51 | } 52 | 53 | @Override 54 | public boolean hasNext() { 55 | if(queue.peek() == null) return false; 56 | return true; 57 | } 58 | 59 | @Override 60 | public TreeNode next() { 61 | if(queue.peek() == null) return null; 62 | if(queue.peek().left != null) queue.offer(queue.peek().left); 63 | if(queue.peek().right != null) queue.offer(queue.peek().right); 64 | return queue.poll(); 65 | } 66 | } -------------------------------------------------------------------------------- /DesignPatterns/BehavioralPatterns/MediatorPattern.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Mediator pattern is used when we want to reduce chaotic communication between various objects in a system. 3 | * In such a situation, we want all communication to be handled by a central object called the mediator; no object communicates directly to each other. 4 | * This helps remove dependencies and enables reuse of objects. 5 | * In this example, the submit button and loginOrRegister checkbox don't communicate with each other, and neither do they communicate with the title. 6 | * All state changes of any object are notified to the mediator which in turn changes states of other objects as required. 7 | * By doing this, we didn't have to write code of updating title in loginOrRegister checkbox, which means this checkbox can be reused somewhere else which doesn't need title related logic. 8 | */ 9 | 10 | class MediatorPattern { 11 | public static void main(String[] args) { 12 | LoginForm form = new LoginForm(); 13 | form.loginOrRegister.check(); 14 | form.submit.click(); 15 | form.loginOrRegister.check(); 16 | form.submit.click(); 17 | } 18 | } 19 | 20 | interface Mediator { 21 | public void notify(Component component, Event event); 22 | } 23 | 24 | class LoginForm implements Mediator { 25 | public String title; 26 | public Checkbox loginOrRegister; 27 | public Textbox loginUsername, loginPassword; 28 | public Textbox registerUsername, registerPassword; 29 | public Button submit, cancel; 30 | 31 | public LoginForm() { 32 | loginOrRegister = new Checkbox(this); 33 | loginUsername = new Textbox(this); 34 | loginPassword = new Textbox(this); 35 | registerUsername = new Textbox(this); 36 | registerPassword = new Textbox(this); 37 | submit = new Button(this); 38 | cancel = new Button(this); 39 | } 40 | 41 | @Override 42 | public void notify(Component component, Event event) { 43 | if(component == loginOrRegister && event == Event.CHECK) { 44 | if(loginOrRegister.checked) title = "Login"; 45 | else title = "Register"; 46 | System.out.println("Title: " + title); 47 | } 48 | if(component == submit && event == Event.CLICK) { 49 | if(loginOrRegister.checked) { 50 | System.out.println("Logging in..."); 51 | } else System.out.println("Registering..."); 52 | } 53 | } 54 | } 55 | 56 | class Component { 57 | protected Mediator mediator; 58 | 59 | public Component(Mediator mediator) { 60 | this.mediator = mediator; 61 | } 62 | 63 | public void click() { 64 | mediator.notify(this, Event.CLICK); 65 | } 66 | 67 | public void keypress() { 68 | mediator.notify(this, Event.KEYPRESS); 69 | } 70 | } 71 | 72 | class Button extends Component { 73 | public Button(Mediator mediator) { 74 | super(mediator); 75 | } 76 | } 77 | 78 | class Checkbox extends Component { 79 | public boolean checked = false; 80 | 81 | public Checkbox(Mediator mediator) { 82 | super(mediator); 83 | } 84 | public void check() { 85 | if(checked) checked = false; 86 | else checked = true; 87 | mediator.notify(this, Event.CHECK); 88 | } 89 | } 90 | 91 | class Textbox extends Component { 92 | public Textbox(Mediator mediator) { 93 | super(mediator); 94 | } 95 | 96 | public String input; 97 | } 98 | 99 | enum Event { 100 | CLICK, 101 | KEYPRESS, 102 | CHECK 103 | } -------------------------------------------------------------------------------- /DesignPatterns/BehavioralPatterns/MementoPattern.java: -------------------------------------------------------------------------------- 1 | /** 2 | * This pattern is used when we want to store snapshot of an object's state in different times. 3 | * Instead of having a separate class manage this externally, it is better to have an utility within the concerned class itself to create a snapshot. 4 | * This promotes seperation of concerns. 5 | * Also, we need a caretaker class which maintains all mementos/snapshots of the object. 6 | * In this example, we are taking snapshots of the TextBox class using Memento class. 7 | * Editor class is storing a list of all mementos and asks the TextBox class to restore to a certain memento based on user input (undo/redo). 8 | */ 9 | 10 | import java.util.*; 11 | class MementoPattern { 12 | public static void main(String[] args) { 13 | Editor editor = new Editor(); 14 | editor.write("Hi"); 15 | editor.save(); 16 | editor.write(" I am"); 17 | editor.save(); 18 | editor.write(" Anuva"); 19 | editor.save(); 20 | editor.undo(); 21 | editor.undo(); 22 | editor.undo(); 23 | editor.redo(); 24 | editor.redo(); 25 | editor.redo(); 26 | editor.redo(); 27 | } 28 | } 29 | 30 | class Editor { 31 | private List mementoList; 32 | private int pointer; 33 | private TextBox textBox; 34 | 35 | public Editor() { 36 | mementoList = new ArrayList<>(); 37 | mementoList.add(new Memento("")); 38 | pointer = 0; 39 | textBox = new TextBox(); 40 | } 41 | 42 | public void write(String s) { 43 | textBox.addText(s); 44 | System.out.println(textBox.showText()); 45 | } 46 | 47 | public void save() { 48 | mementoList.add(textBox.save()); 49 | pointer++; 50 | System.out.println("Saved"); 51 | } 52 | 53 | public void undo() { 54 | if(pointer > 0) pointer--; 55 | textBox.restore(mementoList.get(pointer)); 56 | System.out.println(textBox.showText()); 57 | } 58 | 59 | public void redo() { 60 | if(pointer < mementoList.size() - 1) pointer++; 61 | textBox.restore(mementoList.get(pointer)); 62 | System.out.println(textBox.showText()); 63 | } 64 | } 65 | 66 | class TextBox { 67 | private StringBuilder text; 68 | 69 | public TextBox() { 70 | this.text = new StringBuilder(); 71 | } 72 | 73 | public void addText(String s) { 74 | text.append(s); 75 | } 76 | 77 | public String showText() { 78 | return text.toString(); 79 | } 80 | 81 | public Memento save() { 82 | return new Memento(text.toString()); 83 | } 84 | 85 | public void restore(Memento m) { 86 | this.text = new StringBuilder(m.getText()); 87 | } 88 | } 89 | 90 | class Memento { 91 | private String text; 92 | public Memento(String text) { 93 | this.text = text; 94 | } 95 | 96 | public String getText() { 97 | return this.text; 98 | } 99 | } -------------------------------------------------------------------------------- /DesignPatterns/BehavioralPatterns/ObserverPattern.java: -------------------------------------------------------------------------------- 1 | /** 2 | * This pattern is used when multiple objects needs to be updated (called subscribers) when a certain change occurs in an object's state (called observable). 3 | * Instead of updating all subscribers of the change, or letting subscribers poll the publisher continuously, 4 | * the subscribers can subscribe to a system (called publisher) which will notify them whenever the observable's state changes. 5 | * In this example, channels are the observables. 6 | * YoutubeNotification class is the publisher which stores a list of all Subscribers for each channel. 7 | * User class implements Subscriber so that its notify() method can be used to notify it. 8 | * Whenever a new video is added to the channel, publisher notifies all subscribers. 9 | */ 10 | 11 | import java.util.*; 12 | class ObserverPattern { 13 | public static void main(String[] args) { 14 | YoutubeNotification ytNotification = new YoutubeNotification(); 15 | User user1 = new User("user1"); 16 | User user2 = new User("user2"); 17 | ytNotification.addSubscriber("channel1", user1); 18 | ytNotification.addSubscriber("channel1", user2); 19 | 20 | User user3 = new User("user3"); 21 | ytNotification.addSubscriber("channel2", user3); 22 | 23 | ytNotification.addVideo("channel1"); 24 | ytNotification.addVideo("channel2"); 25 | 26 | ytNotification.removeSubscriber("channel1", user1); 27 | ytNotification.addVideo("channel1"); 28 | } 29 | } 30 | 31 | class YoutubeNotification { 32 | private Map> channelSubsMap; 33 | 34 | public YoutubeNotification() { 35 | this.channelSubsMap = new HashMap<>(); 36 | } 37 | public void addSubscriber(String channel, Subscriber subscriber) { 38 | if(!channelSubsMap.containsKey(channel)) { 39 | channelSubsMap.put(channel, new ArrayList<>()); 40 | } 41 | channelSubsMap.get(channel).add(subscriber); 42 | } 43 | 44 | public void removeSubscriber(String channel, Subscriber subscriber) { 45 | channelSubsMap.get(channel).remove(subscriber); 46 | } 47 | 48 | public void addVideo(String channel) { 49 | for(Subscriber s : channelSubsMap.get(channel)) s.notify(channel); 50 | } 51 | } 52 | 53 | interface Subscriber { 54 | public void notify(String channel); 55 | } 56 | 57 | class User implements Subscriber { 58 | public String name; 59 | 60 | public User(String name) { 61 | this.name = name; 62 | } 63 | 64 | @Override 65 | public void notify(String channel) { 66 | System.out.println(this.name + ", " + channel + " has posted a new video!"); 67 | } 68 | } -------------------------------------------------------------------------------- /DesignPatterns/BehavioralPatterns/StatePattern.java: -------------------------------------------------------------------------------- 1 | /** 2 | * This pattern is used when we need to change an object's behavior when its state changes. 3 | * Instead of accomplising this by maintaining a state variable and adding a lot of conditional statements, 4 | * we can store a different class instance (called state) inside our concerned class. 5 | * All information and logic required for object's behavior will be stored in the state object. 6 | * In this example, state stores the logic for phone ring/vibration/silence. 7 | * PhoneRinger stores an instance of state. 8 | * Whenever state changes, the state object is also changed inside PhoneRinger. 9 | */ 10 | class StatePattern { 11 | public static void main(String[] args) { 12 | PhoneRinger ringer = new PhoneRinger("arial"); 13 | ringer.ring(); 14 | 15 | ringer.changeState(new SilentState(ringer)); 16 | ringer.ring(); 17 | 18 | ringer.changeState(new VibrationState(ringer)); 19 | ringer.ring(); 20 | } 21 | } 22 | 23 | class PhoneRinger { 24 | private State state; 25 | public String ringtone; 26 | 27 | public PhoneRinger(String ringtone) { 28 | state = new RingState(this); 29 | this.ringtone = ringtone; 30 | } 31 | 32 | public void changeState(State state) { 33 | this.state = state; 34 | } 35 | 36 | public void ring() { 37 | this.state.ring(); 38 | } 39 | } 40 | 41 | abstract class State { 42 | protected PhoneRinger phoneRinger; 43 | 44 | public State(PhoneRinger phoneRinger) { 45 | this.phoneRinger = phoneRinger; 46 | } 47 | 48 | abstract void ring(); 49 | } 50 | 51 | class RingState extends State { 52 | public RingState(PhoneRinger phoneRinger) { 53 | super(phoneRinger); 54 | } 55 | @Override 56 | public void ring() { 57 | System.out.println("Received call: Playing ringtone " + this.phoneRinger.ringtone); 58 | } 59 | } 60 | 61 | class VibrationState extends State { 62 | public VibrationState(PhoneRinger phoneRinger) { 63 | super(phoneRinger); 64 | } 65 | @Override 66 | public void ring() { 67 | System.out.println("Received call: Phone is vibrating"); 68 | } 69 | } 70 | 71 | class SilentState extends State { 72 | public SilentState(PhoneRinger phoneRinger) { 73 | super(phoneRinger); 74 | } 75 | @Override 76 | public void ring() { 77 | System.out.println("Received call: Phone is silent"); 78 | } 79 | } -------------------------------------------------------------------------------- /DesignPatterns/BehavioralPatterns/StrategyPattern.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Strategy pattern is used when we need to change the behavior of an algorithm at runtime. 3 | * We create an interface with a method which is implemented by all concrete classes. 4 | * This method contains the logic of the algorithm and can be implemented differently in each class. 5 | * The caller class stores an instance of this interface which can be set to required class at runtime. 6 | * In this example, LoadBalancer class contains an instance of Strategy which is implemented by RoundRobin and Random strategy. 7 | * setStrategy method of the LoadBalancer class is used to set/modify the required concrete class at runtime. 8 | */ 9 | 10 | import java.util.*; 11 | class StrategyPattern { 12 | public static void main(String[] args) { 13 | LoadBalancer lb = new LoadBalancer(100); 14 | lb.setStrategy(new RoundRobinStrategy()); 15 | 16 | lb.assignTask(1); 17 | lb.assignTask(2); 18 | lb.assignTask(3); 19 | lb.assignTask(4); 20 | 21 | lb.setStrategy(new RandomStrategy()); 22 | 23 | lb.assignTask(5); 24 | lb.assignTask(6); 25 | lb.assignTask(7); 26 | lb.assignTask(8); 27 | } 28 | } 29 | 30 | class LoadBalancer { 31 | private Strategy strategy; 32 | public List> instances; 33 | 34 | public LoadBalancer(int numInstances) { 35 | instances = new ArrayList<>(); 36 | for(int i=0; i()); 38 | } 39 | } 40 | 41 | public void setStrategy(Strategy strategy) { 42 | this.strategy = strategy; 43 | } 44 | 45 | public void assignTask(Integer task) { 46 | strategy.assignTask(this, task); 47 | } 48 | } 49 | 50 | interface Strategy { 51 | public void assignTask(LoadBalancer lb, Integer task); 52 | } 53 | 54 | class RoundRobinStrategy implements Strategy { 55 | int index = 0; 56 | @Override 57 | public void assignTask(LoadBalancer lb, Integer task) { 58 | lb.instances.get(index).add(task); 59 | System.out.println("Task " + task + " assigned to instance " + index + " using round robin strategy"); 60 | index = (index + 1)%lb.instances.size(); 61 | } 62 | } 63 | 64 | class RandomStrategy implements Strategy { 65 | Random random = new Random(); 66 | @Override 67 | public void assignTask(LoadBalancer lb, Integer task) { 68 | int index = random.nextInt(lb.instances.size()); 69 | System.out.println("Task " + task + " assigned to instance " + index + " using round robin strategy"); 70 | } 71 | } -------------------------------------------------------------------------------- /DesignPatterns/BehavioralPatterns/TemplatePattern.java: -------------------------------------------------------------------------------- 1 | /** 2 | * This pattern is used when we have multiple code workflows with almost similar logic and only minor differences. 3 | * Instead of writing redundant logic for each workflow, we can segragate the code into multiple methods. 4 | * Code which is similar for all workflows can be implemented in base class, and dissimilar code can be implemented in child classes. 5 | * In this example, in a CI/CD pipeline, stages code checkout and compliance check logic are same for all pipelines so they are implemented in the abstract class. 6 | * Build, Test and Deploy logic are different so they are implemented in the extended classes. 7 | */ 8 | 9 | class TemplatePattern { 10 | public static void main(String[] args) { 11 | FunctionAppPipeline functionAppPipeline = new FunctionAppPipeline(); 12 | functionAppPipeline.runPipeline(); 13 | 14 | WebappPipeline webappPipeline = new WebappPipeline(); 15 | webappPipeline.runPipeline(); 16 | } 17 | } 18 | 19 | abstract class CICDPipeline { 20 | public void runPipeline() { 21 | checkout(); 22 | build(); 23 | runTests(); 24 | runComplianceChecks(); 25 | deploy(); 26 | } 27 | protected void checkout() { 28 | System.out.println("Checking out code..."); 29 | System.out.println("Checked out code."); 30 | } 31 | 32 | protected abstract void build(); 33 | protected abstract void runTests(); 34 | 35 | protected void runComplianceChecks() { 36 | System.out.println("Running compliance checks..."); 37 | System.out.println("Compliance checked cleared."); 38 | } 39 | 40 | protected abstract void deploy(); 41 | } 42 | 43 | class FunctionAppPipeline extends CICDPipeline { 44 | @Override 45 | protected void build() { 46 | System.out.println("Building functionapp code..."); 47 | System.out.println("Functionapp code built."); 48 | } 49 | 50 | @Override 51 | protected void runTests() { 52 | System.out.println("Running functionapp tests..."); 53 | System.out.println("Tests cleared."); 54 | } 55 | 56 | @Override 57 | protected void deploy() { 58 | System.out.println("Deploying code to functionapp slot..."); 59 | System.out.println("Deployed code to functionapp slot."); 60 | } 61 | } 62 | 63 | class WebappPipeline extends CICDPipeline { 64 | @Override 65 | protected void build() { 66 | System.out.println("Building webapp code..."); 67 | System.out.println("Webapp code built."); 68 | } 69 | 70 | @Override 71 | protected void runTests() { 72 | System.out.println("Running webapp tests..."); 73 | System.out.println("Tests cleared."); 74 | } 75 | 76 | @Override 77 | protected void deploy() { 78 | System.out.println("Deploying code to webapp slot..."); 79 | System.out.println("Deployed code to webapp slot."); 80 | } 81 | } -------------------------------------------------------------------------------- /DesignPatterns/BehavioralPatterns/VisitorPattern.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Visitor pattern is used in cases we need to add new methods for a group of classes but we don't want to make major changes in the class's code. 3 | * In that case, the new methods are added in a separate class called visitor. 4 | * The visitor visits the concerned class using its accept method. 5 | * So, honestly, we do need to add this accept() method in classes (which is basically a code change) but it is very minimal and risk-free. 6 | * In this example, the different shapes implement accept() method to allow a visitor to execute its visit() method on the shape. 7 | * The visitor class has methods tailored for different types of shapes, and are leveraged using method overloading. 8 | */ 9 | 10 | import java.util.*; 11 | class VisitorPattern { 12 | public static void main(String[] args) { 13 | List shapes = new ArrayList<>(); 14 | shapes.add(new Circle(2)); 15 | shapes.add(new Circle(3)); 16 | shapes.add(new Square(4)); 17 | shapes.add(new Rectangle(2, 3)); 18 | 19 | ShapeVisitor visitor = new ShapeVisitor(); 20 | 21 | for(Shape shape : shapes) { 22 | shape.accept(visitor); 23 | } 24 | } 25 | } 26 | 27 | interface Shape { 28 | public void accept(ShapeVisitor v); 29 | } 30 | 31 | class Circle implements Shape { 32 | public int radius; 33 | 34 | public Circle(int radius) { 35 | this.radius = radius; 36 | } 37 | 38 | @Override 39 | public void accept(ShapeVisitor v) { 40 | v.visit(this); 41 | } 42 | } 43 | 44 | class Square implements Shape { 45 | public int length; 46 | 47 | public Square(int length) { 48 | this.length = length; 49 | } 50 | 51 | @Override 52 | public void accept(ShapeVisitor v) { 53 | v.visit(this); 54 | } 55 | } 56 | 57 | class Rectangle implements Shape { 58 | public int length, width; 59 | 60 | public Rectangle(int length, int width) { 61 | this.length = length; 62 | this.width = width; 63 | } 64 | 65 | @Override 66 | public void accept(ShapeVisitor v) { 67 | v.visit(this); 68 | } 69 | } 70 | 71 | class ShapeVisitor { 72 | public void visit(Circle circle) { 73 | double perimeter = 2 * 3.14 * circle.radius; 74 | double area = 3.14 * circle.radius * circle.radius; 75 | System.out.println("[Circle] Radius: " + circle.radius + ", Perimeter: " + perimeter + ", Area: " + area); 76 | } 77 | 78 | public void visit(Square square) { 79 | int perimeter = 4 * square.length; 80 | int area = square.length * square.length; 81 | System.out.println("[Square] Length: " + square.length + ", Perimeter: " + perimeter + ", Area: " + area); 82 | } 83 | 84 | public void visit(Rectangle rectangle) { 85 | int perimeter = 2 * (rectangle.length + rectangle.width); 86 | int area = rectangle.length * rectangle.width; 87 | System.out.println("[Rectangle] Length: " + rectangle.length + ", Width: " + rectangle.width + ", Perimeter: " + perimeter + ", Area: " + area); 88 | } 89 | } -------------------------------------------------------------------------------- /DesignPatterns/CreationalPatterns/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anuva04/SystemDesign/2338ea19d4f60812e6e37206f7b13a150e4ca78a/DesignPatterns/CreationalPatterns/.DS_Store -------------------------------------------------------------------------------- /DesignPatterns/CreationalPatterns/AbstractFactoryPattern.java: -------------------------------------------------------------------------------- 1 | /** 2 | Abstract factory pattern provides another level of abstraction to factory pattern. 3 | User provides required params to abstract factory which in turn creates a concrete factory which ultimately creates required objects. 4 | Type of factory to be created is determined at runtime based on user parameters (in this case, the OS on which this program runs). 5 | 6 | In this example, user provides length, width and mode for creating a GUI to an abstract factory. 7 | Abstract factory determines the OS on which this programs runs and creates a corresponding concrete factory. 8 | The concrete factory creates GUI using user provided arguments. 9 | **/ 10 | 11 | import java.util.*; 12 | public class AbstractFactoryPattern { 13 | public static void main(String[] args) { 14 | Scanner sc = new Scanner(System.in); 15 | System.out.println("Enter mode: "); 16 | String userMode = sc.next(); 17 | Mode mode; 18 | if (userMode.equalsIgnoreCase("dark")) mode = Mode.DARK; 19 | else mode = Mode.LIGHT; 20 | System.out.println("Enter length: "); 21 | int length = sc.nextInt(); 22 | System.out.println("Enter width: "); 23 | int width = sc.nextInt(); 24 | 25 | System.out.println(GUIFactory.buildGUI(mode, length, width)); 26 | } 27 | } 28 | 29 | abstract class GUI { 30 | int length; 31 | int width; 32 | Mode mode; 33 | OS os; 34 | 35 | GUI(int length, int width, Mode mode, OS os) { 36 | this.length = length; 37 | this.width = width; 38 | this.mode = mode; 39 | this.os = os; 40 | } 41 | 42 | @Override 43 | public String toString() { 44 | return "Length: " + length + " width: " + width + " mode: " + mode + " OS: " + os; 45 | } 46 | } 47 | 48 | class DarkGui extends GUI { 49 | DarkGui(OS os, int length, int width) { 50 | super(length, width, Mode.DARK, os); 51 | } 52 | } 53 | 54 | class LightGui extends GUI { 55 | LightGui(OS os, int length, int width) { 56 | super(length, width, Mode.LIGHT, os); 57 | } 58 | } 59 | 60 | class WindowsGuiFactory { 61 | public static GUI buildGUI (Mode mode, int length, int width) { 62 | GUI gui = null; 63 | switch (mode) { 64 | case DARK: 65 | gui = new DarkGui(OS.WINDOWS, length, width); 66 | break; 67 | case LIGHT: 68 | gui = new LightGui(OS.WINDOWS, length, width); 69 | break; 70 | default: 71 | break; 72 | } 73 | return gui; 74 | } 75 | } 76 | 77 | class MacGuiFactory { 78 | public static GUI buildGUI (Mode mode, int length, int width) { 79 | GUI gui = null; 80 | switch (mode) { 81 | case DARK: 82 | gui = new DarkGui(OS.MAC_OS, length, width); 83 | break; 84 | case LIGHT: 85 | gui = new LightGui(OS.MAC_OS, length, width); 86 | break; 87 | default: 88 | break; 89 | } 90 | return gui; 91 | } 92 | } 93 | 94 | class LinuxGuiFactory { 95 | public static GUI buildGUI (Mode mode, int length, int width) { 96 | GUI gui = null; 97 | switch (mode) { 98 | case DARK: 99 | gui = new DarkGui(OS.LINUX, length, width); 100 | break; 101 | case LIGHT: 102 | gui = new LightGui(OS.LINUX, length, width); 103 | break; 104 | default: 105 | break; 106 | } 107 | return gui; 108 | } 109 | } 110 | 111 | class GUIFactory { 112 | public static GUI buildGUI(Mode mode, int length, int width) { 113 | GUI gui = null; 114 | OS os = null; 115 | 116 | String systemOs = System.getProperty("os.name"); 117 | if(systemOs.toLowerCase().contains("windows")) os = OS.WINDOWS; 118 | else if(systemOs.toLowerCase().contains("mac")) os = OS.MAC_OS; 119 | else if(systemOs.toLowerCase().contains("ubuntu")) os = OS.LINUX; 120 | 121 | 122 | switch (os) { 123 | case WINDOWS: 124 | gui = WindowsGuiFactory.buildGUI(mode, length, width); 125 | break; 126 | case MAC_OS: 127 | gui = MacGuiFactory.buildGUI(mode, length, width); 128 | break; 129 | case LINUX: 130 | gui = LinuxGuiFactory.buildGUI(mode, length, width); 131 | break; 132 | default: 133 | break; 134 | } 135 | return gui; 136 | } 137 | } 138 | 139 | enum Mode { 140 | DARK, LIGHT; 141 | } 142 | 143 | enum OS { 144 | WINDOWS, MAC_OS, LINUX 145 | } -------------------------------------------------------------------------------- /DesignPatterns/CreationalPatterns/BuilderPattern.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Builder pattern is used in cases where object creation is a complex process 3 | * Complexity arises when a class has several fields and not all of them are necessary during object creation 4 | * This pattern provides separate methods for setting each of the fields 5 | * Once the final build() method is called, the object is created and thereafter it is immutable. 6 | */ 7 | 8 | public class BuilderPattern { 9 | public static void main(String[] args){ 10 | System.out.println("Preparing medium Caesar salad..."); 11 | Salad caesarSalad = new Salad.Builder(Size.MEDIUM) 12 | .addLettuce() 13 | .addBrocolli() 14 | .addJalapeno() 15 | .addTomato() 16 | .addDressing(Dressing.THOUSAND_ISLAND) 17 | .build(); 18 | System.out.println(caesarSalad); 19 | 20 | System.out.println("Preparing large Russian salad..."); 21 | Salad russianSalad = new Salad.Builder(Size.LARGE) 22 | .addLettuce() 23 | .addBrocolli() 24 | .addTomato() 25 | .addMushroom() 26 | .addDressing(Dressing.MAYO) 27 | .addJalapeno() 28 | .build(); 29 | System.out.println(russianSalad); 30 | } 31 | } 32 | 33 | class Salad { 34 | private Size size; 35 | private boolean lettuce; 36 | private boolean brocolli; 37 | private boolean tomato; 38 | private boolean jalapeno; 39 | private boolean mushroom; 40 | private Dressing dressing; 41 | 42 | private int price; 43 | 44 | public static class Builder { 45 | private Size size; 46 | private boolean lettuce = false; 47 | private boolean brocolli = false; 48 | private boolean tomato = false; 49 | private boolean jalapeno = false; 50 | private boolean mushroom = false; 51 | private Dressing dressing = null; 52 | 53 | private int price = 0; 54 | 55 | public Builder(Size size) { 56 | this.size = size; 57 | 58 | switch(size) { 59 | case SMALL: 60 | price += 20; 61 | break; 62 | case MEDIUM: 63 | price += 30; 64 | break; 65 | case LARGE: 66 | price += 40; 67 | break; 68 | default: 69 | break; 70 | } 71 | } 72 | 73 | public Builder addLettuce() { 74 | lettuce = true; 75 | price += 5; 76 | return this; 77 | } 78 | 79 | public Builder addBrocolli() { 80 | brocolli = true; 81 | price += 15; 82 | return this; 83 | } 84 | 85 | public Builder addTomato() { 86 | tomato = true; 87 | price += 5; 88 | return this; 89 | } 90 | 91 | public Builder addJalapeno() { 92 | jalapeno = true; 93 | price += 7; 94 | return this; 95 | } 96 | 97 | public Builder addMushroom() { 98 | mushroom = true; 99 | price += 10; 100 | return this; 101 | } 102 | 103 | public Builder addDressing(Dressing dressing) { 104 | this.dressing = dressing; 105 | price += 12; 106 | return this; 107 | } 108 | 109 | public Salad build() { 110 | return new Salad(this); 111 | } 112 | } 113 | private Salad(Builder builder) { 114 | size = builder.size; 115 | lettuce = builder.lettuce; 116 | brocolli = builder.brocolli; 117 | tomato = builder.tomato; 118 | jalapeno = builder.jalapeno; 119 | mushroom = builder.mushroom; 120 | dressing = builder.dressing; 121 | 122 | price = builder.price; 123 | } 124 | 125 | @Override 126 | public String toString() { 127 | String out = "Here's your salad with: "; 128 | if(lettuce) out += "lettuce, "; 129 | if(brocolli) out += "brocolli, "; 130 | if(tomato) out += "tomato, "; 131 | if(jalapeno) out += "jalapeno, "; 132 | if(mushroom) out += "mushroom, "; 133 | if(dressing != null) out += (dressing); 134 | 135 | out += ("\nPrice: " + price); 136 | 137 | return out; 138 | } 139 | } 140 | 141 | enum Size { 142 | SMALL, MEDIUM, LARGE 143 | } 144 | 145 | enum Dressing { 146 | HONEY_MUSTARD, MAYO, THOUSAND_ISLAND 147 | } -------------------------------------------------------------------------------- /DesignPatterns/CreationalPatterns/FactoryPattern.java: -------------------------------------------------------------------------------- 1 | /** 2 | Factory pattern is used when we want to hide the logic of object creation from users. 3 | In this pattern, a factory class containing a method "getObject" is exposed to the user. 4 | In the implementation, "getObject" method returns an object of an abstract class. 5 | Based on input provided by users at runtime, "getObject" chooses the type of concrete object to create. 6 | This eliminates the need of coupling concrete object creation logic with client code and hence promotes loose-coupling. 7 | 8 | In this example, user provides pizza name to a factory. 9 | Based on pizza name, factory determines which pizza object to create. 10 | Each concrete pizza class has its own instructions for ingredients. User doesn't need to know about these details. 11 | **/ 12 | 13 | 14 | import java.util.*; 15 | public class FactoryPattern { 16 | public static void main(String[] args) { 17 | String pizzaName = ""; 18 | Scanner sc = new Scanner(System.in); 19 | System.out.println("Enter pizza name: "); 20 | pizzaName = sc.next(); 21 | PizzaFactory pizzaFactory = new PizzaFactory(); 22 | Pizza pizza = pizzaFactory.getPizza(pizzaName); 23 | if(pizza == null) { 24 | System.out.println(pizzaName + " is not made in our restaurant!"); 25 | return; 26 | } 27 | pizza.getRecipe(); 28 | } 29 | } 30 | 31 | class PizzaFactory { 32 | public Pizza getPizza(String name) { 33 | if(name.equalsIgnoreCase("ThreeCheesePizza")) { 34 | return new ThreeCheesePizza(); 35 | } 36 | if(name.equalsIgnoreCase("ChicagoDeepDishPizza")) { 37 | return new ChicagoDeepDishPizza(); 38 | } 39 | return null; 40 | } 41 | } 42 | 43 | abstract class Pizza { 44 | protected String cheese; 45 | protected String toppings; 46 | protected String crust; 47 | 48 | protected abstract void getRecipe(); 49 | 50 | protected void printPizzaDetails() { 51 | System.out.println("Cheese: " + this.cheese); 52 | System.out.println("Toppings: " + this.toppings); 53 | System.out.println("Crust: " + this.crust); 54 | } 55 | } 56 | 57 | class ThreeCheesePizza extends Pizza { 58 | @Override 59 | public void getRecipe() { 60 | this.cheese = "Mozzarella + Cheddar + Parmesan"; 61 | this.toppings = "Only cheese!"; 62 | this.crust = "Handmade extra thin crust"; 63 | System.out.println("Cooking a three-cheese pizza..."); 64 | printPizzaDetails(); 65 | } 66 | } 67 | 68 | class ChicagoDeepDishPizza extends Pizza { 69 | @Override 70 | public void getRecipe() { 71 | this.cheese = "Mozzarella + Parmesan"; 72 | this.toppings = "Bacon + Pepperoni + Tomato"; 73 | this.crust = "Thick cornmeal crust"; 74 | System.out.println("Cooking a Chicago Deep Dish pizza..."); 75 | printPizzaDetails(); 76 | } 77 | } 78 | 79 | -------------------------------------------------------------------------------- /DesignPatterns/CreationalPatterns/PrototypePattern.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Prototype pattern is used when we want to create a multiple copies of an object without having to copy each parameter separately. 3 | * It can be used to create both shallow and deep copies. 4 | */ 5 | 6 | import java.util.Formatter; 7 | public class PrototypePattern { 8 | public static void main(String[] args) { 9 | Car car = new Car(15, "black", 5); 10 | 11 | Car deepCopy = car.deepClone(); 12 | Car shallowCopy = car.shallowClone(); 13 | 14 | Formatter formatter = new Formatter(); 15 | formatter.format("------------------------------------------------------------------------------------------\n"); 16 | formatter.format("%20s %20s %20s %20s\n", "Parameter", "Original", "DeepClone", "ShallowClone"); 17 | formatter.format("------------------------------------------------------------------------------------------\n"); 18 | formatter.format("%20s %20s %20s %20s\n", "Object", car, deepCopy, shallowCopy); 19 | formatter.format("%20s %20s %20s %20s\n", "Mileage", car.mileage, deepCopy.mileage, shallowCopy.mileage); 20 | formatter.format("%20s %20s %20s %20s\n", "Color", car.color, deepCopy.color, shallowCopy.color); 21 | formatter.format("%20s %20s %20s %20s\n", "Seats", car.seats, deepCopy.seats, shallowCopy.seats); 22 | 23 | System.out.println(formatter); 24 | } 25 | } 26 | 27 | class Car { 28 | public int mileage; 29 | public String color; 30 | public int seats; 31 | 32 | public Car(int mileage, String color, int seats) { 33 | this.mileage = mileage; 34 | this.color = color; 35 | this.seats = seats; 36 | } 37 | public Car deepClone() { 38 | Car car = new Car(this.mileage, this.color, this.seats); 39 | return car; 40 | } 41 | 42 | public Car shallowClone() { 43 | return this; 44 | } 45 | } 46 | 47 | -------------------------------------------------------------------------------- /DesignPatterns/CreationalPatterns/SingletonPattern.java: -------------------------------------------------------------------------------- 1 | /** 2 | Singleton pattern is used in cases where we want only a single instance of a class throughout the application. 3 | 4 | In this example, we want a single instance of Logger class. 5 | So, instead of creating Logger class instances using new keyword, we use a getInstance() method. 6 | getInstance() method first checks if instance is null or not. If not, it returns instance. 7 | Otherwise, it takes a lock on Logger class (to ensure that no parallel thread calls getInstance() at the same time), 8 | checks again if instance is null or not. 9 | If so, a new instance is created and then returned. 10 | **/ 11 | public class SingletonPattern { 12 | public static void main(String[] args) { 13 | Logger logger1 = Logger.getInstance(); 14 | Logger logger2 = Logger.getInstance(); 15 | Logger logger3 = new Logger(); // this is a new instance 16 | 17 | System.out.println("Logger1: " + logger1); 18 | System.out.println("Logger2: " + logger2); 19 | System.out.println("Logger3: " + logger3); 20 | } 21 | } 22 | 23 | class Logger { 24 | private static Logger instance; 25 | 26 | public static Logger getInstance() { 27 | if(instance == null) { 28 | synchronized (Logger.class) { 29 | if(instance == null) { 30 | instance = new Logger(); 31 | } 32 | } 33 | } 34 | return instance; 35 | } 36 | } -------------------------------------------------------------------------------- /DesignPatterns/DesignPatterns.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /DesignPatterns/StructuralPatterns/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anuva04/SystemDesign/2338ea19d4f60812e6e37206f7b13a150e4ca78a/DesignPatterns/StructuralPatterns/.DS_Store -------------------------------------------------------------------------------- /DesignPatterns/StructuralPatterns/AdapterPattern.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Adapter design pattern is used in scenarios where we want 2 incompatible types to collaborate. 3 | * One typical example of this scenario is when we want data from obtained from a certain API/database to be utilised in a different 4 | * code which is expecting a different schema. 5 | * It is used to make legacy code compatible with new code. 6 | * In this example, Summary class is legacy code which is compatible with OldEmployee class but not NewEmployee. 7 | * We don't want to modify legacy code. 8 | * So we create an adapter for NewEmployee which extends OldEmployee and hence is compatible with Summary. 9 | */ 10 | class AdapterPattern { 11 | public static void main(String[] args) { 12 | OldEmployee employee1 = new OldEmployee("firstname1", "lastname1", "designation1", "old-employee-abc"); 13 | NewEmployee employee2 = new NewEmployee("employee2", "lastname2", "designation2", "new-employee-def"); 14 | 15 | Summary.getSummary(employee1); // works fine 16 | // Summary.getSummary(employee2); // throws compilation error 17 | 18 | NewEmployeeAdapter employeeAdapter = new NewEmployeeAdapter(employee2); 19 | Summary.getSummary(employeeAdapter); // works fine 20 | } 21 | } 22 | 23 | class Summary { 24 | public static void getSummary(OldEmployee employee) { 25 | System.out.println("Summary of employee with id " + employee.getEmployeeId()); 26 | System.out.println("First Name: " + employee.getFirstName()); 27 | System.out.println("Last Name: " + employee.getLastName()); 28 | System.out.println("Designation: " + employee.getDesignation()); 29 | } 30 | } 31 | 32 | class OldEmployee { 33 | private String firstName; 34 | private String lastName; 35 | private String designation; 36 | private String employeeId; 37 | 38 | public OldEmployee(String firstName, String lastName, String designation, String employeeId) { 39 | this.firstName = firstName; 40 | this.lastName = lastName; 41 | this.designation = designation; 42 | this.employeeId = employeeId; 43 | } 44 | 45 | public String getFirstName() { 46 | return firstName; 47 | } 48 | 49 | public String getLastName() { 50 | return lastName; 51 | } 52 | 53 | public String getDesignation() { 54 | return designation; 55 | } 56 | 57 | public String getEmployeeId() { 58 | return employeeId; 59 | } 60 | } 61 | 62 | class NewEmployee { 63 | private String name; 64 | private String surname; 65 | private String jobTitle; 66 | private String employeeId; 67 | 68 | public NewEmployee(String name, String surname, String jobTitle, String employeeId) { 69 | this.name = name; 70 | this.surname = surname; 71 | this.jobTitle = jobTitle; 72 | this.employeeId = employeeId; 73 | } 74 | 75 | public String getName() { 76 | return name; 77 | } 78 | 79 | public String getSurname() { 80 | return surname; 81 | } 82 | 83 | public String getJobTitle() { 84 | return jobTitle; 85 | } 86 | 87 | public String getEmployeeId() { 88 | return employeeId; 89 | } 90 | } 91 | 92 | class NewEmployeeAdapter extends OldEmployee { 93 | public NewEmployeeAdapter(NewEmployee newEmployee) { 94 | super(newEmployee.getName(), newEmployee.getSurname(), newEmployee.getJobTitle(), newEmployee.getEmployeeId()); 95 | } 96 | } -------------------------------------------------------------------------------- /DesignPatterns/StructuralPatterns/BridgePattern.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Bridge pattern helps in separating out orthogonal properties into separate classes so as to reduce the number of combinations. 3 | * In this example, if Type is added as a property in Card class, for every new Type, a new class would have to be created for every Card. 4 | * Similarly, for every new Card, a new class would have to created for every Type. 5 | * This would lead to creation of N(number of cards) * M(number of types) classes 6 | * However, using this pattern, a new Type or Card can be created independently. 7 | * Hence, there will be a total of N+M classes only. 8 | */ 9 | class BridgePattern { 10 | public static void main(String[] args) { 11 | Card sbiGoldCard = new SbiCard(new GoldType()); 12 | sbiGoldCard.withdraw(50000); 13 | sbiGoldCard.withdraw(120000); 14 | 15 | Card axisPlatinumCard = new AxisCard(new PlatinumType()); 16 | axisPlatinumCard.withdraw(80000); 17 | } 18 | } 19 | 20 | abstract class Card { 21 | protected Type type; 22 | protected Card(Type type) { 23 | this.type = type; 24 | } 25 | protected void withdraw(int amount) { 26 | if(amount > type.getLimit()) { 27 | System.out.println("Failure. Amount " + amount + " exceeds daily limit."); 28 | } else { 29 | this.withdrawFromBank(amount); 30 | } 31 | } 32 | 33 | protected abstract void withdrawFromBank(int amount); 34 | } 35 | 36 | class SbiCard extends Card { 37 | public SbiCard(Type type) { 38 | super(type); 39 | } 40 | @Override 41 | public void withdrawFromBank(int amount) { 42 | System.out.println("Calling SBI API... to withdraw amount: " + amount); 43 | } 44 | } 45 | 46 | class AxisCard extends Card { 47 | public AxisCard(Type type) { 48 | super(type); 49 | } 50 | @Override 51 | public void withdrawFromBank(int amount) { 52 | System.out.println("Calling Axis Bank API... to withdraw amount: " + amount); 53 | } 54 | } 55 | 56 | class Type { 57 | private int limit; 58 | 59 | protected Type(int limit) { 60 | this.limit = limit; 61 | } 62 | public int getLimit() { 63 | return limit; 64 | } 65 | } 66 | 67 | class StandardType extends Type { 68 | public StandardType() { 69 | super(20000); 70 | } 71 | } 72 | 73 | class GoldType extends Type { 74 | public GoldType() { 75 | super(50000); 76 | } 77 | } 78 | 79 | class PlatinumType extends Type { 80 | public PlatinumType() { 81 | super(100000); 82 | } 83 | } -------------------------------------------------------------------------------- /DesignPatterns/StructuralPatterns/CompositePattern.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Composite pattern is used in cases where we have a tree-like hierarchy of similar elements. 3 | * In this example, there are multiple instances of folders and files in a tree-like directory. 4 | * So we implement them from same interface and same methods are available to them. 5 | * Composite elements are elements with children, while leaf elements have no children. 6 | * We store a link of children/parent element in each element so that we are able to navigate through the tree. 7 | */ 8 | 9 | import java.util.*; 10 | import java.lang.UnsupportedOperationException; 11 | 12 | class CompositePattern { 13 | public static void main(String[] args) { 14 | Folder SystemDesign = new Folder("SystemDesign", null); 15 | 16 | Directory DesignPatterns = new Folder("DesignPatterns", SystemDesign); 17 | System.out.println("Path of DesignPatterns:"); 18 | DesignPatterns.printPath(); 19 | System.out.println(""); 20 | 21 | Directory ElevatorSystem = new Folder("ElevatorSystem", SystemDesign); 22 | System.out.println("Path of ElevatorSystem:"); 23 | ElevatorSystem.printPath(); 24 | System.out.println(""); 25 | 26 | Directory StructuralDesignPatterns = new Folder("StructuralDesignPatterns", DesignPatterns); 27 | System.out.println("Path of StructuralDesignPatterns:"); 28 | StructuralDesignPatterns.printPath(); 29 | System.out.println(""); 30 | 31 | System.out.println("Subdirectories of DesignPatterns:"); 32 | DesignPatterns.printSubDirectories(); 33 | System.out.println(""); 34 | 35 | Directory CompositePattern = new File("CompositePattern", StructuralDesignPatterns); 36 | System.out.println("Path of CompositePattern:"); 37 | CompositePattern.printPath(); 38 | System.out.println(""); 39 | System.out.println("Subdirectories of CompositePattern:"); 40 | CompositePattern.printSubDirectories(); 41 | System.out.println(""); 42 | 43 | System.out.println("Subdirectories of SystemDesign:"); 44 | SystemDesign.printSubDirectories(); 45 | System.out.println(""); 46 | } 47 | } 48 | 49 | interface Directory { 50 | public void printPath(); 51 | public void printSubDirectories(); 52 | public void add(Directory directory); 53 | public void remove(Directory directory); 54 | } 55 | 56 | // Composite element 57 | class Folder implements Directory { 58 | public String name; 59 | public Directory parent; 60 | public List subFolders; 61 | 62 | public Folder(String name, Directory parent) { 63 | this.name = name; 64 | this.parent = parent; 65 | this.subFolders = new ArrayList<>(); 66 | if(parent != null) parent.add(this); 67 | } 68 | 69 | @Override 70 | public void printPath() { 71 | if(parent != null) parent.printPath(); 72 | System.out.print("/" + this.name); 73 | } 74 | 75 | @Override 76 | public void printSubDirectories() { 77 | for(Directory child : this.subFolders) { 78 | if(child instanceof Folder) { 79 | Folder folder = (Folder) child; 80 | System.out.println(folder.name); 81 | } else { 82 | File file = (File) child; 83 | System.out.println(file.name); 84 | } 85 | } 86 | } 87 | 88 | @Override 89 | public void add(Directory child) { 90 | this.subFolders.add(child); 91 | } 92 | 93 | @Override 94 | public void remove(Directory child) { 95 | this.subFolders.remove(child); 96 | } 97 | } 98 | 99 | // Leaf element 100 | class File implements Directory { 101 | public String name; 102 | public Directory parent; 103 | 104 | public File(String name, Directory parent) { 105 | this.name = name; 106 | this.parent = parent; 107 | } 108 | 109 | @Override 110 | public void printPath() { 111 | if(parent != null) parent.printPath(); 112 | System.out.print("/" + this.name); 113 | } 114 | 115 | @Override 116 | public void printSubDirectories() { 117 | System.out.print("This is a file."); 118 | } 119 | 120 | @Override 121 | public void add(Directory child) { 122 | throw new UnsupportedOperationException("Can't add in a file."); 123 | } 124 | 125 | @Override 126 | public void remove(Directory child) { 127 | throw new UnsupportedOperationException("Can't remove from a file."); 128 | } 129 | } -------------------------------------------------------------------------------- /DesignPatterns/StructuralPatterns/DecoratorPattern.java: -------------------------------------------------------------------------------- 1 | /** 2 | * This pattern is used to attach additional behaviors/characteristics to elements. 3 | * When we have a situation where new responsibility asks arise of an object regularly, it is not a good idea to keep exntending the class. 4 | * This is because there can a huge number of combinations of various responsibilities and creating child classes for each of these is not feasible. 5 | * Instead we wrap the original class in various decorator classes to provide it with the required responsibilties. 6 | * This can be done statically or at runtime. 7 | * In this example, we have the original object Venue, and we keep decorating it with added charateristics like Balloons and Lights. 8 | */ 9 | 10 | class DecoratorPattern { 11 | public static void main(String[] args) { 12 | Venue partyVenue = new PartyVenue(); 13 | partyVenue = new Balloons(partyVenue); 14 | partyVenue = new Lights(partyVenue); 15 | partyVenue = new Sequins(partyVenue); 16 | 17 | System.out.println(partyVenue.decorate()); 18 | } 19 | } 20 | 21 | interface Venue { 22 | String decorate(); 23 | } 24 | 25 | class PartyVenue implements Venue { 26 | @Override 27 | public String decorate() { 28 | return "Party Venue"; 29 | } 30 | } 31 | 32 | abstract class VenueDecorator implements Venue { 33 | private Venue venue; 34 | 35 | public VenueDecorator(Venue venue) { 36 | this.venue = venue; 37 | } 38 | @Override 39 | public String decorate() { 40 | return venue.decorate(); 41 | } 42 | } 43 | 44 | class Balloons extends VenueDecorator { 45 | public Balloons(Venue venue) { 46 | super(venue); 47 | } 48 | 49 | public String decorate() { 50 | return super.decorate() + balloonDecorator(); 51 | } 52 | 53 | private String balloonDecorator() { 54 | return " with balloons"; 55 | } 56 | } 57 | 58 | class Sequins extends VenueDecorator { 59 | public Sequins(Venue venue) { 60 | super(venue); 61 | } 62 | 63 | public String decorate() { 64 | return super.decorate() + sequinDecorator(); 65 | } 66 | 67 | private String sequinDecorator() { 68 | return " with sequins"; 69 | } 70 | } 71 | 72 | class Lights extends VenueDecorator { 73 | public Lights(Venue venue) { 74 | super(venue); 75 | } 76 | 77 | public String decorate() { 78 | return super.decorate() + lightsDecorator(); 79 | } 80 | 81 | private String lightsDecorator() { 82 | return " with lights"; 83 | } 84 | } -------------------------------------------------------------------------------- /DesignPatterns/StructuralPatterns/FacadePattern.java: -------------------------------------------------------------------------------- 1 | /** 2 | * This pattern is used to abstract complexity of various operations of a class from users. 3 | * In this example, user has knowledge of only SendMoney() and GetBalance() methods. 4 | * Other intricate details of UPI service providers, getting details from bank etc. are not made visible to the users. 5 | */ 6 | 7 | class FacadePattern { 8 | public static void main(String[] args) { 9 | UPIPaymentApp app = new UPIPaymentApp("dummy@okdummy"); 10 | 11 | System.out.println("Balance: " + app.GetBalance()); 12 | 13 | app.SendMoney(500); 14 | app.SendMoney(1500); 15 | } 16 | } 17 | 18 | class UPIPaymentApp { 19 | private String upiId; 20 | 21 | public UPIPaymentApp(String upiId) { 22 | this.upiId = upiId; 23 | } 24 | public void SendMoney(int amount) { 25 | int balance = GetBalance(); 26 | 27 | if(balance >= amount) { 28 | System.out.println("Money transfer successful"); 29 | } else { 30 | System.out.println("Not enough balance"); 31 | } 32 | } 33 | 34 | public int GetBalance() { 35 | UpiServiceProvider upiServiceProvider = new UpiServiceProvider(); 36 | upiServiceProvider.IsUpiValid(upiId); 37 | int accountNumber = upiServiceProvider.GetBank(upiId); 38 | 39 | Bank bank = new Bank(); 40 | int balance = bank.GetBankBalance(accountNumber); 41 | 42 | System.out.println("Balance: " + balance); 43 | 44 | return balance; 45 | } 46 | } 47 | 48 | class UpiServiceProvider { 49 | public void IsUpiValid(String upiId) { 50 | System.out.println("Validating UPI ID..."); 51 | System.out.println("UPI ID valid."); 52 | } 53 | 54 | public int GetBank(String upiId) { 55 | System.out.println("Finding Bank..."); 56 | return 12345; 57 | } 58 | } 59 | 60 | class Bank { 61 | public int GetBankBalance(int accountNumber) { 62 | System.out.println("Getting balance for account " + accountNumber + "..."); 63 | return 1000; 64 | } 65 | } -------------------------------------------------------------------------------- /DesignPatterns/StructuralPatterns/FlyweightPattern.java: -------------------------------------------------------------------------------- 1 | /** 2 | * This pattern is used when we need to create a lot of objects of same class with very minimal differences in properties. 3 | * Each object requires memory, so it is efficient to create one object for a certain set of properties and reuse is wherever possible. 4 | * Ofcourse these objects should be immutable so as to avoid any unintentional behavior anywhere they get used. 5 | * In this example, say we are creating a terrain for a game. It can be viewed as a grid with different elements in each cell. 6 | * Most of these elements will be immutable. 7 | * So it is way more efficient to create 1 instance of each element and render it multiple times on the terrain.` 8 | */ 9 | 10 | import java.util.HashMap; 11 | import java.util.ArrayList; 12 | 13 | class FlyweightPattern { 14 | public static void main(String[] args) { 15 | Terrain terrain = new Terrain(2, 2); 16 | terrain.setElement(ElementType.TREE, 0, 0); 17 | terrain.setElement(ElementType.WATER, 0, 1); 18 | terrain.setElement(ElementType.WATER, 1, 1); 19 | terrain.setElement(ElementType.LAND, 1, 0); 20 | 21 | terrain.render(); 22 | 23 | // Observe that object address for same element types are same. 24 | System.out.println("0,0 element: " + terrain.terrain[0][0].render() + ", address: " + terrain.terrain[0][0]); 25 | System.out.println("0,0 element: " + terrain.terrain[0][1].render() + ", address: " + terrain.terrain[0][1]); 26 | System.out.println("0,0 element: " + terrain.terrain[1][0].render() + ", address: " + terrain.terrain[1][0]); 27 | System.out.println("0,0 element: " + terrain.terrain[1][1].render() + ", address: " + terrain.terrain[1][1]); 28 | } 29 | } 30 | 31 | interface Element { 32 | char render(); 33 | } 34 | 35 | class Tree implements Element { 36 | @Override 37 | public char render() { 38 | return 'T'; 39 | } 40 | } 41 | 42 | class Water implements Element { 43 | @Override 44 | public char render() { 45 | return 'W'; 46 | } 47 | } 48 | 49 | class Rock implements Element { 50 | @Override 51 | public char render() { 52 | return 'W'; 53 | } 54 | } 55 | 56 | class Land implements Element { 57 | @Override 58 | public char render() { 59 | return 'L'; 60 | } 61 | } 62 | 63 | class ElementFactory { 64 | private HashMap elementMap = new HashMap<>(); 65 | 66 | public Element getElement(ElementType elementType) { 67 | if(elementMap.containsKey(elementType)) { 68 | return elementMap.get(elementType); 69 | } 70 | 71 | Element element; 72 | switch(elementType) { 73 | case TREE: 74 | element = new Tree(); 75 | break; 76 | case WATER: 77 | element = new Water(); 78 | break; 79 | case ROCK: 80 | element = new Rock(); 81 | break; 82 | case LAND: 83 | element = new Land(); 84 | break; 85 | default: 86 | element = null; 87 | } 88 | 89 | if(element == null) 90 | System.out.println("Invalid element type " + elementType); 91 | 92 | elementMap.put(elementType, element); 93 | return element; 94 | } 95 | } 96 | 97 | enum ElementType { 98 | LAND, TREE, ROCK, WATER 99 | } 100 | 101 | class Terrain { 102 | public int length; 103 | public int width; 104 | Element[][] terrain; 105 | ElementFactory factory = new ElementFactory(); 106 | 107 | public Terrain(int length, int width) { 108 | this.length = length; 109 | this.width = width; 110 | terrain = new Element[length][width]; 111 | } 112 | 113 | public void setElement(ElementType elementType, int x, int y) { 114 | if(x >= this.length || y >= this.width) { 115 | throw new IndexOutOfBoundsException("Given coordinates are not within terrain dimensions"); 116 | } 117 | 118 | terrain[x][y] = factory.getElement(elementType); 119 | } 120 | 121 | public void render() { 122 | for(int i=0; i employeeIdName = new HashMap<>(); 50 | 51 | @Override 52 | public void add(int id, String name) { 53 | if(employeeIdName.containsKey(id)) { 54 | System.out.println("EmployeeId exists already"); 55 | return; 56 | } 57 | employeeIdName.put(id, name); 58 | } 59 | 60 | @Override 61 | public void remove(int id) { 62 | if(!employeeIdName.containsKey(id)) { 63 | System.out.println("EmployeeId doesn't exist"); 64 | return; 65 | } 66 | employeeIdName.remove(id); 67 | } 68 | 69 | @Override 70 | public void update(int id, String name) { 71 | if(!employeeIdName.containsKey(id)) { 72 | System.out.println("EmployeeId doesn't exist"); 73 | return; 74 | } 75 | employeeIdName.put(id, name); 76 | } 77 | 78 | @Override 79 | public void printTable() { 80 | for (Map.Entry entry : employeeIdName.entrySet()) { 81 | System.out.println(entry.getKey() + " " + entry.getValue()); 82 | } 83 | } 84 | } 85 | 86 | // Cached table in memory 87 | class TableProxy implements Table { 88 | private RealTable realTable = new RealTable(); 89 | private String del = "DELETED"; 90 | 91 | private HashMap employeeIdName = new HashMap<>(); 92 | 93 | @Override 94 | public void add(int id, String name) { 95 | if(employeeIdName.containsKey(id)) { 96 | System.out.println("EmployeeId exists already"); 97 | return; 98 | } 99 | employeeIdName.put(id, name); 100 | } 101 | 102 | @Override 103 | public void remove(int id) { 104 | if(!employeeIdName.containsKey(id) || employeeIdName.get(id).equals(del)) { 105 | System.out.println("EmployeeId doesn't exist"); 106 | return; 107 | } 108 | employeeIdName.put(id, del); 109 | } 110 | 111 | @Override 112 | public void update(int id, String name) { 113 | if(!employeeIdName.containsKey(id) || employeeIdName.get(id).equals(del)) { 114 | System.out.println("EmployeeId doesn't exist"); 115 | return; 116 | } 117 | employeeIdName.put(id, name); 118 | } 119 | 120 | public void backup() { 121 | // Updating real table only from this method 122 | for(Map.Entry entry : employeeIdName.entrySet()) { 123 | if(entry.getValue().equals(del)) { 124 | realTable.remove(entry.getKey()); 125 | } else { 126 | if(realTable.employeeIdName.containsKey(entry.getKey())) { 127 | realTable.update(entry.getKey(), entry.getValue()); 128 | } else { 129 | realTable.add(entry.getKey(), entry.getValue()); 130 | } 131 | } 132 | } 133 | } 134 | 135 | @Override 136 | public void printTable() { 137 | realTable.printTable(); 138 | } 139 | } -------------------------------------------------------------------------------- /HLD/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anuva04/SystemDesign/2338ea19d4f60812e6e37206f7b13a150e4ca78a/HLD/.DS_Store -------------------------------------------------------------------------------- /HLD/ChatApp/ChatService.drawio: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /HLD/ChatApp/ChatService.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anuva04/SystemDesign/2338ea19d4f60812e6e37206f7b13a150e4ca78a/HLD/ChatApp/ChatService.png -------------------------------------------------------------------------------- /HLD/ECommerceStore/ecommerce.drawio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anuva04/SystemDesign/2338ea19d4f60812e6e37206f7b13a150e4ca78a/HLD/ECommerceStore/ecommerce.drawio.png -------------------------------------------------------------------------------- /HLD/GoogleDocs/GoogleDoc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anuva04/SystemDesign/2338ea19d4f60812e6e37206f7b13a150e4ca78a/HLD/GoogleDocs/GoogleDoc.png -------------------------------------------------------------------------------- /HLD/Twitter/TwitterHLD.drawio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anuva04/SystemDesign/2338ea19d4f60812e6e37206f7b13a150e4ca78a/HLD/Twitter/TwitterHLD.drawio.png -------------------------------------------------------------------------------- /HLD/URLShortener/URLshortener.drawio: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /HLD/URLShortener/URLshortener.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anuva04/SystemDesign/2338ea19d4f60812e6e37206f7b13a150e4ca78a/HLD/URLShortener/URLshortener.png -------------------------------------------------------------------------------- /HLD/Uber/Uber.drawio: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /HLD/Uber/Uber.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anuva04/SystemDesign/2338ea19d4f60812e6e37206f7b13a150e4ca78a/HLD/Uber/Uber.png -------------------------------------------------------------------------------- /HLD/WebHook/WebHook.md: -------------------------------------------------------------------------------- 1 | # WebHook 2 | Design a WebHook system 3 | 4 | ## Functional Requirements 5 | - Clients should be able to register to the webhook service along with event types they are interested in. 6 | - Clients should be notified on registered URLs when events they're interested in happen. 7 | - Security is an important consideration as clients will be open to receiving communication from the service. 8 | - Guarantee ordering of events per webhook subscriber (especially for related events). 9 | - Allow clients to list, update, delete their webhook subscriptions. 10 | 11 | ## Non-functional Requirements 12 | - System should be scalable and should be able to handle millions of webhook events every day. 13 | - System should be reliable and should guarantee at-least-once delivery. 14 | - System should have low latency. 15 | - System should be durable and persist undelivered events until they're successfully delivered or expired. 16 | 17 | ## Resource estimation 18 | 19 | ### Requirements 20 | - 1M webhook events per day with 5x increase during spikes. 21 | - Default expiry of events is 30 days. 22 | - All delivered/undelivered events should be retained for 30 days. 23 | - Each event size is 5KB. 24 | 25 | During peak hours, as traffic increases by 5x, `5M events need to be processed and delivered`. 26 | 27 | As each event is of size 5KB, `average bandwidth required = 1M x 5KB /day = 57KB/s`. This can go up to `57 x 5 = 285KB/s during peak hours`. 28 | 29 | Given, events need to be stored for 30 days, `amount of storage needed for usual traffic = 1M x 5KB x 30 = 150GB`. 30 | 31 | ## API Design 32 | 1. Register a WebHook 33 |
`POST /webhooks` 34 |
Request: 35 | ```json 36 | { 37 | "url": "https://client.example.com/webhook", 38 | "event_types": ["document.created", "document.updated"] 39 | } 40 | 41 | ``` 42 |
Response: 43 | ```json 44 | { 45 | "webhook_id": "wh_12345", 46 | "status": "active" 47 | } 48 | ``` 49 | 2. Get WebHook Details 50 |
`GET /webhooks/{webhook_id}` 51 |
Response: 52 | ```json 53 | { 54 | "webhook_id": "wh_12345", 55 | "url": "https://client.example.com/webhook", 56 | "event_types": ["document.created"], 57 | "status": "active", 58 | "created_at": "2025-05-07T12:00:00Z" 59 | } 60 | ``` 61 | 3. List all WebHooks 62 |
`GET /webhooks` 63 |
Response: 64 | ```json 65 | { 66 | "webhooks": [ 67 | { 68 | "webhook_id": "wh_12345", 69 | "url": "https://client.example.com/webhook", 70 | "event_types": ["document.created"] 71 | }, 72 | { 73 | "webhook_id": "wh_67890", 74 | "url": "https://client.example.com/user-webhook", 75 | "event_types": ["user.updated"] 76 | } 77 | ] 78 | } 79 | ``` 80 | 4. Update a WebHook 81 |
`PUT /webhooks/{webhook_id}` 82 |
Request: 83 | ```json 84 | { 85 | "url": "https://new.example.com/webhook", 86 | "event_types": ["document.deleted"] 87 | } 88 | ``` 89 |
Response: 90 | ```json 91 | { 92 | "status": "updated" 93 | } 94 | ``` 95 | 5. Delete a WebHook 96 |
`DELETE /webhooks/{webhook_id}` 97 |
Response: 98 | ```json 99 | { 100 | "status": "deleted" 101 | } 102 | ``` 103 | 6. Deliver an event (initiated by Server) 104 |
`POST https://client.example.com/webhook` 105 |
Request: 106 | ```json 107 | { 108 | "event_id": "evt_abc123", 109 | "event_type": "document.created", 110 | "timestamp": "2025-05-07T12:34:56Z", 111 | "payload": { 112 | "document_id": "doc_7890", 113 | "created_by": "user_123" 114 | } 115 | } 116 | ``` 117 | 118 | ## Data Storage 119 | 120 | ### `Subscription` table 121 | Stores registered webhook subscriptions of all users. We can use relational DB (e.g., `Postgres`, `MySQL`) for this table because it’s structured, transactional and queryable. It can be indexed on `user_id` and `event_types` to quickly fetch subscriptions of a particular user or all subscriptions for a particular event type. 122 |
Schema: 123 | ``` 124 | webhook_id (UUID, PK) 125 | user_id (UUID) 126 | callback_url (string) 127 | event_types (Enum[]) 128 | status (Enum (ACTIVE/INACTIVE)) 129 | created_at (timestamp) 130 | ``` 131 | 132 | ### `EventsDelivery` table 133 | Records delivery attempts and results for observability and retries. This table will have to handle high read and write traffic. As we need at-least-once delivery guarantee, strong ACID properties are not necessary. Keeping these in mind, a non-relational DB like `MongoDB` would be a good choice. This table can be indexed on `status` to quickly fetch failed deliveries and retry them. 134 | 135 | Also, if `payload` or `response_message` are too long, they can be stored separately in object storage and keep references here. 136 |
Schema: 137 | ``` 138 | delivery_id (UUID, PK) 139 | event_id (UUID) 140 | webhook_id (UUID, FK) 141 | event_type (Enum) 142 | payload (JSON) 143 | status (Enum) 144 | retry_count (integer) 145 | delivery_time (timestamp) 146 | response_code (integer) 147 | response_message (string) 148 | ``` 149 | 150 | ## High-level Design 151 | ![Webhook Architecture](Webhook.png) 152 | 153 | `Webhook Management Service` handles the creation, updates and management of webhooks. This service exposes CRUD APIs for webhooks. This service stores registered webhooks in `Subscription` table. 154 | 155 | `Events Producer Service` creates new events from external triggers. It creates individual events for each subscriber using data from `Subscription` table and stores in `Events Delivery` table. It also publishes the events to a Message Queue for async handling. 156 | 157 | `Dispatcher Service` workers consume events from the message queue and attempt delivery to clients. Status is updated in `Events Delivery` table according to success or failure. `Retry Service` retries failed deliveries for a fixed number of times before abandoning them permanently. 158 | 159 | ## Bottlenecks and scaling 160 | 161 | ### Dispatcher scalability 162 | `Dispatcher service` has to deliver millions of events daily and can easily become a bottleneck. This will lead to high latency, failed deliveries and increased retries. To resolve this, dispatcher service should have multiple workers that can be scaled horizontally as per traffic. Also, `event delivery message queue` can be sharded by event type and dedicated dispatcher workers can be created so that they can scale independently. 163 | 164 | ### Retry backlog 165 | Misconfigured or flaky client URLs can lead to significant increase in retry backlog. This leads to wastage of immense resources in `Retry Service`. Hence, retries should be handled by a separate service instead of `Dispatcher service` itself. Exponential backoff should be used while retrying for the same client URL. Also, a max retry limit should be set and thereafter the event should be expired. 166 | 167 | ### Metadata cache 168 | Each time an event needs to be dispatched to a certain webhook, all metadata related to the webhook is required. This info can be cached in `Dispatcher Service` and `Retry Service` for heavily used webhooks. 169 | 170 | ## Security 171 | Security is critical in webhook system since client URLs are exposed over public internet — anyone can POST data to them. Following measures can be taken to secure such a system. 172 | 173 | ### Authentication 174 | Only signed payloads should be accepted by client endpoints (e.g., `HMAC-SHA256`). When a client registers a webhook, generate a secret key (e.g., per-client or per-webhook). When sending a webhook event, compute HMAC over payload using the secret key and include the signature in header. Client receiving the webhook will verify the signature using their secret. Secrets should be rotated periodically. 175 | 176 | ### Secure client URLs 177 | Enforce HTTPS-only client URLs. Use TLS 1.2 or higher. Optionally validate server certificates — prevent MITM attacks. Reject IPs with self-signed or expired certs. 178 | 179 | Validate webhook URLs at registration time and before each dispatch. Block internal IPs (e.g., `127.0.0.1`, `169.254.x.x`, `10.x.x.x`, etc.) 180 | 181 | ### Rate Limiting + Circuit Breakers 182 | To protect from malicious clients, implement per client rate limits, circuit breakers for clients failing consistently and exponential backoff on retry attempts. 183 | 184 | > On top of all of these, implement proper audit and monitoring system to identify malicious clients. 185 | -------------------------------------------------------------------------------- /HLD/WebHook/Webhook.drawio: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /HLD/WebHook/Webhook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anuva04/SystemDesign/2338ea19d4f60812e6e37206f7b13a150e4ca78a/HLD/WebHook/Webhook.png -------------------------------------------------------------------------------- /HLD/Youtube/youtube.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anuva04/SystemDesign/2338ea19d4f60812e6e37206f7b13a150e4ca78a/HLD/Youtube/youtube.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Anuva Bhattacharjee 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LLD/ATM/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | !**/src/main/**/build/ 5 | !**/src/test/**/build/ 6 | 7 | ### IntelliJ IDEA ### 8 | .idea/modules.xml 9 | .idea/jarRepositories.xml 10 | .idea/compiler.xml 11 | .idea/libraries/ 12 | *.iws 13 | *.iml 14 | *.ipr 15 | out/ 16 | !**/src/main/**/out/ 17 | !**/src/test/**/out/ 18 | 19 | ### Eclipse ### 20 | .apt_generated 21 | .classpath 22 | .factorypath 23 | .project 24 | .settings 25 | .springBeans 26 | .sts4-cache 27 | bin/ 28 | !**/src/main/**/bin/ 29 | !**/src/test/**/bin/ 30 | 31 | ### NetBeans ### 32 | /nbproject/private/ 33 | /nbbuild/ 34 | /dist/ 35 | /nbdist/ 36 | /.nb-gradle/ 37 | 38 | ### VS Code ### 39 | .vscode/ 40 | 41 | ### Mac OS ### 42 | .DS_Store -------------------------------------------------------------------------------- /LLD/ATM/.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /LLD/ATM/.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 16 | 17 | -------------------------------------------------------------------------------- /LLD/ATM/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /LLD/ATM/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /LLD/ATM/README.md: -------------------------------------------------------------------------------- 1 | # ATM System - Low-Level Design (LLD) 2 | 3 | The ATM System is a software application that simulates the functionalities of an Automated Teller Machine (ATM). This system is designed to demonstrate the low-level design of an ATM using object-oriented principles. 4 | 5 | ## Architecture Overview 6 | 7 | The ATM System follows a layered architecture, comprising the following layers: 8 | 9 | - **User Interface Layer:** This layer is responsible for interacting with the user, capturing input, and displaying information. It includes the `Main` class, which serves as the entry point for the system and handles user interactions through the command-line interface. 10 | 11 | - **Controller Layer:** This layer contains the controllers that handle user requests and orchestrate the interactions between different components of the system. It includes the `ATM` class, which manages the overall ATM operations, such as cash reserve management and user transactions. 12 | 13 | - **Model Layer:** This layer consists of the data models and entities used in the system. It includes classes like `AtmCard`, `CashReserve`, and `Transaction` that represent ATM cards, cash reserves, and user transactions, respectively. 14 | 15 | - **Strategy Layer:** This layer encapsulates different strategies for cash dispensing. It includes the `CashDispenserStrategy` interface and its implementation, such as the `SimpleCashDispenseStrategy`, which defines the logic for dispensing cash based on the available cash reserve. 16 | 17 | - **Enums:** This package contains different enumerations used in the system, such as `Denomination` for representing various currency denominations and `PaymentNetwork` for defining payment network types. 18 | 19 | - **Models Package:** This package contains the concrete implementations of ATM cards, such as `AxisVisaCard` and `SbiRupayCard`, which extend the `AtmCard` class and define card-specific details like the bank and payment network. 20 | 21 | ## Features and Functionality 22 | 23 | The ATM System supports the following features: 24 | 25 | - **User Login:** The system allows users to log in using an ATM card, currently implemented with dummy card details. 26 | 27 | - **Cash Withdrawal:** Users can withdraw cash from the ATM. The system verifies the PIN and checks the cash availability before dispensing the requested amount. 28 | 29 | - **PIN Change:** Users can change the PIN associated with their ATM card. 30 | 31 | - **Balance Inquiry:** Users can check the balance of their linked bank account. 32 | 33 | - **Mini Statement:** Users can request a mini statement of their recent transactions. 34 | 35 | - **Admin Login:** The system provides an admin login option to manage the cash reserve. Only authorized administrators can modify the cash reserve. 36 | 37 | ## Getting Started 38 | 39 | To run the ATM System, follow these steps: 40 | 41 | 1. Clone the repository to your local machine. 42 | 2. Open the project in your preferred Java IDE. 43 | 3. Build the project using Gradle to resolve dependencies. You can use the following command in the terminal or command prompt: 44 | ./gradlew build 45 | 4. Run the `Main` class to start the ATM system. 46 | 5. Follow the instructions displayed in the console to perform different operations. 47 | - For user login, enter `1`. 48 | - For admin login, enter `2`. Use the password "admin12345" for admin login. 49 | 50 | ## Extensibility and Customization 51 | 52 | The ATM System is designed to be extensible and customizable. You can add new features or modify existing functionalities by extending or implementing the relevant classes and interfaces. For example, you can introduce new types of ATM cards, implement additional cash dispensing strategies, or integrate the system with real banking services. 53 | 54 | ## Note 55 | 56 | The ATM System is a simulated application and does not interact with real banks or handle actual monetary transactions. It serves as a demonstration of low-level design concepts and can be further developed for practical usage. -------------------------------------------------------------------------------- /LLD/ATM/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("java") 3 | } 4 | 5 | group = "org.anuva04" 6 | version = "1.0-SNAPSHOT" 7 | 8 | repositories { 9 | mavenCentral() 10 | } 11 | 12 | dependencies { 13 | implementation("org.jetbrains:annotations:24.0.0") 14 | testImplementation(platform("org.junit:junit-bom:5.9.1")) 15 | testImplementation("org.junit.jupiter:junit-jupiter") 16 | } 17 | 18 | tasks.test { 19 | useJUnitPlatform() 20 | } -------------------------------------------------------------------------------- /LLD/ATM/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anuva04/SystemDesign/2338ea19d4f60812e6e37206f7b13a150e4ca78a/LLD/ATM/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /LLD/ATM/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat Jun 17 19:25:54 IST 2023 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /LLD/ATM/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /LLD/ATM/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "ATM" 2 | 3 | -------------------------------------------------------------------------------- /LLD/ATM/src/main/java/org/anuva04/Controllers/ATM.java: -------------------------------------------------------------------------------- 1 | package org.anuva04.Controllers; 2 | 3 | import org.anuva04.Models.AtmCards.AtmCard; 4 | import org.anuva04.Models.CashReserve; 5 | import org.anuva04.Strategies.CashDispenserStrategy; 6 | 7 | import java.util.Scanner; 8 | import java.util.UUID; 9 | 10 | public class ATM { 11 | private UUID id; 12 | private CashReserve cashReserve; 13 | private CashDispenserStrategy cashDispenserStrategy; 14 | 15 | public ATM() { 16 | cashReserve = new CashReserve(); 17 | this.id = UUID.randomUUID(); 18 | } 19 | 20 | // Method to set cash dispensing strategy 21 | public void setCashDispenserStrategy(CashDispenserStrategy cashDispenserStrategy) { 22 | this.cashDispenserStrategy = cashDispenserStrategy; 23 | } 24 | 25 | // Method to handle user requests after ATM card is inserted and PIN is verified 26 | public void startTransaction(AtmCard atmCard) { 27 | Scanner sc = new Scanner(System.in); 28 | Transaction transaction = new Transaction(); 29 | if (!transaction.verifyPin(atmCard)) { 30 | System.out.println("Wrong PIN!!!"); 31 | return; 32 | } 33 | while (true) { 34 | System.out.println("Choose an option: "); 35 | System.out.println( 36 | "1: Withdraw cash\n" + 37 | "2: Change Pin\n" + 38 | "3: Check balance\n" + 39 | "4: Get mini statement\n" + 40 | "5: Exit"); 41 | int option = sc.nextInt(); 42 | boolean exit = false; 43 | switch (option) { 44 | case 1: 45 | transaction.withdraw(cashReserve, cashDispenserStrategy); 46 | break; 47 | case 2: 48 | transaction.changePin(atmCard); 49 | break; 50 | case 3: 51 | transaction.checkBalance(atmCard); 52 | break; 53 | case 4: 54 | transaction.getMiniStatement(atmCard); 55 | break; 56 | case 5: 57 | System.out.println("Thank you for visiting!!!"); 58 | exit = true; 59 | break; 60 | default: 61 | System.out.println("Invalid option!!!"); 62 | } 63 | if (exit) { 64 | break; 65 | } 66 | } 67 | } 68 | 69 | // Method to update data about cash reserve in ATM 70 | public void modifyCashReserve(CashReserve newCashReserve) { 71 | this.cashReserve.modifyCashReserve(newCashReserve); 72 | System.out.println("Cash reserve modified successfully!"); 73 | } 74 | 75 | // Method to verify admin login 76 | public boolean adminLogin(String password) { 77 | if (password.equalsIgnoreCase("admin12345")) { 78 | return true; 79 | } 80 | return false; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /LLD/ATM/src/main/java/org/anuva04/Controllers/Transaction.java: -------------------------------------------------------------------------------- 1 | package org.anuva04.Controllers; 2 | 3 | import org.anuva04.Models.AtmCards.AtmCard; 4 | import org.anuva04.Models.CashAvailabilityDataResponseModel; 5 | import org.anuva04.Models.CashReserve; 6 | import org.anuva04.Strategies.CashDispenserStrategy; 7 | 8 | import java.util.Scanner; 9 | 10 | // Contains all facilities provided by ATM to users 11 | public class Transaction { 12 | Scanner sc = new Scanner(System.in); 13 | 14 | // Method to withdraw cash from ATM 15 | public void withdraw(CashReserve cashReserve, CashDispenserStrategy cashDispenserStrategy) { 16 | System.out.println("Enter amount: "); 17 | int amount = sc.nextInt(); 18 | // Contact bank to check user's balance 19 | // If not enough balance, cancel transaction 20 | // else 21 | CashAvailabilityDataResponseModel response = cashDispenserStrategy.getCashAvailability(cashReserve, amount); 22 | if (!response.isCashAvailable) { 23 | System.out.println("Cash not available!"); 24 | return; 25 | } 26 | // Contact bank to deduct balance 27 | CashReserve cash = cashDispenserStrategy.dispenseCash(cashReserve, response.cashReserve); 28 | if (cash == null) { 29 | System.out.println("Something went wrong! Please contact XXXX"); 30 | // Contact bank to initiate refund 31 | } else { 32 | System.out.println("Please collect cash!"); 33 | } 34 | } 35 | 36 | // Method to change ATM card PIN 37 | public void changePin(AtmCard atmCard) { 38 | System.out.println("Enter new PIN: "); 39 | int newPin = sc.nextInt(); 40 | // Contact bank to change PIN 41 | System.out.println("Pin changed successfully!"); 42 | } 43 | 44 | // Method to check balance of bank account linked to given ATM card 45 | public void checkBalance(AtmCard atmCard) { 46 | // Contact bank to check balance 47 | System.out.println("Balance for account linked to card " + atmCard.getCardNumber() + " is YY"); 48 | } 49 | 50 | // Method to get Mini-statement of bank account linked to given ATM card 51 | public void getMiniStatement(AtmCard atmCard) { 52 | // Contact bank to get mini-statement 53 | System.out.println("Mini-statement: ###########"); 54 | } 55 | 56 | // Method to verify ATM card PIN 57 | public boolean verifyPin(AtmCard atmCard) { 58 | System.out.println("Please enter PIN: "); 59 | int pin = sc.nextInt(); 60 | 61 | // PIN verification process 62 | return atmCard.getBank().verifyAtmPin(atmCard.getCardNumber(), pin); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /LLD/ATM/src/main/java/org/anuva04/Enums/Denomination.java: -------------------------------------------------------------------------------- 1 | package org.anuva04.Enums; 2 | 3 | public enum Denomination { 4 | TWO_THOUSAND, THOUSAND, FIVE_HUNDRED, HUNDRED, FIFTY 5 | } 6 | -------------------------------------------------------------------------------- /LLD/ATM/src/main/java/org/anuva04/Enums/PaymentNetwork.java: -------------------------------------------------------------------------------- 1 | package org.anuva04.Enums; 2 | 3 | public enum PaymentNetwork { 4 | VISA, MASTERCARD, RUPAY 5 | } 6 | -------------------------------------------------------------------------------- /LLD/ATM/src/main/java/org/anuva04/Main.java: -------------------------------------------------------------------------------- 1 | package org.anuva04; 2 | 3 | import org.anuva04.Controllers.ATM; 4 | import org.anuva04.Enums.Denomination; 5 | import org.anuva04.Models.AtmCards.AtmCard; 6 | import org.anuva04.Models.AtmCards.SbiRupayCard; 7 | import org.anuva04.Models.CashReserve; 8 | import org.anuva04.Strategies.SimpleCashDispenseStrategy; 9 | 10 | import java.time.LocalDate; 11 | import java.util.Scanner; 12 | 13 | public class Main { 14 | 15 | // Method to simulate insertion of cash in ATM 16 | public static void handleModifyCashReserve(ATM atm) { 17 | Scanner sc = new Scanner(System.in); 18 | System.out.println("Enter details to modify cash reserve... Enter q to exit: "); 19 | CashReserve request = new CashReserve(); 20 | while (true) { 21 | System.out.print("Enter denomination type (TWO_THOUSAND, THOUSAND, FIVE_HUNDRED, HUNDRED, FIFTY) or \"q\" to exit: "); 22 | String input = sc.next(); 23 | if (input.equalsIgnoreCase("q")) { 24 | break; 25 | } 26 | Denomination denomination = Denomination.valueOf(input.toUpperCase()); 27 | System.out.println("Enter count: "); 28 | Integer count = sc.nextInt(); 29 | try { 30 | request.updateCash(denomination, count); 31 | } catch (Exception e) { 32 | System.out.println(e.getMessage()); 33 | } 34 | } 35 | atm.modifyCashReserve(request); 36 | } 37 | 38 | // Running main method means ATM is up and running 39 | public static void main(String[] args) { 40 | ATM atm = new ATM(); 41 | atm.setCashDispenserStrategy(new SimpleCashDispenseStrategy()); 42 | Scanner sc = new Scanner(System.in); 43 | 44 | while (true) { 45 | System.out.println("1: User login\n2: Admin login"); 46 | int loginType = sc.nextInt(); 47 | switch (loginType) { 48 | case 1: 49 | // ATM card insertion stage 50 | AtmCard dummyAtmCard = new SbiRupayCard("1234321", 999, "Anuva", LocalDate.now()); 51 | atm.startTransaction(dummyAtmCard); 52 | break; 53 | case 2: 54 | System.out.print("Enter admin password: "); 55 | String input = sc.next(); 56 | if (atm.adminLogin(input)) { 57 | System.out.println("Admin Logged in successfully!"); 58 | handleModifyCashReserve(atm); 59 | } else { 60 | System.out.println("Invalid password!"); 61 | } 62 | break; 63 | default: 64 | System.out.println("Invalid option!"); 65 | break; 66 | } 67 | } 68 | 69 | } 70 | } -------------------------------------------------------------------------------- /LLD/ATM/src/main/java/org/anuva04/Models/AtmCards/AtmCard.java: -------------------------------------------------------------------------------- 1 | package org.anuva04.Models.AtmCards; 2 | 3 | import org.anuva04.Enums.PaymentNetwork; 4 | import org.anuva04.Models.Bank; 5 | 6 | import java.time.LocalDate; 7 | 8 | public class AtmCard { 9 | private String cardNumber; 10 | private int CVV; 11 | private PaymentNetwork paymentNetwork; 12 | private String ownerName; 13 | private LocalDate expiryDate; 14 | private Bank bank; 15 | 16 | public AtmCard(String cardNumber, int CVV, String ownerName, LocalDate expiryDate) { 17 | this.cardNumber = cardNumber; 18 | this.CVV = CVV; 19 | this.ownerName = ownerName; 20 | this.expiryDate = expiryDate; 21 | } 22 | 23 | public void setPaymentNetwork(PaymentNetwork paymentNetwork) { 24 | this.paymentNetwork = paymentNetwork; 25 | } 26 | 27 | public void setBank(Bank bank) { 28 | this.bank = bank; 29 | } 30 | 31 | public Bank getBank() { 32 | return this.bank; 33 | } 34 | 35 | public String getCardNumber() { 36 | return cardNumber; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /LLD/ATM/src/main/java/org/anuva04/Models/AtmCards/AxisVisaCard.java: -------------------------------------------------------------------------------- 1 | package org.anuva04.Models.AtmCards; 2 | 3 | import org.anuva04.Enums.PaymentNetwork; 4 | import org.anuva04.Models.Bank; 5 | 6 | import java.time.LocalDate; 7 | 8 | public class AxisVisaCard extends AtmCard { 9 | public AxisVisaCard(String cardNumber, int CVV, String ownerName, LocalDate expiryDate) { 10 | super(cardNumber, CVV, ownerName, expiryDate); 11 | this.setBank(new Bank("Axis Bank")); 12 | this.setPaymentNetwork(PaymentNetwork.VISA); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /LLD/ATM/src/main/java/org/anuva04/Models/AtmCards/SbiRupayCard.java: -------------------------------------------------------------------------------- 1 | package org.anuva04.Models.AtmCards; 2 | 3 | import org.anuva04.Enums.PaymentNetwork; 4 | import org.anuva04.Models.Bank; 5 | 6 | import java.time.LocalDate; 7 | 8 | public class SbiRupayCard extends AtmCard { 9 | public SbiRupayCard(String cardNumber, int CVV, String ownerName, LocalDate expiryDate) { 10 | super(cardNumber, CVV, ownerName, expiryDate); 11 | this.setBank(new Bank("State Bank of India")); 12 | this.setPaymentNetwork(PaymentNetwork.RUPAY); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /LLD/ATM/src/main/java/org/anuva04/Models/Bank.java: -------------------------------------------------------------------------------- 1 | package org.anuva04.Models; 2 | 3 | public class Bank { 4 | private String bankName; 5 | 6 | public Bank(String bankName) { 7 | this.bankName = bankName; 8 | } 9 | 10 | public boolean verifyAtmPin(String cardNumber, int pin) { 11 | // Pin verification logic here 12 | return true; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /LLD/ATM/src/main/java/org/anuva04/Models/CashAvailabilityDataResponseModel.java: -------------------------------------------------------------------------------- 1 | package org.anuva04.Models; 2 | 3 | public class CashAvailabilityDataResponseModel { 4 | public boolean isCashAvailable; 5 | public CashReserve cashReserve; 6 | 7 | // Response returned by getCashAvailability method Cash dispensing strategy 8 | public CashAvailabilityDataResponseModel(boolean isCashAvailable, CashReserve cashReserve) { 9 | this.isCashAvailable = isCashAvailable; 10 | this.cashReserve = cashReserve; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /LLD/ATM/src/main/java/org/anuva04/Models/CashReserve.java: -------------------------------------------------------------------------------- 1 | package org.anuva04.Models; 2 | 3 | import org.anuva04.Enums.Denomination; 4 | 5 | import java.util.HashMap; 6 | 7 | public class CashReserve { 8 | private HashMap cashReserveMap; 9 | 10 | public void modifyCashReserve(CashReserve newCashReserve) { 11 | newCashReserve.cashReserveMap.forEach((currency, count) -> { 12 | this.cashReserveMap.put(currency, this.cashReserveMap.get(currency) + count); 13 | }); 14 | } 15 | 16 | public CashReserve() { 17 | cashReserveMap = new HashMap<>(); 18 | for (Denomination denomination : Denomination.values()) { 19 | cashReserveMap.put(denomination, 0); 20 | } 21 | } 22 | 23 | public HashMap getCashReserveMap() { 24 | return cashReserveMap; 25 | } 26 | 27 | public void updateCash(Denomination denomination, Integer count) { 28 | cashReserveMap.put(denomination, count); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /LLD/ATM/src/main/java/org/anuva04/Models/Currency.java: -------------------------------------------------------------------------------- 1 | package org.anuva04.Models; 2 | 3 | import org.anuva04.Enums.Denomination; 4 | 5 | public class Currency { 6 | public static int getValue(Denomination denomination) { 7 | switch (denomination) { 8 | case FIFTY: 9 | return 50; 10 | case HUNDRED: 11 | return 100; 12 | case FIVE_HUNDRED: 13 | return 500; 14 | case THOUSAND: 15 | return 1000; 16 | case TWO_THOUSAND: 17 | return 2000; 18 | default: 19 | throw new IllegalArgumentException("Denomination not supported!"); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /LLD/ATM/src/main/java/org/anuva04/Strategies/CashDispenserStrategy.java: -------------------------------------------------------------------------------- 1 | package org.anuva04.Strategies; 2 | 3 | import org.anuva04.Models.CashAvailabilityDataResponseModel; 4 | import org.anuva04.Models.CashReserve; 5 | 6 | public interface CashDispenserStrategy { 7 | CashReserve dispenseCash(CashReserve cashReserve, CashReserve cashRequest); 8 | 9 | CashAvailabilityDataResponseModel getCashAvailability(CashReserve cashReserve, int amount); 10 | } 11 | -------------------------------------------------------------------------------- /LLD/ATM/src/main/java/org/anuva04/Strategies/SimpleCashDispenseStrategy.java: -------------------------------------------------------------------------------- 1 | package org.anuva04.Strategies; 2 | 3 | import org.anuva04.Enums.Denomination; 4 | import org.anuva04.Models.CashAvailabilityDataResponseModel; 5 | import org.anuva04.Models.CashReserve; 6 | import org.anuva04.Models.Currency; 7 | 8 | import java.util.Map; 9 | 10 | // Works for denomination [50, 100, 500, 1000, 2000] 11 | public class SimpleCashDispenseStrategy implements CashDispenserStrategy { 12 | @Override 13 | public CashReserve dispenseCash(CashReserve cashReserve, CashReserve cashRequest) { 14 | cashRequest.getCashReserveMap().forEach((key, value) -> { 15 | cashReserve.getCashReserveMap().put(key, cashReserve.getCashReserveMap().get(key) - value); 16 | }); 17 | return cashRequest; 18 | } 19 | 20 | @Override 21 | public CashAvailabilityDataResponseModel getCashAvailability(CashReserve cashReserve, int amount) { 22 | boolean isCashAvailable = false; 23 | CashReserve result = new CashReserve(); 24 | 25 | for (Map.Entry entry : cashReserve.getCashReserveMap().entrySet()) { 26 | Denomination denomination = entry.getKey(); 27 | Integer count = entry.getValue(); 28 | 29 | while (count > 0 && amount > 0 && Currency.getValue(denomination) <= amount) { 30 | amount -= Currency.getValue(denomination); 31 | count -= 1; 32 | if (result.getCashReserveMap().containsKey(denomination)) { 33 | result.getCashReserveMap().put(denomination, result.getCashReserveMap().get(denomination) + 1); 34 | } else { 35 | result.getCashReserveMap().put(denomination, 1); 36 | } 37 | } 38 | } 39 | 40 | if (amount == 0) isCashAvailable = true; 41 | 42 | return new CashAvailabilityDataResponseModel(isCashAvailable, result); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /LLD/ElevatorSystem/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | !**/src/main/**/target/ 4 | !**/src/test/**/target/ 5 | 6 | ### IntelliJ IDEA ### 7 | .idea/modules.xml 8 | .idea/jarRepositories.xml 9 | .idea/compiler.xml 10 | .idea/libraries/ 11 | *.iws 12 | *.iml 13 | *.ipr 14 | 15 | ### Eclipse ### 16 | .apt_generated 17 | .classpath 18 | .factorypath 19 | .project 20 | .settings 21 | .springBeans 22 | .sts4-cache 23 | 24 | ### NetBeans ### 25 | /nbproject/private/ 26 | /nbbuild/ 27 | /dist/ 28 | /nbdist/ 29 | /.nb-gradle/ 30 | build/ 31 | !**/src/main/**/build/ 32 | !**/src/test/**/build/ 33 | 34 | ### VS Code ### 35 | .vscode/ 36 | 37 | ### Mac OS ### 38 | .DS_Store -------------------------------------------------------------------------------- /LLD/ElevatorSystem/.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /LLD/ElevatorSystem/.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /LLD/ElevatorSystem/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /LLD/ElevatorSystem/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /LLD/ElevatorSystem/README.md: -------------------------------------------------------------------------------- 1 | # Elevator System 2 | 3 | The Elevator System is a program that simulates an elevator control system. It includes classes for managing elevators, different elevator types, and strategies for allocating elevators to requests. 4 | 5 | ## ElevatorSystem 6 | 7 | The `ElevatorSystem` class is the main controller class for the elevator system. It maintains a list of elevators, handles elevator requests from external switches, and processes these requests using an elevator allocation strategy. 8 | 9 | ### Class Structure 10 | 11 | - `ElevatorSystem` 12 | - `elevatorList`: A list of elevators in the system. 13 | - `numElevators`: The number of elevators currently in the system. 14 | - `requests`: A queue to store elevator requests from external switches. 15 | - `requestProcessorScheduler`: A scheduled executor service for processing elevator requests. 16 | - `allocationStrategy`: The elevator allocation strategy used to assign elevators to requests. 17 | 18 | ### Methods 19 | 20 | - `addElevator(ElevatorType type)`: Adds a new elevator of the specified type to the system. 21 | - `requestElevator(int fromFloor)`: Simulates an external switch requesting an elevator from a specific floor. 22 | - `addStop(int elevatorId, int floor)`: Simulates an internal switch in an elevator, adding a stop at a specific floor. 23 | - `requestProcessor()`: Processes elevator requests from the queue and assigns them to the appropriate elevator based on the allocation strategy. 24 | - `getStatus()`: Prints the current status of all elevators in the system. 25 | - `setAllocationStrategy(ElevatorAllocationStrategy allocationStrategy)`: Sets the elevator allocation strategy for the system. 26 | 27 | ## Elevator 28 | 29 | The `Elevator` class represents an elevator in the system. It maintains information such as the current floor, direction, capacity, and stops requested by internal switches. 30 | 31 | ### Class Structure 32 | 33 | - `Elevator` 34 | - `requestProcessorScheduler`: A scheduled executor service for processing elevator requests. 35 | - `id`: The unique identifier of the elevator. 36 | - `currentFloor`: The current floor where the elevator is located. 37 | - `status`: The status of the elevator (e.g., working, out of service). 38 | - `floors`: A list to track the floors where the elevator has stops. 39 | - `direction`: The direction in which the elevator is moving. 40 | - `maxCapacity`: The maximum capacity of the elevator. 41 | - `currentCapacity`: The current number of passengers in the elevator. 42 | 43 | ### Methods 44 | 45 | - `addStop(int floor)`: Simulates an internal switch in the elevator, adding a stop at a specific floor. 46 | - `requestProcessor()`: Processes the stops requested by internal switches and updates the elevator's state accordingly. 47 | - `getStatus()`: Gets the status of the elevator. 48 | - Getter and setter methods for various properties of the elevator. 49 | 50 | ## ElevatorAllocationStrategy 51 | 52 | The `ElevatorAllocationStrategy` interface defines the contract for different strategies used to allocate elevators to requests. It includes a single method, `getElevator`, which takes a list of elevators and a floor as input and returns the ID of the elevator that should be assigned to the request. 53 | 54 | ### Interface Structure 55 | 56 | - `ElevatorAllocationStrategy` 57 | - `getElevator(List elevatorList, int floor)`: Assigns an elevator from the given list to the specified floor. 58 | 59 | ### SimpleElevatorAllocationStrategy 60 | 61 | The `SimpleElevatorAllocationStrategy` class is an implementation of the `ElevatorAllocationStrategy` interface. It provides a simple strategy for allocating elevators based on certain criteria. 62 | 63 | #### Class Structure 64 | 65 | - `SimpleElevatorAllocationStrategy` 66 | 67 | #### Methods 68 | 69 | - `getElevator(List elevatorList, int floor)`: Assigns the elevator with the closest distance to the specified floor, considering factors such as elevator direction and current capacity. 70 | --- 71 | #### Java Version 72 | 73 | The code is written in Java 11. 74 | 75 | #### Build System 76 | 77 | The build system used for this project is Maven. Maven provides a standard project structure and handles dependencies, compilation, and packaging. 78 | 79 | To build the project using Maven, navigate to the root directory of the project and run the following command: 80 | 81 | `mvn clean install` 82 | 83 | This will compile the source code, run tests, and package the project into a JAR file. 84 | 85 | --- 86 | 87 | ### Scope for developments 88 | - Error Handling and Exception Handling: The code lacks explicit error handling and exception handling mechanisms. Adding appropriate error handling and exception handling would make the code more robust and reliable. 89 | - Improve Logging: Instead of using System.out.println() for logging, consider using a logging framework such as Log4j or java.util.logging. This allows for more advanced logging capabilities, such as log levels, log file rotation, and customizable log formats. 90 | - Implement Unit Tests: Write unit tests to verify the functionality of the classes and ensure that they work as expected. This will help catch bugs early and make it easier to maintain and refactor the code in the future. 91 | -------------------------------------------------------------------------------- /LLD/ElevatorSystem/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.anuva04 8 | ElevatorSystem 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 11 13 | 11 14 | UTF-8 15 | 16 | 17 | -------------------------------------------------------------------------------- /LLD/ElevatorSystem/src/main/java/org/anuva04/Controllers/ElevatorSystem.java: -------------------------------------------------------------------------------- 1 | package org.anuva04.Controllers; 2 | 3 | import org.anuva04.Enums.ElevatorType; 4 | import org.anuva04.Models.AccelElevator; 5 | import org.anuva04.Models.ClassicElevator; 6 | import org.anuva04.Models.Elevator; 7 | import org.anuva04.Models.ServiceElevator; 8 | import org.anuva04.Strategies.ElevatorAllocationStrategy; 9 | 10 | import java.util.ArrayDeque; 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | import java.util.concurrent.Executors; 14 | import java.util.concurrent.ScheduledExecutorService; 15 | import java.util.concurrent.TimeUnit; 16 | 17 | public class ElevatorSystem { 18 | private final List elevatorList = new ArrayList<>(); 19 | private int numElevators = 0; 20 | private final ArrayDeque requests = new ArrayDeque<>(); 21 | private final ScheduledExecutorService requestProcessorScheduler; 22 | private ElevatorAllocationStrategy allocationStrategy; 23 | 24 | public ElevatorSystem() { 25 | this.requestProcessorScheduler = Executors.newSingleThreadScheduledExecutor(); 26 | this.requestProcessorScheduler.scheduleAtFixedRate(this::requestProcessor, 100, 100, TimeUnit.MILLISECONDS); 27 | } 28 | 29 | // Add new elevator of required type to Controllers.ElevatorSystem 30 | public void addElevator(ElevatorType type) { 31 | System.out.println("Received request to add elevator of type:" + type); 32 | this.numElevators++; 33 | switch (type) { 34 | case CLASSIC: 35 | this.elevatorList.add(new ClassicElevator(this.numElevators)); 36 | break; 37 | case SERVICE: 38 | this.elevatorList.add(new ServiceElevator(this.numElevators)); 39 | break; 40 | case ACCEL: 41 | this.elevatorList.add(new AccelElevator(this.numElevators)); 42 | break; 43 | } 44 | } 45 | 46 | // Simulates external switch to request for elevator from any floor 47 | public void requestElevator(int fromFloor) { 48 | this.requests.add(fromFloor); 49 | } 50 | 51 | // Simulates internal switch in an elevator to stop at any floor 52 | public void addStop(int elevatorId, int floor) { 53 | this.elevatorList.get(elevatorId - 1).addStop(floor); 54 | } 55 | 56 | // Method to process all requests coming from external switches 57 | private void requestProcessor() { 58 | Integer floor = this.requests.poll(); 59 | if (floor != null) { 60 | System.out.println("Got elevator request for floor: " + floor); 61 | int closestElevatorId = this.allocationStrategy.getElevator(elevatorList, floor); 62 | if (closestElevatorId == 0) { 63 | System.out.println("No elevator available, trying again in a while!"); 64 | this.requests.addFirst(floor); 65 | return; 66 | } 67 | System.out.println("Found closest elevator: " + closestElevatorId); 68 | this.elevatorList.get(closestElevatorId - 1).addStop(floor); 69 | } 70 | } 71 | 72 | // Method to print current status of all the elevators 73 | public void getStatus() { 74 | System.out.println("Status of all elevators:"); 75 | for (Elevator elevator : this.elevatorList) { 76 | System.out.println("Models.Elevator ID: " + elevator.getId() + 77 | " | Current Floor: " + elevator.getCurrentFloor() + 78 | " | Direction: " + elevator.getDirection()); 79 | } 80 | } 81 | 82 | public void setAllocationStrategy(ElevatorAllocationStrategy allocationStrategy) { 83 | this.allocationStrategy = allocationStrategy; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /LLD/ElevatorSystem/src/main/java/org/anuva04/Enums/ElevatorDirection.java: -------------------------------------------------------------------------------- 1 | package org.anuva04.Enums; 2 | 3 | public enum ElevatorDirection { 4 | UP, DOWN 5 | } 6 | -------------------------------------------------------------------------------- /LLD/ElevatorSystem/src/main/java/org/anuva04/Enums/ElevatorStatus.java: -------------------------------------------------------------------------------- 1 | package org.anuva04.Enums; 2 | 3 | public enum ElevatorStatus { 4 | WORKING, NOT_WORKING 5 | } 6 | -------------------------------------------------------------------------------- /LLD/ElevatorSystem/src/main/java/org/anuva04/Enums/ElevatorType.java: -------------------------------------------------------------------------------- 1 | package org.anuva04.Enums; 2 | 3 | public enum ElevatorType { 4 | CLASSIC, SERVICE, ACCEL 5 | } 6 | -------------------------------------------------------------------------------- /LLD/ElevatorSystem/src/main/java/org/anuva04/Main.java: -------------------------------------------------------------------------------- 1 | package org.anuva04; 2 | 3 | import org.anuva04.Controllers.ElevatorSystem; 4 | import org.anuva04.Enums.ElevatorType; 5 | import org.anuva04.Strategies.SimpleElevatorAllocationStrategy; 6 | 7 | import java.util.Scanner; 8 | 9 | public class Main { 10 | public static void main(String[] args) { 11 | ElevatorSystem elevatorSystem = new ElevatorSystem(); 12 | elevatorSystem.setAllocationStrategy(new SimpleElevatorAllocationStrategy()); 13 | System.out.println("For elevator status - 1"); 14 | System.out.println("For requesting elevator - 2"); 15 | System.out.println("For adding stop - 3"); 16 | System.out.println("For adding new elevator - 4"); 17 | Scanner scanner = new Scanner(System.in); 18 | int floor; 19 | while (true) { 20 | int req = scanner.nextInt(); 21 | switch(req){ 22 | case 1: 23 | elevatorSystem.getStatus(); 24 | break; 25 | case 2: 26 | System.out.print("Type floor number: "); 27 | floor = scanner.nextInt(); 28 | elevatorSystem.requestElevator(floor); 29 | break; 30 | case 3: 31 | System.out.print("Type floor number: "); 32 | floor = scanner.nextInt(); 33 | System.out.print("Type elevator number: "); 34 | int elevator = scanner.nextInt(); 35 | elevatorSystem.addStop(elevator, floor); 36 | break; 37 | case 4: 38 | System.out.print("Enter type of elevator (CLASSIC/SERVICE/ACCEL): "); 39 | String type = scanner.next(); 40 | ElevatorType elevatorType; 41 | try { 42 | elevatorType = ElevatorType.valueOf(type.toUpperCase()); 43 | elevatorSystem.addElevator(elevatorType); 44 | } catch (IllegalArgumentException e) { 45 | System.out.println("Invalid elevator type: " + type); 46 | } 47 | break; 48 | default: 49 | System.out.println("Invalid input!!!"); 50 | } 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /LLD/ElevatorSystem/src/main/java/org/anuva04/Models/AccelElevator.java: -------------------------------------------------------------------------------- 1 | package org.anuva04.Models; 2 | 3 | import java.util.concurrent.TimeUnit; 4 | 5 | public class AccelElevator extends Elevator { 6 | public AccelElevator(int id) { 7 | super(id); 8 | this.setMaxCapacity(500); 9 | this.requestProcessorScheduler.scheduleAtFixedRate(this::requestProcessor, 100, 100, TimeUnit.MILLISECONDS); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /LLD/ElevatorSystem/src/main/java/org/anuva04/Models/ClassicElevator.java: -------------------------------------------------------------------------------- 1 | package org.anuva04.Models; 2 | 3 | import java.util.concurrent.TimeUnit; 4 | 5 | public class ClassicElevator extends Elevator { 6 | public ClassicElevator(int id) { 7 | super(id); 8 | this.setMaxCapacity(500); 9 | this.requestProcessorScheduler.scheduleAtFixedRate(this::requestProcessor, 100, 1000, TimeUnit.MILLISECONDS); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /LLD/ElevatorSystem/src/main/java/org/anuva04/Models/Elevator.java: -------------------------------------------------------------------------------- 1 | package org.anuva04.Models; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collections; 5 | import java.util.List; 6 | import java.util.Random; 7 | import java.util.concurrent.Executors; 8 | import java.util.concurrent.ScheduledExecutorService; 9 | 10 | import org.anuva04.Enums.ElevatorStatus; 11 | import org.anuva04.Enums.ElevatorDirection; 12 | import org.anuva04.Utils.Constants; 13 | 14 | public class Elevator { 15 | protected ScheduledExecutorService requestProcessorScheduler = Executors.newSingleThreadScheduledExecutor(); 16 | private final int id; 17 | private int currentFloor; 18 | private final ElevatorStatus status; 19 | private final List floors; 20 | private ElevatorDirection direction; 21 | private int maxCapacity; 22 | private int currentCapacity = 0; 23 | 24 | public Elevator(int id) { 25 | this.id = id; 26 | this.currentFloor = 0; 27 | this.status = ElevatorStatus.WORKING; 28 | this.direction = ElevatorDirection.UP; 29 | this.floors = new ArrayList<>(Collections.nCopies(Constants.NUM_FLOORS, false)); 30 | } 31 | 32 | // Simulates internal switch in an elevator to stop at any floor 33 | public void addStop(int floor) { 34 | System.out.println("Got request to stop at floor: " + floor + " for elevator ID: " + this.id); 35 | this.floors.set(floor, true); 36 | } 37 | 38 | // Method to process all requests coming from internal switches of the elevator 39 | protected void requestProcessor() { 40 | if (this.currentFloor == 0) this.direction = ElevatorDirection.UP; 41 | if (this.currentFloor == Constants.NUM_FLOORS - 1) this.direction = ElevatorDirection.DOWN; 42 | if (this.direction == ElevatorDirection.UP) { 43 | if (findAndProcessRequest(ElevatorDirection.UP)) return; 44 | if (findAndProcessRequest(ElevatorDirection.DOWN)) { 45 | this.direction = ElevatorDirection.DOWN; 46 | } 47 | } else { 48 | if (findAndProcessRequest(ElevatorDirection.DOWN)) return; 49 | if (findAndProcessRequest(ElevatorDirection.UP)) { 50 | this.direction = ElevatorDirection.UP; 51 | } 52 | } 53 | } 54 | 55 | private boolean findAndProcessRequest(ElevatorDirection direction) { 56 | if (direction == ElevatorDirection.UP) { 57 | for (int floor = this.currentFloor; floor < Constants.NUM_FLOORS; floor++) { 58 | if (this.floors.get(floor)) { 59 | this.updateElevatorInfo(floor); 60 | return true; 61 | } 62 | } 63 | } else { 64 | for (int floor = this.currentFloor; floor >= 0; floor--) { 65 | if (this.floors.get(floor)) { 66 | this.updateElevatorInfo(floor); 67 | return true; 68 | } 69 | } 70 | } 71 | return false; 72 | } 73 | 74 | private void updateElevatorInfo(int floor) { 75 | this.floors.set(floor, false); 76 | this.currentFloor = floor; 77 | Random random = new Random(); 78 | int randomNumber = random.nextInt(this.maxCapacity + 30); 79 | this.setCurrentCapacity(randomNumber); 80 | } 81 | 82 | public ElevatorStatus getStatus() { 83 | return status; 84 | } 85 | 86 | public ElevatorDirection getDirection() { 87 | return direction; 88 | } 89 | 90 | public int getId() { 91 | return id; 92 | } 93 | 94 | public int getCurrentFloor() { 95 | return currentFloor; 96 | } 97 | 98 | public int getCurrentCapacity() { 99 | return currentCapacity; 100 | } 101 | 102 | public void setCurrentCapacity(int currentCapacity) { 103 | this.currentCapacity = currentCapacity; 104 | } 105 | 106 | public int getMaxCapacity() { 107 | return maxCapacity; 108 | } 109 | 110 | public void setMaxCapacity(int maxCapacity) { 111 | this.maxCapacity = maxCapacity; 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /LLD/ElevatorSystem/src/main/java/org/anuva04/Models/ServiceElevator.java: -------------------------------------------------------------------------------- 1 | package org.anuva04.Models; 2 | 3 | import java.util.concurrent.TimeUnit; 4 | 5 | public class ServiceElevator extends Elevator { 6 | public ServiceElevator(int id) { 7 | super(id); 8 | this.setMaxCapacity(1000); 9 | this.requestProcessorScheduler.scheduleAtFixedRate(this::requestProcessor, 100, 1000, TimeUnit.MILLISECONDS); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /LLD/ElevatorSystem/src/main/java/org/anuva04/Strategies/ElevatorAllocationStrategy.java: -------------------------------------------------------------------------------- 1 | package org.anuva04.Strategies; 2 | 3 | import org.anuva04.Models.Elevator; 4 | import java.util.List; 5 | 6 | public interface ElevatorAllocationStrategy { 7 | public int getElevator(List elevatorList, int floor); 8 | } 9 | -------------------------------------------------------------------------------- /LLD/ElevatorSystem/src/main/java/org/anuva04/Strategies/SimpleElevatorAllocationStrategy.java: -------------------------------------------------------------------------------- 1 | package org.anuva04.Strategies; 2 | 3 | import org.anuva04.Enums.ElevatorDirection; 4 | import org.anuva04.Models.Elevator; 5 | import org.anuva04.Enums.ElevatorStatus; 6 | import org.anuva04.Utils.Constants; 7 | 8 | import java.util.List; 9 | 10 | public class SimpleElevatorAllocationStrategy implements ElevatorAllocationStrategy{ 11 | @Override 12 | public int getElevator(List elevatorList, int floor) { 13 | int closestElevatorId = 0; 14 | int closestDistance = Integer.MAX_VALUE; 15 | for (Elevator elevator : elevatorList) { 16 | if (elevator.getStatus() == ElevatorStatus.NOT_WORKING) continue; 17 | if (elevator.getCurrentCapacity() >= elevator.getMaxCapacity() - 30) continue; 18 | int distance; 19 | if (elevator.getDirection() == ElevatorDirection.UP) { 20 | if (floor >= elevator.getCurrentFloor()) { 21 | distance = floor - elevator.getCurrentFloor(); 22 | } else { 23 | distance = (Constants.NUM_FLOORS - elevator.getCurrentFloor()) + (Constants.NUM_FLOORS - floor); 24 | } 25 | } else { 26 | if (floor <= elevator.getCurrentFloor()) { 27 | distance = elevator.getCurrentFloor() - floor; 28 | } else { 29 | distance = elevator.getCurrentFloor() + floor; 30 | } 31 | } 32 | if (distance < closestDistance) { 33 | closestElevatorId = elevator.getId(); 34 | closestDistance = distance; 35 | } 36 | } 37 | return closestElevatorId; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /LLD/ElevatorSystem/src/main/java/org/anuva04/Utils/Constants.java: -------------------------------------------------------------------------------- 1 | package org.anuva04.Utils; 2 | 3 | public class Constants { 4 | public static int NUM_FLOORS = 20; 5 | } 6 | -------------------------------------------------------------------------------- /LLD/MeetingScheduler/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | !**/src/main/**/build/ 5 | !**/src/test/**/build/ 6 | 7 | ### IntelliJ IDEA ### 8 | .idea/modules.xml 9 | .idea/jarRepositories.xml 10 | .idea/compiler.xml 11 | .idea/libraries/ 12 | *.iws 13 | *.iml 14 | *.ipr 15 | out/ 16 | !**/src/main/**/out/ 17 | !**/src/test/**/out/ 18 | 19 | ### Eclipse ### 20 | .apt_generated 21 | .classpath 22 | .factorypath 23 | .project 24 | .settings 25 | .springBeans 26 | .sts4-cache 27 | bin/ 28 | !**/src/main/**/bin/ 29 | !**/src/test/**/bin/ 30 | 31 | ### NetBeans ### 32 | /nbproject/private/ 33 | /nbbuild/ 34 | /dist/ 35 | /nbdist/ 36 | /.nb-gradle/ 37 | 38 | ### VS Code ### 39 | .vscode/ 40 | 41 | ### Mac OS ### 42 | .DS_Store -------------------------------------------------------------------------------- /LLD/MeetingScheduler/.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /LLD/MeetingScheduler/.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 16 | 17 | -------------------------------------------------------------------------------- /LLD/MeetingScheduler/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /LLD/MeetingScheduler/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /LLD/MeetingScheduler/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("java") 3 | } 4 | 5 | group = "org.anuva04" 6 | version = "1.0-SNAPSHOT" 7 | 8 | repositories { 9 | mavenCentral() 10 | } 11 | 12 | dependencies { 13 | testImplementation(platform("org.junit:junit-bom:5.9.1")) 14 | testImplementation("org.junit.jupiter:junit-jupiter") 15 | } 16 | 17 | tasks.test { 18 | useJUnitPlatform() 19 | } -------------------------------------------------------------------------------- /LLD/MeetingScheduler/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anuva04/SystemDesign/2338ea19d4f60812e6e37206f7b13a150e4ca78a/LLD/MeetingScheduler/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /LLD/MeetingScheduler/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sun Jul 16 14:10:46 IST 2023 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /LLD/MeetingScheduler/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /LLD/MeetingScheduler/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "MeetingScheduler" 2 | 3 | -------------------------------------------------------------------------------- /LLD/MeetingScheduler/src/main/java/org/anuva04/Controllers/CommandLineParser.java: -------------------------------------------------------------------------------- 1 | package org.anuva04.Controllers; 2 | 3 | import org.anuva04.Utils.Parsers.*; 4 | import org.anuva04.Utils.Constants; 5 | 6 | import java.util.Map; 7 | 8 | public class CommandLineParser { 9 | // Method to determine command type and call appropriate CommandParser instance 10 | public static String parseAndExecuteCommand(String command, Map args) { 11 | AbstractCommandParser parser; 12 | switch (command) { 13 | case Constants.CREATE_MEETING: 14 | parser = new CreateMeetingCommandParser(); 15 | break; 16 | case Constants.DELETE_MEETING: 17 | parser = new DeleteMeetingCommandParser(); 18 | break; 19 | case Constants.GET_MEETINGS: 20 | parser = new GetMeetingsForUsernameForDateCommandParser(); 21 | break; 22 | case Constants.CHANGE_LOCATION: 23 | parser = new ChangeMeetingLocationCommandParser(); 24 | break; 25 | case Constants.CHANGE_TIME: 26 | parser = new ChangeMeetingTimeCommandParser(); 27 | break; 28 | case Constants.FORWARD_MEETING: 29 | parser = new ForwardMeetingCommandParser(); 30 | break; 31 | case Constants.SHOW_CALENDAR: 32 | parser = new ShowCalendarCommandParser(); 33 | break; 34 | default: 35 | throw new IllegalArgumentException("Command doesn't exist."); 36 | } 37 | String error = parser.validate(args); 38 | if(error == null) { 39 | parser.callMethod(args); 40 | } 41 | return error; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /LLD/MeetingScheduler/src/main/java/org/anuva04/Controllers/MeetingScheduler.java: -------------------------------------------------------------------------------- 1 | package org.anuva04.Controllers; 2 | 3 | import org.anuva04.Models.MeetingDb; 4 | 5 | import java.util.*; 6 | 7 | // Entry point to a Meeting Scheduler app 8 | public class MeetingScheduler { 9 | public MeetingDb meetingDb; 10 | 11 | public MeetingScheduler() { 12 | this.meetingDb = MeetingDb.getInstance(); 13 | } 14 | 15 | // Simulates calling various methods on UI 16 | public void init() { 17 | Scanner sc = new Scanner(System.in); 18 | while(true) { 19 | System.out.println("Enter command: "); 20 | String command = sc.nextLine(); 21 | Scanner scanner = new Scanner(command); 22 | List tokens = new ArrayList<>(); 23 | while (scanner.hasNext()) { 24 | tokens.add(scanner.next()); 25 | } 26 | scanner.close(); 27 | 28 | // Command 29 | String method = tokens.get(0); 30 | // List of arguments 31 | Map args = new HashMap<>(); 32 | for (int i = 1; i < tokens.size(); i += 2) { 33 | String option = tokens.get(i); 34 | String value = tokens.get(i + 1); 35 | args.put(option, value); 36 | } 37 | 38 | try { 39 | String errorMessage = CommandLineParser.parseAndExecuteCommand(method, args); 40 | if(errorMessage == null) { 41 | System.out.println("Command executed successfully."); 42 | } else { 43 | System.out.println("Command validation failed at: " + errorMessage); 44 | } 45 | } catch (Exception e) { 46 | System.out.println("An exception has occurred while executing command: " + e.getMessage()); 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /LLD/MeetingScheduler/src/main/java/org/anuva04/Main.java: -------------------------------------------------------------------------------- 1 | package org.anuva04; 2 | 3 | import org.anuva04.Controllers.MeetingScheduler; 4 | 5 | public class Main { 6 | public static void main(String[] args) { 7 | MeetingScheduler ms = new MeetingScheduler(); 8 | ms.init(); 9 | } 10 | } -------------------------------------------------------------------------------- /LLD/MeetingScheduler/src/main/java/org/anuva04/Models/Meeting.java: -------------------------------------------------------------------------------- 1 | package org.anuva04.Models; 2 | 3 | import java.time.LocalDateTime; 4 | import java.util.*; 5 | 6 | public class Meeting { 7 | public String title; 8 | public String location; 9 | public String organizer; 10 | public LocalDateTime startTime; 11 | public int duration; 12 | public UUID id; 13 | public Set invitees; 14 | 15 | public Meeting(String title, String location, String organizer, LocalDateTime startTime, int duration, UUID id) { 16 | this.title = title; 17 | this.location = location; 18 | this.organizer = organizer; 19 | this.startTime = startTime; 20 | this.duration = duration; 21 | this.id = id; 22 | this.invitees = new HashSet<>(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /LLD/MeetingScheduler/src/main/java/org/anuva04/Utils/Constants.java: -------------------------------------------------------------------------------- 1 | package org.anuva04.Utils; 2 | 3 | public class Constants { 4 | // Method names 5 | public static final String CREATE_MEETING = "create-meeting"; 6 | public static final String DELETE_MEETING = "delete-meeting"; 7 | public static final String GET_MEETINGS = "get-meetings"; 8 | public static final String CHANGE_LOCATION = "change-location"; 9 | public static final String CHANGE_TIME = "change-time"; 10 | public static final String FORWARD_MEETING = "forward-meeting"; 11 | public static final String SHOW_CALENDAR = "show-calendar"; 12 | 13 | // Method arguments 14 | public static final String ORGANIZER = "--organizer"; 15 | public static final String REQUESTER = "--requester"; 16 | public static final String START_TIME = "--startTime"; 17 | public static final String DURATION = "--duration"; 18 | public static final String LOCATION = "--location"; 19 | public static final String TITLE = "--title"; 20 | public static final String INVITEES = "--invitees"; 21 | public static final String MEETING_ID = "--meetingId"; 22 | public static final String USERNAME = "--username"; 23 | public static final String DATE = "--date"; 24 | 25 | public static final String INVITEES_DELIMITER = ","; 26 | } 27 | -------------------------------------------------------------------------------- /LLD/MeetingScheduler/src/main/java/org/anuva04/Utils/Helper.java: -------------------------------------------------------------------------------- 1 | package org.anuva04.Utils; 2 | 3 | import java.time.LocalDate; 4 | import java.time.LocalDateTime; 5 | import java.time.format.DateTimeFormatter; 6 | 7 | public class Helper { 8 | // Method to convert string to LocalDateTime 9 | public static LocalDateTime getLocalDateTime(String date) { 10 | DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm"); 11 | LocalDateTime dateTime = LocalDateTime.parse(date, formatter); 12 | return dateTime; 13 | } 14 | 15 | // Method to extract date from datetime 16 | public static LocalDate getLocalDate(String sdate) { 17 | DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); 18 | LocalDate date = LocalDate.parse(sdate, formatter); 19 | return date; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /LLD/MeetingScheduler/src/main/java/org/anuva04/Utils/Parsers/AbstractCommandParser.java: -------------------------------------------------------------------------------- 1 | package org.anuva04.Utils.Parsers; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | 6 | public abstract class AbstractCommandParser { 7 | public String validate(Map args) { 8 | for(String arg : getRequiredArgs()) { 9 | if(!args.containsKey(arg)) return arg; 10 | } 11 | return null; 12 | } 13 | 14 | protected abstract List getRequiredArgs(); 15 | public abstract void callMethod(Map args); 16 | } 17 | -------------------------------------------------------------------------------- /LLD/MeetingScheduler/src/main/java/org/anuva04/Utils/Parsers/ChangeMeetingLocationCommandParser.java: -------------------------------------------------------------------------------- 1 | package org.anuva04.Utils.Parsers; 2 | 3 | import org.anuva04.Models.MeetingDb; 4 | import org.anuva04.Utils.Constants; 5 | 6 | import java.util.List; 7 | import java.util.Map; 8 | import java.util.UUID; 9 | 10 | public class ChangeMeetingLocationCommandParser extends AbstractCommandParser { 11 | private static List requiredArgs = List.of(Constants.MEETING_ID, Constants.LOCATION, Constants.REQUESTER); 12 | 13 | @Override 14 | protected List getRequiredArgs() { 15 | return requiredArgs; 16 | } 17 | 18 | @Override 19 | public void callMethod(Map args) { 20 | MeetingDb meetingDb = MeetingDb.getInstance(); 21 | 22 | UUID meetingId = UUID.fromString(args.get(Constants.MEETING_ID)); 23 | String location = args.get(Constants.LOCATION); 24 | String requester = args.get(Constants.REQUESTER); 25 | 26 | meetingDb.changeMeetingLocation(meetingId, location, requester); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /LLD/MeetingScheduler/src/main/java/org/anuva04/Utils/Parsers/ChangeMeetingTimeCommandParser.java: -------------------------------------------------------------------------------- 1 | package org.anuva04.Utils.Parsers; 2 | 3 | import org.anuva04.Models.MeetingDb; 4 | import org.anuva04.Utils.Constants; 5 | import org.anuva04.Utils.Helper; 6 | 7 | import java.time.LocalDateTime; 8 | import java.util.List; 9 | import java.util.Map; 10 | import java.util.UUID; 11 | 12 | public class ChangeMeetingTimeCommandParser extends AbstractCommandParser { 13 | private static List requiredArgs = List.of(Constants.MEETING_ID, Constants.START_TIME, Constants.DURATION, Constants.REQUESTER); 14 | 15 | @Override 16 | protected List getRequiredArgs() { 17 | return requiredArgs; 18 | } 19 | 20 | @Override 21 | public void callMethod(Map args) { 22 | MeetingDb meetingDb = MeetingDb.getInstance(); 23 | 24 | UUID meetingId = UUID.fromString(args.get(Constants.MEETING_ID)); 25 | int duration = Integer.parseInt(args.get(Constants.DURATION)); 26 | LocalDateTime startTime = Helper.getLocalDateTime(args.get(Constants.START_TIME)); 27 | String requester = args.get(Constants.REQUESTER); 28 | 29 | meetingDb.changeMeetingTime(meetingId, startTime, duration, requester); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /LLD/MeetingScheduler/src/main/java/org/anuva04/Utils/Parsers/CreateMeetingCommandParser.java: -------------------------------------------------------------------------------- 1 | package org.anuva04.Utils.Parsers; 2 | 3 | import org.anuva04.Models.MeetingDb; 4 | import org.anuva04.Utils.Constants; 5 | import org.anuva04.Utils.Helper; 6 | 7 | import java.time.LocalDateTime; 8 | import java.util.List; 9 | import java.util.Map; 10 | 11 | public class CreateMeetingCommandParser extends AbstractCommandParser { 12 | private static List requiredArgs = List.of(Constants.ORGANIZER, Constants.START_TIME, Constants.TITLE, Constants.DURATION, Constants.LOCATION, Constants.INVITEES); 13 | 14 | @Override 15 | protected List getRequiredArgs() { 16 | return requiredArgs; 17 | } 18 | 19 | @Override 20 | public void callMethod(Map args) { 21 | MeetingDb meetingDb = MeetingDb.getInstance(); 22 | 23 | String organizer = args.get(Constants.ORGANIZER); 24 | String title = args.get(Constants.TITLE); 25 | LocalDateTime startTime = Helper.getLocalDateTime(args.get(Constants.START_TIME)); 26 | int duration = Integer.parseInt(args.get(Constants.DURATION)); 27 | String location = args.get(Constants.LOCATION); 28 | List invitees = List.of(args.get(Constants.INVITEES).split(Constants.INVITEES_DELIMITER)); 29 | 30 | meetingDb.createMeeting(organizer, startTime, duration, location, title, invitees); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /LLD/MeetingScheduler/src/main/java/org/anuva04/Utils/Parsers/DeleteMeetingCommandParser.java: -------------------------------------------------------------------------------- 1 | package org.anuva04.Utils.Parsers; 2 | 3 | import org.anuva04.Models.MeetingDb; 4 | import org.anuva04.Utils.Constants; 5 | 6 | import java.util.List; 7 | import java.util.Map; 8 | import java.util.UUID; 9 | 10 | public class DeleteMeetingCommandParser extends AbstractCommandParser { 11 | private static List requiredArgs = List.of(Constants.REQUESTER, Constants.MEETING_ID); 12 | 13 | @Override 14 | protected List getRequiredArgs() { 15 | return requiredArgs; 16 | } 17 | 18 | @Override 19 | public void callMethod(Map args) { 20 | MeetingDb meetingDb = MeetingDb.getInstance(); 21 | 22 | String requester = args.get(Constants.REQUESTER); 23 | UUID meetingId = UUID.fromString(args.get(Constants.MEETING_ID)); 24 | 25 | meetingDb.deleteMeeting(meetingId, requester); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /LLD/MeetingScheduler/src/main/java/org/anuva04/Utils/Parsers/ForwardMeetingCommandParser.java: -------------------------------------------------------------------------------- 1 | package org.anuva04.Utils.Parsers; 2 | 3 | import org.anuva04.Models.MeetingDb; 4 | import org.anuva04.Utils.Constants; 5 | 6 | import java.util.List; 7 | import java.util.Map; 8 | import java.util.UUID; 9 | 10 | public class ForwardMeetingCommandParser extends AbstractCommandParser { 11 | private static List requiredArgs = List.of(Constants.MEETING_ID, Constants.INVITEES); 12 | 13 | @Override 14 | protected List getRequiredArgs() { 15 | return requiredArgs; 16 | } 17 | 18 | @Override 19 | public void callMethod(Map args) { 20 | MeetingDb meetingDb = MeetingDb.getInstance(); 21 | 22 | UUID meetingId = UUID.fromString(args.get(Constants.MEETING_ID)); 23 | List invitees = List.of(args.get(Constants.INVITEES).split(Constants.INVITEES_DELIMITER)); 24 | 25 | meetingDb.forwardMeeting(meetingId, invitees); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /LLD/MeetingScheduler/src/main/java/org/anuva04/Utils/Parsers/GetMeetingsForUsernameForDateCommandParser.java: -------------------------------------------------------------------------------- 1 | package org.anuva04.Utils.Parsers; 2 | 3 | import org.anuva04.Models.MeetingDb; 4 | import org.anuva04.Utils.Constants; 5 | import org.anuva04.Utils.Helper; 6 | 7 | import java.time.LocalDate; 8 | import java.util.List; 9 | import java.util.Map; 10 | 11 | public class GetMeetingsForUsernameForDateCommandParser extends AbstractCommandParser { 12 | private static List requiredArgs = List.of(Constants.USERNAME, Constants.DATE); 13 | 14 | @Override 15 | protected List getRequiredArgs() { 16 | return requiredArgs; 17 | } 18 | 19 | @Override 20 | public void callMethod(Map args) { 21 | MeetingDb meetingDb = MeetingDb.getInstance(); 22 | 23 | String username = args.get(Constants.USERNAME); 24 | LocalDate date = Helper.getLocalDate(args.get(Constants.DATE)); 25 | 26 | meetingDb.getMeetingsForUsernameForDate(username, date); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /LLD/MeetingScheduler/src/main/java/org/anuva04/Utils/Parsers/ShowCalendarCommandParser.java: -------------------------------------------------------------------------------- 1 | package org.anuva04.Utils.Parsers; 2 | 3 | import org.anuva04.Models.MeetingDb; 4 | import org.anuva04.Utils.Constants; 5 | import org.anuva04.Utils.Helper; 6 | 7 | import java.time.LocalDate; 8 | import java.util.List; 9 | import java.util.Map; 10 | 11 | public class ShowCalendarCommandParser extends AbstractCommandParser { 12 | private static List requiredArgs = List.of(Constants.DATE, Constants.INVITEES); 13 | 14 | @Override 15 | protected List getRequiredArgs() { 16 | return requiredArgs; 17 | } 18 | 19 | @Override 20 | public void callMethod(Map args) { 21 | MeetingDb meetingDb = MeetingDb.getInstance(); 22 | 23 | LocalDate date = Helper.getLocalDate(args.get(Constants.DATE)); 24 | List invitees = List.of(args.get(Constants.INVITEES).split(Constants.INVITEES_DELIMITER)); 25 | 26 | meetingDb.showCalendar(date, invitees); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /LLD/MeetingScheduler/src/main/resources/Requirements.txt: -------------------------------------------------------------------------------- 1 | Meeting scheduler will work under the scope of a single organization. 2 | Functional requirements: 3 | - User should be able to schedule a meeting at a desired time with desired participants. 4 | - User can search for participants by their username. 5 | - User should be able to cancel self-created meetings. 6 | - User should be able to view meeting invites (self-created and invitations from others). 7 | - User should be able to accept/decline invites -- not implemented yet. 8 | - User should be able to check others' schedules to determine suitable time for meeting. 9 | - Schedule will be shown for next 30 days -------------------------------------------------------------------------------- /LLD/MeetingScheduler/src/main/resources/SampleInput.txt: -------------------------------------------------------------------------------- 1 | // Create new meeting by user1 2 | create-meeting --organizer user1 --startTime 2023-08-08T12:00 --duration 30 --title SampleMeeting --location MSTeams --invitees in1,in2,in3 3 | 4 | // Create another meeting for user1 5 | create-meeting --organizer user1 --startTime 2023-08-08T14:00 --duration 30 --title AnotherSampleMeeting --location MSTeams --invitees user2,in2,in4 6 | 7 | // Create new meeting for user2 8 | create-meeting --organizer user2 --startTime 2023-08-09T12:00 --duration 60 --title DifferentSampleMeeting --location MSTeams --invitees in1,in2,user1 9 | 10 | // Get meetings for user1 11 | get-meetings --username user1 --date 2023-08-08 12 | 13 | // Get meetings for user2 14 | get-meetings --username user2 --date 2023-08-08 15 | 16 | // Delete meeting by user1 17 | delete-meeting --meetingId --requester user1 18 | 19 | // Change meeting time 20 | change-time --meetingId --startTime 2023-08-15T12:00 --duration 25 --requester user2 21 | 22 | // Change meeting location 23 | change-location --meetingId --location GoogleMeet --requester user2 24 | 25 | // Show calendar 26 | show-calendar --date 2023-08-08 --invitees user1,user2 27 | 28 | // Forward meeting 29 | forward-meeting --meetingId --invitees newInvitee -------------------------------------------------------------------------------- /LLD/VendingMachine/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | !**/src/main/**/target/ 4 | !**/src/test/**/target/ 5 | 6 | ### IntelliJ IDEA ### 7 | .idea/modules.xml 8 | .idea/jarRepositories.xml 9 | .idea/compiler.xml 10 | .idea/libraries/ 11 | *.iws 12 | *.iml 13 | *.ipr 14 | 15 | ### Eclipse ### 16 | .apt_generated 17 | .classpath 18 | .factorypath 19 | .project 20 | .settings 21 | .springBeans 22 | .sts4-cache 23 | 24 | ### NetBeans ### 25 | /nbproject/private/ 26 | /nbbuild/ 27 | /dist/ 28 | /nbdist/ 29 | /.nb-gradle/ 30 | build/ 31 | !**/src/main/**/build/ 32 | !**/src/test/**/build/ 33 | 34 | ### VS Code ### 35 | .vscode/ 36 | 37 | ### Mac OS ### 38 | .DS_Store -------------------------------------------------------------------------------- /LLD/VendingMachine/.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /LLD/VendingMachine/.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /LLD/VendingMachine/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /LLD/VendingMachine/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /LLD/VendingMachine/README.md: -------------------------------------------------------------------------------- 1 | # Vending Machine LLD 2 | 3 | This is a simple implementation of a Vending Machine using Java. The Vending Machine allows users to select and purchase items, and administrators to manage the inventory. It provides a command-line interface for interaction. 4 | 5 | ## Code Files 6 | 7 | The code files are organized under the following packages: 8 | - `org.anuva04`: The main package that contains the `Main` class as the entry point of the program. 9 | - `Main.java`: **Entry point** of the program. It initializes the Vending Machine and starts the application. 10 | 11 | 12 | - `org.anuva04.Controllers`: Contains the `VendingMachine`, `UserOperations`, and `AdminOperations` classes. 13 | - `VendingMachine.java`: Simulates the display and input options (keypad) of the Vending Machine. It initializes the machine and handles user and admin operations. 14 | - `UserOperations.java`: Implements the methods available to users. It displays the available items, handles user input, and performs the transaction. 15 | - `AdminOperations.java`: Implements the methods available to administrators. It handles admin authentication and provides functionality to manage the inventory. 16 | 17 | 18 | - `org.anuva04.Models`: Contains the `Item` and `Tray` classes. 19 | - `Item.java`: Defines the `Item` class, representing a physical item that can be bought by a user. 20 | - `Tray.java`: Defines the `Tray` class, representing a tray in each slot of the Vending Machine. It contains items of the same type and manages item addition, removal, and retrieval. 21 | 22 | 23 | - `org.anuva04.Constants`: Contains the `Constants` class that provides environment variables and secrets. 24 | - `Constants.java`: Provides environment variables and secrets, such as an emergency message and an admin password. 25 | 26 | ## Usage 27 | 28 | To use the Vending Machine, follow these steps: 29 | 30 | 1. Ensure you have **JDK 11** and **Maven** installed on your system. 31 | 2. Clone the project repository and navigate to the project's root directory. 32 | 3. Build the project using the command: `mvn clean install`. 33 | 4. Run the program using the command: `mvn exec:java -Dexec.mainClass="org.anuva04.Main"`. 34 | 5. The program will display a home screen with two options: "1: User" and "2: Admin". 35 | 6. Proceed with the desired option as explained in the previous version of the README. -------------------------------------------------------------------------------- /LLD/VendingMachine/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.anuva04 8 | VendingMachine 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 11 13 | 11 14 | UTF-8 15 | 16 | 17 | -------------------------------------------------------------------------------- /LLD/VendingMachine/src/main/java/org/anuva04/Constants.java: -------------------------------------------------------------------------------- 1 | package org.anuva04; 2 | 3 | // Environment variables and secrets 4 | public class Constants { 5 | public static String EmergencyMessage = "If you face any difficulty during the transaction, please contact us at: XXXXXXXXX"; 6 | public static int AdminPass = 12345; 7 | } -------------------------------------------------------------------------------- /LLD/VendingMachine/src/main/java/org/anuva04/Controllers/AdminOperations.java: -------------------------------------------------------------------------------- 1 | package org.anuva04.Controllers; 2 | 3 | import org.anuva04.Constants; 4 | import org.anuva04.Models.Item; 5 | 6 | import java.util.Scanner; 7 | 8 | // Collection of methods available to admins 9 | public class AdminOperations { 10 | private VendingMachine vm; 11 | 12 | public AdminOperations(VendingMachine vm) { 13 | this.vm = vm; 14 | } 15 | 16 | public void init() { 17 | System.out.println("Enter card in slot"); 18 | // Card entered by admin 19 | System.out.println("Enter PIN"); 20 | Scanner sc = new Scanner(System.in); 21 | int pin = sc.nextInt(); 22 | if(pin != Constants.AdminPass) { 23 | System.out.println("Invalid PIN!"); 24 | return; 25 | } 26 | // Provide access 27 | handleAdminOperation(); 28 | } 29 | 30 | private void handleAdminOperation() { 31 | Scanner sc = new Scanner(System.in); 32 | while(true) { 33 | System.out.println("Enter slot number or 0 to exit"); 34 | int slot = sc.nextInt(); 35 | if(slot == 0) { 36 | break; 37 | } else if(slot > vm.inventory.size() || slot < 0){ 38 | System.out.println("Invalid slot number!"); 39 | break; 40 | } 41 | vm.inventory.get(slot).removeAllItems(); 42 | 43 | System.out.println("Enter item name: "); 44 | String itemName = sc.next(); 45 | System.out.println("Enter item price: "); 46 | int itemPrice = sc.nextInt(); 47 | System.out.println("Enter item quantity: "); 48 | int itemQuantity = sc.nextInt(); 49 | 50 | vm.inventory.get(slot).setPrice(itemPrice); 51 | 52 | for(int i = 1; i <= itemQuantity; i++) { 53 | boolean status = vm.inventory.get(slot).addItem(new Item(itemName, itemPrice)); 54 | if(!status) { 55 | System.out.println("Maximum capacity reached."); 56 | break; 57 | } 58 | } 59 | 60 | System.out.println("Items added to slot " + slot + " successfully!"); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /LLD/VendingMachine/src/main/java/org/anuva04/Controllers/UserOperations.java: -------------------------------------------------------------------------------- 1 | // TODO: user can cancel transaction anytime before item is dispensed 2 | 3 | package org.anuva04.Controllers; 4 | 5 | import java.util.Scanner; 6 | 7 | import org.anuva04.Constants; 8 | 9 | // Collection of methods available to users 10 | public class UserOperations { 11 | VendingMachine vm; 12 | 13 | public UserOperations(VendingMachine vm) { 14 | this.vm = vm; 15 | } 16 | public void init(){ 17 | System.out.println("Displaying all items"); 18 | for(int slot = 1; slot <= vm.numSlots; slot++){ 19 | if(vm.inventory.get(slot).peekItem() == null) continue; 20 | System.out.println("Slot: " + slot + 21 | " | price: " + vm.inventory.get(slot).getPrice() + 22 | " | item: " + vm.inventory.get(slot).peekItem().name + 23 | " | quantity: " + vm.inventory.get(slot).getItemsQuantity() 24 | ); 25 | } 26 | System.out.println("Enter slot number or 0 for exit "); 27 | Scanner sc = new Scanner(System.in); 28 | int input = sc.nextInt(); 29 | if(input == 0){ 30 | System.out.println("Thank you for visiting!"); 31 | } else if(input >= 1 && input <= vm.numSlots) { 32 | handleTransaction(input); 33 | } else { 34 | System.out.println("Invalid option!"); 35 | } 36 | } 37 | 38 | private void handleTransaction(int slot) { 39 | if(vm.inventory.get(slot).peekItem() == null) { 40 | System.out.println("Slot is empty!"); 41 | return; 42 | } 43 | 44 | if(!handleMoneyTransaction(slot)) return; 45 | 46 | // Dispense item 47 | System.out.println("Please collect item: " + vm.inventory.get(slot).dispenseItem().name); 48 | System.out.println(Constants.EmergencyMessage); 49 | } 50 | 51 | private boolean handleMoneyTransaction(int slot) { 52 | System.out.println("Please enter amount: " + vm.inventory.get(slot).getPrice()); 53 | 54 | // User can pay via cash or card 55 | Scanner sc = new Scanner(System.in); 56 | int amount = sc.nextInt(); 57 | if(amount == vm.inventory.get(slot).getPrice()) { 58 | return true; 59 | } else if (amount < vm.inventory.get(slot).getPrice()) { 60 | System.out.println("Amount entered is less than price of product."); 61 | return false; 62 | } else { 63 | // library methods to handle CashDispenserStrategy 64 | // similar to ATM system 65 | System.out.println("Please collect change: " + (amount - vm.inventory.get(slot).getPrice())); 66 | return true; 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /LLD/VendingMachine/src/main/java/org/anuva04/Controllers/VendingMachine.java: -------------------------------------------------------------------------------- 1 | package org.anuva04.Controllers; 2 | 3 | import org.anuva04.Models.Tray; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | import java.util.Scanner; 8 | 9 | // Simulates display and input options (keypad) of Vending Machine 10 | public class VendingMachine { 11 | int numSlots; 12 | Map inventory; 13 | 14 | public VendingMachine(int numSlots, int traySize) { 15 | this.numSlots = numSlots; 16 | inventory = new HashMap<>(); 17 | for(int slot = 1; slot <= numSlots; slot++) { 18 | inventory.put(slot, new Tray(0, traySize)); 19 | } 20 | } 21 | 22 | // Simulates home-screen of Vending Machine display 23 | public void init() { 24 | while(true) { 25 | System.out.println("Press 1: User, 2: Admin "); 26 | Scanner sc = new Scanner(System.in); 27 | int input = sc.nextInt(); 28 | switch (input) { 29 | case 1: 30 | UserOperations userOperations = new UserOperations(this); 31 | userOperations.init(); 32 | break; 33 | case 2: 34 | AdminOperations adminOperations = new AdminOperations(this); 35 | adminOperations.init(); 36 | break; 37 | default: 38 | System.out.println("Invalid option!"); 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /LLD/VendingMachine/src/main/java/org/anuva04/Main.java: -------------------------------------------------------------------------------- 1 | package org.anuva04; 2 | 3 | import org.anuva04.Controllers.VendingMachine; 4 | 5 | public class Main { 6 | // Method to simulate a physical Vending Machine up and running 7 | public static void main(String[] args) { 8 | VendingMachine vm = new VendingMachine(9, 10); 9 | vm.init(); 10 | } 11 | } -------------------------------------------------------------------------------- /LLD/VendingMachine/src/main/java/org/anuva04/Models/Item.java: -------------------------------------------------------------------------------- 1 | package org.anuva04.Models; 2 | 3 | // A physical item that can be bought by an user 4 | public class Item { 5 | public String name; 6 | public int price; 7 | 8 | public Item(String name, int price) { 9 | this.name = name; 10 | this.price = price; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /LLD/VendingMachine/src/main/java/org/anuva04/Models/Tray.java: -------------------------------------------------------------------------------- 1 | package org.anuva04.Models; 2 | 3 | import java.util.Queue; 4 | import java.util.LinkedList; 5 | 6 | // A tray in each slot of Vending Machine which usually contains items of same type 7 | public class Tray { 8 | private int price; 9 | private Queue items; 10 | 11 | private int traySize; 12 | 13 | public Tray(int price, int traySize) { 14 | this.price = price; 15 | this.items = new LinkedList<>(); 16 | this.traySize = traySize; 17 | } 18 | 19 | public void setPrice(int price) { 20 | this.price = price; 21 | } 22 | 23 | public int getPrice() { 24 | return price; 25 | } 26 | 27 | public boolean addItem(Item item) { 28 | if(items.size() >= traySize) { 29 | return false; 30 | } 31 | this.items.add(item); 32 | return true; 33 | } 34 | 35 | public Item peekItem() { 36 | return this.items.peek(); 37 | } 38 | 39 | public Item dispenseItem(){ 40 | return this.items.poll(); 41 | } 42 | 43 | public void removeAllItems() { 44 | this.items.clear(); 45 | } 46 | 47 | public int getTraySize() { 48 | return traySize; 49 | } 50 | 51 | public int getItemsQuantity () { 52 | return this.items.size(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /LLD/VendingMachine/src/main/resources/Requirements.txt: -------------------------------------------------------------------------------- 1 | Functional Requirements: 2 | 1. Vending machine (VM) displays available items, their codes and prices. 3 | 2. User can choose to pay with cash or card (debit or credit card). 4 | 2.a. If paying with cash, VM will return change (if any) if available. 5 | 2.b. If change not available, VM will return entire amount and not dispense any item. 6 | 3. Emergency contact number is available to reach out to in case of hardware failures. 7 | 4. User has the option to cancel transaction at any point before item is dispensed. 8 | 5. Admin should be able to insert/remove items from VM. 9 | 5.a. To do so, admin can insert card and enter PIN to get access to VM. 10 | 5.b. Once items are modified, VM updates its metadata automatically. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Table of contents: 2 | - [HLD](https://github.com/anuva04/SystemDesign/tree/main/HLD) 3 | - [Rate Limiter](https://github.com/anuva04/SystemDesign/blob/main/HLD/RateLimiter/RateLimiter.md) 4 | - [URL Shortener](https://github.com/anuva04/SystemDesign/blob/main/HLD/URLShortener/URLShortener.md) 5 | - [E-commerce Store](https://github.com/anuva04/SystemDesign/blob/main/HLD/ECommerceStore/ECommerceStore.md) 6 | - [Twitter](https://github.com/anuva04/SystemDesign/blob/main/HLD/Twitter/Twitter.md) 7 | - [Chat System](https://github.com/anuva04/SystemDesign/blob/main/HLD/ChatApp/ChatApp.md) 8 | - [Video Sharing Platform](https://github.com/anuva04/SystemDesign/blob/main/HLD/Youtube/Youtube.md) 9 | - [Cab Aggregator](https://github.com/anuva04/SystemDesign/blob/main/HLD/Uber/Uber.md) 10 | - [Collaborative Editing System](https://github.com/anuva04/SystemDesign/blob/main/HLD/GoogleDocs/GoogleDocs.md) 11 | - [WebHook System](https://github.com/anuva04/SystemDesign/blob/main/HLD/WebHook/WebHook.md) 12 | - [LLD](https://github.com/anuva04/SystemDesign/tree/main/LLD) 13 | - [ATM](https://github.com/anuva04/SystemDesign/tree/main/LLD/ATM) 14 | - [Elevator System](https://github.com/anuva04/SystemDesign/tree/main/LLD/ElevatorSystem) 15 | - [Vending Machine](https://github.com/anuva04/SystemDesign/tree/main/LLD/VendingMachine) 16 | - [Meeting Scheduler](https://github.com/anuva04/SystemDesign/tree/main/LLD/MeetingScheduler) 17 | - [Design Patterns](https://github.com/anuva04/SystemDesign/tree/main/DesignPatterns) 18 | - [Creational Patterns](https://github.com/anuva04/SystemDesign/tree/main/DesignPatterns/CreationalPatterns) 19 | - [Factory](https://github.com/anuva04/SystemDesign/blob/main/DesignPatterns/CreationalPatterns/FactoryPattern.java) 20 | - [Abstract Factory](https://github.com/anuva04/SystemDesign/blob/main/DesignPatterns/CreationalPatterns/AbstractFactoryPattern.java) 21 | - [Builder](https://github.com/anuva04/SystemDesign/blob/main/DesignPatterns/CreationalPatterns/BuilderPattern.java) 22 | - [Singleton](https://github.com/anuva04/SystemDesign/blob/main/DesignPatterns/CreationalPatterns/SingletonPattern.java) 23 | - [Prototype](https://github.com/anuva04/SystemDesign/blob/main/DesignPatterns/CreationalPatterns/PrototypePattern.java) 24 | - [Structural Patterns](https://github.com/anuva04/SystemDesign/tree/main/DesignPatterns/StructuralPatterns) 25 | - [Adapter](https://github.com/anuva04/SystemDesign/blob/main/DesignPatterns/StructuralPatterns/AdapterPattern.java) 26 | - [Bridge](https://github.com/anuva04/SystemDesign/blob/main/DesignPatterns/StructuralPatterns/BridgePattern.java) 27 | - [Composite](https://github.com/anuva04/SystemDesign/blob/main/DesignPatterns/StructuralPatterns/CompositePattern.java) 28 | - [Decorator](https://github.com/anuva04/SystemDesign/blob/main/DesignPatterns/StructuralPatterns/DecoratorPattern.java) 29 | - [Facade](https://github.com/anuva04/SystemDesign/blob/main/DesignPatterns/StructuralPatterns/FacadePattern.java) 30 | - [Flyweight](https://github.com/anuva04/SystemDesign/blob/main/DesignPatterns/StructuralPatterns/FlyweightPattern.java) 31 | - [Proxy](https://github.com/anuva04/SystemDesign/blob/main/DesignPatterns/StructuralPatterns/ProxyPattern.java) 32 | - [Behavioral Patterns](https://github.com/anuva04/SystemDesign/tree/main/DesignPatterns/BehavioralPatterns) 33 | - [Chain of Responsibility](https://github.com/anuva04/SystemDesign/blob/main/DesignPatterns/BehavioralPatterns/ChainOfResponsibilityPattern.java) 34 | - [Command](https://github.com/anuva04/SystemDesign/blob/main/DesignPatterns/BehavioralPatterns/CommandPattern.java) 35 | - [Iterator](https://github.com/anuva04/SystemDesign/blob/main/DesignPatterns/BehavioralPatterns/IteratorPattern.java) 36 | - [Mediator](https://github.com/anuva04/SystemDesign/blob/main/DesignPatterns/BehavioralPatterns/MediatorPattern.java) 37 | - [Memento](https://github.com/anuva04/SystemDesign/blob/main/DesignPatterns/BehavioralPatterns/MementoPattern.java) 38 | - [Observer](https://github.com/anuva04/SystemDesign/blob/main/DesignPatterns/BehavioralPatterns/ObserverPattern.java) 39 | - [Template](https://github.com/anuva04/SystemDesign/blob/main/DesignPatterns/BehavioralPatterns/TemplatePattern.java) 40 | - [State](https://github.com/anuva04/SystemDesign/blob/main/DesignPatterns/BehavioralPatterns/StatePattern.java) 41 | - [Strategy](https://github.com/anuva04/SystemDesign/blob/main/DesignPatterns/BehavioralPatterns/StrategyPattern.java) 42 | - [Visitor](https://github.com/anuva04/SystemDesign/blob/main/DesignPatterns/BehavioralPatterns/VisitorPattern.java) --------------------------------------------------------------------------------