2013-03-11 4 views
0

У меня есть класс в Ruby, который содержит некоторые вещи, я буду называть FooBox:Как переключить реализацию во время создания экземпляра?

class FooBox 
    ... 
end 

У меня есть два возможных магазин бэк-данные для FooBox называемых BoxA и BoxB с различными характеристиками, но один и тот же интерфейс:

class BoxA 
    include Enumerable 
    def put_stuff(thing) 
    ... 
    end 
end 


class BoxB 
    include Enumerable 
    def put_stuff(thing) 
    ... 
    end 
end 

Как я могу создать экземпляр FooBox, и, на основе параметра, решить, следует ли поддержать его с BoxA или BoxB реализации? Я не хочу передавать реализацию в конструктор; Я только хочу передать что-то, чтобы определить, какой вид использовать.

class FooBox 
    def initialize(implementation_choice) 
    # ??? 
    end 
end 
+0

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

+0

, так что вы хотите реализовать шаблон Factory Design в Ruby? – donnior

+0

Потребитель FooBox не знает о поддержке реализации, как он хочет, чтобы данные обрабатывались (в абстрактном смысле). FooBox смотрит на параметр и идет «Я вижу, что вы запросили здесь. Позвольте мне выкопать мою реализацию для этого». –

ответ

1

я обычно делаю что-то вроде этого:

class BoxA 
    def self.match? options 
    # figure out if BoxA can be used given options 
    end 
end 

# Implement BoxB (and other strategies) similarly to BoxA 

class FooBox 
    STRATEGIES = [BoxA, BoxB] 

    def initialize options 
    @options = options 
    end 

    def strategy 
    @strategy ||= STRATEGIES.detect { |strategy| strategy.match? @options } 
    end 
end 

Это сохраняет ответственность «зная», если стратегия может быть использована в рамках самой стратегии (а не делает класс контекста монолитный) , а затем просто выбирает первый в списке, который говорит, что он может работать.

Я использовал этот шаблон (и аналогичные варианты для немного разных проблем) несколько раз и нашел его очень чистым.

+0

Ницца. Это хороший материал. Спасибо. –

0

Простое решение создать отображение для типа и стратегии классе стратегию, так же, как решение @ Андрей Маршалла

Но лучше я бы рассматривает две вещи:

  • держатель Стратегий (вот FooxBox) теперь нужно знать каждую имплантацию коробки и жестко кодировать свои имена себе; это не гибкий подход , учитывая, что в один прекрасный день вы хотите добавить еще одну стратегию, перейти к коду и добавить его? С рубином мы можем сделать это с помощью «самостоятельной регистрации».
  • Вы не хотите, чтобы держатель стратегий возвращался к реализации дико, я имею в виду, что «BoxA» и «BoxB» или «BoxXYZ» когда-нибудь должны принадлежать одной и той же стратегии , на Java это может означать, что все они должны реализовывать interface с рубином мы обычно делаем это с include SomeMoudle

в моем приложении я использую следующее решение (только демо)

module Strategies 
    def self.strategies 
    @@strategies ||= {} 
    end 

    def self.strategy_for(strategy_name) 
    @@strategies[strategy_name] 
    end 
end 

module Strategy 
    def self.included(base) 
    base.class_eval do 
     def self.strategy_as(strategy_name) 
     Strategies.strategies[strategy_name] = self 
     end 
    end 
    end 
end 


class BoxA 
    include Strategy 

    strategy_as :box_a 

    def do_stuff 
    puts "do stuff in BoxA" 
    end 
end 

class BoxB 
    include Strategy 

    strategy_as :box_b 

    def do_stuff 
    p "do stuff in BoxB" 
    end 
end 

## test 
Strategies.strategy_for(:box_a).new.do_stuff 
Strategies.strategy_for(:box_b).new.do_stuff 

Если вы хотите, чтобы обнаружить стратегию блока матча, вы можете изменить strategy_as принять блок. затем используйте Strategies.strategy_for{...}.new.do_stuff

+0

Правильно, хотя я бы предпочел что-то вроде «Strategies.add_strategy BoxA», чем попал в структуру данных внутренней структуры «Стратегии». Ruby не имеет интерфейсов, а 'include' не заменяет их. Вы также можете просто определить «strategy_as» в «Стратегии» как правило (вместо крючка) и «продолжить стратегию». –

+0

@AndrewMarshall спасибо, Это моя ошибка, я просто хочу сказать «включить SomeMoudle», хотя это не замена интерфейса, но он более мощный, чем он. И лично мне нравится включить модуль,^_ ^ – donnior

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