Pokazywanie postów oznaczonych etykietą design pattern. Pokaż wszystkie posty
Pokazywanie postów oznaczonych etykietą design pattern. Pokaż wszystkie posty

28 sierpnia 2014

[C++] RAII - Resource Acquisition Is Initialization

Notatka z wykładu Michaela Caissea "Introduction to Modern C++ Techniques" na C++Now (dawniej BoostCon), dotycząca wzorca projektowego RAII. Choć mam okazję korzystać z jej dobrodziejstw przy okazji boost-a, oraz biblioteki standardowej to jednak sam nigdy nic nie napisałem. Technika polega na przyjęciu zasobu, podczas konstruowania obiektu i zwalniania podczas jego destrukcji. Programista nie musi pamiętać o wykonaniu dodatkowych czynności, gdy zasób już nie będzie potrzebny, zwalnianiem zajmie się kompilator.
Przykład poniżej pokazuje zarządzeniem uchwytem do pliku (w konstruktorze plik jest otwierany, w destruktorze niszczony), za pomocą funkcji znanych z C.
#include <iostream>
#include <string>
#include <cstdio>

class FileManager {
public:
    FileManager(const std::string& filePath) {
        std::cout << "Open file" << std::endl;
        _fileHandler = std::fopen(filePath.c_str(), "w+");
    }

    void write(const std::string& str) {
        if(_fileHandler == nullptr)
            return;

        if(std::fputs(str.c_str(), _fileHandler) == EOF)
            throw std::exception();
    }

    ~FileManager() noexcept {
        std::cout << "Close file" << std::endl;
        if(_fileHandler != nullptr)
            std::fclose(_fileHandler);
    }

private:
    FILE* _fileHandler;
};

void modifyFile() {
    FileManager f("aaa.txt");
    f.write("some text\n");
}

int main() {
    modifyFile();
    std::cout << "End" << std::endl;
    return 0;
}
Wynik:
Open file
Close file
End

29 grudnia 2013

[python] Dekoratory

Python ma wbudowany mechanizm pozwalający na implementowanie dekoratorów (wzorzec projektowy), dla istniejących metod. W skrócie, przed wywołaniem pożądanej funkcji wołana jest wcześniej zdefiniowana funkcja dekoratora, której argumentem jest pierwotna funkcja. Dekorator zwraca nową funkcję, która ma być wołana w miejsce pierwotnej.

Poniżej przykład dla funkcji obliczającej rekurencyjnie silnię. Stworzone zostały dwa dekoratory. Pierwszy zajmuje się zapamiętywaniem obliczonych już wartości. Druga metoda drukuje na ekranie parametry wywołania funkcji. Fajny sposób na zapewnienie single responsibility principle.
def cache(fun):
    cache_map = {}
    def _f(*args):
        try:
            if args in cache_map:
                return cache_map[args]
            cache_map[args] = result = fun(*args)
            return result
        except:
            # some element of args can't be a dict key
            return fun(args)
    return _f

def log(fun):
    def _f(*args):
        print('log: ' + fun.__name__ + '(' + str(*args) + ')')
        return fun(*args)
    return _f

@cache
@log
def factorial(n):
    if n == 0:
        return 1
    return n * factorial(n-1)

assert factorial(3) == 6
assert factorial(1) == 1
Wynik:
log: factorial(3)
log: factorial(2)
log: factorial(1)
log: factorial(0)

27 czerwca 2013

[C++] Trochę o wirtualności (kiedy stosować wirtualne destruktory) i metodach szablonowa

Szukając informacji na temat stosowania destruktorów wirtualnych natknąłem się na artykuł (http://www.gotw.ca/publications/mill18.htm) Herba Suttera, dotyczący stosowania tego mechanizmu i troszkę więcej. Poniżej moje notatki.

Metody wirtualne bardzo rzadko powinny być publiczne (jeśli w ogóle). Publiczna wirtualna metoda, robi dwie rzeczy: specyfikuje interfejs i pokazuje detale implementacyjne - pozwalające na zmianę zachowania. Dobrze by było rozdzielić te dwie rzeczy. Sutter zaleca stosowania NVI (Non-virtual interface - wersja wzorca projektowego "metoda szblonowa" - definiuje szkielet algorytmu, który korzysta m.in. z operacji pierwotnych przedefiniowanych przez klasy pochodne). Korzyści: Interfejs staje się stabilny, a wszystkie modyfikacje oddelegowane zostają do klas pochodnych.
  • Porada #1: Preferuj tworzenie interfejsu jako nonvirutal, korzystająć z wzorca projektowego "metoda szablonowa"
  • Porada #2: Preferuj tworzenie metod wirtualnych jako prywatne. Metoda wirtualne stosuje się tylko by umożliwić modyfikacje działania.
  • Porada #3: Jeżeli klasa pochodna, potrzebuje wykonać metodę z klasy bazowej tylko wtedy może być zmieniona na protected.
Klasa powinna mieć wirtualny destruktor, jeżeli zamierzamy niszczyć (polimorficznie) obiekt korzystając ze wskaźnika na klasę bazową.
  • Porada #4: Destruktor klasy bazowej powinien być publiczny i wirtualny, albo protected i niewirtualny (gdy nie stosujemy polimorfizmu).
  • O ile stosowanie publicznego wirtualnego destruktor do mnie jeszcze przemawia, to nie jestem w stanie w tej chwili znaleźć zastosowania dla protected nonvirtual.
  • Porada #5: Nie dziedzicz po klasach konkretnych - pozostaw klasy nie będące liśćmi jako abstrakcyjne.
Oczywiście nietrudno znaleźć głosy, które nie do końca się z tym zgadzają. C++FAQ [23.4], zaleca stosowanie chronionych (protected) metod wirtualnych, ponieważ dla wielu deweloperów jest zaskoczeniem to, że prywatne wirtualne metody mogą zostać nadpisane. Marshall Cline, podaje też, dwa przypadki (C++FAQ [23.3]), dla których jego zdaniem wirtualne metody powinny być publiczne.

Pierwsza jest odwróceniem, metody szablonowej tj. chcemy by główny algorytm, mógł być modyfikowany przez klasy pochodne, natomiast prymitywne metody pozostały stałe w klasie bazowej - nie muszą być one wirtualne, bo i tak klasa pochodna zdecyduje, czy będzie chciała z nich skorzystać, czy z czegoś innego (C++FAQ [23.2]).

Ze zrozumieniem drugiej (C++FAQ [23.9]) mam niestety problem.