Eu não sou desenvolvedor de PHP, por isso estou me perguntando se no PHP é mais popular usar getter / setters explícitos, em um estilo OOP puro, com campos privados (do jeito que eu gosto):
class MyClass {
private $firstField;
private $secondField;
public function getFirstField() {
return $this->firstField;
}
public function setFirstField($x) {
$this->firstField = $x;
}
public function getSecondField() {
return $this->secondField;
}
public function setSecondField($x) {
$this->secondField = $x;
}
}
ou apenas campos públicos:
class MyClass {
public $firstField;
public $secondField;
}
obrigado
php
oop
coding-style
Marca
fonte
fonte
Respostas:
Você pode usar métodos mágicos php
__get
e__set
.fonte
__get
e__set
. Existem dois sublinhados, não um. Aqui está o link direto para a parte direita da página: php.net/manual/en/… (+1 para uma resposta correta)public
propriedades, se não houver validação / saneamento?Por que usar getters e setters?
fonte
O Google já publicou um guia sobre otimização de PHP e a conclusão foi:
Sem getter e setter Otimizando o PHP
E não, você não deve usar métodos mágicos . Para PHP, o Magic Method é mau. Por quê?
PHP não é Java, C ++ ou C #. PHP é diferente e desempenha papéis diferentes.
fonte
$dog->name = 'fido'
é melhor que$dog->setName('fido')
. Quando realmente mutação de um imóvel (ex:$dog->increaseAge(1)
. Eu posso construir o método que faz a validação e sofre mutações necessário que a propriedade Mas nem todas as ações realmente necessitam mutação nesse sentido.O encapsulamento é importante em qualquer idioma OO, a popularidade não tem nada a ver com isso. Em linguagens tipadas dinamicamente, como PHP, é especialmente útil porque existem poucas maneiras de garantir que uma propriedade seja de um tipo específico sem o uso de setters.
No PHP, isso funciona:
Em Java, não:
O uso de métodos mágicos (
__get
e__set
) também funciona, mas somente ao acessar uma propriedade com menor visibilidade do que o escopo atual pode acessar. Ele pode facilmente causar dores de cabeça ao tentar depurar, se não for usado corretamente.fonte
$bar
comoint
no primeiro exemplo: wiki.php.net/rfc/typed_properties_v2Se você preferir usar a função __call, poderá usar este método. Trabalha com
$this->property()
$this->property($value)
$this->getProperty()
$this->setProperty($value)
kalsdas
fonte
property_exists(get_class($this), $name)
e a recursão é lenta. Existe uma maneira de atenuar isso com o cache, mas ainda será mais lento do que criar os getters e os setters manualmente. Eu apenas escrevi isso como uma alternativa. Na verdade, eu não recomendo o uso de "Métodos Mágicos". O tempo extra de criação de getters e setters geralmente é insignificante.Além das respostas já excelentes e respeitadas aqui, eu gostaria de expandir o PHP sem setters / getters.
O PHP não possui sintaxe getter e setter . Ele fornecemétodossubclassificados ou mágicos para permitir "ligar" e substituir o processo de pesquisa de propriedades, conforme apontado por Dave .
O Magic permite que programadores preguiçosos façam mais com menos código em um momento em que estamos ativamente envolvidos em um projeto e o conheçam intimamente, mas geralmente à custa da legibilidade.
Desempenho Todas as funções desnecessárias, resultantes da imposição de uma arquitetura de código do tipo getter / setter no PHP, envolvem seu próprio quadro de pilha de memória após a chamada e estão desperdiçando ciclos da CPU.
Legibilidade: A base de código incorre em linhas de código inchadas, o que afeta a navegação de código, pois mais LOC significa mais rolagem.
Preferência: pessoalmente, como regra geral, tomo o fracasso da análise de código estático como um sinal para evitar seguir o caminho mágico, desde que os óbvios benefícios a longo prazo me escapem naquele momento.
Falácias:
Um argumento comum é legibilidade. Por exemplo,
$someobject->width
é mais fácil ler do que$someobject->width()
. No entanto, ao contrário do planetacircumference
ouwidth
, que pode ser assumidostatic
, a instância de um objeto como$someobject
, que requer uma função de largura, provavelmente faz uma medição da largura da instância do objeto.Portanto, a legibilidade aumenta principalmente devido a esquemas de nomeação assertivos e não ocultando a função que gera um determinado valor de propriedade.
__get / __set usa:
pré-validação e pré-saneamento de valores de propriedades
strings eg
Nesse caso
generatelatex
seguiria um esquema de nomeação de actionname + methodnamecasos especiais e óbvios
Nota: O PHP optou por não implementar a sintaxe getter / setter. Não estou afirmando que getters / setter são geralmente ruins.
fonte
Pronto!
fonte
Bem, PHP tem métodos mágicos
__get
,__set
,__isset
e__unset
, o que é sempre um começo. Infelizmente, as propriedades OO apropriadas são mais do que métodos mágicos. O principal problema com a implementação do PHP é que métodos mágicos são chamados para todas as propriedades inacessíveis. O que significa que você deve se repetir (por exemplo, chamando property_exists ()) nos métodos mágicos ao determinar se o nome é realmente uma propriedade do seu objeto. E você realmente não pode resolver esse problema geral com uma classe base, a menos que todas as suas classes herdem ie. ClassWithProperties, já que o PHP não possui herança múltipla.Por outro lado, as novas classes de estilos do Python oferecem a você
property()
, o que permite definir explicitamente todas as suas propriedades. C # tem sintaxe especial.http://en.wikipedia.org/wiki/Property_(programming)
fonte
Fiz um experimento usando o método mágico __call. Não tenho certeza se devo publicá-lo (por causa de todos os avisos "NÃO USAR MÉTODOS MÁGICOS" nas outras respostas e comentários), mas vou deixá-lo aqui .. apenas no caso de alguém achar útil.
Basta adicionar esse método acima em sua classe, agora você pode digitar:
Dessa forma, você pode obter / definir tudo em sua classe, se existir, se precisar apenas de alguns elementos específicos, poderá usar uma "lista de permissões" como filtro.
Exemplo:
Agora você só pode obter / definir "foo" e "fee".
Você também pode usar essa "lista de permissões" para atribuir nomes personalizados para acessar seus vars.
Por exemplo,
Com essa lista, agora você pode digitar:
.
.
.
Isso é tudo.
Doc: __call () é acionado ao chamar métodos inacessíveis em um contexto de objeto.
fonte
Depois de ler os outros conselhos, estou inclinado a dizer o seguinte:
Como regra GENERIC , você nem sempre definirá setters para TODAS as propriedades, especialmente as "internas" (semáforos, sinalizadores internos ...). As propriedades somente leitura não terão setters, obviamente, portanto, algumas propriedades terão apenas getters; é aí que __get () reduz o código:
Sim! também poderíamos escrever um método privado para fazer isso, mas, novamente, teremos MUITOS métodos declarados (memória ++) que acabam chamando outro método, sempre o mesmo. Por que simplesmente não escrever um método ÚNICO para governá-los todos ...? [Sim! trocadilho absolutamente pretendido! :)]
Os setters mágicos também podem responder SOMENTE a propriedades específicas, para que todas as propriedades do tipo de data possam ser rastreadas em relação a valores inválidos em um único método. Se as propriedades do tipo de data foram listadas em uma matriz, seus setters podem ser definidos facilmente. Apenas um exemplo, é claro. há muitas situações.
Sobre legibilidade ... Bem ... Esse é outro debate: eu não gosto de estar vinculado aos usos de um IDE (na verdade, eu não os uso, eles tendem a me dizer (e me forçar ) como escreva ... e eu gosto de codificar "beleza"). Eu costumo ser consistente em nomear, portanto, usar ctags e algumas outras ajudas é suficiente para mim ... Enfim: depois que todos esses setters e getters mágicos terminarem, escrevo os outros setters que são específicos demais ou "especiais" para ser generalizado em um método __set (). E isso abrange tudo o que preciso sobre como obter e definir propriedades. É claro: nem sempre existe um terreno comum, ou existem poucas propriedades que não valem a pena codificar um método mágico, e ainda há o bom e tradicional par tradicional de setter / getter.
Linguagens de programação são exatamente isso: linguagens artificiais humanas. Então, cada um deles tem sua própria entonação ou sotaque, sintaxe e sabor, então não pretendo escrever um código Ruby ou Python usando o mesmo "sotaque" que Java ou C #, nem escreveria um JavaScript ou PHP para se parecer Perl ou SQL ... Use-os da maneira como devem ser usados.
fonte
De um modo geral, a primeira maneira é mais popular em geral, porque aqueles com conhecimento prévio em programação podem facilmente fazer a transição para PHP e realizar o trabalho de maneira orientada a objetos. A primeira maneira é mais universal. Meu conselho seria seguir o que é experimentado e verdadeiro em muitos idiomas. Então, quando e se você usar outro idioma, estará pronto para realizar alguma coisa (em vez de gastar tempo reinventando a roda ).
fonte
Existem várias maneiras de criar código-fonte em uma convenção de netbeans. Isso é legal. Isso torna os pensamentos mais fáceis === FALSE. Basta usar o tradicional, especialmente se você não tiver certeza de qual das propriedades deve ser encapsulada e qual não. Eu sei, é um código boi .... pla ..., mas para trabalhos de depuração e muitos outros acha que é o caminho melhor e mais claro. Não gaste muito tempo com milhares de artes como criar getters e setters simples. Você não pode implementar também alguns padrões de design, como a regra do deímetro e assim por diante, se você usar magics. Em situações específicas, você pode usar magic_calls ou para soluções pequenas, rápidas e claras. Claro que você também pode criar soluções para padrões de design dessa maneira, mas por que torná-lo mais difícil de viver.
fonte
Validando + Formatação / Derivação de Valores
Setters permitem validar dados e getters permitem formatar ou derivar dados. Os objetos permitem que você encapsule dados e seu código de validação e formatação em um pacote puro que incentive o DRY.
Por exemplo, considere a seguinte classe simples que contém uma data de nascimento.
Você deseja validar que o valor que está sendo definido é
E você não deseja fazer essa validação em todo o seu aplicativo (ou em vários aplicativos). Em vez disso, é mais fácil tornar a variável membro protegida ou privada (para tornar o levantador o único ponto de acesso) e validar no levantador, porque você saberá que o objeto contém uma data de nascimento válida, independentemente da parte do aplicativo de origem do objeto e, se você quiser adicionar mais validação, poderá adicioná-lo em um único local.
Você pode querer adicionar vários formatadores que operam no mesmo ie variável de membro
getAge()
egetDaysUntilBirthday()
e você pode querer impor um formato configurável emgetBirthDate()
acordo com a localidade. Portanto, eu prefiro acessar valores consistentemente através de getters, em vez de misturar$date->getAge()
com$date->birth_date
.getters e setters também são úteis quando você estende objetos. Por exemplo, suponha que seu aplicativo seja necessário para permitir datas de nascimento de mais de 150 anos em alguns lugares, mas não em outros. Uma maneira de resolver o problema sem repetir qualquer código seria estender o
BirthDate
objeto e colocar a validação adicional no setter.fonte
setHired
esetHireDate
. Não é válido permitir que alguém defina uma data de contratação sem também definir o funcionário como contratado. Mas não há como você aplicar isso. Se você aplicá-lo em um desses setters, está forçando a ordem da "configuração" e isso exige mais leitura de código de um desenvolvedor para saber. Então, quando você adota um método como$employee->promote($newPosition);
o de verificar uma sinalização para ver se a validação foi feita ou assume que não foi feita e a executa novamente (redundante).$employee->updateWorkStatus($hired, $hireDate);
ou se mais avançado$employee->adminUpdate(\Employee\AdminUpdateDTO $dto);
. Agora você pode validar no contexto necessário e decidir se é necessária alguma validação extra.Este post não é especificamente sobre
__get
e__set
sim__call
que é a mesma idéia, exceto para chamadas de método. Como regra geral, eu me afasto de qualquer tipo de método mágico que permita sobrecarga por motivos descritos nos comentários e postagens , NO ENTANTO , recentemente, deparei-me com uma API de terceiros que utilizo, que usa um SERVICE e um SUB-SERVICE, por exemplo :A parte importante disso é que essa API tem tudo a mesma coisa, exceto a sub-ação, neste caso
doActionOne
. A ideia é que o desenvolvedor (eu e outros usuários dessa classe) possa chamar o sub-serviço pelo nome, em vez de algo como:Eu poderia fazer:
Para codificar isso seria apenas muita duplicação (este exemplo se parece muito com o código):
Mas com o método mágico de
__call()
eu sou capaz de acessar todos os serviços com métodos dinâmicos:O benefício dessa chamada dinâmica para o retorno de dados é que, se o fornecedor adicionar outro sub-serviço, não preciso adicionar outro método à classe ou criar uma classe estendida etc. Não tenho certeza se isso é útil. ninguém, mas eu percebi que eu iria mostrar um exemplo onde
__set
,__get
,__call
, etc. pode ser uma opção para a consideração já que a função principal é o retorno de dados.EDITAR:
Coincidentemente, vi isso alguns dias após a publicação, descrevendo exatamente o meu cenário. Não é a API à qual eu estava me referindo, mas a aplicação dos métodos é idêntica:
Estou usando a API corretamente?
fonte
Atualização: Não use esta resposta, pois esse é um código muito idiota que eu encontrei enquanto aprendia. Basta usar getter e setter simples, é muito melhor.
Normalmente, eu uso esse nome de variável como nome da função e adiciono parâmetro opcional a essa função para que, quando esse parâmetro opcional for preenchido pelo chamador, defina-o para a propriedade e retorne $ this object (encadeamento) e, em seguida, quando esse parâmetro opcional não especificado por chamador, eu apenas retorno a propriedade ao chamador.
Meu exemplo:
fonte