As interfaces fluentes violam a Lei de Demeter?
-
09-06-2019 - |
Pergunta
O artigo da Wikipédia sobre Lei de Deméter diz:
A lei pode ser declarada simplesmente como “use apenas um ponto”.
Contudo um exemplo simples de um interface fluente pode ser assim:
static void Main(string[] args)
{
new ZRLabs.Yael.Pipeline("cat.jpg")
.Rotate(90)
.Watermark("Monkey")
.RoundCorners(100, Color.Bisque)
.Save("test.png");
}
Então isso vai junto?
Solução
Bem, a definição curta da lei a encurta demais.A verdadeira "lei" (na realidade, conselhos sobre um bom design de API) diz basicamente:Acesse apenas objetos que você mesmo criou ou que foram passados para você como argumento.Não acesse objetos indiretamente por meio de outros objetos.Os métodos de interfaces fluentes geralmente retornam o próprio objeto, portanto, não violam a lei, se você usar o objeto novamente.Outros métodos criam objetos para você, portanto também não há violação.
Observe também que a "lei" é apenas um conselho de práticas recomendadas para APIs "clássicas".Interfaces fluentes são uma abordagem completamente diferente para o design de APIs e não podem ser avaliadas com a Lei de Demeter.
Outras dicas
Não necessariamente.“Use apenas um ponto” é um resumo impreciso da Lei de Deméter.
A Lei de Deméter desencoraja o uso de múltiplos pontos quando cada ponto representa o resultado de um objeto diferente, por exemplo:
- O primeiro ponto é um método chamado de ObjectA, retornando um objeto do tipo ObjectB
- Próximo ponto é um método disponível apenas em ObjectB, retornando um objeto do tipo ObjectC
- Próximo ponto é uma propriedade disponível apenas em ObjectC
- ao infinito
No entanto, pelo menos na minha opinião, a Lei de Deméter não é violada se o objeto de retorno de cada ponto ainda for do mesmo tipo que o chamador original:
var List<SomeObj> list = new List<SomeObj>();
//initialize data here
return list.FindAll( i => i == someValue ).Sort( i1, i2 => i2 > i1).ToArray();
No exemplo acima, FindAll() e Sort() retornam o mesmo tipo de objeto da lista original.A Lei de Deméter não é violada:a lista só conversou com seus amigos imediatos.
Dito isto De jeito nenhum interfaces fluentes violam a Lei de Demeter, desde que retornem o mesmo tipo que seu chamador.
Sim, embora seja necessário aplicar algum pragmatismo à situação.Sempre tomo a Lei de Deméter como uma diretriz e não como uma regra.
Certamente você pode querer evitar o seguinte:
CurrentCustomer.Orders[0].Manufacturer.Address.Email(text);
talvez substitua por:
CurrentCustomer.Orders[0].EmailManufacturer(text);
À medida que mais de nós usamos o ORM, que geralmente apresenta todo o domínio como um gráfico de objeto, pode ser uma ideia definir um "escopo" aceitável para um objeto específico.Talvez devêssemos usar a lei de Deméter para sugerir que você não deveria mapear o gráfico inteiro como alcançável.
O espírito da Lei de Deméter é que, dada uma referência de objeto ou classe, você deve evitar acessar as propriedades de uma classe que esteja a mais de uma subpropriedade ou método de distância, pois isso unirá fortemente as duas classes, o que pode ser não intencional e pode causar problemas de manutenção.
Interfaces fluentes são uma exceção aceitável à lei, pois são significou deve ser pelo menos um tanto fortemente acoplado, já que todas as propriedades e métodos são os termos de uma minilinguagem que são compostas juntas para formar sentenças funcionais.
1) Não viola nada.
O código é equivalente a
var a = new ZRLabs.Yael.Pipeline("cat.jpg");
a = a.Rotate(90);
a = a.Watermark("Monkey");
a = a.RoundCorners(100, Color.Bisque);
a = a.Save("test.png");
2) Como diz o bom e velho Phil Haack: A lei de Deméter não é um exercício de contagem de pontos
Não há problema com o seu exemplo.Afinal, você está girando, marcando água, etc...sempre a mesma imagem.Acredito que você esteja conversando com um objeto Pipeline o tempo todo, portanto, desde que seu código dependa apenas da classe do Pipeline, você não está violando o LoD.