Értesítés küldés állapotváltozásról üzenetobjektum átadásával – megfigyelő minta, push változat

Az előző bejegyzésben azt a helyzetet modelleztük, amikor egy áruház új termék érkezésekor értesíti a címüket megadó vásárlókat, lehetőséget adva, hogy ha az adott termék, az adott áron érdekli őket, akkor meg tudják vásárolni. Láttuk, hogy a modellben csak a vásárlási hajlandóságot tudtuk megjeleníteni, aminek oka az, hogy a potenciális vásárlók csak a termékinformációt (terméknév és ár) kapták meg, de azt nem, hogy hol, kitől, milyen elérhetőségen tudják megvásárolni az árut.

Ahhoz, tehát, hogy ténylegesen megvehessék és birtokolhassák a felkínált terméket az kell, hogy az áruház elérhetőségét, azaz referenciáját is megkapják. Ahogy szó volt róla, a modellként alkalmazott programtervezési mintában ezt két módon is megtehetjük: ha a vevők tájékoztatásakor adjuk át az elérhetőségi információt, akkor beszélünk Push változatról, ha pedig a vásárló állandó jelleggel, például a hírlevélre való feliratkozáskor kapja meg a szupermarket elérhetőségét, akkor Pull változatról van szó.

Most az előző bejegyzésben szereplő Push változatot fejlesztjük tovább úgy, hogy az értesítéskor a vevő a termékadatokkal együtt megkapja az áruház objektumreferenciáját is. Ezt lehetne úgy megtenni, hogy kiegészítjük az értesítést végrehajtó notify_subscribers(), valamint az update() metódusokat még egy, a feladóra/küldőre vonatkozó argumentummal. Ennél rugalmasabb megoldás, és a valóságot is jobban modellezi, ha egy külön üzenet objektumban, annak attribútumaiként definiálva, adjuk át a releváns adatokat. Ehhez létrehozunk egy PromotionMessage nevű osztályt, amelyet jelen esetben – mivel ennek példányai létrehozása után nem kell azok attribútumait változtatni – egyszerűen egy mezőneves tuple-lal definiálunk.

Ezek után nem kell mást tenni, mint a Supermarket osztályban a new_product setter() metóduson belül a notify_subscribers() hívásakor a PromotionMessage egy példányát átadni, ahol a konstruktorban szerepeltetjük a küldőként magát a Supermarket példányt (ami a self), valamint az új termék nevét és árát. Ezt követhetjük nyomon alább.

A Customer osztályban pedig az update() metódust módosítjuk úgy, hogy egyetlen argumentumot fog fogadni, ami a PromotionMessage példánya. Ebből ki tudja nyerni nem csak a terméknevet és árat, hanem a küldő áruház hivatkozását is. Ha a vevőt érdekli a termék és árban is megfelelő neki, akkor elindítja a vásárlási folyamatot a saját buy() metódusának meghívásával átadva azt, hogy kitől, mit és mennyiért kíván megvenni.

A buy() metódus az áruház referenciájára meghívja annak sell() metódusát, megkérve az áruházat, hogy az adott terméket az adott áron adja oda. A sell() metódus csak akkor adja el (vagyis visszatérési értékként szolgáltatja) a terméket, ha a termék raktáron van (pl. más még nem vette meg), és a vételi ár nem kisebb, mint az eladási ár. Ha e feltételek valamelyike nem teljesül, akkor nem ad vissza semmit, pontosabban None lesz a visszatérési érték. A konkrét kódolást az alábbi osztálydefiníciójában láthatjuk.

Mivel az eladott terméket az eladási áron értékesíti az áruház, ezért mielőtt a sell() metódus azt visszaadná, a termék price attribútumát az eladási árra kell változtatni. Mivel ez az objektum módosíthatóságát igényli, ezért eltérően az előző bejegyzésben alkalmazott megvalósítástól (nem változtatható mezőneves tuple), most normál osztályként definiáljuk a Product osztályt.

Vegyük észre, hogy az öröklött Subscriber és Publisher osztályokon nem kellett változtatni, így azok kódját most nem tüntettük fel, ezek az előző bejegyzésben láthatók.

A tesztsorok és a kiírt eredmények mutatják az új termékről történő tájékoztatás következtében az egyes vásárlók reakcióit, valamint a végén láthatjuk azt is, hogy miket vásároltak.

A következő bejegyzésben a Pull változatot mutatjuk be.

E cikk témájához a Python tudásépítés lépésről lépésre című e-könyv következő részei kapcsolódnak: az „Osztály vigyázz! – típuslétrehozás osztályokkal” fejezet, a „Készétel fogyasztás a szabványos könyvtár moduljainak használata” fejezet „Speciális konténer típusok” alfejezetén belül a „Mezőneves tuple – namedtuple” cím, az „Attribútumműveletek kontrollált végrehajtása” fejezeten belül a „Tulajdonságok létrehozása” és „Dekorátorral létrehozott tulajdonságok” alfejezetek.

Érdekel a Python tudásépítés lépésről lépésre az alapoktól az első asztali alkalmazásig című e-könyv.