Query + Proxy Objects para Sweet Data Access

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 Consumerinstância e a usa como prefixo Voucherpara 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_displayfornecesse 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_missingpara delegar suas chamadas a outro objeto. Nesse caso, esse alvo era a Voucherclasse. Mas com o escopo de for_displayaplicado. 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_displaye ligar MyVouchers.new(current_consumer).active. O método activeainda vive como uma definição de escopo dentro da Voucherclasse, 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.