- The Single Responsibility Principle
- The Open Closed Principle
- The Liskov Substitution Principle
- The Interface Segregation Principle
- The Dependency Inversion Principle
A class should have one, and only one, reason to change. (read more)
Example:
interface CanBeOpened {
fun open()
}
interface CanBeClosed {
fun closed()
}
// I'm the door. I have an encapsulated state and you can change it using methods.
class PodBayDoor : CanBeOpened, CanBeClosed {
private enum class State {
Open, Closed
}
private var state: State = State.Closed
override fun open() {
state = State.Open
}
override fun closed() {
state = State.Closed
}
}
// I'm only responsible for opening, no idea what's inside or how to close.
class DoorOpener(private val door: CanBeOpened) {
fun execute() {
door.open()
}
}
// I'm only responsible for closing, no idea what's inside or how to open.
class DoorCloser(private val door: CanBeClosed) {
fun execute() {
door.closed()
}
}
val door = PodBayDoor()
⚠ Only the
DoorOpener
is responsible for opening the door.
val doorOpener = DoorOpener(door)
doorOpener.execute()
⚠ If another operation should be made upon closing the door, like switching on the alarm, you don't have to change the
DoorOpener
class.
val doorCloser = DoorCloser(door)
doorCloser.execute()
You should be able to extend a classes behavior, without modifying it. (read more)
Example:
interface CanShoot {
fun shoot(): String
}
// I'm a laser beam. I can shoot.
class LaserBeam : CanShoot {
override fun shoot(): String {
return "Ziiiip!"
}
}
// I have weapons and trust me I can fire them all at once. Boom! Boom! Boom!
class WeaponsComposite(var weapons: Array<CanShoot>) {
fun shoot(): String {
return weapons.map { it.shoot() }[0]
}
}
val laser = LaserBeam()
var weapons = WeaponsComposite(weapons = arrayOf(laser))
weapons.shoot()
I'm a rocket launcher. I can shoot a rocket.
⚠️ To add rocket launcher support I don't need to change anything in existing classes.
class RocketLauncher : CanShoot {
override fun shoot(): String {
return "Whoosh!"
}
}
val rocket = RocketLauncher()
weapons = WeaponsComposite(weapons = arrayOf(laser, rocket))
weapons.shoot()
Derived classes must be substitutable for their base classes. (read more)
Example:
private interface Bird {
fun eat() {
println("Eat!")
}
}
private interface FlightBird : Bird {
fun fly() {
println("Fly!")
}
}
private interface NonFlightBird : Bird {
}
private class Crow : FlightBird {
override fun fly() {
super.fly()
println("It is Crow - it can fly")
}
override fun eat() {
super.eat()
}
}
private class Ostrich : NonFlightBird {
override fun eat() {
super.eat()
println("It is Ostrich - it can eat but it can't fly")
}
}
Make fine grained interfaces that are client specific. (read more)
Example:
interface LandingSiteHaving {
var landingSite: String
}
interface Landing {
fun landOn(on: LandingSiteHaving): String
}
interface PayloadHaving {
var payload: String
}
class InternationalSpaceStation {
fun fetchPayload(vehicle: PayloadHaving): String {
return "Deployed ${vehicle.payload} at April 10, 2016, 11:23 UTC"
}
}
class OfCourseIStillLoveYouBarge : LandingSiteHaving {
override var landingSite = "a barge on the Atlantic Ocean"
}
class SpaceXCRS8 : Landing, PayloadHaving {
override var payload = "BEAM and some Cube Sats"
override fun landOn(on: LandingSiteHaving): String {
return "Landed on ${on.landingSite} at April 8, 2016 20:52 UTC"
}
}
val crs8 = SpaceXCRS8()
val barge = OfCourseIStillLoveYouBarge()
val spaceStation = InternationalSpaceStation()
spaceStation.fetchPayload(crs8)
crs8.landOn(barge)
Depend on abstractions, not on concretions. (read more)
Example:
interface CoffeeMachine {
fun brew(coffee:Coffee)
}
interface Coffee
class Arabica : Coffee
class Rabusta : Coffee
class BrewMachine : CoffeeMachine {
override fun brew(coffee: Coffee) {
println("Brew: $coffee")
}
}
val brewMachine = BrewMachine()
brewMachine.brew(Arabica())
brewMachine.brew(Rabusta())
📖 Descriptions from: ochococo/OOD-Principles-In-Swift Thank's