Introduction
Let's say we have a set of objects with some data, and we need to manipulate these objects.
Let's say the most common example is filling a basket with fruit. We will implement a certain art class, into which we will add fruits. Next, we need the base class Fruit in order to define the volume parameter to which we will assign a value depending on the fruit. The problem is solved, the fruits are added until the volume of the basket reaches the threshold.
After several iterations, when our program for filling baskets has folded a few rotten pears, apples with worms, or something else goes wrong, we will extend the base Fruit class, adding freshness, purity, etc. parameters to it, while new verification conditions appear.
And this is where the problem begins. Yes, our class has become more versatile, but not only the number of problems with fruits can increase, but the situations of fruit picking themselves can be different. For example, we pick fruits where absolutely every fruit is fresh. Why, in this case, do we need a field responsible for freshness, if all fruits are known to be fresh? To fence extra functionality to indicate mandatory and optional types? - Of course not. We put a number of optional types into an array (dictionary), where each type is not directly part of the class. Our functionality is smarter again, great.
However, I decided to go further and develop this topic a little.
Idea
We define a type that will be responsible for storing optional variables in string format. In fact, it is a wrapper over an array. As a minimal set of information, we will store the name of the variable, the type (it can be either basic, int, string, bool, or a composition of several basic ones) and value.
The methods of this object can be variable, but I have highlighted the following for myself:
getting a list of variables and their types in string format (in general, you can limit yourself to names). This can be useful if we want to mark some object with the "origin".
getter that returns our array with information about variables in constant format.
getting the id of the type and the value by the name of the variable (needed for casting to a specific type).
class IVariable
{
public:
using Type = std::variant < int, double, bool, std::string>; //
virtual std::vector < std::pair < std::string_view, std::string_view > > abstract() = 0;
virtual std::vector < std::tuple < std::string_view, VarTypes, std::string_view > > released() = 0;
virtual std::pair < VarTypes, std::string_view > released(const std::string_view& name) = 0;
};
, , :
, "" , .
, ""
bool tryVector(const std::string_view& dX)
{
auto type = range.front()->released(dX);
if(type.first == VarTypes::_null)
{
return false;
}
for(auto&& var : _range)
{
if(var->released(dX).first != type.first)
{
return false;
}
}
return true;
}
, ( tryVector).
, , , .
, , .
The ability to adjust the scope of working with objects, when, depending on the situation, we need either more parameters, or vice versa, fewer.
Think not about how to design an object, but about what actions should be performed with the object
disadvantages
Not suitable for calculating a large number of objects of the same type. For example, for some class that defines the rgb component of a pixel, it will be more efficient to explicitly define the properties.
Such an approach will consume a lot of memory, and, accordingly, it makes sense to use it only for working with objects, the essence of which cannot be expressed in terms of several simple variables, i.e. more for logical processing of objects than direct calculations.