Policy Based Design#
Policy Based Design to technika programowania, która pozwala na wybór zachowania w czasie kompilacji. W C++ jest realizowana za pomocą szablonów klas i techniki statycznego polimorfizmu.
Polega ona na rozdzieleniu klasy algorytmu od tzw. klasy wytycznej, która określa szczegóły implementacyjne (policy). Klasa wytycznej jest przekazywana jako parametr szablonu klasy.
Każda wytyczna:
Ustala interfejs dotyczący jednej konkretnej czynności
Określa jeden sposób zachowania lub implementacji
Może być implementowana na wiele sposobów
Note
Policy Based Design jest podobny do wzorca projektowego Strategia (ang. Strategy) z tą różnicą, że w przypadku użycia wytycznych wybór zachowania odbywa się w czasie kompilacji.
Przykład klasy wektora sparametryzowanej wytycznymi:
RangeCheckPolicy- wytyczna sprawdzająca poprawność zakresuLockingPolicy- wytyczna realizująca mechanizm synchronizacji dostępu do obiektu w środowisku wielowątkowym
template
<
typename T,
typename RangeCheckPolicy,
typename LockingPolicy = NullMutex
>
class Vector : public RangeCheckPolicy
{
std::vector<T> items_;
using mutex_type = LockingPolicy;
mutable mutex_type mtx_;
public:
using iterator = typename std::vector<T>::iterator;
using const_iterator = typename std::vector<T>::const_iterator;
T& at(size_t index); // implementation is using the range check policy
};
Przykładowa klasa wytycznej sprawdzającej poprawność zakresu:
class ThrowingRangeChecker
{
protected:
~ThrowingRangeChecker() = default;
void check_range(size_t index, size_t size) const
{
if (index >= size)
throw std::out_of_range("Index out of range...");
}
};
Inna implementacja wytycznej kontrolującej zakres indeksów:
class LoggingErrorRangeChecker
{
public:
void set_log_file(std::ostream& log_file)
{
log_ = &log_file;
}
protected:
~LoggingErrorRangeChecker() = default;
void check_range(size_t index, size_t size) const
{
if ((index >= size) && (log_ != nullptr))
*log_ << "Error: Index out of range. Index="
<< index << "; Size=" << size << std::endl;
}
private:
std::ostream* log_{};
};
Implementacja metody at() wektora z uwzględnieniem wytycznych:
template
<
typename T,
typename RangeCheckPolicy,
typename LockingPolicy
>
T& Vector<T, RangeCheckPolicy, LockingPolicy>::at(size_t index)
{
std::lock_guard<mutex_type> lk{mtx_}; // using the locking policy
RangeCheckPolicy::check_range(index, size()); // using the range check policy
return items_[index];
}
Kod klienta korzystający z klasy wektora sparametryzowanej wytycznymi:
using StdLock = std::mutex;
Vector<int, ThrowingRangeChecker, StdLock> vec = {1, 2, 3};
vec.push_back(4);
try
{
auto value = vec.at(8);
}
catch(const std::out_of_range& e)
{
std::cout << e.what() << std::endl;
}