S.O.L.I.D. with examples

S)ingle Responsibility Principle

A class should have one, and only one, reason to change. A class should have a single responsibility within the software.

Bad Example:

1class User {
2    fun save() {
3        // Logic to save user to the database
4    }
5
6    fun sendEmail() {
7        // Logic to send email to the user
8    }
9}

Good Example:

 1class User {
 2    fun save() {
 3        // Logic to save user to the database
 4    }
 5}
 6
 7class EmailService {
 8    fun sendEmail(user: User) {
 9        // Logic to send email to the user
10    }
11}

O)pen Closed Principle

You should be able to extend a class’s behavior without modifying it. Objects should be open for extension but closed for modification.

Bad Example:

 1class Rectangle(val width: Double, val height: Double) {
 2    fun area(): Double {
 3        return width * height
 4    }
 5}
 6
 7class Circle(val radius: Double) {
 8    fun area(): Double {
 9        return Math.PI * radius * radius
10    }
11}

Good Example:

 1interface Shape {
 2    fun area(): Double
 3}
 4
 5class Rectangle(val width: Double, val height: Double) : Shape {
 6    override fun area(): Double {
 7        return width * height
 8    }
 9}
10
11class Circle(val radius: Double) : Shape {
12    override fun area(): Double {
13        return Math.PI * radius * radius
14    }
15}

L)iskov Substitution Principle

Derived classes should be substitutable for their base classes. The Liskov Substitution Principle was introduced by Barbara Liskov in 1987: “If for every object o1 of type S there is an object o2 of type T such that for all programs P, the behavior of P is unchanged when o1 is substituted by o2, then S is a subtype of T.”

Bad Example:

 1open class Bird {
 2    open fun fly() {
 3        println("Flying")
 4    }
 5}
 6
 7class Ostrich : Bird() {
 8    override fun fly() {
 9        throw Exception("Ostriches can't fly")
10    }
11}

Good Example:

 1open class Bird {
 2    open fun makeSound() {
 3        println("Chirp")
 4    }
 5}
 6
 7class Sparrow : Bird() {
 8    override fun makeSound() {
 9        println("Chirp chirp")
10    }
11}
12
13class Ostrich : Bird() {
14    override fun makeSound() {
15        println("Hiss")
16    }
17}

I)nterface Segregation Principle

Make interfaces that are client-specific. A class should not be forced to implement interfaces and methods that will not be used.

Bad Example:

 1interface Machine {
 2    fun print()
 3    fun scan()
 4    fun fax()
 5}
 6
 7class Printer : Machine {
 8    override fun print() {
 9        // Logic to print
10    }
11
12    override fun scan() {
13        throw UnsupportedOperationException("Printer cannot scan")
14    }
15
16    override fun fax() {
17        throw UnsupportedOperationException("Printer cannot fax")
18    }
19}

Good Example:

 1interface Printer {
 2    fun print()
 3}
 4
 5interface Scanner {
 6    fun scan()
 7}
 8
 9class SimplePrinter : Printer {
10    override fun print() {
11        // Logic to print
12    }
13}
14
15class MultiFunctionPrinter : Printer, Scanner {
16    override fun print() {
17        // Logic to print
18    }
19
20    override fun scan() {
21        // Logic to scan
22    }
23}

D)ependency Inversion Principle

Depend on abstractions, not on concretions. A high-level module should not depend on low-level modules; both should depend on abstractions.

Bad Example:

 1class EmailService {
 2    fun sendEmail(message: String) {
 3        // Logic to send email
 4    }
 5}
 6
 7class Notification {
 8    private val emailService = EmailService()
 9
10    fun notify(message: String) {
11        emailService.sendEmail(message)
12    }
13}

Good Example:

 1interface MessageSender {
 2    fun send(message: String)
 3}
 4
 5class EmailService : MessageSender {
 6    override fun send(message: String) {
 7        // Logic to send email
 8    }
 9}
10
11class Notification(private val sender: MessageSender) {
12    fun notify(message: String) {
13        sender.send(message)
14    }
15}
16
17// Usage
18val emailService = EmailService()
19val notification = Notification(emailService)
20notification.notify("Hello, World!")