Magento 2 - boas práticas para usar / evitar getters mágicos?

21

Os getters mágicos em Varien_Object(M1) e DataObject(M2) são práticas comuns, mas com o Magento 2 parece errado usá-lo.

Boa:

  • fácil de ler / escrever

Ruim

Questão

Com o Magento 2, temos dois novos métodos:

  • getDataByKey($key)
  • getDataByPath($path)

Existe alguma boa razão para continuar usando getData($key)ou algum método mágico?


Editar:

@Vinai thanks. Não mencionei o @methodmétodo, porque minha abordagem era bem diferente.

Isso apenas ajuda o IDE, mas não tem impacto em outras coisas.

Existem vários PRs mesclados que são "micro-otimizações", como converter para em (int)vez de intval()ou obter o tamanho da matriz fora de loops (mesmo para pequenas matrizes).

Por outro lado, existem

  1. getters mágicos, que têm algumas "despesas gerais", como Marius descreveu ....

    strtolower(trim(preg_replace('/([A-Z]|[0-9]+)/', "_$1", $name), '_'));
  2. getData($key) os mehtods também precisam de 2-3 verificações adicionais ...

    • if ('' === $key) {
    • if (strpos($key, '/')) {
    • if ($index !== null) {

Para o próprio código, concordo totalmente em preferir métodos reais, mas nos mesmos casos não é possível ... por exemplo, você criou um evento personalizado ...

$value = $observer->getVar_1();
$value = $observer->getData('var_1');
$value = $observer->getDataByKey('var_1');

Usar o terceiro com /** @var some $value */parece melhor para mim. (?)

sv3n
fonte
11
Você pode adicionar os métodos no bloco de documentos da classe, para que as ferramentas de análise de código não se queixem de métodos inexistentes. Também acho que o uso de dígitos nas teclas é uma má prática, portanto não deve ser listado como "Ruim" aqui.
Lily Bergonzat

Respostas:

20

A questão acima é de cerca usando métodos mágicas vs. getDataByKeyou getDataByPath. Eu acho que também existe uma terceira opção, que é implementar métodos getter e setter reais.

Todos os getData*métodos têm a desvantagem de que precisam ser anotados para que a inferência de tipo funcione.
Geralmente isso é feito com uma /* @var string $foo */anotação acima da getData*chamada.
Isso é um pouco fedorento, porque o tipo de dados deve ser declarado na classe que contém os dados, não na classe que chama getData*.
A razão para isso é que, se os dados forem alterados, é mais provável que a classe seja atualizada, nem todos getData*os sites de chamada.
É por isso que acho que métodos reais aumentam a capacidade de manutenção em comparação ao uso de getData*acessadores.

Então eu acho que tudo se resume a uma troca entre manutenção e implementação mais rápida (menos código para escrever).

Felizmente, hoje em dia os IDEs são realmente bons em criar as implementações getter e setter para nós, de modo que esse argumento não se aplica mais.

Mais um argumento contra getters e setters mágicos que está faltando na pergunta acima é que não é possível criar plugins para eles.

O único outro valor que acho que posso acrescentar ao tópico é tentar coletar os motivos para usar ou não as @methodanotações, se a implementação de métodos reais estiver fora de questão por algum motivo.

Prós

  • Uma @methodanotação é um pouco menos de código para escrever em comparação com a implementação de um getter e setter reais. Isso ainda é verdade atualmente, porque os IDEs são bons em gerar métodos de acessadores, portanto, isso não é mais um benefício real.

Contras

  • É fácil as coisas darem errado.
    • As anotações são comentários, elas se tornam obsoletas facilmente quando o código evolui, mas as anotações não são atualizadas. Métodos reais são mais robustos.
    • É possível adicionar várias anotações com diferentes assinaturas de tipo sem um erro de intérprete - o comportamento da análise de código estático é indefinido e pode levar a erros sutis que são difíceis de rastrear.
    • Se @methodexistir uma anotação e um método real com o mesmo nome, a assinatura do tipo de anotação substituirá o método real durante a análise estática do código, que é o oposto do que o intérprete PHP faz. Isso novamente pode facilmente levar a erros sutis.

Pelas razões acima, eu pessoalmente não uso @methodanotações se puder evitá-las.
Para código que se destina a durar muito tempo, implementei métodos getter e setter reais. O ganho de capacidade de manutenção vale o esforço de acionar o IDE para gerá-los.

Para um código mais experimental durante um pico, ou para um simples detalhe de implementação de um módulo getData*, também uso métodos, porque sou preguiçoso.

Vinai
fonte
Bom resumo. Obrigado Vinai. Isso responde mais do que eu realmente pedi.
sv3n
1

Todos os getData*métodos têm a desvantagem de que precisam ser anotados para que a inferência de tipo funcione.

Geralmente isso é feito com uma /*@var string $foo */anotação acima da getData*chamada. Isso é um pouco fedorento, porque o tipo de dados deve ser declarado na classe que contém os dados, não na classe que chama getData *.

A razão para isso é que, se os dados forem alterados, é mais provável que a classe seja atualizada, nem todos getData*os sites de chamada. É por isso que acho que métodos reais aumentam a capacidade de manutenção em comparação ao uso de acessadores getData *.

Sim, é mal cheiroso, mas pode (e deve?) Ser evitado. Eu acho que esse é um código muito comum e frequentemente sugerido:

/** @var Foo $product */
$product = $model->getProduct()
if ($product->getId()) {
    $product->doSomething();
}

O problema é que você simplesmente adivinha que o valor de retorno é do tipo Foocom um método que pode ser getId()chamado.

Para manutenção, por que não assumir o tipo de variável e adicionar um InvalidArgumentException?

$product = $model->getProduct()
if ($product instanceof Foo && $product->getId()) {
    $product->doSomething();
}

Isso também corrige a análise de código estático no caso de $model->getProduct()diferentes tipos de retorno - como Foo|false. No primeiro caso, ele reclamaria sobre doSomething()a possibilidade de recorrer false.

sv3n
fonte