Fiquei interessado em aplicar os padrões estabelecidos pela Gangue dos Quatro em meu código sempre que possível. Isso me ajuda a entender o que funciona bem e o que não funciona.
Ao refatorar alguns dados de acesso confuso em uma ação do controlador Rails, me deparei com uma combinação interessante de dois padrões diferentes – o padrão de objeto de consulta com o padrão de objeto proxy .
Objeto de Consulta
Eu comecei a implementar o padrão de consulta criando uma classe que usa uma Consumer
instância e a usa como prefixo Voucher
para restringir seus resultados por sua chave estrangeira associada consumer_id
:
class MyVouchers
attr_accessor :consumer
def initialize(consumer)
self.consumer = consumer
end
def for_display
self.consumer.vouchers.joins(:partners)
.where(["partners.business_name != ?)", Partner::LEGO])
end
end
Isso significava que, em vez de incorporar muitas pesquisas de modelo ActiveRecord e encadeamento de escopo no controlador, eu poderia agora executar o seguinte e retornar o mesmo conjunto de resultados:
MyVouchers.new(current_consumer).for_display.active
Objeto proxy
Isso era bom, mas uma interface pública prolixo. Não achei que a chamada de método to_display
fornecesse muito valor, então queria eliminá-la das chamadas de método. Após alguma experimentação, implementei o padrão proxy, que utiliza method_missing
para delegar suas chamadas a outro objeto. Nesse caso, esse alvo era a Voucher
classe. Mas com o escopo de for_display
aplicado. Modifiquei minha classe para ficar assim:
class MyVouchers
attr_accessor :consumer
def initialize(consumer)
self.consumer = consumer
end
def for_display
self.consumer.vouchers.joins(:partners)
.where(["partners.business_name != ?)", Partner::LEGO])
end
private
def method_missing(method, *args, &block)
for_display.send(method, *args, &block)
end
Agora, no controlador, posso eliminar to_display
e ligar MyVouchers.new(current_consumer).active
. O método active
ainda vive como uma definição de escopo dentro da Voucher
classe, portanto, pode ser reutilizado fora do meu objeto de consulta. E menos conhecimento de domínio é necessário para invocar meu objeto de consulta, pois a instanciação é suficiente para iniciar o encadeamento de outros escopos.
Fiquei satisfeito em ver que, além do uso dos padrões básicos isoladamente, eles podem ser combinados de maneiras interessantes que potencializam os pontos fortes de ambos os padrões.