Caching
Solid Design Principle.
It makes the code Scalable, Maintainable, Testable and Readable.
Single Responsibility Principle - A class should have only one reason to change which means every class should have a single responsibility or single job or single purpose.
Open/Closed Principle
Liskov Substitution Principle
Interface Segregation Principle
Dependency Inversion Principle
Single Responsibility Principle - A baker should only bake the product and should not take care of the inventory, order.
class BreadBaker {
public
void bakeBread() { System.out.println("Baking high-quality bread..."); }
public
void manageInventory() { System.out.println("Managing inventory..."); }
public
void orderSupplies() { System.out.println("Ordering supplies..."); }
public
void serveCustomer() { System.out.println("Serving customers..."); }
public
void cleanBakery() { System.out.println("Cleaning the bakery..."); }
public
static void main(String[] args) {
BreadBaker baker = new BreadBaker();
baker.bakeBread();
baker.manageInventory();
baker.orderSupplies();
baker.serveCustomer();
baker.cleanBakery();
}
}
One class should maintain one thing. It becomes a monolithic block of code that is harder to test and debug.
class BreadBaker {
public
void bakeBread() { System.out.println("Baking high-quality bread..."); }
}
// Class for managing inventory
class InventoryManager {
public
void manageInventory() { System.out.println("Managing inventory..."); }
}
// Class for ordering supplies
class SupplyOrder {
public
void orderSupplies() { System.out.println("Ordering supplies..."); }
}
// Class for serving customers
class CustomerService {
public
void serveCustomer() { System.out.println("Serving customers..."); }
}
// Class for cleaning the bakery
class BakeryCleaner {
public
void cleanBakery() { System.out.println("Cleaning the bakery..."); }
}
public class Bakery {
public
static void main(String[] args) {
BreadBaker baker = new BreadBaker();
InventoryManager inventoryManager = new InventoryManager();
SupplyOrder supplyOrder = new SupplyOrder();
CustomerService customerService = new CustomerService();
BakeryCleaner cleaner = new BakeryCleaner();
// Each class focuses on its specific responsibility
baker.bakeBread();
inventoryManager.manageInventory();
supplyOrder.orderSupplies();
customerService.serveCustomer();
cleaner.cleanBakery();
}
}
Similarly the Invoice class should only calculate the invoice.
public class Invoice {
private Marker marker; // Invoice has a marker.
private int quantity;
public int calculatePrice() {
return this.quantity * marker.price;
}
public void printInvoice(){
System.out.println("Text.");
}
}
It is not maintaining SRP. The PrintClass should be there.
package lld.solid;
public class Invoice {
private static Marker m;
private static int quantity;
public static int calculate(){
return quantity*m.price;
}
public Invoice(Marker m, int quantity){
this.m = m;
this.quantity = quantity;
}
public static void main(String[] args) {
Marker marker1 = new Marker();
marker1.marker = "Pen";
marker1.price = 10;
// m = marker1; No Constructor Invoice then refernce to m needed else NullPointerException.
Invoice i = new Invoice(marker1, 5); // Constructure then can pass the value.
System.out.print("Marker price - " + marker1.marker+ " " + i.calculate());
}
}
class Marker{
String marker;
int price;
}
Open Close Principle.
Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification which means you should be able to extend a class behavior, without modifying it.
Class Shape has circle and rectangle and when add triangle then there is a need to change the class.
class Shape {
private
String type;
public
double calculateArea() {
if (type.equals("circle")) {
// Circle area calculation
} else if (type.equals("rectangle")) {
// Rectangle area calculation
}
// Adding a triangle requires modifying this method
}
}
It is not OCP.
abstract class Shape {
abstract double calculateArea();
// We can also use an interface instead of an abstract class
}
class Circle extends Shape {
private
double radius;
@Override
public double calculateArea() {
return Math.PI * radius * radius;
}
}
class Rectangle extends Shape {
private
double width;
private
double height;
@Override
public double calculateArea() {
return width * height;
}
}
Notification Sender is sending message based on Notification Type and when new notification type is added then it needs to change the notification type.
public class NotificationSender {
public void sendNotifications(List<NotificationType> notificationTypeList){
for(NotificationType types:notificationTypeList){
if(types==NotificationType.SMS){
types.sendSMSNotification();
}
else if(types==NotificationType.EMAIL){
types.sendEmailNotification();
}
else if(types==NotificationType.WHATSAPP){
types.sendWhatsappNotification();
}
}
}
}
public enum NotificationType {
SMS,
EMAIL,
WHATSAPP;
public void sendSMSNotification(){
System.out.println("Sending SMS Notification.");
}
public void sendEmailNotification(){
System.out.println("Sending Email Notification.");
}
public void sendWhatsappNotification(){
System.out.println("Sending Whatsapp Notification.");
}
}
General solution - Making class of the type and making the class abstract.
public interface Notification {
void send();
}
public class EmailNotification implements Notification{
@Override
public void send() {
System.out.println("Email Notification.");
}
}
public class SmsNotification implements Notification{
@Override
public void send() {
System.out.println("Sms Notification.");
}
}
public class NotificationSender {
public void sendNotifications(List<Notification> notifications){
for(Notification notification:notifications){
notification.send();
}
}
}