No telegram fazendo suporte à comunidade, acabei me deparando com um exemplo que me chamou muita a atenção. Um código que mentia.
E vamos às minhas colocações sobre a questão:
Sobre as abstrações
Eu citei abstrações em Abstrações – Tradeoffs e co-responsabilidade, também falei sobre a absorção de complexidade pela arquitetura em Oragon – Princípios de Design – Complexidade Reside na Arquitetura, ambos são bons textos auxiliares para esse esse texto aqui. Mas falando de abstrações, quando criamos abstrações, estamos tentando abstrair complexidade. Isso quer dizer, delegar à abstração alguma complexidade em prol de simplificar algo. Ou seja, entre as diferentes N formas de se usar algo, elegemos algumas para simplificar de forma a facilitar seu uso. Abstrações em geral são assim.
public IQueryable<T> GetBlaBlaBla(Expression<Func<T, bool> filterExpression) => return session .Query<T>() .Where(filterExpression) .ToList() .AsQueryable();
O código do acima é de uma abstração de um repositório genérico.
O problema em questão é que:
Ao retornar IQueryable<T> um consumidor espera que o método seja uma promessa de execução de uma query.
Essa expectativa não reflete a realidade. Na tentativa de facilitar o trabalho do consumidor, você está dando uma falsa ideia…
Entre 9 definições fico com 2 delas: “4 – Aquilo que dá falsa ideia” e “5 – O que ilude“.
Entenda, não há má vontade nenhuma no processo, mas não é pela boa vontade que o código deixa de ser uma mentira.
A solução óbvia para esse problema seria:
public IList<T> Metodo() => return session .Query<T>() .Where(filterExpression) .ToList();
Pronto, agora:
- está claro que você está realizando a operação.
- está claro que você está trazendo os dados para a memória.
Essa simples mudança evita que consumidores enganadamente achem que seu método faz algo que ele não faz. A versão anterior dava a entender que você teria como resultado uma promessa e não os dados todos materializados em memória.
A diferença para quem consome, é que IQueryable<T> sugere que você pode refinar sua consulta com novos filtros e novas projections, e conectado a um bom ORM, a impressão é de que isso acontecerá de forma otimizada em banco. No entanto quanto você chamou o ToList() acabou o amor! O dado veio imediatamente para a memória com o filtro que estava ali. Tudo que você fizer depois de chamar o ToList(), mesmo fazendo cast com AsQueryable(), será realizado em memória.
0 comentários