Az objektumorientált szoftvertervezés rövid áttekintése

Egy Szerepjáték osztályainak megvalósításával mutatják be

Bevezetés

A legtöbb modern programozási nyelv támogatja és ösztönzi az objektum-orientált programozást (OOP). Annak ellenére, hogy a közelmúltban enyhe elmozdulást tapasztalunk ettől, mivel az emberek olyan nyelveket kezdenek használni, amelyekre nincs nagy hatással az OOP (például Go, Rust, Elixir, Elm, Scala), a legtöbbnek vannak tárgyai. A tervezési elvek, amelyeket itt felvázolunk, a nem OOP nyelvekre is érvényesek.

Ahhoz, hogy sikerül világos, jó minőségű, karbantartható és bővíthető kódot írni, ismernie kell a tervezési elvekről, amelyek több évtizedes tapasztalat alapján beváltak.

Közzététel: A példa, amelyet át fogunk élni, a Pythonban lesz . Példák vannak egy pont bizonyítására, és más, nyilvánvaló módon hanyag lehet.

Objektumtípusok

Mivel a kódunkat objektumok köré fogjuk modellezni, hasznos lenne megkülönböztetni a különböző felelősségeket és variációkat.

Három típusú objektum létezik:

1. Entitás objektum

Ez az objektum általában megfelel valamilyen valós entitásnak a problématerületen. Tegyük fel, hogy szerepjátékot építünk (RPG), az entitás objektum lenne az egyszerű Heroosztályunk:

Ezek az objektumok általában tartalmaznak tulajdonságokat magukról (például healthvagy mana), és bizonyos szabályokkal módosíthatók.

2. Vezérlőobjektum

A vezérlőobjektumok (más néven Manager-objektumok ) más objektumok koordinálásáért felelnek. Ezek olyan objektumok, amelyek vezérelnekés használjon más tárgyakat. Remek példa az RPG-hasonlatunkra az Fightosztály, amely két hős irányítását és harcra készteti őket.

A harc logikájának egy ilyen osztályba foglalása számos előnnyel jár: az egyik a cselekvés könnyű kiterjeszthetősége. Nagyon könnyen átadhat egy nem játékos jellegű (NPC) típust a hősnek, hogy harcoljon, feltéve, hogy ugyanaz az API látható. Nagyon könnyen örökölheti az osztályt, és felülírhatja a funkciók egy részét, hogy megfeleljen az igényeinek.

3. Határ objektum

Ezek olyan objektumok, amelyek a rendszer határainál ülnek. Bármely objektum, amely bemenetet vesz vagy egy másik rendszerből állít elő kimenetet, függetlenül attól, hogy ez a rendszer Felhasználó, az Internet vagy az adatbázis - határoló objektumnak minősíthető.

Ezek a határobjektumok felelősek az információk rendszerünkbe való be- és visszafordításáért. Egy példában, ahol Felhasználói parancsokat veszünk, szükségünk lenne a határobjektumra, hogy a billentyűzet bevitelét (például egy szóközt) felismerhető tartományi eseménysé (például karakterlökéssé) fordítsuk.

Bónusz: Értékobjektum

Az értékobjektumok egyszerű értéket képviselnek a domainben. Változhatatlanok és nincs identitásuk.

Ha beépítenénk őket a játékunkba, egy osztály Moneyvagy Damageosztály nagyon jól illene. Az említett objektumok segítségével könnyedén megkülönböztethetjük, megtalálhatjuk és hibakereshetjük a kapcsolódó funkciókat, míg a primitív típus - egész tömb vagy egy egész - használatának naiv megközelítése nem.

A következők alkategóriájába sorolhatók Entitytárgyakat.

A legfontosabb tervezési elvek

A tervezési elvek a szoftvertervezés szabályai, amelyek az évek során értékesnek bizonyultak. Szigorú követésük segíthet abban, hogy a szoftver kiváló minőségű legyen.

Absztrakció

Az absztrakció az a gondolat, hogy egy fogalmat bizonyos kontextusban a lényegéhez egyszerűsítsünk. Ez lehetővé teszi a koncepció jobb megértését azáltal, hogy lecsupaszítja egy egyszerűsített változatra.

A fenti példák az absztrakciót szemléltetik - nézze meg az Fightosztály felépítését. Használata a lehető legegyszerűbb - két hőst ad meg érvként a példányosításban, és meghívja a fight()módszert. Se több, se kevesebb.

A kód absztrakciójának a legkevésbé meglepő szabályt kell követnie. Absztrakciója nem okozhat meglepetést senkinek felesleges és független viselkedéssel / tulajdonságokkal. Más szóval - intuitívnak kell lennie.

