Skip to content

Kategória: Elvek

Open Closed Principle

Miért?
Azért, mert új feature-ök hozzáadásával egy addig hibátlan rendszer instabillá válásának kockázatát a lehető legalacsonyabban kell tartani.

Az Open Closed Principle (OCP) azt mondja ki, hogy egy osztálynak nyitottnak kell lennie a bővítésre, de el kell zárkóznia a változtatások elől. Ez egy további SOLID-elv. A következő kód mutatja be, hogy milyen problémákhoz vezethet ennek az elvnek a be nem tartása:
public double Ár() {
    const decimal TörzsvásárlóiKedvezmény = 0.95m;
    switch(vásárlóTípus) {
        case VásárlóTípus.Egyszeri:
            return mennyiség * darabár;
        case VásárlóTípus.TörzsVásárló:
            return mennyiség * darabÁr * TörzsvásárlóiKedvezmény;
        default:
            throw new ArgumentOutOfRangeException();
    }
}

Ebben az implementációban az problematikus, hogy az osztályt meg kell változtatni, ha az árszámítás egy további módja kerül hozzá. Ennél nagy a veszélye annak, hogy hibát vétünk, és a már létező funkcionalitások nem működnek megfelelően. Ha vannak automatizált unit tesztek és integrációs tesztek is, még akkor is fennál annak a veszélye, hogy új bug-okat hagyunk hátra, mert nem lehet elérni százszázalékos tesztlefedettséget. Tehát általánosságban egy olyan eljárást keresünk, amely az osztályt bővíthetővé teszi anélkül, hogy az osztályt magát meg kellene változtatni. Ezt például a Strategy Pattern segítségével lehet elérni:

public interface IÁrSzámító {
    double Ár(int mennyiség, double darabÁr);
}

private IÁrSzámító árSzámító;

public double Ár() {
    return árSzámító.Ár(mennyiség, darabÁr);
}

public class EgyszeriVevő : IÁrSzámító {
    public double Ár(int mennyiség, double darabÁr) {
        return mennyiség * darabár;
    }
}

public class TörzsVásárló : IÁrSzámító {
    const decimal TörzsvásárlóiKedvezmény = 0.95m;
    
    public double Ár(int mennyiség, double darabÁr) {
        return mennyiség * darabÁr * TörzsvásárlóiKedvezmény;
    }
}

Az ár konkrét kiszámítását egy interfészen keresztül más osztályokba helyeztük. Ezáltal lehetségessé válik a tetszés szerinti bővítés az interfész új implementációival. Így az osztály nyitottá vált a bővítésekkel szemben, és egyidejűleg zárttá a változtatásokkal szemben. Létező kódot pl. a Replace Conditional with Strategy refaktorálással lehet úgy átépíteni, hogy a Open Closed Principle-t betartsuk.

Tell, don´t ask

Miért?
Az erős összetartás (cohesion) és a laza csatolás (loose coupling) erények. Osztályok nyilvános állapotrészletei ellentmondanak ennek.

Egy kicsit provokatívan megfogalmazva: az osztályoknak ne legyen property getter-e. Ezek egy osztály felhasználóit arra csábítják, hogy egy objektum értékei alapján döntéseket hozzanak. Ahelyett tehát, hogy az objektumnak megmondanánk, hogy mit tegyen, kikérdezzük, és kívülről az objektum belső állapotára vonatkozó megállapításokat hozunk.Az objektumorientált programozás egyik alapelve az Information Hiding (lásd még a sárga fokozaton). Az osztályoknak nem volna szabad olyan információkat mutatni magukról, amiből kiderül, hogy hogyan implementálták őket. Amennyiben egy osztálynak a munkájához szüksége van egy belső állapotra, akkor ezt általában egy belső mezőben tároljuk. Ha ez az érték kifelé is látható, akkor az osztály felhasználóit arra csábítjuk, hogy az objektumnak ezt a tulajdonképpen belső állapotát saját döntéseikre használják. Ezáltal az osztályt gyorsan lefokozzuk a tiszta adattárolásra. Mindenképpen előnyben kell részesíteni egy olyan implementálást, melyben az objektummal közüljük, hogy mit tegyen. Ezáltal az osztály felhasználóját már nem kell érdekelje az, hogy az osztály hogyan fogja a feladatot belül elintézni.

A Tell don’t ask-elv eredményeként viselkedéssel rendelkező objektumok jönnek „buta” adattároló objektumok helyett. Az objektumok összjátéka lazán csatolt, mivel az objektumoknak nem kell feltételezésekbe bocsátkozniuk az együttműködő objektumokról. De nem csak ennyi! Ha az objektumok nem hozzák nyilvánosságra az állapotukat, akkor megtartják a döntéshozatali fennhatóságukat. Ezzel erősödik a döntő kód összetartása (cohesion), mert egy helyre koncentrálódik.

Law of Demeter

Miért?
Objektumok függőségei egy szolgáltatási lánc több szemén keresztül csúnya szoros csatoláshoz (coupling) vezetnek.

A Law of Demeter-nél az a cél, hogy az objektumok összjátékát egészséges mértékre korlátozzuk. Ezt egyszerűsítve a következőképpen lehet megfogalmazni: “Don’t talk to strangers”. A Law of Demeter szerint egy eljárásnak csak a következő eljárásokat szabadna használnia:
  • saját osztályának eljárásait
  • paramétereinek eljárásait
  • saját osztálya által használt osztályok eljárásait
  • saját maga által létrehozott objektumok eljárásait

Azonban: Figyelembe kell vennünk, hogy néha van értelme a tisztán adattároló osztályoknak. Ezekre természetesen nem kell alkalmazni a Law of Demeter-t. Éppenséggel lehet, hogy van értelme annak, hogy a konfigurációs adatokat több hierarchikus osztályra osszuk szét, úgy hogy a végén a következő hozzáférés adódik egy értékhez:

int margin = config.Pages.Margins.Left;

Ha itt a Law of Demeter-t akarnánk alkalmazni, akkor csak a config.Pages volna megengedett.