Princípy SOLID

päť princípov objektovo orientovaného návrhu

Pre písanie objektového kódu existuje niekoľko odporúčaní, pomocou ktorých je možné kód organizovať a písať. Jeden z najznámejších a najpoužívanejších princípov pre objektovo orientovaný návrh (OOD) sa volá SOLID a ponúka päť princípov objektovo orientovaného návrhu.

SOLID Design Principles

Názov SOLID je akronymom z názvov týchto piatich princípov:

Význam jednotlivých princípov je opísaný v nasledujúcich podkapitolách.

Single Responsibility Principle (SRP)

Princíp jedinej zodpovednosti (z angl. Single Responsibility Principle) sformuloval uznávaný odborník v softvérovom inžinierstve Robert C. Martin. Hovorí, že:

A class should have only one reason to change.

To znamená, že trieda by mala mať iba jednu zodpovednosť, ktorá sa prejavuje prostredníctvom jej metód. Ak sa trieda stará o viac než jednu úlohu, je vhodné tieto úlohy rozdeliť do samostatných tried s výstižnými názvami.

Obr. 1: Single Responsibility Principle

SRP sa netýka iba zodpovednosti, ale aj dôvodov, pre ktoré sa implementácia triedy môže meniť.

Koncept zodpovednosti pritom môže byť veľmi subjektívny. Jedna zodpovednosť však neznamená, že trieda má mať len jednu metódu – zodpovednosť sa neviaže na počet metód.

Tento princíp má blízko ku konceptu oddelenia záujmov (z angl. separation of concerns), ktorý odporúča rozdeliť program na komponenty, z ktorých každý bude riešiť samostatný problém.

V projekte THSensor to znamená, že trieda reprezentujúca teplomer (senzor teploty) nebude riešiť pripojenie k internetu ani serializáciu dát do súboru.

Open/Closed Principle (OCP)

Autorom tohto princípu je Bertrand Meyer. Tento princíp hovorí, že:

Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.

Ak je potrebné pridať nové správanie (funkcionalitu), tá by nemala byť pridaná modifikovaním existujúcej triedy, ale jej rozšírením pomocou abstrakcií (rozhraní, dedičnosti, polymorfizmu).

OCP doesn’t mean your classes should never be modified. It means that you should minimize the need to modify existing classes when adding new functionality to your code.

Príkladom môže byť napríklad servisný terminál zariadenia, kedy je možné ovládať zariadenie pomocou príkazov. Zlým riešením je jeden veľký if-elif-else. Správnym riešením je vytvorenie spoločnej triedy pre všetky príkazy a následne samostatnú triedu pre každý jeden príkaz. Pridanie nového príkazu znamená, že vytvoríme novú triedu s novou funkcionalitou a rozšírime existujúci parser o nový príkaz.

Ďalším príkladom aplikácie OCP môže byť aj stavový stroj, ktorý reprezentuje správanie celého zariadenia. Jednotlivé stavy sú implementované ako samostatné triedy, ktoré implementujú spoločné rozhranie (napr. BaseState).

Ak potrebujeme pridať nový stav, vytvoríme novú triedu s definovaným správaním bez zásahu do jadra stavového stroja. Stavový kontext (State Machine) zostáva stabilný a pracuje iba s abstrakciou stavu.

Prechody medzi stavmi sa realizujú cez rozhranie, pričom existujúce stavy nie je potrebné upravovať — iba sa rozšíri systém o nový konkrétny stav.

Tým je stavový stroj uzavretý voči modifikáciám, ale otvorený voči rozšíreniu o nové správanie, čo je praktická aplikácia princípu Open/Closed.

Liskov Substitution Principle (LSP)

Tento princíp predstavila Barbara Liskov na konferencii OOPSLA v roku 1987. Odvtedy sa tento princíp stal fundamentálnou súčasťou objektovo-orientovaného programovania. Tento princíp hovorí, že:

Subtypes must be substitutable for their base types.

Objekt potomka musí byť možné použiť kdekoľvek, kde sa očakáva objekt predka – bez toho, aby sa zmenila správnosť programu. Rodič definuje správanie v podobe kontraktu, ktorý potom musia jeho potomkovia dodržať.

Takto fungujú napr. komponenty, ktoré sú kedykoľvek zameniteľné jeden za druhý. Napr. v IoT by to mohli byť rozličné senzory teploty (analógový, I2C, one-wire ,…), kde každý jeden by bol implementovaný ako samostatná trieda, pričom všetci by mali spoločného predka TemperatureSensor (kontrakt), ktorý by obsahoval napr. metódu temperature().

Samotná metóda však nemusí stačiť. Ak všetky implementácie TemperatureSensor dodržiavajú rovnaký kontrakt správania (jednotky, rozsah, časovanie, spôsob hlásenia chýb), potom je možné ktorýkoľvek senzor zameniť za iný bez zásahu do zvyšku systému. To je aplikácia Liskovej substitučného princípu.

Interface Segregation Principle (ISP)

Autorom tohto princípu je opäť Robert C. Martin. Tento princíp hovorí, že:

Clients should not be forced to depend upon methods that they do not use. Interfaces belong to clients, not to hierarchies.

Tento princíp sa týka veľkých rozhraní/rodičovských tried. Ak však potomok nepoužíva niektorú metódu alebo atribút rodiča, potom by mal byť taký rodič rozdelený na menšie viac špecifické triedy.

Napr. nie každé zariadenie umožňuje zapisovať a čítať. V takom prípade by som musel implementácie metód, ktoré zariadenie nevyužíva, nahradiť vyvolaním výnimky NotImplementedError. A to je porušenie tohto princípu. Miesto jedného rodiča je potrebné vytvoriť dvoch s názvami ako Reader a Writer. Podobne Sender a Receiver.

Alebo DHT11 senzor je senzor teploty a vlhkosti. Miesto toho, aby existovalo jedno rozhranie s dvoma metódami, bude tento senzor implementovať dve rozhrania - TemperatureMixin a HumidityMixin.

Dependency Inversion Principle (DIP)

Tento princíp hovorí, že:

Abstractions should not depend upon details. Details should depend upon abstractions.

High-level moduly nemajú závisieť od low-level modulov. Oba majú závisieť od abstrakcií. Abstrakcie nemajú závisieť od detailov. Detaily majú závisieť od abstrakcií.

  1. SOLID Design Principles: Improve Object-Oriented Code in Python