Это подходящий дизайн для шаблона стратегии в Ruby?


Я очень доволен моим решением здесь, но я определенно ценю любую конструктивную критику моего стиля и дизайна. Основная идея заключается в использовании шаблон стратегии, чтобы упростить монтаж пользовательский процедурный генератор. Я использую модули, а не занятия для улучшения простоты "смешение" все вместе.

Вот корень 'стратегии' класс деревьев, AbstractStrategy:

      #
      #    the contract here is that the child strategy should implement an apply
      #    function which takes a target problem instance and hash options.
      #
      module AbstractStrategy
        def apply!(problem, opts={})
          log.debug "--- AbstractStrategy.apply! problem=#{problem}, opts=#{opts}"
          apply(problem, opts)  
        end
      end

На один уровень вниз, у нас есть еще один "родитель" шаблон, который реализует действие той или иной стратегии 'или 'стиль'. В данном случае это AbstractGenerationStrategy , который реализует применить в соответствии с контрактом выше.

      #
      # the contract here is that the child strategy needs to implement "generate_[component]!({})"
      #
      module AbstractGenerationStrategy extend AbstractStrategy
        include AbstractStrategy

        def extend!(base)
          log.debug("--- extending #{base}")
          self.extend(base)
        end

        #
        # => use like -- generate :rooms, ConstructNestedRoomsStrategy, ...opts...
        #
        def generate(component, strategy, opts={})
          log.debug "--- AbstractGenerationStrategy.generate[component=#{component}, strategy=#{strategy}]"
          extend!(strategy)
          apply!(component,opts)
        end

        def apply(component_to_generate, opts)
          log.debug "--- AbstractGenerationStrategy.apply component=#{component_to_generate}, opts=#{opts})"
          self.send("generate_#{component_to_generate}!", opts)
        end
      end

Чтобы увидеть это в действии, одна стратегия модуль, который реализует вышеуказанный договор является AbstractRoomGenerationStrategy:

      #
      # contract -- expects children to implement generate_room(opts)
      #      
      module AbstractRoomGenerationStrategy extend AbstractGenerationStrategy
        include AbstractGenerationStrategy
        include LoggingHelpers

        DEFAULT_ROOM_COUNT = 3

        def generate_rooms!(opts={})
          room_count = opts[:count] || DEFAULT_ROOM_COUNT
          log.debug "generating #{opts[:count]} rooms"
          room_count.times { generate_room!(opts) }
        end

      end

Наконец, "конкретный" пример AbstractRoomGenerationStrategy является ConstructSingleHugeRoomStrategy. Отметим, на данный момент мы ожидаем, чтобы быть "смешанные" В (С технической точки зрения, расширитьРед от) определенного рода занятий, и поэтому мы ожидаем, что некоторые члены переменные ( @ширину и @высота) существовать:

      module ConstructSingleHugeRoomStrategy extend RoomGenerationStrategy
        include RoomGenerationStrategy

        def generate_room!(opts={})
          @rooms ||= []
          log.debug 'generating room'
          if @rooms.empty?
            place_first_room(opts)
          else
            # do nothing
          end
        end

        #
        #  place a huge room in the center of the space
        #
        def place_first_room(opts)
          log.debug "placing first huge room"        
          default_opts = { 
            :width => @width/2, :height => @height/2,
            :position => [@width/4, @height/4]
          }
          opts ||= default_opts        
          room = Room.new(opts)
          log.debug "new room created: #{room}"
          @rooms << room
        end
      end

В контексте соответствующего класса операции может быть вызвана как

generate :rooms, ConstructSingleHugeRoomStrategy, opts

Вот фрагмент из тестового сценария реализации этой стратегии:

log.info '--- building new space with defaults (no opts)'
@@space = Space.new 

log.debug 'calling space.generate_rooms! with ConstructSingleHugeRoomStrategy'
@@space.generate :rooms, ConstructSingleHugeRoomStrategy

log.debug 'asserting room count == 1'
@@space.rooms.count.should == 1

На данный момент я не могу предоставить прямую ссылку на более как репозиторий в собственной системе Bitbucket, хотя я и рад стараться предоставить больше контекста, если это необходимо.

Я хотела бы отзывы о дизайн класса и использование рисунка (и, конечно, стиль кодирования и правильность, если вы видите реальные проблемы здесь.) Обратите внимание, основная цель использования "стратегиями" здесь было очень просто, чтобы я мог смешивать и сочетать алгоритмы в многоразовые и относительно простым способом.



612
5
задан 20 сентября 2011 в 08:09 Источник Поделиться
Комментарии
1 ответ

Прекрати писать на Ruby, как Java. У вас есть целая куча модулей, чтобы сделать что-то довольно простое.

Большинство (не все) Банда четырех паттерны обходные пути из-за отсутствия гибкости в таких языках, как C++ и Java, и менее необходимо на языке, как Рубин, что классы являются объектами первого класса и всегда расширяемый.

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

2
ответ дан 21 октября 2011 в 09:10 Источник Поделиться