2016-05-13 7 views
2

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

Предположим, у меня есть класс Counter, которому поручено просто подсчитывать элементы, если они соответствуют определенным критериям (большое упрощение). Эти элементы могут иметь различную структуру, поэтому, например:

  • если счетчик работает для «текст» пунктов, я рассчитывать только элемент, если его длина больше, чем X
  • , если счетчик работает для " image ", я считаю только элемент, если это изображение в формате JPEG.

Он чувствует себя хорошо (для меня), чтобы иметь AbstractItem класс, счетчик будет работать, но тогда она также нуждается в связанных с ними AbstractCriteria, поскольку критерии оцениваются по-разному для каждого типа элемента.

Своего рода псевдо-код:

class Counter 
{ 
    AbstractItemFactory factory; // initialized in constructor 
    AbstractCriteria criteria; // initialized in constructor 
    DataProvider provider;  // initialized in constructor 

    int count() 
    { 
    int result; 

    while((Data data = provider.get()) != null) 
    { 
     AbstractItem item = factory.create(data); 
     if(criteria.match(item)) 
     { 
     result++; 
     } 
    } 
    return result; 
    } 
} 

Проблема здесь состоит в том, что, конечно, AbstractCriteria не будет знать, как обращаться с AbstractItem. Я знаю, что это можно решить, используя метод base-to-производный литье внутри overriden match(), но это не очень хороший дизайн. Также возможно использовать шаблоны, но это сделает код более сложным, поэтому сначала хочу знать, есть ли какой-либо шаблон проектирования, который я мог бы использовать для достижения того, что я хочу. Или, может быть, мой подход к этой проблеме совершенно неверен, и есть какое-то простое решение, которое я просто не вижу?

Спасибо!

+0

насчет заимствования некоторых приемов программирования Функциональных и использовать логическое выражение лямбды для представления Характеристики (функция эталонной в основном)? Например, в JS это будет так же просто, как: 'function filterCount (items, filter) { return items.filter (filter) .length; } filterCount ([1, 2, 3], function (num) {return num% 2}); filterCount (['a', 'bb', 'ccc'], function (str) {return str.length> = 3;}); ' – plalx

+0

Могут ли критерии быть жестко закодированы? Или вы хотите иметь возможность обновлять критерии без повторной компиляции, то есть через файлы конфигурации? – denniskb

+0

@plalx К сожалению, я использую типизированный язык, поэтому 'function (num)' и 'function (str)' будет два разных типа. Чтобы использовать их, я должен заставить их наследовать от некоторого общего типа. Они также должны были бы принять некоторый абстрактный тип как параметр, так как вы не можете ничего рассказать о содержимом элемента, созданного 'AbstractItemFactory' в моем примере. На этом этапе мы вернемся к своей первоначальной проблеме: внутри этих функций мне пришлось бы отбросить абстрактный тип обратно к производному, и я хочу избежать этого :) – xba

ответ

0

В конце концов я решил свою проблему с помощью Visitor шаблона :)

+0

Шаблон посетителя хорошо подходит, когда у вас есть структура данных и вы хотите быть в состоянии добавить к нему новые операции. Канонический пример - это дерево синтаксиса, и вы хотите выполнять операции над деревом, например. симпатичная печать, проверка типа и т. д. Однако, когда набор алгоритмов закрыт и структура данных изменяется, шаблон посетителя является плохим выбором, поскольку изменение структуры данных подразумевает изменение всех посетителей, например. при добавлении нового типа узла. – Jens

+0

Хотите поделиться своим кодом/деталями? – denniskb

+0

@Jens это работало довольно хорошо для меня, потому что количество посетителей в моем коде всегда будет намного ниже числа «элементов». Кроме того, мои классы посетителя должны обрабатывать только один класс «элемент» (т. Е. Если это другой класс, это ошибка). Так что на самом деле я использовал модифицированную версию шаблона посетителя, возможно, это просто двойная отправка. Но, как я уже сказал, мои навыки с шаблонами не очень хороши, и я стараюсь их улучшить, поэтому, если у вас есть лучшее решение, я с удовольствием посмотрю :) – xba

0

ПОЦЕЛУЙ

template<typename T> 
bool predicate(T const & val); 

template<> 
bool predicate<string>(string const & s) { 
    return s.length() > 5; 
} 

template<> 
bool predicate<Image>(Image const & i) { 
    return i.format() == "JPEG"; 
} 

template<class Iter> 
iterator_traits<Iter>::difference_type count(Iter first, Iter last) { 
    using element_type = decay<iterator_traits<Iter>::value_type>::type; 
    return count_if(first, last, predicate<element_type>); 
} 

Поместите их в пространстве имен или сделать их статические методы класса счетчика, если вам нравится.

+0

Это довольно аккуратный фрагмент кода, я обязательно сохраню его в своей библиотеке :) Я смог скомпилировать его после добавления пары 'typename' здесь и там. Однако он будет работать только при прохождении итератора контейнера определенного типа. В моем случае 'AbstractItemFactory' создаст' AbstractItem' (который будет родительским классом для строки и изображения в вашем примере), поэтому мы вернемся к квадрату, если я не вижу ничего очевидного ...? – xba

+0

@xba Можно ли запросить динамический/реальный/производный тип вашего объекта AbstractItem? – denniskb

+0

Здравствуйте, конечно, возможно, я просто хотел этого избежать, потому что это довольно дорогостоящая операция :) Я решил это с использованием шаблона посетителя в конце :) – xba

Смежные вопросы