Principles SOLID

Принципы S.O.L.I.D.

Подробно о каждой букве)

Для чего вообще нужны эти ваши принципы SOLID?

При создании программных систем использование принципов SOLID способствует созданию такой системы, которую будет легко поддерживать и расширять в течение долгого времени. Принципы SOLID — это руководства, которые также могут применяться во время работы над существующим программным обеспечением для его улучшения, например, для удаления «дурно пахнущего кода». В сфера разработки Android приложений эти принципы основополагающие в Clean Architecture.
'S' - Single Responsibility(принцип единственной отвественности).
Класс, модуль, метод должен делать только одну задачу, либо иметь только одну причину для его изменения.
Если класс, метод, ... отвечает за решение нескольких задач, его подсистемы, реализующие решение этих задач, оказываются связанными друг с другом. Изменения в одной такой подсистеме ведут к изменениям в другой.
Этот принцип применим не только к классам, но и к компонентам программного обеспечения в более широком смысле.  Справа приведен пример кода с нарушением этого принципа:
class ServerManager(val client:AndroidHttpClient) { fun createRoomDatabase() { Log.d("ServerManager", "createRoomDatabase") } fun logThis(value: String) { Log.w("TAG", "logThis: $value") } fun geoRequest(request: String)= "Server geo request: $request" fun IPRequest(request: String)="Server ip request: $request" fun pingSite(site: String)= client.ping("Server ping: $site") inner class LocalDatabase { fun getUser(id: Int)= "Get user: $id" fun saveUser(user: String) { println("Save user: $user") } } }
Пример правильного применения принципа 'S':
class ServerManager(val client:AndroidHttpClient) { fun makeGeoRequest(request: String)= "Server geo request: $request" fun makeIPRequest(request: String)="Server ip request: $request" fun pingSite(site: String)= client.ping("Server ping: $site") }
'O' - Open-Closed Principle (принцип открытости-закрытости).
Программные сущности должны быть открыты для расширения и закрыты для изменения.

Приглядитесь...

При добавлении в массив нового животного придется дополнять код функции.
class Animal(val name: String) fun makeSound( animals: List<Animal> = listOf( Animal("lion"), Animal("cat"), Animal("dog"), Animal("parrot") ) ) { animals.forEach { when (it.name) { "lion" -> println("Roar") "cat" -> println("Meow") "dog" -> println("Woof") "parrot" -> println("Tweet") } } }
Пример правильного применения принципа 'O':
open class Animal { open fun sound(): String = "Is it animal?" } class Lion : Animal() { override fun sound() = "Roar" } class Cat : Animal() { override fun sound() = "Meow" } class Dog : Animal() { override fun sound() = "Woof" } class Parrot : Animal() { override fun sound() = "Squawk" } fun makeSound( animals: List<Animal> = listOf(Lion(),Cat(),Dog(),Parrot()) ){ animals.forEach { println(it.sound()) } }
'L' - Liskov Substitution Principle(принцип подстановки Барбары Лисков).
Необходимо, чтобы подклассы могли бы служить заменой для своих суперклассов.

Цель этого принципа заключаются в том, чтобы классы-наследники могли бы использоваться вместо родительских классов, от которых они образованы, не нарушая работу программы. Если оказывается, что в коде проверяется тип класса, значит принцип подстановки нарушается.
fun countLegs(animals: List<Animal> = listOf( Lion(),Cat(),Dog(),Parrot() )){ animals.forEach { when(it) { is Lion -> println(lionLegs(it)) is Cat -> println(catLegs(it)) is Dog -> println(dogLegs(it)) is Parrot -> println(parrotLegs(it)) } } }
Пример правильного применения принципа 'L':
fun countLegs( animals: List<Animal> = listOf( Lion(), Cat(), Dog(), Parrot() ) ) { for (animal in animals) { println(animal.legCount()) } }
'I' - Interface Segregation Principle (принцип разделения интерфейсов).
Создавайте узкоспециализированные интерфейсы, предназначенные для конкретного клиента. Клиенты не должны зависеть от интерфейсов, которые они не используют.

Этот принцип направлен на устранение недостатков, связанных с реализацией больших интерфейсов.
interface SuperCoolInterface { fun doSomethingCool() fun doSomethingElseCool() fun onClick() fun onLongClick() fun onTouch() fun saveUser(user: CoolUser) suspend fun getUser(): CoolUser suspend fun createUser(user: CoolUser) suspend fun deleteUser(user: CoolUser) }
Пример правильного применения принципа 'I':
interface CoolInterface{ fun doSomethingCool() fun doSomethingElseCool() } interface UserInteractActions{ fun onClick() fun onLongClick() fun onTouch() } interface CoolUserActions{ fun saveCoolUser(user: CoolUser) suspend fun getCoolUser(): CoolUser suspend fun createCoolUser(user: CoolUser) suspend fun deleteCoolUser(user: CoolUser) }
'D' - Dependency Inversion Principle (принцип инверсии зависимости).
Объектом зависимости должна быть абстракция, а не что-то конкретное.
- Модули верхних уровней не должны зависеть от модулей нижних уровней. Оба типа модулей должны зависеть от абстракций.
- Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций.
class CacheLinkDatabase{ fun getCacheLink(id: Int) = "..." fun saveCacheLink(link: String) { ... } } class View(val db: CacheLinkDatabase){ fun getCacheLink(id: Int){ db.getCacheLink(id) } fun saveCacheLink(user: String){ db.saveCacheLink(user) } }
Пример правильного применения принципа 'D':
interface CacheDao{ fun getCacheLink(id: Int):String fun saveCacheLink(link: String) } class View(val dao: CacheDao){ fun getCacheLink(id: Int){ dao.getCacheLink(id) } fun saveCacheLink(user: String){ dao.saveCacheLink(user) } } class CacheHTTPDatabase:CacheDao{ override fun getCacheLink(id: Int)= "http: ..." override fun saveCacheLink(link: String) { ... } } class CacheHTTPSDatabase:CacheDao{ override fun getCacheLink(id: Int)= "https: ..." override fun saveCacheLink(link: String) { ... } } class CacheURIDatabase:CacheDao{ override fun getCacheLink(id: Int)= "URI: ..." override fun saveCacheLink(link: String) { ... } }

Дерзайте, всем отличного кодинга!)

Теперь вы знаете что такое принципы SOLID и с чем их едят. С ними вкусно, но главное помнить, что это всего лишь рекомендации по написанию качественного кода для хорошего фундамента приложения и его дальнейшего "легкого" развития/модифицирования.  И "желательно" следовать им, но в вашем коде идеально нигде ничего не получится - в таких случаях рождаются новые принципы и архитектуры, как трешовые, так и крутые навека. 
У каждой проблемы свои решения! Удачи!