Ne feledje, hogy Hero#take_damage()funkciónk nem okoz váratlan dolgokat, például nem törli a karakterünket halála után. De számíthatunk rá, hogy megöli karakterünket, ha egészsége nulla alá süllyed.

Egységbezárás

A beágyazást úgy lehet elképzelni, hogy valamit behelyezünk egy kapszulába - korlátozhatja annak kitettségét a külvilág felé. A szoftverben a belső objektumokhoz és tulajdonságokhoz való hozzáférés korlátozása elősegíti az adatok integritását.

A fekete dobozok beillesztése a belső logikába, és megkönnyíti az osztályok kezelését, mert tudja, hogy a többi rendszer melyik részt használja, és mi nem. Ez azt jelenti, hogy könnyedén átdolgozhatja a belső logikát, miközben megtartja a nyilvános részeket, és biztos lehet benne, hogy nem tört meg semmit. Mellékhatásként a kapszulázott funkcionalitás kívülről történő kezelése egyszerűbbé válik, mivel kevesebb dologra kell gondolni.

A legtöbb nyelvben ez az úgynevezett hozzáférés-módosítókon keresztül történik (privát, védett stb.). A Python nem a legjobb példa erre, mivel hiányoznak a futásidejébe beépített ilyen explicit módosítók, de ennek kiküszöbölésére konvenciókat használunk. A _változók / módszerek előtagja privátként jelöli őket.

Például képzelje el, hogy megváltoztatjuk a Fight#_run_attackmódszerünket, hogy egy logikai változót adjunk vissza, amely jelzi, hogy a harcnak vége, ahelyett, hogy kivételt tenne. Tudni fogjuk, hogy az egyetlen kód, amelyet megtörhettünk, az Fightosztályon belül van , mert a metódust priváttá tettük.

Ne feledje, hogy a kód gyakrabban változik, mint az újból írva. Fejlesztőként kívánt rugalmasság az, hogy a kódot a lehető legtisztább és legkevesebb visszahatással változtathatja meg.

Bomlás

A bontás egy objektum több különálló kisebb részre osztása. Az említett részeket könnyebb megérteni, karbantartani és programozni.

Képzelje el, hogy több RPG-funkciót, például buffokat, készletet, felszerelést és karakterattribútumokat akartunk beépíteni a következőkre Hero:

Feltételezem, elmondhatja, hogy ez a kód elég rendetlen lesz. Az Heroobjektumunk túl sok dolgot csinál egyszerre, és ez a kód ennek következtében elég törékennyé válik.

Például egy állóképességi pont 5 életpontot ér. Ha valaha is szeretnénk ezen változtatni a jövőben, hogy 6 életpontot érjen el, akkor több helyen is meg kell változtatnunk a megvalósítást.

A válasz az, hogy az Heroobjektumot több kisebb objektummá bontjuk, amelyek mindegyike magában foglalja a funkcionalitás egy részét.

Most, miután lebontó hősünk objektum funkcionalitását HeroAttributes, HeroInventory, HeroEquipmentés HeroBufftárgyak, hozzátéve jövőben alkalmassága lesz könnyebb, zárt és jobb elvonatkoztatott. Elmondhatja, hogy a kódunk sokkal tisztább és világosabb a működésében.

A bomlási kapcsolatoknak három típusa van:

  • Egyesület- Két elem közötti laza kapcsolatot határoz meg. Mindkét komponens nem függ egymástól, de együtt működhet.

Példa:Hero és egy Zoneobjektum.

  • összesítés - Meghatározza az egész és részei közötti gyenge „van-e” kapcsolatot. Gyengének tartják, mert a részek létezhetnek az egész nélkül.

Példa:HeroInventory és Item.

Egy A- HeroInventorynak sok lehet, Itemsés egy Itembármelyikhez tartozhat HeroInventory(például kereskedési cikkekhez).

  • kompozíció - Erős „van-van” kapcsolat, ahol az egész és a rész nem létezhet egymás nélkül. Az alkatrészeket nem lehet megosztani, mivel az egész pontosan azoktól a részektől függ.

Példa:Hero és HeroAttributes.

Ezek a Hős tulajdonságai - nem változtathatja meg a tulajdonosukat.

Általánosítás

Az általánosítás lehet a legfontosabb tervezési elv - ez a közös jellemzők kinyerésének és egy helyen történő egyesítésének folyamata. Mindannyian ismerjük a funkciók és az osztályöröklés fogalmát - mindkettő egyfajta általánosítás.

Az összehasonlítás tisztázhatja a dolgokat: míg az absztrakció csökkenti a bonyolultságot a felesleges részletek elrejtésével, addig az általánosítás csökkenti a bonyolultságot azáltal, hogy több, hasonló funkciókat ellátó entitást egyetlen konstrukcióval helyettesít.

