"I strike from the shadows!"

Bu arkadaşın adı dark templar. Kendisi, starcraft evreninde acımasız bir adamdır. Normal zamanlarda kendisini göremezsiniz. "invisible" bir ünitedir.

Bu arkadaş bir çok ünitenizi tek vuruşta keser. Bu yüzden savunma olarak detection yapabilen bir üniteye sahip olmanız gerekir. Dark templar rush olarak bilinen stratejiye nice yiğitler heba olmuştur.


Bu arkadaşın adı da observer. Kendisi, protoss mühendislerinin ürettiği etkili bir drone'dur. Savaş alanında olan bitenleri gözlemleyip, protoss arşivlerine iletir.

Hem görünmez olup, hem de "dark templar" gibi üniteleri detect (tespit) edebilir.


Bu çirkin şey ise raven. Bu da terranların drone'u. Uzaktan bir terran pilotu tarafından kontrol edilir. Ama içerisinde mükemmel bir AI (yapay zeka) bulundurur. Drone içerisindeki bir çok konuya kendisi karar verir ve ayarlar. (Sıcaklık, Genel savaş durumu, enerji seviyeleri ve dış tehditler gibi.)

1 saniye içerisinde milyarlara varan komutu işleyebilir bir cpu'ya sahiptir. Birçok terran mühendisi, AI'sinin bu derece güçlü olmasından dolayı (bağımsız karar verebilme) tamamen bağımsız duruma geçebileceğinden ve felaketlere yol açabileceği şeklinde rapor vermiş.

Ama çok şükür iş daha terminator'e dönmemiş durumda. Şimdilik, raven gayet sadık bir ünite.

Bu arkadaş da, dark templar gibi görünmez ve saldırgan üniteleri tespit ettiği için terran taburları genelde 1 tane yanında bulundurur.


Bu arkadaş da Serdar Ortaç.

İçinde "Serdar Ortaç" bulunan evrene dönüyoruz... Yıl 95-96. Blizzard'ın ofisindeyiz. Yazılımcılar, gün içinde sigara-kahve molaları ve goygoylardan kalan zamanlarda ufak ufak kod da yazıyorlar.

İşin tespit edilme, detection'a sıra geldiğinde şöyle bir yapı kuruyorlar.

Demişler ki, Dark Templar bir nesne olsun. InvisibleUnit sınıfını extend etsin. InvisibleUnit, haritadaki observerları kendisine register etsin. Onların görüş alanına girdiğinde desin ki "ben geldim!".

InvisibleUnit sınıfı

class InvisibleUnit(object):

    def __init__(self):
        self.observers = []

    def register(self, observer):
        if not observer in self.observers:
            self.observers.append(observer)

    def unregister(self, observer):
        if observer in self.observers:
            self.observers.remove(observer)

    def unregister_all(self):
        if self.observers:
            del self.observers[:]

    def on_observer_range(self, *args, **kwargs):
        for observer in self.observers:
            observer.saw_something(*args, **kwargs)

Dark templar sınıfı

from invisibleunit import InvisibleUnit

class DarkTemplar(InvisibleUnit):
    hp = 100
    damage = 50

InvisibleUnit'e ek olarak hp, damage gibi property'ler ekledik. İleride dark templar gibi başka görünmez üniteler de olabilir diye düşünmüşler çünkü.

InvisibleUnit sınıfı register metoduyla haritadaki observerları kendi içine dahil ediyor. Observerların görüş alanına geldiğinde de o listeyi dönüp kendini observerlara saw_something metodu üzerinden bildiriyor. (Basitlik açısından observerların haritada aynı yer üzerinde olduklarını varsayıyoruz.)

observer.py

from abc import ABCMeta, abstractmethod

class Observer(object):
    __metaclass__ = ABCMeta

    @abstractmethod
    def saw_something(self, *args, **kwargs):
        pass

ABCMeta, abstract method gibi şeyler python dahilindeki şeyler.

Basitçe observer sınıƒından türeyen observerların, saw_something metodunu implemente ettiğinden emin olmak istiyoruz.

protoss_observer.py

from observer import Observer

class ProtossObserver(Observer):
    def saw_something(self, unit_name, hp, damage):
        print "I saw a {}. (HP: {}, Damage: {}) - {} reported.".format(
            unit_name,
            hp,
            damage,
            self.__class__.__name__
        )

raven.py

from observer import Observer

class Raven(Observer):
    def saw_something(self, unit_name, hp, damage):
        print "I saw a {}. (HP: {}, Damage: {}) - {} reported.".format(
            unit_name,
            hp,
            damage,
            self.__class__.__name__
        )

Son olarak, test yapabileceğiniz bir main.py

from darktemplar import DarkTemplar

from protoss_observer import ProtossObserver
from raven import Raven

# dark templar
dt = DarkTemplar()

# protoss observer
protoss_observer = ProtossObserver()

# terran observer (Raven)
raven = Raven()


dt.register(protoss_observer)
dt.register(raven)

dt.on_observer_range(
    "Dark Templar",
    dt.hp,
    dt.damage,
)

Bu şekilde, observer yazılım desenini starcraft içinde basitçe
implemente etmiş olduk.

$ python main.py
I saw a Dark Templar. (HP: 100, Damage: 50) - ProtossObserver reported.
I saw a Dark Templar. (HP: 100, Damage: 50) - Raven reported.

Starcraft evreninden çıkıp, bizim evrene dönersek; herhangi bir gazeteye abonelik durumu observer desenini en iyi açıklayan senaryo, pub-sub dediğimiz modeldir. Gazete aboneliği düşünün.

Aboneler=Observer, Gazete=Subject(Observable) gibi. Subject'in, Observerlara karşı one-to-many bir ilişkisi bulunur.

1 Dark templar'ın, birden fazla gözlemcisi olabilir. (Raven, OverSeer, Protoss Observer gibi.)

Duruma göre, bu observerlar dinlemeyi bırakabilir. Bunu da ana subject sınıfında (unregister) yapabilirler. Observer deseninin isminden haber olmasanız da birçok problemi bu veya buna benzer bir şekilde çözmüşsünüzdür.

Dark Templarsız günler dilerim.

"Though we strike at you from the shadows, do not think that we lack the courage to stand in the light. - Dark Templar"

Yazıda bulunan kodların derli toplu hali: @emre/starcraft-observer-pattern

Tags: design patterns, starcraft