Дизайн на основе политик: как правильно настроить структуру хоста?

У меня есть куча алгоритмов и коллекций, и я использую дизайн на основе политики (см. Книгу Современный дизайн C ++) иметь дело с произвольной комбинаторной сложностью. Это здорово, но для того, чтобы предотвратить разрушение класса Host с помощью указателя на политику, 1 предлагает сделать деструкторов охраняемых. Однако, если я сделаю защищенными деструкторы Algorithm и Collection, я не смогу использовать их самостоятельно, но только в качестве политик. Кроме того, я не вижу преимуществ дизайна на основе политик по сравнению с общим шаблоном фабрики …

Вот модель кода:

#include <iostream>

template<class Collection>
class AlgorithmOne
{
public:
void doSomethingWithData(Collection& data)
{
// Use Collection iterators to build up an algorithm.
}
};

template<class Collection>
class AlgorithmTwo
{
public:
void doSomethingWithData(Collection& data)
{
// Use Collection iterators to build up an algorithm.
}
};

template<class Collection>
class AlgorithmThree
{
public:
void doSomethingWithData(Collection& data)
{
// Use Collection iterators to build up an algorithm.
}
};template<class Element>
class CollectionOne
{
public:
typedef Element ElementType;

};

template<class Element>
class CollectionTwo
{
public:
typedef Element ElementType;

};

template<class Element>
class CollectionThree
{
public:
typedef Element ElementType;

};

template<typename HostTraits>
class HostInheritsData
:
public HostTraits::Collection,
public HostTraits::Algorithm
{
public:
typedef HostTraits Traits;

using Traits::Algorithm::doSomethingWithData;

void doSomethingWithData()
{
doSomethingWithData(*this);
}
};

template<typename HostTraits>
class HostCompositsData
:
public HostTraits::Algorithm
{
typename HostTraits::Collection data_;

public:
typedef HostTraits Traits;

using Traits::Algorithm::doSomethingWithData;

void doSomethingWithData()
{
doSomethingWithData(data_);
}

// Clumsy and breaking encapsulation
typename HostTraits::Collection& data()
{
return data_;
}
};

template<typename HostTraits>
class GenericStrategy
{
typename HostTraits::Collection data_;
typename HostTraits::Algorithm algorithm_;

public:

void doSomethingWithData()
{
algorithm_.doSomethingWithData(data_);
}
};

class ElementOne {};
class ElementTwo {};
class ElementThree {};

struct MyConfig
{
typedef ElementOne                  Element;
typedef CollectionThree<Element>      Collection;
typedef AlgorithmOne<Collection>  Algorithm;
};

int main(int argc, const char *argv[])
{
HostInheritsData<MyConfig> hostInherits;
hostInherits.doSomethingWithData();

// This must be a mistake, are policies meant to be used this way?
hostInherits.doSomethingWithData(hostInherits);

HostCompositsData<MyConfig> hostComposits;
hostComposits.doSomethingWithData();

// Clumsy to use, not intuitive and breaking encapsulation.
hostComposits.doSomethingWithData(hostComposits.data());

// Combinatorics are there, I can combine whatever I want in MyConfig as for
// policies, but I can also have global Algorithm and Collection objects
// (no protected destructors).
GenericStrategy<MyConfig> strategy;
strategy.doSomethingWithData();

return 0;
}

Вот мои вопросы:

Я настраиваю структуру класса Host с помощью политик. Как я могу полностью обогатить интерфейс класса Host, когда каждый реалистичный Алгоритм требует для работы Collection, а Collection инкапсулируется в хост?

Когда я сравниваю основанный на политике дизайн вместе с универсальной фабрикой, разве универсальная фабрика не приносит мне ту же комбинаторную сложность? Кажется, что использование Generic Factory лучше, так как я могу поменять Element, Container и Algorithm во всех возможных комбинациях, и у меня все еще могут быть открытые деструкторы для всех политик, что позволяет мне комбинировать их любым способом, например, как я хочу. глобальная комбинация Элемент, Коллекция и Алгоритм.

Мне кажется, что Расширенные Политики становятся проблемой, как только структура настроена. Даже если позже я добавлю функцию-член к алгоритму, он, вероятно, будет иметь параметры, относящиеся к Collection (например, итераторы Collection): если Хост инкапсулирует Collection с использованием композиции, мне нужно запросить его для параметра его собственной функции-члена:

// Clumsy to use, not intuitive and breaking encapsulation.
hostComposits.doSomethingWithData(hostComposits.data());

и если Хост инкапсулирует Коллекцию с использованием наследования, он становится (по крайней мере для меня) еще более странным:

 // This must be a mistake, are policies meant to be used this way?
hostInherits.doSomethingWithData(hostInherits);

Полностью ли я неправильно понял дизайн, основанный на политике (еще раз), правильно ли я использую черты?
Является ли шаблон общей стратегии лучшим выбором в этом случае?

2

Решение

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

Для выполнения других задач, кроме итерации и доступа, алгоритмы принимают в качестве параметров несколько более обогащенные типы, например back_inserters для доступа к push_back() член контейнера. Но в целом — без предшествующих знаний — абсолютно нет необходимости передавать весь интерфейс контейнера всем алгоритмам. Чтобы сделать действительно конкретные вещи контейнера, встраивая алгоритм как функция-член контейнера (например, sort() функция-член std::list) более уместно.

Для выполнения немного разных вещей есть несколько перегрузок (например, std::transform) с тем же именем функции. Только если ваш алгоритм должен поддерживать состояние действительно ли необходимо сделать его шаблоном класса, и предпочтительнее сделать его функциональный объект, т.е. содержащий перегруженный оператор () вместо DoSomethingWithData() функция-член.

Ваши данные параметризуются так же, как и стандартная библиотека: шаблон класса с Element в качестве параметра шаблона. Способ подачи таких данных в алгоритмы заключается в предоставлении доступ к итератору к вашим данным в виде begin() а также end() функции-члены. Стандартные контейнеры (vector, map, unorderd_map и т. д.) также использовать политики в качестве параметров шаблона, например, Allocator, Compare или же Hash класс, с помощью которого вы можете настроить поведение ваших данных. Стандартные умные указатели Deleter параметр политики для настройки их поведения.

Короче: внимательно проверьте свой дизайн. Что вы хотите сделать? Что каждый компонент (алгоритм, структура данных) должен знать о другом? И можете ли вы добраться туда при правильном использовании Стандартной библиотеки? Можно с уверенностью сказать, что большинство вещей, которые вы, возможно, захотите, уже закодированы, и вы можете сосредоточиться на написании логики вашего приложения, а не на алгоритмических или структурных деталях данных.

1

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]