Az adott példában általánosítottuk Heroés NPC osztályaink funkcionalitását egy közös ősévé nevezzük Entity. Ez mindig öröklés útján valósul meg.

Ahelyett, hogy az osztályaink NPCés az Heroosztályaink kétszer is végrehajtanák az összes módszert, és megsértenék a DRY elvét, csökkentettük a bonyolultságot azáltal, hogy közös funkcionalitásukat alaposztályba helyeztük.

Előzetes figyelmeztetésként - ne vigyük túlzásba az öröklést. Sok tapasztalt ember javasolja, hogy az öröklés helyett az összetételt részesítse előnyben.

Az amatőr programozók gyakran visszaélnek az örökséggel, valószínűleg azért, mert ez az egyik első OOP technika, amelyet egyszerűsége miatt megértenek.

Fogalmazás

A kompozíció az az elv, amikor több objektumot összetettebbé egyesítünk. Gyakorlatilag elmondva - objektumok példányainak létrehozása és funkcionalitásuk használata a közvetlen öröklés helyett.

A kompozíciót használó objektum nevezhető összetett objektumnak . Fontos, hogy ez az összetett egyszerűbb legyen, mint társainak összege. Ha több osztályt egyesítünk, magasabbra akarjuk emelni az absztrakció szintjét és egyszerűbbé tenni az objektumot.

Az összetett objektum API-jának el kell rejtenie belső összetevőit és a közöttük lévő interakciókat. Gondoljunk csak egy mechanikus órára: három keze van az idő megjelenítésére és egy gomb a beállításra - de belül több tucat mozgó és egymástól függő alkatrészt tartalmaz.

Mint mondtam, a kompozíciót részesítik előnyben az öröklés helyett, ami azt jelenti, hogy törekednie kell arra, hogy a közös funkcionalitást külön objektumba helyezze át, amelyet az osztályok használnak - ahelyett, hogy az örökölt alaposztályba helyezné.

Ábrázoljuk a túl öröklődő funkcionalitás lehetséges problémáját:

Most adtunk mozgást a játékunkhoz.

Mint megtudtuk, a kód megismétlése helyett általánosítást használtunk a move_rightés move_leftfüggvények Entityosztályba helyezéséhez.

Oké, most mi van, ha szerelvényeket akarunk bevezetni a játékba?

A szerelőknek szintén jobbra és balra kell mozogniuk, de nem képesek támadni. Ha jobban belegondolunk - lehet, hogy még egészségük sincs!

Tudom, mi a megoldása:

Egyszerűen helyezze át a movelogikát egy külön osztályba MoveableEntityvagy MoveableObjectosztályba, amely csak ezzel a funkcióval rendelkezik. Az Mountosztály ezt örökölheti.

Akkor mit tegyünk, ha olyan tartókat akarunk, amelyeknek van egészségük, de nem tudnak támadni? Jobban felosztva alosztályokra? Remélem, láthatja, hogyan kezdene összetetté válni az osztályhierarchiánk, annak ellenére, hogy üzleti logikánk még mindig nagyon egyszerű.

Valamivel jobb megközelítés lenne a mozgalmi logika Movementosztályba (vagy valamilyen jobb névbe) való elvonatkoztatása, és azokban az osztályokban történő példányosítása, amelyekre szükség lehet. Ez szépen összecsomagolja a funkcionalitást, és újrafelhasználhatóvá teszi mindenféle objektumban, nem korlátozódik ezekre Entity.

Hurrá, kompozíció!

Kritikus gondolkodás felelősség kizárása

Annak ellenére, hogy ezek a tervezési elvek több évtizedes tapasztalat révén alakultak ki, még mindig rendkívül fontos, hogy képes legyen kritikusan gondolkodni, mielőtt vakon alkalmazna egy elvet a kódjára.

Mint minden dolog, a túl sok is rossz dolog lehet. Néha az elveket túl messzire lehet vinni, túl okos lehet velük, és olyan dologhoz juthat, amellyel valójában nehezebb dolgozni.

Mérnökként az a fő tulajdonsága, hogy kritikusan értékeli az egyedi helyzetének legjobb megközelítését, nem pedig vakon követi és alkalmazza az önkényes szabályokat.

Összefogás, összekapcsolás és az aggodalmak elkülönítése

Kohézió

A kohézió a modulon belüli felelősség egyértelműségét jelenti, más szóval - annak összetettségét.

Ha az osztály egy feladatot lát el, és semmi mást nem, vagy világos célja van - az osztály nagy kohézióval rendelkezik . Másrészt, ha kissé nem világos, hogy mit csinál, vagy több célja van - alacsony a kohéziója .

Azt akarja, hogy az osztályai magas kohézióval bírjanak. Csak egy felelősségük kell, és ha elkapja őket, hogy többük van - akkor ideje lenne megosztani.

