Паттерн Легковес на C++
Легковес – это структурный паттерн, который экономит память, благодаря разделению общего состояния, вынесенного в один объект, между множеством объектов.
Легковес позволяет экономить память, кешируя одинаковые данные, используемые в разных объектах.
Особенности паттерна на C++
Сложность:
Популярность:
Применимость: Весь смысл использования Легковеса – в экономии памяти. Поэтому, если в приложении нет такой проблемы, то вы вряд ли найдёте там примеры Легковеса.
Признаки применения паттерна: Легковес можно определить по создающим методам класса, которые возвращают закешированные объекты, вместо создания новых.
Концептуальный пример
Этот пример показывает структуру паттерна Легковес, а именно – из каких классов он состоит, какие роли эти классы выполняют и как они взаимодействуют друг с другом.
main.cc: Пример структуры паттерна
/**
* Паттерн Легковес
*
* Назначение: Позволяет вместить бóльшее количество объектов в отведённую
* оперативную память. Легковес экономит память, разделяя общее состояние
* объектов между собой, вместо хранения одинаковых данных в каждом объекте.
*/
struct SharedState
{
std::string brand_;
std::string model_;
std::string color_;
SharedState(const std::string &brand, const std::string &model, const std::string &color)
: brand_(brand), model_(model), color_(color)
{
}
friend std::ostream &operator<<(std::ostream &os, const SharedState &ss)
{
return os << "[ " << ss.brand_ << " , " << ss.model_ << " , " << ss.color_ << " ]";
}
};
struct UniqueState
{
std::string owner_;
std::string plates_;
UniqueState(const std::string &owner, const std::string &plates)
: owner_(owner), plates_(plates)
{
}
friend std::ostream &operator<<(std::ostream &os, const UniqueState &us)
{
return os << "[ " << us.owner_ << " , " << us.plates_ << " ]";
}
};
/**
* Легковес хранит общую часть состояния (также называемую внутренним
* состоянием), которая принадлежит нескольким реальным бизнес-объектам.
* Легковес принимает оставшуюся часть состояния (внешнее состояние, уникальное
* для каждого объекта) через его параметры метода.
*/
class Flyweight
{
private:
SharedState *shared_state_;
public:
Flyweight(const SharedState *shared_state) : shared_state_(new SharedState(*shared_state))
{
}
Flyweight(const Flyweight &other) : shared_state_(new SharedState(*other.shared_state_))
{
}
~Flyweight()
{
delete shared_state_;
}
SharedState *shared_state() const
{
return shared_state_;
}
void Operation(const UniqueState &unique_state) const
{
std::cout << "Flyweight: Displaying shared (" << *shared_state_ << ") and unique (" << unique_state << ") state.\n";
}
};
/**
* Фабрика Легковесов создает объекты-Легковесы и управляет ими. Она
* обеспечивает правильное разделение легковесов. Когда клиент запрашивает
* легковес, фабрика либо возвращает существующий экземпляр, либо создает новый,
* если он ещё не существует.
*/
class FlyweightFactory
{
/**
* @var Flyweight[]
*/
private:
std::unordered_map<std::string, Flyweight> flyweights_;
/**
* Возвращает хеш строки Легковеса для данного состояния.
*/
std::string GetKey(const SharedState &ss) const
{
return ss.brand_ + "_" + ss.model_ + "_" + ss.color_;
}
public:
FlyweightFactory(std::initializer_list<SharedState> share_states)
{
for (const SharedState &ss : share_states)
{
this->flyweights_.insert(std::make_pair<std::string, Flyweight>(this->GetKey(ss), Flyweight(&ss)));
}
}
/**
* Возвращает существующий Легковес с заданным состоянием или создает новый.
*/
Flyweight GetFlyweight(const SharedState &shared_state)
{
std::string key = this->GetKey(shared_state);
if (this->flyweights_.find(key) == this->flyweights_.end())
{
std::cout << "FlyweightFactory: Can't find a flyweight, creating new one.\n";
this->flyweights_.insert(std::make_pair(key, Flyweight(&shared_state)));
}
else
{
std::cout << "FlyweightFactory: Reusing existing flyweight.\n";
}
return this->flyweights_.at(key);
}
void ListFlyweights() const
{
size_t count = this->flyweights_.size();
std::cout << "\nFlyweightFactory: I have " << count << " flyweights:\n";
for (std::pair<std::string, Flyweight> pair : this->flyweights_)
{
std::cout << pair.first << "\n";
}
}
};
// ...
void AddCarToPoliceDatabase(
FlyweightFactory &ff, const std::string &plates, const std::string &owner,
const std::string &brand, const std::string &model, const std::string &color)
{
std::cout << "\nClient: Adding a car to database.\n";
const Flyweight &flyweight = ff.GetFlyweight({brand, model, color});
// Клиентский код либо сохраняет, либо вычисляет внешнее состояние и
// передает его методам легковеса.
flyweight.Operation({owner, plates});
}
/**
* Клиентский код обычно создает кучу предварительно заполненных легковесов на
* этапе инициализации приложения.
*/
int main()
{
FlyweightFactory *factory = new FlyweightFactory({{"Chevrolet", "Camaro2018", "pink"}, {"Mercedes Benz", "C300", "black"}, {"Mercedes Benz", "C500", "red"}, {"BMW", "M5", "red"}, {"BMW", "X6", "white"}});
factory->ListFlyweights();
AddCarToPoliceDatabase(*factory,
"CL234IR",
"James Doe",
"BMW",
"M5",
"red");
AddCarToPoliceDatabase(*factory,
"CL234IR",
"James Doe",
"BMW",
"X1",
"red");
factory->ListFlyweights();
delete factory;
return 0;
}
Output.txt: Результат выполнения
FlyweightFactory: I have 5 flyweights:
BMW_X6_white
Mercedes Benz_C500_red
Mercedes Benz_C300_black
BMW_M5_red
Chevrolet_Camaro2018_pink
Client: Adding a car to database.
FlyweightFactory: Reusing existing flyweight.
Flyweight: Displaying shared ([ BMW , M5 , red ]) and unique ([ CL234IR , James Doe ]) state.
Client: Adding a car to database.
FlyweightFactory: Can't find a flyweight, creating new one.
Flyweight: Displaying shared ([ BMW , X1 , red ]) and unique ([ CL234IR , James Doe ]) state.
FlyweightFactory: I have 6 flyweights:
BMW_X1_red
Mercedes Benz_C300_black
BMW_X6_white
Mercedes Benz_C500_red
BMW_M5_red
Chevrolet_Camaro2018_pink