# Explain SOLID Principles
The SOLID principles are a set of design principles aimed at making software designs more understandable, flexible, and maintainable. These principles were introduced by Robert C. Martin, also known as Uncle Bob. The acronym SOLID stands for:
1. Single Responsibility Principle (SRP)
2. Open/Closed Principle (OCP)
3. Liskov Substitution Principle (LSP)
4. Interface Segregation Principle (ISP)
5. Dependency Inversion Principle (DIP)
Let's go through each principle with a Ruby implementation example:
1. Single Responsibility Principle (SRP)
2. Open/Closed Principle (OCP)
3. Liskov Substitution Principle (LSP)
4. Interface Segregation Principle (ISP)
5. Dependency Inversion Principle (DIP)
Let's go through each principle with a Ruby implementation example:
1. Single Responsibility Principle (SRP)
A class should have only one reason to change, meaning it should only have one job or responsibility.
Example:
Example:
class Invoice
def initialize(customer)
@customer = customer
end
def calculate_total
# calculation logic
end
end
class InvoicePrinter
def print(invoice)
# printing logic
end
endHere, `Invoice` is responsible for invoice-related data and calculations, and `InvoicePrinter` is responsible for printing the invoice. Each class has a single responsibility.
2. Open/Closed Principle (OCP)
Software entities (classes, modules, functions, etc.) should be open for extension but closed for modification.
Example:
Example:
class Order
def initialize
@items = []
end
def add_item(item)
@items << item
end
def total_price
@items.sum(&:price)
end
end
class Discount
def apply(order)
raise NotImplementedError, "Subclasses must implement"
end
end
class ChristmasDiscount < Discount
def apply(order)
order.total_price * 0.1
end
end
class NewYearDiscount < Discount
def apply(order)
order.total_price * 0.2
end
endHere, the `Order` class is closed for modification but can be extended with different discount strategies by subclassing `Discount`.
3. Liskov Substitution Principle (LSP)
Objects of a superclass should be replaceable with objects of a subclass without affecting the functionality.
Example:
Example:
class Bird
def fly
# fly logic
end
end
class Sparrow < Bird
def fly
super
end
end
class Penguin < Bird
def fly
raise "Penguins can't fly"
end
endHere, `Penguin` violates the LSP because it can't fly, which contradicts the behavior expected from the `Bird` superclass. A better approach is to redesign the hierarchy so that flight capability is separated from the general bird characteristics.
4. Interface Segregation Principle (ISP)
Clients should not be forced to depend on interfaces they do not use.
Example:
Example:
module Printer
def print_document
raise NotImplementedError, "This method should be overridden"
end
end
module Scanner
def scan_document
raise NotImplementedError, "This method should be overridden"
end
end
class AllInOnePrinter
include Printer
include Scanner
def print_document
# print logic
end
def scan_document
# scan logic
end
end
class SimplePrinter
include Printer
def print_document
# print logic
end
endHere, `SimplePrinter` only includes the `Printer` module because it does not need to implement scanning functionality, adhering to the ISP.
5. Dependency Inversion Principle (DIP)
High-level modules should not depend on low-level modules. Both should depend on abstractions (e.g., interfaces). Abstractions should not depend on details. Details should depend on abstractions.
Example:
Example:
class FileStorage
def save(data)
# save data to file
end
end
class DatabaseStorage
def save(data)
# save data to database
end
end
class DataSaver
def initialize(storage)
@storage = storage
end
def save(data)
@storage.save(data)
end
end
file_storage = FileStorage.new
db_storage = DatabaseStorage.new
data_saver = DataSaver.new(file_storage)
data_saver.save("some data")
data_saver = DataSaver.new(db_storage)
data_saver.save("some other data")Here, `DataSaver` depends on an abstraction (storage) and not on a specific implementation, adhering to the DIP. This allows for flexible and interchangeable storage strategies.
By following these principles, your Ruby code will be more modular, easier to maintain, and adaptable to change.
By following these principles, your Ruby code will be more modular, easier to maintain, and adaptable to change.