Csatolás

A párosítás rögzíti a különböző osztályok összekapcsolásának összetettségét. Azt szeretné, hogy az osztályai a lehető legkevesebb és legegyszerűbb kapcsolattal rendelkezzenek más osztályokkal, hogy a későbbi eseményekben (például a webkeretek megváltoztatásakor) felcserélhessék őket. A cél a laza kapcsolás .

Sok nyelven ezt az interfészek intenzív használatával lehet elérni - elvonják a logikát kezelő saját osztályt, és egyfajta adapter réteget képviselnek, amelybe bármelyik osztály bekapcsolódhat.

Az aggodalmak elkülönítése

Az aggályok elkülönítése (SoC) az az elképzelés, hogy a szoftverrendszert olyan részekre kell bontani, amelyek funkcionalitása nem fedi egymást. Vagy ahogy a neve is mondja - aggodalomra - Általános kifejezés bármit, hogy megoldást kínál a problémára - el kell különíteni a különböző helyeken.

A weblap erre jó példa - három rétege (Információ, Prezentáció és Viselkedés) három helyre (HTML, CSS és JavaScript) van elválasztva.

Ha újra megnézi az RPG Heropéldát, látni fogja, hogy már a kezdetek kezdetén sok gondja volt (alkalmazzon buffokat, számítsa ki a támadások sebzését, kezelje a készletet, szerelje fel az elemeket, kezelje az attribútumokat). Mi szétválasztjuk ezeket az aggályokat keresztül bomlik a több összetartó osztályok absztrakt és magukba azok adatait. A Heroosztály most működik, mint egy összetett objektumot, és sokkal egyszerűbb, mint korábban.

Kifizetni

Az ilyen elvek alkalmazása túl bonyolultnak tűnhet egy ilyen kis kóddarabnál. Az igazság az, hogy minden szoftverprojektnek kötelező , amelyet fejleszteni és fenntartani tervez a jövőben. Egy ilyen kód megírása már a legelején tartalmaz egy kis rezsit, de hosszú távon többször megtérül.

Ezek az elvek biztosítják, hogy rendszerünk több:

  • Kiterjeszthető : A nagy kohézió megkönnyíti az új modulok bevezetését a független funkcionalitás gondozása nélkül. Az alacsony csatolás azt jelenti, hogy egy új modul kevesebb anyagot tud csatlakoztatni, ezért könnyebben kivitelezhető.
  • Karbantartható : Az alacsony tengelykapcsoló biztosítja, hogy az egyik modul változása általában nem érinti a többieket. A magas kohézió biztosítja, hogy a rendszerkövetelmények változása a lehető legkevesebb osztály módosítását igényli.
  • Újrafelhasználható : A nagy kohézió biztosítja a modul teljes funkcionalitását és jól definiálható funkcióit. Az alacsony csatolás miatt a modul kevésbé függ a rendszer többi részétől, megkönnyítve ezzel más szoftverek újrafelhasználását.

Összegzés

Először néhány alapvető magas szintű objektumtípust (Entity, Boundary és Control) vezettünk be.

Ezután megismertük az említett objektumok strukturálásának alapelveit (absztrakció, általánosítás, összetétel, bomlás és kapszulázás).

Követésként bevezettünk két szoftverminőségi mutatót (összekapcsolás és kohézió), és megismertük az említett elvek alkalmazásának előnyeit.

Remélem, hogy ez a cikk hasznos áttekintést adott néhány tervezési elvről. Ha tovább akarja képezni magát ezen a területen, íme néhány forrás, amelyet ajánlanék.

További olvasmányok

Tervezési minták: Újrafelhasználható objektum-orientált szoftver elemei - a szakma vitathatatlanul a legbefolyásosabb könyve. Kicsit kelt a példáiban (C ++ 98), de a minták és az ötletek továbbra is nagyon relevánsak.

Tesztek segítségével növekvő objektum-orientált szoftver - Nagyszerű könyv, amely bemutatja, hogyan lehet a cikkben (és még sok másban) vázolt elveket gyakorlatilag alkalmazni egy projekt kidolgozásával.

Hatékony szoftvertervezés - A csúcsminőségű blog sokkal többet tartalmaz, mint a tervezéssel kapcsolatos betekintés.

Szoftvertervezés és építészeti specializáció - 4 videotanfolyam nagyszerű sorozata, amely hatékony tervezésre tanít Önnek mind a négy tanfolyamot átfogó projekt során.

Ha ez az áttekintés informatív volt számodra, kérjük, fontolja meg, hogy adj neki annyi tapsot, amelyet szerinted megérdemel, hogy többen megbotolhassák és értéket szerezhessenek belőle.