Обработка создания производного класса с помощью картографов в C++

Я сейчас читаю PoEAA Мартина Фаулера об объектно-реляционных структурных паттернах. Как проект, который я должен был изучать, я решил создать мини-систему электронной коммерции на C++. У меня проблемы с выяснением, как вернуть объекты из картографа.

у меня есть Product базовый класс, который имеет производные классы Hat а также Shirt, Products иметь type член, чтобы определить, какой производный класс они являются. у меня тоже есть ProductMapper класс, с производными классами HatMapper а также ShirtMapper, все из которых реализуют кучу методов поиска, которые позволяют мне пытаться получить определенные шляпы и рубашки.

class Product
{
    unsigned long long int id;
    std::string name;
    unsigned int type;
};


// Derived classes don't necessarily have the same members.

class Hat : public Product
{
    unsigned char fitted;
    unsigned char color;
    unsigned char style;
};

class Shirt : public Product
{
    unsigned char size;
};

В логической части моего приложения, где я создаю экземпляры этих картографов и получаю продукты, у меня возникают проблемы. Я могу создать экземпляр HatMapper и отступить Hat объекты без каких-либо проблем, то же самое с ShirtMapper а также Shirt объекты. В этих случаях шаблоны отлично работают (в частности, я использую наследование таблиц классов, то есть одну таблицу продуктов с данными о продуктах, одну таблицу для шляп с данными, относящимися к шляпам, и одну таблицу для рубашек с данными, относящимися к рубашкам).

Моя проблема в том, что мне делать, если я хочу получить все товары, как шляпы, так и рубашки? Я могу создать экземпляр ProductMapper и получить все данные о продукте, но это кажется неправильным подходом, так как я должен был бы пройти через все Products Я извлекаю и наращиваю Hats а также Shirts основываясь на их типе в моей логической части программы. Кроме того, мне придется изменить любой код, который обрабатывает ситуацию таким образом, когда я добавляю новые типы продуктов. Кажется плохим

В книге Фаулера есть примеры картографов с базовым картографом, использующих производные картографы, что мне кажется совершенно неправильным (приходится модифицировать базовый картограф каждый раз, когда я добавляю новый продукт, не очень хорошо). Вот быстрый пример того, как это делается там:

class ProductMapper
{
    unsigned long long int productId;
    unsigned long long int productType;

    HatMapper * hm;
    ShirtMapper * sm;

    Product * FindById(unsigned long long int id)
    {
        // Query database for data.

        if (this->productType == PRODUCT_TYPE_HAT)
        {
            return hm->FindById(id); // Hat object.
        }

        else if (this->productType == PRODUCT_TYPE_SHIRT)
        {
            return sm->FindById(id); // Shirt object.
        }

        return NULL;
    }
};

Вот как я бы использовал это в логической части моей программы. Примеры этого не приводятся в книге:

ProductMapper * pm = new ProductMapper();
Product * p = pm->FindById(1); // It's a Product, but a Hat or Shirt?

// Have to check type since a Product was returned.
switch (p->type)
{
    case PRODUCT_TYPE_HAT:
    {
        Hat * h = (Hat) p;
        break;
    }

    // Etc. Modify this every time a new product type is added or removed.
}

Это введет круговые зависимости. Кроме того, предполагая, что я как-то устраняю круговые зависимости, результат HatMapper а также ShirtMapper классы Hat объекты и Shirt объекты. Таким образом, когда я вернусь из ProductMapperЯ буду опускать руки, поэтому мне придется снова манипулировать результатом в моей логике, что снова ставит вопрос об изменении кода, когда я представляю новые типы продуктов.

Я в недоумении, что делать. В идеальном мире я хотел бы иметь Product класс и ProductMapper Класс, который я могу быстро расширить, вводя новые типы продуктов без необходимости изменять существующий код (по крайней мере, слишком много).

Я хотел бы иметь возможность использовать эти шаблоны из PoEAA, они кажутся хорошими и полезными, но я не уверен, что это просто то, что я не могу сделать в C++, или мне не хватает того, что мешает мне это делать. Альтернативные модели и подходы также действительно приветствуются.

1 ответ

Такое ощущение, что шаблон Type Object может помочь в этом случае, я знаю, что ссылка о программировании игры, но достаточно применить шаблон к другим доменам.

Проблема сейчас в том, что если вы хотите добавить продукты, вам нужно добавить несколько классов, которые, как вы заметили, могут быть сложны в обслуживании.

Изменить: Может быть, вы могли бы использовать что-то подобное (код для C++11 для упрощения примера):

class ProductProperty
{
    typedef std::map<std::string, unsigned char> PropertyMap;

    PropertyMap properties;

    public:
    ProductProperty(std::initializer_list<PropertyMap::value_type> il):
        properties(il)
    {}
    // Use of at() is intended to only deal with the defined properties
    const PropertyMap::value_type::second_type& 
        get(const PropertyMap::value_type::first_type& prop) const 
    { 
        return properties.at(prop); 
    }
    PropertyMap::value_type::second_type& 
        get(const PropertyMap::value_type::first_type& prop) 
    { 
        return properties.at(prop); 
    }

};

// Some helpers to illustrate
std::shared_ptr<ProductProperty> makeHatProperty()
{
    return std::make_shared<ProductProperty>(
        ProductProperty{
            {"fitted", ***whatever**},
            {"color", ***whatever**}, 
            {"style", ***whatever**}
        });
}

std::shared_ptr<ProductProperty> makeShirtProperty()
{
    return std::make_shared<ProductProperty>(
        ProductProperty{{"size", ***whatever**}}
        );
}

class Product
{
    unsigned long long int id;
    std::string name;
    unsigned int type;

    std::shared_ptr<ProductProperty> properties;

    public:
    Product(std::shared_ptr<ProductProperty> props):
        properties(props)
    {}

    // Whatever function you need to get/set/check properties
};
Другие вопросы по тегам