Low Level Design (LLD)
Low Level Design (LLD)
LLD – Its refers to the Design of small components of an application or an application that serves the requirement or serves a set of business requirements.
Building Blocks of LLD :
Requirement Gathering
Laying Down Use Cases
UML/Class Diagrams
OOD(Object Oriented Design) to Model Problem
Implement Code (Design Patterns + SOLID)
CLASS DIAGRAM : Class Diagram Reflect the real world problem in terms of classes their relationship with other classes how they interact with other classes
Responsibility of every class in terms of Class Methods & Functions and the Data and Information those Classes hold in terms of Class Attributes
While Designing the Complex System these Class interactions can get fairly complicated and hence
UML SEQUENCE DIAGRAM : UML is not an Programming Language it is rather a Visual Language
We use UML diagrams to Portray the behavior and Structure of a System
Its helps us to understand how different instances of classes are interacting with each other what messages they are passing to each other in what particular order .
If you are developing the Complex Application use cases diagram do tell the story of how the software is going to look like and it helps you to build and design a better software .
ADVANCE LLD : The advance step of LLD includes how to write testable maintainable refactored code which is production ready and which can include the changes that will come with time and how we can write such advance code
LLD OBJECTS & CLASSES :
OOD – Object Oriented Design its Not Equals to OOP – Object Oriented Programming
OOP – Coding up an application using the Object Oriented (OOP) features of an Particular Language
OOD – Model a Real World Problem using Object Oriented Design (OOD) Techniques and Concepts
Objects – Represents the Real World Entity (Information & Behavior)
Classes – Blueprints of Objects(Classify)
NVT (Noun Verb Technique) :
Noun – Objects (Classes)
Verb – Behavior
Classes have to interact and depend on other classes in order to fulfil the use cases of a problem
once we have designed what are the Classes in our system or how we can represent our system using classes.
Next step to figure out the relationship between those two classes or multiple classes and what are the different responsibilities of each and every class
Relationship Among Classes :
Primarily there are two kind of Relationships that any two classes can have either that relationship could be,
1.has a relation
2.Is a relation
Has a relation : sometimes can also be considered as Composition/Aggregation
Ex : Customer – Credit Card , here Customer Has a Credit Card its an Has a Relation Because Without an Customer there should be no need of Credit Card
Its also called as an Composition Relationship
Product – Cart , here we can add multiple products in an single cart , even though the cart is not available the product will be exist
Its called as an Aggregation another type of has a relation
Is a Relation – Its usually points to the Inheritance
EX : Lesson is class , Parking lesson is type of an lesson class & driving lesson is an type of an Lesson class
SOLID PRINCIPLES :
SRP – Single Responsibility Principles :
In an Module , Module means Set of Functions , Class , Package , Source Code should have a reason to change by Only 1 Stakeholders can change this / The Whole functionality can be changed by only one Stakeholder (or) Group of Stakeholders
Inheritance & Polymorphism :
Inheritance : A Child Class its Inherits the Attributes and Properties of the Parent Class (Is a Relation)
Polymorphism : Actually an Implementation of an Inheritance , Child classes inherited from the same parent class can have multiple forms and attitudes
Compile Time Polymorphism – Method Overloading
Run Time Polymorphism – Method Overriding
SOLID :
L – LISKOV SUBSTITUTION PRINCIPLE :
If a Function takes an Instance of a Class , That same function should also be able to take the Instance of Derived Class from that Class
Its used to Inherit Properly
If you are Inheriting Properly This Principle won’t be Violated , But if you are Inheriting Wrong This Principal will be Violated
We can Figure out if any source code is Violating this Principle
Design Classes in a way that this principle is followed
SOLID
I – Interface Segregation Principle :
Keep Things abstracting(hiding details) and let classes implement them
Problem with Interface – Include a lot of functionality , Hide a lot of functionality These are the major problems so this principle its used to overcome those problems
One Interface should not be handling a lot of responsibilities Instead , multiple Interface can should be hiding different responsibilities
Design Interfaces in a such a way that the classes that implement those interfaces does not have many unused functions , if you used many un-used functions in the class this Principle will be violated
SOLID
D – Dependency Inversion Principle :
High Level Modules or Low Level Modules in your code should not depend on the actual implementation they should depend on abstractions
DESIGN PATTERNS :
Each Pattern describes a problem that occurs over and over multiple times in our environment and core of the solution to that problem can be used by a million times without ever doing it same way twice.
Any Pattern Consists of 4 parts – Pattern Name , Problem , Solution , Consequences
Pattern Name - Gives an Idea about the problem what the Pattern is going to solve
Problem – The Pattern actually describes the Problem it is going to solve
Solution – Each Problem Will be have an Solution to resolve the problem in Patterns
Consequences – Where its an good idea to use an Pattern and Where its an not an good idea to t use the Pattern , if you do any mistakes in this it will be an Consequences.
Types of Design Patterns :
Design Patterns - Thumbrules or different concepts using which you can solve the problem of modeling real world examples into object oriented design
1.PURPOSE – What is the Purpose any Design Patterns Solves , here there are 3 types of Purpose
1.1. Creational – Used for Creating or Instantiating Objects and Classes
Classes – Factory Method
Objects - Factory Method , Abstract Factory , Builder , Prototype , Singleton
1.2 Structural – Used for Structuring more than one classes or objects together , Moreover we will deep dive into Inheritance , Interface Segregation Principle etc while we study this
Classes – Adapter (Class)
Objects - Adapter (Class) , Adapter (Object) , Bridge , Composite , Decorator , Facade , Flyweight , Proxy
1.3 Behavioral – Used for Identifying and setting up common communication patterns among Objects
Classes – Interpreter Template Method
Objects - Interpreter , Template Method , Chain of Responsibility , Command , Iterator , Mediator , Memento , Observer , State , Strategy
Here the Patterns are Divided into Classes & Objects
Factory Method (Class) – Used for Creating Classes
Objects of Factory Method – Used to Create Objects
Structural Adapter(Class) – Tells you how to use Inheritance
Objects of Adapter – Used to organize and assemble different Objects
Interpreter Template Method (Class) – How to use inheritance in order to implement the algorithms and the control and flow of classes so as to fulfill a certain behavior
Objects of Interpreter Template Method – How to write algorithms around objects in order to fulfill a task which one single object cannot fulfill.
Factory Method Design Pattern :
Its an Creational Design Pattern , used to create Object of classes or Instantiate Objects.
Its Hides an Complexity
The Catch however is that it is used to create the Objects which can be of similar type or which are usually of similar type but vary a little in terms of Implementation
The Goal of Factory Method Pattern is to hide that Complexity while creating such Objects which are similar type but can have different Implementations
So the Client Code which is actually using the factory method pattern has no idea how those Objects are created
Client Code – Any other application calling your code or any other module of application calling your code which calls an Interface or a Function which resides under a Factory Method Interface , Now the Client Code will call that function which is exposed by this factory method Interface.
Factory Method Interface – f1() here calls Object creation logic
Concreter Classes – Implements creation of the Object
Factory Class(Abstract Interface) :
Abstract Interface , Factory Method
Concrete Class (Actual Implementation) :
Real Implementation
It will be an Sub class of Factory Class or it will be Inheriting from Factory Class , it will be Implementing the function which is defined in this interface here
Pros and Corns
Pros :
Guarantees Abstraction
Code is flexible and adaptable
Cons :
Complex Code
Takes time to set the base
Not a Pattern that can be refactored into
Builder Design Pattern
Its an Creational Design Pattern and its utilized for creation of Objects , but the problem that builder pattern specifically solves is creation of complex Objects.
Helps with Immutable classes , Define Objects which once created never change their value
Less need for exposing setters of a class
PROS :
Good way to handle complexity
Easy to Implement
Can be refactored into
CONS :
Class instance returned is immutable
User inner static class
Sometimes number of lines of code can be huge
Have to think of end to end chain
Abstract Factory Method Design Pattern
Factory of Factory Pattern, Creational – Creating Objects which belong to a family of similar Objects
Implemented using Common Interface (Implementation is deferred to sub classes)
Ex : Java – Document Builder
PROS :
Good for abstraction and family of similar Objects
Loose Coupling between client and actual/concrete code
All Classes follow Single Responsibility Principle
Supports Open Closed Principle
CONS :
Code becomes Complicated/Complex
Pattern inside a Pattern
Singleton Design Pattern
When Only One Instance of a Class is needed(Shared Resource) Access to that Instance from the Whole Application
Make the Variable(Instance) initialized by a Private Constructor
Access to be given only by a getter() method
Singleton Class Never accepts parameters , if it accepts parameter then it becomes a factory so avoid it.
Eager Loading – The Instance is already initialized as soon as the application is up , Works well with one Singleton Class
Lazy Loading – The Instance is initialized only when any App Module Calls for it , Suitable for Multiple Singleton Classes
PROS :
Neat way to handle access to shared global resource
Easy to Implement
Guarantees 1 Instance
Solves a well defined problem
CONS :
Sometimes abused
Used with parameters and confused with Factory
Hard to Write Unit Tests
Thread Safety has to be insured else can be Dangerous
Facade Design Pattern
Its an Structural Pattern , used to Structure your Code neat & clean
It makes Complex APIs Easy to Use
More of a Refactoring Method
Used in Both Low Level And High Level Design
A Refactoring Pattern
Makes the Code Clean
Simplest Structural Design Pattern
Good to think about API Design
Adapter Design Pattern
We have to Connect two interfaces which are not compatible with each other and in order to do so you write an adapter or you write a wrapper so that both those Interfaces can understand each other .
When those interfaces are not compatible you can go and change one of those Interfaces , but we cannot do that because this situation arises usually in the cases when a legacy code has to be integrated with a new code or another set of programs which are incompatible with the legacy code
So in this situation when the legacy code cannot be modified but you still have to integrate it we come up with adapter design pattern in order to solve for the same
This Pattern is a Client Focus Pattern
Easy to Implement
Simple and Plain
Multiple adapters can be used
Circuit Breaker in Microservices
We talk about Circuit Breaker we also hear terms like Non Transient Failures , Resilient Systems , Cascading Failures , Thundering herds , Fault Tolerance , Retries and Recovery
What is Circuit Breaker ?
It is a Cloud Design Pattern to deal with non-transient failures in Microservices.
Non Transient Failures – Permanent failures which can render the system unavailable or the recovery can take longer than few seconds
Ex : Timeouts or Delays where the APIs do not Respond or Take a longer time to Respond resulting in timeouts or databases going down or the connections to databases failing.
There are 3 States of Circuit Breakers , Closed , Half Open , Open
Closed – By Default a Circuit Breaker stays in Closed state that means the traffic will go from Service A to B as it is and the response will also be returned from service B to A
Half Open – in case the Circuit Breaker is half open that means some traffic can go for the testing purposes if the calling service can actually call the downstream service and check if there are any failures or not
Open – Incase the circuit breaker is open that means no traffic can go through and the calling service will just be written with that this service is that they are trying to call is not working right now
Closed – (Failures exceed the threshold then open criteria) – Open (Timeout/Delay)-Half Open (No More Failures)
Cloud Design Pattern for Microservices
Not Need in case of a few services or scenarios where load is not heavy
Can be configured as per the services and requirements
Easy yet effective way to build resilience into systems
Should not be used to solves for transient failures
Flyweight Pattern
Its an Structural and Optimization Pattern
Used to Optimize RAM Usage by a lot of Objects which share some Immutable State
Ex : Car games with a lot of repeating Objects
Pros :
Optimisation Pattern
Very Useful
Saves Resources
Cons :
Pattern inside a Pattern
Complex to Understand
Trap for Premature Optimization
Subscribe to my newsletter
Read articles from OBULIPURUSOTHAMAN K directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
OBULIPURUSOTHAMAN K
OBULIPURUSOTHAMAN K
As a Computer Science and Engineering graduate, I have cultivated a deep understanding of software development principles and technologies. With a strong foundation in Java programming, coupled with expertise in frontend and backend development, I thrive in crafting robust and scalable solutions. Currently, I am leveraging my skills as a Java Full Stack Engineer at Cognizant, where I am involved in designing and implementing end-to-end solutions that meet the complex requirements of our clients. I am passionate about leveraging emerging technologies to drive innovation and deliver tangible business value. My goal is to continually enhance my expertise and contribute to the advancement of software engineering practices in the industry.