Usamos cookies para medir audiência e melhorar sua experiência. Você pode aceitar ou recusar a qualquer momento. Veja sobre o iMasters.
1 - Porque que quando eu não declaro uma propriedade, mas dentro de um método eu faço isso e funciona:
<?php
class WithoutProperty
{
public function setProperty($property)
{
$this->property = $property;
}
public function getProperty()
{
return $this->property;
}
}
2 - Qual a “função” de um método set e um get?
A pergunta deve ser meia sem nexo, mas vamos aos fatos.
Vamos supor que eu tenho um carro, mas existem vários modelos correto? Então (Exemplo simples apenas explicando onde eu quero chegar):
<?php
interface Carro
{
public function setCarModel($model);
public function getCarModel();
}
class Corsa implements Carro
{
private $model;
public function setCarModel($model)
{
$this->model = $model;
}
public function getCarModel()
{
return $this->model;
}
}
$Corsa = new Corsa();
$Corsa->setCarModel("G1");
Por mais que tenha sentido a classe Corsa implementar a interface Carro, qual a lógica dos métodos get e set sendo que eles só fazem uma função simples? – Por mais que a propriedade seja private, o que impede de mudar para public e fazer assim, sendo que o set faz praticamente a mesma coisa?
class Corsa implements Carro
{
public $model;
[...]
}
$Corsa = new Corsa();
$Corsa->model = "G1";
O mesmo se aplica com o get. Qual a finalidade dele em um método apenas com return sendo que eu posso acessar ele diretamente? – Caso ele seja public, é claro.
3 - Continuando no questão de get e set: Entrando um pouco em visibilidade também. Um método privado está falando que ele não pode ser acessado externamente e nem por classes que herdam ela correto? Então, qual a lógica de ter um método (set) para alterar essa variável sendo que se eu quero que ela seja privada, não é apenas para eu alterar ou acessar ela dentro do meu escopo e não de fora(set)?
Por enquanto são essas perguntas, mas de acordo com que eu vou praticando e surgindo dúvidas eu vou perguntando.
A interface serve pra definir o comportamento de um objeto, logo não faz sentido, nem há motivo ter getters e settes nela.Quanto a private, serve para proteger dados internos da classe.
E se um set ou get fosse para validar o que está entrando na classe? A interface seria inútil? E o "conceito" de que Corsa é um Carro?
Devem definir comportamentos:
Ok; Carro pode ter um comportamento: Andar(), mas e uma fruta? Qual seria a ação de uma fruta?
Abacate é uma fruta logo ela iria implementar Fruta correto? Mas qual a lógica de ter uma interface sem "comportamento"
<?php
interface Fruta {
}
class Abacate implements Fruta
{
}
Cara, você esta pensando muito em código..
Orientação a Objeto é conceito.
Getters e Setters é por que um método só processa algo enquanto uma propriedade é algo "estático" .
Trazendo o problema para o mundo real é simples, uma propriedade conseguiria se setar?
A resposta clara é não, existe um método que seta uma propriedade.
Quem executa qualquer ação é um método então se vc não tiver um getter e um setter como vc seta uma propriedade e recupera a mesma sendo que uma propriedade não pode executar nada?
E se um set ou get fosse para validar o que está entrando na classe? A interface seria inútil? E o "conceito" de que Corsa é um Carro?
Não vai precisar disso por que você vai dizer ao objeto que ele sempre vai ter um veiculo logo é um ERRO enorme você dizer que uma banana é um carro.
tente sair do código e entrar mais em conceito.
trazer o problema para o mundo real e ver como "objetos físicos" se comportam.
A interface serve pra definir o comportamento de um objeto, logo não faz sentido, nem há motivo ter getters e settes nela.
Cuidado com essa afirmação. Apesar de que em Java interfaces podem ter comportamento (acho que você confundiu c/ Java), em PHP elas apenas definem contrato.
Interfaces são um tipo, que podem definir métodos para que um(as) classe(s) tenha que implementar.
É uma forma de abstrair e desacoplar o código com polimorfismo.
>
1 - Porque que quando eu não declaro uma propriedade, mas dentro de um método eu faço isso e funciona:
<?php
class WithoutProperty
{
public function setProperty($property)
{
$this->property = $property;
}
public function getProperty()
{
return $this->property;
}
}
Você não necessita declarar uma propriedade previamente. Todavia, essa propriedade será pública.
2 - Qual a “função” de um método set e um get?
Definir e recuperar um valor. Em algumas linguagens (como o PHP), eles são usados. Porém, em algumas há recursos melhores para isso.
Usamos getters/setters ao invés de propriedades públicas pois eles permitem validação, lazy loading, etc. Lista de razões aqui: http://stackoverflow.com/a/1568230
Em algumas linguagens, como o Ruby, eles não são necessários:
class User
attr_accessor :name, :email
end
user = User.new
user.name = "Joãozinho"
user.email = "joao@pedefeijao.com"
puts user.name + "\n"
puts user.email + "\n"
Logo, depende da linguagem utilizada e de seu ecossistema.
E não simplesmente faça a IDE "vomitar" getters e setters por todo lado, pense bem antes de implementá-los.
@Vinicius Rangel
Obrigado pelas dicas.
Não é que eu não pense ou tente pensar em conceito, eu tento muito trazer para a vida real, mas a questão é que eu estou aprendendo ainda, e não é tudo que só porque eu trouxe para o mundo real que eu vou saber implementar. Justamente isso as perguntas.
@Enrico Pereira
Você não necessita declarar uma propriedade previamente. Todavia, essa propriedade será pública.
Realmente, percebi isso, mas depois que eu postei a pergunta. - Deixei apenas se alguém tivesse alguma resposta diferente. (Ninguém sabe né :P)
Muito obrigado pela lista de vantagens de usar setters and getters.
E não simplesmente faça a IDE "vomitar" getters e setters por todo lado, pense bem antes de implementá-los.
Eu tenho isso em mente, além de não ser algo interessante em questão de "boas práticas" getters and setters para todos os lados, mas também visualmente.
Tem alguma dica de momentos em qual devo utilizar e não utilizar? No seu ponto de vista ou de outros?
3 - Continuando no questão de get e set: Entrando um pouco em visibilidade também. Um método privado está falando que ele não pode ser acessado externamente e nem por classes que herdam ela correto? Então, qual a lógica de ter um método (set) para alterar essa variável sendo que se eu quero que ela seja privada, não é apenas para eu alterar ou acessar ela dentro do meu escopo e não de fora(set)?
Ninguém respondeu :(
@edit
Encontrei uma penca de tópicos no stackoverflow sobre "getters setters". :o
O problema dos getters/setters é o modelo anêmico, este post deve ajudar-te: http://blog.caelum.com.br/nao-aprender-oo-getters-e-setters/
O normal é usar privado para todas as propriedades.
Há um conceito (não aplicável em todas linguagens) que é: Propriedades -> estado e Métodos -> operações e trocas de estado.
Na comunidade Java getters/setters é algo extremamente discutido.
Vamos as próximas perguntas (lembrando que de acordo com que eu vou tendo dúvidas eu vou perguntando nesse mesmo tópico):
Eu assisto vários vídeos da imasters (hangouts) e também outros vídeos e sempre vejo eles falando de: Horizontal e Vertical.
Talvez uma pergunta resposda as outras, mas como são essas as dúvidas da mente, que venham as respostas.
Obrigado antecipadamente.
Abacate é uma fruta logo ela iria implementar Fruta correto?
O que eu posso fazer com uma fruta ? Comer ?:
interface Fruta{
public function comer();
}
Porque a Orientação a Objetos é horizontal?
Porque é assim que voce a programa, não se esqueça, classe não é objeto.
O que eu posso fazer com uma fruta ? Comer ?:
Quem come é a pessoa e não a fruta.
Porque é assim que voce a programa, não se esqueça, classe não é objeto.
Creio que não foi uma explicação muito válida. Continua a mesma coisa.
>
Horizontal => composição, agregação, associação
Vertical => herança
>
Porque herança é hierarquia, que é vertical. Veja um exemplo:
Lugares
EstadosUnidos -> estende Lugares
NovaIorque -> estende EstadosUnidos
Ohio -> estende EstadosUnidos
Arkansas -> estende EstadosUnidos
Brasil -> estende Lugares
SaoPaulo -> estende Brasil
RioDeJaneiro -> estende Brasil
Parana -> estende Brasil
Já o relacionamento horizontal não é uma hierarquia. Veja um exemplo:
Database
UserStorage possui um Database
ProductsStorage possui um Database
>
Herança é do tipo "é um", portanto quando você diz que classe A estende classe B, você assume semanticamente que A é um B.
Herança é erroneamente utilizada. Você vê muito classe de usuário estendendo classe de banco de dados, o que é errado.
Há uma frase bem popular: "Prefer composition over inheritance".
cai exatamente neste ponto de reutilização através de herança, composição e traits.
>
Na verdade, não é bem assim. Frameworks usam arquitetura vertical geralmente para fazer inversão de controle, mas não há essa classificação. Podemos até falar que boa parte dos frameworks usam mais herança do que composição, mas não falar que um framework só usa um ou outro.
>
Porque é orientação a objetos, não é orientação a hierarquia. O relacionamento de objetos é horizontal e o de tipos (herança) vertical.
Herança é apenas um recurso de boa parte das linguagens de programação que possuem suporte para OOP. Ela é útil e efetiva, se usada cautelosamente, mas não substitui a composição.
Perfeita a resposta @Enrico Pereira. Obrigado.
Nesse meio tempo sumido eu comecei a praticar e escrevi um código de exemplo.
Mas antes vamos as dúvidas:
Vamos ao código que eu criei:
BasketInterface.php
<?php
interface BasketInterface
{
public function setCapacity($capacity);
public function setWeight($weight);
public function getCapacity();
public function getWeight();
}
FruitInterface.php
<?php
interface FruitInterface
{
public function setName($name);
public function setSize($size);
public function setWeight($weight);
public function getName();
public function getSize();
public function getWeight();
}
Banana.php
<?php
abstract class Banana implements FruitInterface
{
private $name;
private $size;
private $weight;
public function __construct($name, $size, $weight)
{
$this->name = $name;
$this->size = $size;
$this->weight = $weight;
}
public function setName($name)
{
$this->name = $name;
}
public function setSize($size)
{
$this->size = $size;
}
public function setWeight($weight)
{
$this->weight = $weight;
}
public function getName()
{
return $this->name;
}
public function getSize()
{
return $this->size;
}
function getWeight()
{
return $this->weight;
}
}
BasketOfFruit.php
<?php
class BasketOfFruit implements BasketInterface
{
private $fruits = array();
private $capacity;
private $weight;
public function __construct($capacity, $weight)
{
$this->capacity = $capacity;
$this->weight = $weight;
}
public function addFruit(FruitInterface $fruit, $id)
{
if(count($this->fruits) < $this->capacity)
{
$this->fruits[$id] = $fruit;
$this->weight += $fruit->getWeight();
}
}
public function removeFruit($id)
{
if($this->hasFruit($id))
{
$this->weight -= $this->fruits[$id]->getWeight();
unset($this->fruits[$id]);
}
}
public function removeAllFruits()
{
foreach($this->fruits as $id => $fruit)
{
$this->weight -= $fruit->getWeight();
}
unset($this->fruits);
}
public function setCapacity($capacity)
{
$this->capacity = $capacity;
}
public function setWeight($weight)
{
$this->weight = $weight;
}
public function getCapacity()
{
return $this->capacity;
}
public function getWeight()
{
return $this->weight;
}
public function getFruit($id)
{
if($this->hasFruit($id))
{
return $this->fruits[$id];
}
}
public function getAllFruits()
{
return $this->fruits;
}
public function hasFruit($id)
{
if(isset($this->fruits[$id]))
{
return true;
}
return false;
}
}
BananaPlantain.php
<?php
class BananaPlantain extends Banana
{
}
index.php
<?php
require_once "BasketInterface.php";
require_once "FruitInterface.php";
require_once "Banana.php";
require_once "BasketOfFruit.php";
require_once "BananaPlantain.php";
$BasketOfFruit->addFruit(new BananaPlantain("Banana da Terra", "36 cm", 2.2), 1);
$BasketOfFruit->addFruit(new BananaPlantain("Banana da Terra", "37 cm", 1.4), 2);
$BasketOfFruit->addFruit(new BananaPlantain("Banana da Terra", "34 cm", 1.5), 3);
Tem algo errado ou eu fiz da forma correta? Lembrando que é um código simples.
>
Não. UML não é somente diagrama de classes. Há diagrama de sequência, diagrama de caso de uso, etc.
Na prática, diagramas de classes são os que menos são usados, principalmente quando você já tem um pouco de noção de como fazer as coisas, porque aí você não vai criar diagramas inúteis para perder tempo.
UML é uma linguagem universal de modelagem, sem o propósito de ser uma linguagem apenas para modelar pacotes/classes.
A principal utilidade de UML é documentação, seja de um pacote, de um padrão, de um conceito, de um fluxograma, de uma regra de negócio, etc.
>
TDD pode ser aplicado em qualquer tipo de código, não somente em códigos orientados a objetos.
TDD é uma metodologia constituída de três passos contínuos (red-green-blue):
-> Criar um teste -> rodar esse teste e vê-lo falhar
-> criar o mínimo de código possível para que o teste criado passe -> rodar o teste e vê-lo passar
-> refatorar
Ou seja, você não precisa ter um conhecimento avançado em OO para aprender TDD. Aliás, você acabou de aprender o conceito de TDD (estou falando sério). Para para testes unitários agora :)
>
Há uma regra para um caso: você quer apenas um contrato -> use uma interface.
Não crie uma classe abstrata sem fim de ter comportamento nem crie uma classe abstrata quando você não precisa (Keep it simple, stupid!).
Quando você quer também comportamento, é sua escolha. Eu, na maioria dos casos, mantenho a interface a fim de evitar um acoplamento. Mas não há regra nesse caso.
----
>
public function hasFruit($id)
{
if(isset($this->fruits[$id]))
{
return true;
}
return false;
}
Você não precisa usar esse if, já que só quer o retorno do isset:
public function hasFruit($id)
{
return isset($this->fruits[$id]);
}
>
abstract class Banana implements FruitInterface
Hã? Você não precisa ter banana como abstrata, afinal banana já é uma fruta concreta. Mesmo que você tenha uma banana-da-terra, uma banana já é uma fruta. Portanto, deixe-a como classe concreta.
>
<?php
require_once "BasketInterface.php";
require_once "FruitInterface.php";
require_once "Banana.php";
require_once "BasketOfFruit.php";
require_once "BananaPlantain.php";
Use autoloading.
Estou tentando entender porque sempre mudam os meus títulos do fórum - Se eu não me engano esse era Explicações Orientação a Objetos, mudaram e colocaram Explicações. :ermm:
@Enrico Pereira
>
Não. UML não é somente diagrama de classes. Há diagrama de sequência, diagrama de caso de uso, etc.
Na prática, diagramas de classes são os que menos são usados, principalmente quando você já tem um pouco de noção de como fazer as coisas, porque aí você não vai criar diagramas inúteis para perder tempo.
UML é uma linguagem universal de modelagem, sem o propósito de ser uma linguagem apenas para modelar pacotes/classes.
A principal utilidade de UML é documentação, seja de um pacote, de um padrão, de um conceito, de um fluxograma, de uma regra de negócio, etc.
Realmente. :grin:
>
TDD pode ser aplicado em qualquer tipo de código, não somente em códigos orientados a objetos.
TDD é uma metodologia constituída de três passos contínuos (red-green-blue):
-> Criar um teste -> rodar esse teste e vê-lo falhar
-> criar o mínimo de código possível para que o teste criado passe -> rodar o teste e vê-lo passar
-> refatorar
Ou seja, você não precisa ter um conhecimento avançado em OO para aprender TDD. Aliás, você acabou de aprender o conceito de TDD (estou falando sério). Para para testes unitários agora
Obrigado. - Acho que já está na hora de parar para entender como funciona, afinal, preciso de meus códigos lindamente lindos. :grin:
>
Há uma regra para um caso: você quer apenas um contrato -> use uma interface.
Não crie uma classe abstrata sem fim de ter comportamento nem crie uma classe abstrata quando você não precisa (Keep it simple, stupid!).
Quando você quer também comportamento, é sua escolha. Eu, na maioria dos casos, mantenho a interface a fim de evitar um acoplamento. Mas não há regra nesse caso.
Quando eu criei o código eu não tinha assistido o hangout sobre OO aqui da Imasters.
Depois que eu verifiquei sua resposta eu indentifiquei muito com o que eles falaram e agora eu compreendo mais ou menos como funciona. - O que devemos fazer é perguntar: Isso é isso? Se for, é generalização (extensão). Como no caso da Banana e Banana da Terra. Banana da Terra é uma Banana.
Você não precisa usar esse if, já que só quer o retorno do isset:
Cara, isso foi uma merda que eu "fiz por fazer" na velocidade da luz para postar aqui e tentar tirar minha dúvida. - Tem nem explicação, realmente foi uma caca. :ermm:
Let's go to the refactored code:
Autoloader.php
<?php
class Autoloader
{
public function __construct()
{
spl_autoload_register(array($this, "autoloadClass"));
spl_autoload_register(array($this, "autoloadInterface"));
}
public function autoloadClass($filename)
{
spl_autoload($filename);
}
public function autoloadInterface($filename)
{
require_once $filename . "Interface.php";
}
}
Banana.php
<?php
class Banana implements Fruit
{
private $name;
private $size;
private $weight;
public function __construct($name, $size, $weight)
{
$this->name = $name;
$this->size = $size;
$this->weight = $weight;
}
public function setName($name)
{
$this->name = $name;
}
public function setSize($size)
{
$this->size = $size;
}
public function setWeight($weight)
{
$this->weight = $weight;
}
public function getName()
{
return $this->name;
}
public function getSize()
{
return $this->size;
}
function getWeight()
{
return $this->weight;
}
}
BananaPlantain.php
<?php
class BananaPlantain extends Banana
{
}
BasketInterface.php
<?php
interface Basket
{
public function setDefinition($definition);
public function setCapacity($capacity);
public function setWeight($weight);
public function getDefinition();
public function getCapacity();
public function getWeight();
}
BasketOfFruit.php
<?php
class BasketOfFruit implements Basket
{
private $definition;
private $capacity;
private $weight;
private $fruits = array();
public function __construct($definition, $capacity, $weight)
{
$this->definition = $definition;
$this->capacity = $capacity;
$this->weight = $weight;
}
public function addFruit(Fruit $fruit, $id)
{
if(count($this->fruits) < $this->capacity)
{
$this->fruits[$id] = $fruit;
$this->weight += $fruit->getWeight();
}
}
public function removeFruit($id)
{
if($this->hasFruit($id))
{
$this->weight -= $this->fruits[$id]->getWeight();
unset($this->fruits[$id]);
}
}
public function removeAllFruits()
{
foreach($this->fruits as $id => $fruit)
{
$this->weight -= $fruit->getWeight();
}
unset($this->fruits);
}
public function setDefinition($definition)
{
$this->definition = $definition;
}
public function setCapacity($capacity)
{
$this->capacity = $capacity;
}
public function setWeight($weight)
{
$this->weight = $weight;
}
public function getDefinition()
{
return $this->definition;
}
public function getCapacity()
{
return $this->capacity;
}
public function getWeight()
{
return $this->weight;
}
public function getFruit($id)
{
if($this->hasFruit($id))
{
return $this->fruits[$id];
}
}
public function getAllFruits()
{
return $this->fruits;
}
public function hasFruit($id)
{
return isset($this->fruits[$id]);
}
}
FruitInterface.php
<?php
interface Fruit
{
public function setName($name);
public function setSize($size);
public function setWeight($weight);
public function getName();
public function getSize();
public function getWeight();
}
index.php
<?php
header("Content-Type: text/html; charset=utf-8");
require_once "Autoloader.php";
$autoloader = new Autoloader();
$BasketOfFruit = new BasketOfFruit("Cesta de Frutas", 3, 2);
echo "A " . strtolower($BasketOfFruit->getDefinition()) . " tem " . $BasketOfFruit->getWeight() . "kg com capacidade para " . $BasketOfFruit->getCapacity() . " frutas.<br>";
$BasketOfFruit->addFruit(new BananaPlantain("Banana da Terra", "36 cm", 2.2), 1);
$BasketOfFruit->addFruit(new BananaPlantain("Banana da Terra", "37 cm", 1.4), 2);
$BasketOfFruit->addFruit(new BananaPlantain("Banana da Terra", "34 cm", 1.5), 3);
echo "Foram adicionadas " . count($BasketOfFruit->getAllFruits()) . " frutas na cesta.<br>";
echo "A " . strtolower($BasketOfFruit->getDefinition()) . " agora tem " . $BasketOfFruit->getWeight() . "kg.<br>";
echo "Frutas da cesta:<br>";
foreach ($BasketOfFruit->getAllFruits() as $fruit) {
echo "A fruta " . strtolower($fruit->getName()) . " tem " . $fruit->getWeight() . "kg.<br>";
}
echo "Adicionando mais uma fruta para a cesta.<br>";;
$BasketOfFruit->addFruit(new BananaPlantain("Banana da Terra", "38 cm", 2.5), 4);
if ($BasketOfFruit->hasFruit(4)) {
echo "Fruta adicionada na cesta.<br>";
} else {
echo "Não foi possível adicionar a fruta. A capacidade da cesta excedida.<br>";
}
echo "Removendo a fruta com " . $BasketOfFruit->getFruit(2)->getWeight() . "kg<br>";
$BasketOfFruit->removeFruit(2);
echo "A " . strtolower($BasketOfFruit->getDefinition()) . " agora tem " . $BasketOfFruit->getWeight() . "kg.<br>";
echo "Frutas da cesta:<br>";
foreach ($BasketOfFruit->getAllFruits() as $fruit) {
echo "A fruta " . strtolower($fruit->getName()) . " tem " . $fruit->getWeight() . "kg.<br>";
}
echo "Removendo todas as frutas.<br>";
$BasketOfFruit->removeAllFruits();
echo "A " . strtolower($BasketOfFruit->getDefinition()) . " agora tem " . $BasketOfFruit->getWeight() . "kg.<br>";
Output
A cesta de frutas tem 2kg com capacidade para 3 frutas.Foram adicionadas 3 frutas na cesta. A cesta de frutas agora tem 7.1kg. Frutas da cesta: A fruta banana da terra tem 2.2kg. A fruta banana da terra tem 1.4kg. A fruta banana da terra tem 1.5kg. Adicionando mais uma fruta para a cesta. Não foi possível adicionar a fruta. A capacidade da cesta excedida. Removendo a fruta com 1.4kg A cesta de frutas agora tem 5.7kg. Frutas da cesta: A fruta banana da terra tem 2.2kg. A fruta banana da terra tem 1.5kg. Removendo todas as frutas. A cesta de frutas agora tem 2kg.
Próxima etapa agora é estudar TDD e tentar melhorar esse código (fiz um macarrão na index.php), adicionar mais classes para tratar de várias coisas da vida real. Nada melhor do que aprender praticando.
Obrigado e qualquer dúvida postarei novamente.
Tá loco, o Enrico não deixou ninguém mais responder :lol:
Mas tudo bem, ele manja mais. :P
Vou simplesmente deixar suas perguntas de TDD de lado porque TDD entra no meu cérebro e fica mais deslocado que um estudante africano numa escola japonesa então se eu pensar em responder alguma coisa vai estar errado. :yay:
Mas duas coisas que eu notei nesses seus códigos é que você está vendo interface como a cura da AIDS. Não é bem assim.
Herança vertical não é uma coisa ruim, basta apenas que seja aplicada corretamente.
Já foi dito que herança representa é um, agora uma coisa que aprendi sobre interfaces é que, para elas considera-se o pode ser um.
Eu quero chegar na sua Cesta de Frutas. Ela é uma cesta ou ela pode ser uma cesta?
Eu sei, eu sei, brasileiro usa até pote de sorvete vazio pra pôr fruta, mas vamos nos ater ao conceito.
Se uma Cesatde Frutas é, na sua essência, uma cesta, então temos sim uma herança:
abstract class AbstractBasket {}
class FruitsBasket extends Basket {}
A super-classe AbstractBasket será a responsável por manipular a cesta em si (adicionar, remover, contar...) e ela operará com objetos quaisquer.
Defini ela como abstrata porque mesmo uma cesta não específica, programaticamente falando, ela não deveria ser utilizada justamente porque um contêiner é um contêiner de alguma coisa.
Mesmo que essa cesta aceite qualquer coisa, você deve, na minha opinião, respeitar tal exigência.
À esse tipo de centário de Collections costuma-se ter um método abstrato comumente chamado de accept() que é invocado na antes da inclusão de um novo item ao array da coleção.
E para você limitar que uma cesta de frutas só aceite frutas, você implementa esse método abstrato e define que só Frutas são aceitas. E aí sim. Se um objeto pode ser uma Fruta, ele implementa uma interface
Muito texto, pouco código. O que eu disse aí cima resume-se, basicamente a isso:
abstract class AbstractBasket {
protected $basket = array();
public function add( $element ) {
if( $this -> accept( $element ) ) {
$this -> basket[] =& $element;
}
return $this;
}
abstract protected function accept( $element );
}
class FruitsBasket extends AbstractBasket {
// Abstract method Implementation
protected function accept( $element ) {
if( ! $element instanceof Fruit ) {
throw new InvalidArgumentException( 'Only fruits are accepted in this basket' );
}
return TRUE;
}
}
interface Fruits {}
Eu prefifo fazer essa cehcagem com um IF do que com um try...catch() porque acho mais legível, embora o try...catch() seja mesmo o mais apropriado.
Ufa!
A segunda coisa que eu ia dizer, que é tão mais simples que deveria ter sido dita em primeiro lugar, é quanto ao seu método removeAllFruits(). Ao final dele você está destruindo a propriedade e você não deve se acostumar a fazer isso.
Hoje o PHP permite que você manipule uma propriedade não definida criando-a em runtime. Mas e amanhã ou depois? Se alguma coisa puder adicionar uma fruta depois de todas terem sido removidas, pelo fato de a propriedade não existir mais, vai estragar tudo.
Eu estou achando esse tópico simplesmente fantástico. Acho que qualquer iniciante em Orientação a Objetos tem dúvidas desse tipo, então nada melhor do que uma série de perguntas respondidas por pessoas que tem um bom coração, que se importam em ajudar o próximo e que já passaram por toda essa situação de aprendizado para nos ajudar.
Quero agradecer a todos e que continue ajudando as pessoas, porque vocês ganharam com isso, de uma forma direta ou indireta.
@offtopic
Por favor, @Bruno Augusto, você como moderador poderia alterar o título do tópico, deixar ele mais definido? - "Explicações... de?" - Poderia alterar para algo em relação a o tópico como: "Orientação a Objetos - Perguntas e Respostas" ou "Explicações - Orientação a Objetos" ou algum outro.
@topic
>
Tá loco, o Enrico não deixou ninguém mais responder :lol:
Mas tudo bem, ele manja mais. :P
Realmente, manja muito.
Mas duas coisas que eu notei nesses seus códigos é que você está vendo interface como a cura da AIDS. Não é bem assim.
Vou te explicar o que acontece.
Quando a gente está iniciando na orientação a objetos, por mais que você tenha entendido algum assunto, como a questão das interfaces, classes abstratas, associação, composição, agregação e afins, você sempre fica com alguma dúvida, e grande parte dessas dúvidas quando você passa a utilizar na prática. Teoria de certo ponto é fácil de entender, mas na hora da aplicação, quando tentamos aplicar o conceito em um problema próprio é muito diferente.
Herança vertical não é uma coisa ruim, basta apenas que seja aplicada corretamente.
Realmente, tenho conhecimento disso. Isso é muito lógico, é com você falou, o Enrico Pereira e eu também postei. Devemos pensar em herença quando perguntamos: "isso é isso"? Pois o conceito de herança se baseia nisso: "É um". Correto?
Até ai tudo bem, mas vamos lá.
Interface são um tipo, além de serem um contrato com métodos abstratos.
Classes abstratas também é um tipo, mas é também uma generalização de um modo mais rígido.
Pensando assim, os dois são "iguais", mas são utilizados de formas diferentes em casos diferentes.
O que aconteceu comigo foi: Como interface é um tipo, logo Basket(Cesta) é um tipo, correto? Logo definiria uma interface.
Uma Cesta de Frutas poderia ser uma definição ou nome, para uma cesta, logo Cesta de Frutas é uma Cesta (é um, mas também é um tipo)
Eu pensei dessa forma, por isso eu declarei uma interface para Basket com comportamentos comuns para todas as cestas que são: Tamanho, capacidade e definição (nome).
E quando eu criasse uma classe BasketOfFruits (Cesta de Frutas) eu implementaria Basket, porque Cesta de Frutas é uma Cesta, logo também é um tipo.
Mas depois que eu percebi o seguinte, a diferença (eu creio), de interface para classes abstratas entra nesse conceito. Quando eu declaro uma interface eu estou falando que todas as classes que implementa esse tipo herdam comportamentos comuns, mas que podem ser tratados de formas diferentes. Já em classes abstratas eu estou falando que se Cesta de Frutas É UMA Cesta, logo ela tem que se comportar como uma cesta, utilizando métodos como Add, pois todas as cestas elas colocaram algo, algo essa que eu não sei e nem quero saber a princípio, então, para não ter repetiçao de código, para cada cesta eu não ter que repetir o código um código comum entre Cestas, eu declaro métodos comuns que trabalhama de uma forma generalizada para todas as classes que vão herdar dela.
Estou correto?
O seu código só me fez cravar ainda mais essa ideia e passar a compreender a questão das interfaces e classes abstratas.
Eu acabei de assistir o hangout de Code Reuse, e foi simplesmente fantástico, me ajudou muito mais. - Mas vamos as dúvidas.
O João citou um caso de "herança múltipla", onde João é uma pessoa, mas ele também é louco e cabeludo, mas o PHP não suporta herança múltipla, sem falar que nesse caso seria errado ter herança múltipla, afinal, cada um se comporta de uma forma. Logo entramos em Traits.
Eu entendi a definição das Traits e em quais momentos podemos utilizar ela pelo Hangout, mas me restou uma dúvida:
Os traits são: comporta-se como, diferente das interfaces que é pode ser uma (onde pode ser algo, mas pode se comportar diferente), e temos as classes abstratas que: é uma.
A segunda pergunta é: Os traits não nos passa uma confiança, para isso precisamos declarar uma interface para termos a segurança de que o método que reutiliza o código de uma Trait ele DEVE ter o método por causa do contrato.
Se isso já existir, me perdoem, não percebi, mas se não existir, qual o seu ponto de vista?
Devemos pensar em herença quando perguntamos: "isso é isso"? Pois o conceito de herança se baseia nisso: "É um". Correto?
Sim, podemos provar isso em código:
class Serializer {}
class JsonSerializer extends Serializer {}
class XmlSerializer extends Serializer {}
class Mysql {}
class Response {
public function __construct(Serializer $s) {}
}
new Response(new Serializer()); // beleza, Serializer é um Serializer
new Response(new JsonSerializer()); // beleza, JsonSerializer é um Serializer
new Response(new XmlSerializer()); // beleza, XmlSerializer é um Serializer
new Response(new Database()); // vish, Database não é um Serializer, dará erro
O que aconteceu comigo foi: Como interface é um tipo, logo Basket(Cesta) é um tipo, correto? Logo definiria uma interface.
Qualquer interface, classe (normal, abstrata ou final) é um tipo.
Mas depois que eu percebi o seguinte, a diferença (eu creio), de interface para classes abstratas entra nesse conceito. Quando eu declaro uma interface eu estou falando que todas as classes que implementa esse tipo herdam comportamentos comuns, mas que podem ser tratados de formas diferentes. Já em classes abstratas eu estou falando que se Cesta de Frutas É UMA Cesta, logo ela tem que se comportar como uma cesta, utilizando métodos como Add, pois todas as cestas elas colocaram algo, algo essa que eu não sei e nem quero saber a princípio, então, para não ter repetiçao de código, para cada cesta eu não ter que repetir o código um código comum entre Cestas, eu declaro métodos comuns que trabalhama de uma forma generalizada para todas as classes que vão herdar dela.
Um pouco estranho o modo de falar assim, o pensamento em geral está correto, mas a definição não.
Quando você implementa uma interface, você também faz o tipo "é um". Porém você não herda NENHUM comportamento, apenas é obrigado a cumprir (implementar) os métodos contidos na interface para cumprir o contrato.
Em ambas você fala que "x é y", tanto em classes quanto em interfaces.
O João citou um caso de "herança múltipla", onde João é uma pessoa, mas ele também é louco e cabeludo, mas o PHP não suporta herança múltipla, sem falar que nesse caso seria errado ter herança múltipla, afinal, cada um se comporta de uma forma. Logo entramos em Traits.
Na-na-nina-não. Traits, diferentemente de classes e interfaces, NÃO são um tipo. Traits apenas inserem comportamento de forma encapsulada, é quase um ctrl-c + ctrl-v feito pela linguagem.
- Podemos dizer que as Traits vieram para substituir a Herança Múltipla de uma forma melhorada?
Não. Traits NÃO são herança.
>
A segunda pergunta é: Os traits não nos passa uma confiança, para isso precisamos declarar uma interface para termos a segurança de que o método que reutiliza o código de uma Trait ele DEVE ter o método por causa do contrato.
Uma trait não pode implementar uma interface, pois traits não atribuem tipos, apenas propriedades/métodos.
A questão de haver uma interface com a implementação da trait é para fazer type hinting. Isso não é obrigatório.
Traits não são necessárias. Elas são recursos adicionais da linguagem e uma feature com grande potencial de abuso, infelizmente. Mas elas não são obrigatoriamente ruins. Eu, particularmente, não gosto delas e não uso. Composição FTW.
Há material na internet discutindo isso:
http://phpmaster.com/using-traits-in-php-5-4/
http://stackoverflow.com/questions/7892749/traits-in-php-any-real-world-examples-best-practices
O problema de classes abstratas serem tratadas como tipos é sofre com Syntatic Sugar que, a grosso modo, seria fazer classes abstratas não abstraírem quase nada ou abstraindo as coisas erradas.
No estado atual dos meus códigos (que eu larguei mão por um tempo de birra) ainda sofro um pouco com isso, mas bem menos que no passado depois que o Evandro Oliveira puxou minha orelha.
Aproveitando que eu não caí de sono no teclado ainda, vou editar o tópico
@Enrico Pereira
Qualquer interface, classe (normal, abstrata ou final) é um tipo.
Justamente, não neguei e compreendo isso. Você me entendeu errado. Agora eu compreendo quando utilizar interface ou classes abstratadas, mas antes o que me deixava intrigado é isso. Se classes e interfaces são um tipo, em qual diabos de hora usar elas? (pensava assim).
Mas depois as coisas foram ficando claras. - E eu tentei explicar mais ou menos, mas você complementou o que eu falei.
Como eu sou o iniciante aqui, vou tentar explicar o que eu entendi até agora para que os que virão a aprender também possam entender pelo meu ponto de vista e se indentificar (talvez).
Vamos a um exemplo:
abstract class Pessoa { abstract public function falar(); }
interface Pessoa { public function falar(); }
Olhando as interfaces e classes abstratadas (sem comportamento) elas são a mesma coisa, MAS é errado. Não é porque ambas são iguais que eu devo utilizar qualquer uma. Porque?
Quem fala, fala alguma coisa. Logo pensamos que "falar alguma coisa" pode ser algo que pode variar referente a vários aspéctos, como: País, "problemas de voz" - disfemia e afins, localidade (Nordestino ou Carioca) há variações, logo a gente não poderia declarar uma classe abstrata e sim um contrato (interface) para que a pessoa possa variar a fala de acordo com o gosto dela e outros fatores.
Exemplo:
As classes abstratas também podem ser consideradas generalização. Logo quem extende ela herdará não só os métodos, mas comportamentos. - Então "falar" seria algo generalizado. Estamos falando que TODO mundo fala igual (ISSO É ERRADO, NINGUÉM FALA IGUAL).
abstract class Pessoa {
public function falar($mensagem) { echo $mensagem; }
}Nesse caso entra as interfaces:
interface Pessoa {
public function falar($mensagem);
}E quando utilizar classes abstratas é realmente a questão da "generalização de um modo mais rígido" pois eu falo que TODOS fazem algo igual ou tem comportamentos iguais.
Um exemplo errado da utilização de interfaces foi o erro meu de criar uma interface "Basket" para outras definições/nomes/coisas que são uma cesta, como Cesta de Frutas ou de Café da Manhã, que foi corrigido pelo @Bruno Augusto.
Podemos dizer que Basket (Cesta) não é algo que "muda", é algo fixo/sólido com uma definição única: Adicionar algo ou tirar algo.
Logo podemos dizer que qualquer tipo de cesta, independente se ela é Cesta de Café da Manhã ou de Frutas, elas sempre tiram e colocam algo da mesma forma, mas com "dependências". Podemos falar que é uma generalização? Creio que sim.
abstract class AbstractBasket {
protected $basket = array();
public function add( $element ) {
if( $this -> accept( $element ) ) {
$this -> basket[] =& $element;
}
return $this;
}
abstract protected function accept( $element );
}
Eu estou errado em alguma coisa, ou eu realmente entendi a diferença entre as duas? rsrs.
@Bruno Augusto
O problema de classes abstratas serem tratadas como tipos é sofre com Syntatic Sugar que, a grosso modo, seria fazer classes abstratas não abstraírem quase nada ou abstraindo as coisas erradas.
Bom ponto de vista.
Aproveitando que eu não caí de sono no teclado ainda, vou editar o tópico
Muito obrigado!
Vamos para a pergunta? :skull: .
Vejo muito o pessoa no Hangout falando sobre a questão de camadas (persistência, transferência, validação), como funciona a interação entre essas camadas?
Vamos fazer uma suposição: o index é terra plana, onde é seguro está, e outras camadas fazem parte do oceano. Quando eu entrar em uma camada que fica no "meio do oceano", como eu vou "sair" dela ou utilizar outra sem ter um intermédio (terra plana) para eu poder ir até ela ou utilizar ela? Entramos ai em Design Patterns?
Obrigado :graduated:
Por "falar" na classe abstrata não ser "abstrata" e ter um comportamento, ela não está FORÇANDO a classe filha ter esse método, logo eu passo a não saber, ter uma certeza de que "uma pessoa fala".
Voce tem certeza sim uai, o metodo é publico...
Bem, você está preso em conceitos e com dificuldades em compreender a real utilização disso.
O problema é que é dito que a intenção da orientação a objetos é aproximar o código ao mundo real, mas na verdade não é. O objetivo da OOP é possibilitar uma abstração e reutilização em alto nível.
Os exemplos do mundo real (pessoas, frutas, carros, etc.) são bons para ensinar a teoria, pois aproximam-se do cotidiano e são intuitivos. Todavia, programação não é sobre frutas.........
Vamos as definições de classes abstratas e interfaces (específico em PHP, isso varia nas linguagens):
Interfaces
Classes abstratas
Na prática
Voce tem certeza sim uai, o metodo é publico...
Isso não significa confiança. ;)
Bem, você está preso em conceitos e com dificuldades em compreender a real utilização disso.
Na realidade sempre foi assim, rsrs. Teoria é "simples" de compreender, mas quando colocado em prática, em um problema real, as coisas mudam. Mas chegaremos lá. :graduated:
>
O problema é que é dito que a intenção da orientação a objetos é aproximar o código ao mundo real, mas na verdade não é. O objetivo da OOP é possibilitar uma abstração e reutilização em alto nível.
Os exemplos do mundo real (pessoas, frutas, carros, etc.) são bons para ensinar a teoria, pois aproximam-se do cotidiano e são intuitivos. Todavia, programação não é sobre frutas.........
Eu entendo, sempre tive isso em mente, afinal a gente não vai criar uma vaca na internet. :( (bem que eu queria)
Eu acho que assim, o conceito da orientação a objeto tem fundamentos parecidos com conceitos do mundo real (herança, por exemplo), por isso essa aproximidade e usar coisas reais da nossa vida para dá exemplos (creio que pegar por exemplo, um sistema REST para explicar para um iniciante não é interessante).
Mas obrigado por fixar isso, compreendo o que você falou e concordo.
>
Interfaces
Classes abstratas
Muito bom, realmente. :joia:
Acho que isso é uma coisa que você deve ter sempre em mente, e quando você chegar diante de uma situação e você se perguntar qual o problema, consequentementevocê saberá qual utilizar (isso se aplica também para várias outras coisas, exemplo: Design Patterns)
>
Na prática
Muito bom, já tinha isso em mente também, mas como estamos usando exemplos mais simples não achei interessante focar nisso agora.
Outra coisa que eu aprendi é que normalmente você saberá a hora de utilizar um ou outro ou ambos é quando você faz testes. Quando entrar na etapa "B", etapa para refatorar você vai perceber a hora de utilizar.
Perguntas:
abstract class AbstractBasket {
protected $basket = array();
public function add( $element ) {
if( $this -> accept( $element ) ) {
$this -> basket[] =& $element;
}
return $this;
}
abstract protected function accept( $element );
}Isso por algum acaso é um padrão de projeto? :oEu vejo ele sendo utilizado em vários casos.
Ninguém respondeu essa pergunta :cry:
>
Vejo muito o pessoa no Hangout falando sobre a questão de camadas (persistência, transferência, validação), como funciona a interação entre essas camadas?
Vamos fazer uma suposição: o index é terra plana, onde é seguro está, e outras camadas fazem parte do oceano. Quando eu entrar em uma camada que fica no "meio do oceano", como eu vou "sair" dela ou utilizar outra sem ter um intermédio (terra plana) para eu poder ir até ela ou utilizar ela? Entramos ai em Design Patterns?
Isso não significa confiança. ;)
Na verdade, significa sim. Exemplo abaixo:
abstract class AbstractDatabase {
public function exec($query) {}
}
class MySQL extends AbstractDatabase {}
class SQLite extends AbstractDatabase {}
class Storage {
public function __construct(AbstractDatabase $db)
{
$db->exec('SELECT * FROM users');
}
}
Um AbstractDatabase sempre terá o método exec, porque não é possível remover métodos.
>
abstract class AbstractBasket {
protected $basket = array();
public function add( $element ) {
if( $this -> accept( $element ) ) {
$this -> basket[] =& $element;
}
return $this;
}
abstract protected function accept( $element );
}Isso por algum acaso é um padrão de projeto? :oEu vejo ele sendo utilizado em vários casos.
Lembra o Visitor, mas não é a implementação descrita dele.
---
Sobre a pergunta de como integrar as camadas, isso é organização do projeto e design patterns ajudam sim.
Sugiro que veja Domain Driven Design.
Classes abstratas não podem ser instâciadas, e como a UNICA forma de a usar é com herança, então como TODOS metodos e propriedades publicas e abstratas não necessariamente herdades pela classe filha..
@Enrico Pereira
Em todos os casos? E em classes de tipos iguais, mas com comportamentos diferentes
Em todos os casos? E em casos comportamentais? Exemplo: Por mais que um Celta seja um Carro, ele não funciona da mesma maneira que todos os outros carros.
Respondi você depois para evitar continuarmos com o assunto acima.
>
Sobre a pergunta de como integrar as camadas, isso é organização do projeto e design patterns ajudam sim.
Sugiro que veja Domain Driven Design.
Vou dá uma olhadinha depois. Obrigado Mestre :worship:
Tópico limpo, brinquem sem brigar. Os dois!
Bom...
Olhando as interfaces e classes abstratadas (sem comportamento) elas são a mesma coisa, MAS é errado. Não é porque ambas são iguais que eu devo utilizar qualquer uma. Porque?
Quem fala, fala alguma coisa. Logo pensamos que "falar alguma coisa" pode ser algo que pode variar referente a vários aspéctos, como: País, "problemas de voz" - disfemia e afins, localidade (Nordestino ou Carioca) há variações, logo a gente não poderia declarar uma classe abstrata e sim um contrato (interface) para que a pessoa possa variar a fala de acordo com o gosto dela e outros fatores.
Como as interfaces só aceitam métodos públicos, elas descrevem melhor uma ação que todo objeto deve fazer. Já os métodos abstratos aceitam quaisquer das três visibilidades (tá... não tenho certeza quanto ao private) e além de descrever uma ação de que o objeto deve fazer elas descrevem um comportamento que o objeto deve ter.
Programaticamente falando, não há diferença entre um método público de interface e um método abstrato público. É tudo uma questão de conceito.
Por preferência e ponderação pessoal, quando seguindo essa linha de raciocínio:
Ou seja, nunca tenho métodos abstract public e, obviamente, nem abstract private
Nem sei se isso é possível de se declarar, de tão absurdo que é. :unsure:
Porém, nem sempre isso vai ser uma regra.
Em casos raros, para evitar um Syntatic Sugar, ganhar uma certa performance e/ou evitar ficar dando voltas e mais voltas no código, se você precisa garantir a existência de um determinado método mas não justifica uma herança ou o cenário de uma abstração terminaria num único método abstrato, use uma interface.
Nesses casos, é preferível ter uma lógica interna que deveria ser protected definida como public.
Se você precisa garantir a existência de um método público e puder determinar mais de uma rotina que se repete entre múltiplos objetos de um mesmo grupo, abstraia o que for comum e estenda.
Interfaces
Eu ia dizer que discordo, mas resolvi pesquisar um pouco antes e cheguei num consenso de que isso é um pouco controverso.
Essa posição, conhecida como Constant Interface Antipattern, é um conceito praticado mais no Java do que no PHP e está intrinsecamente ligado à API de um sistema, onde um determinado comportamento não deve ser exposto publicamente.
Faz sentido, se entendido corretamente. Mas os exemplos desse anti-pattern nem sempre são bons ou nem sempre explicam de forma suficientemente clara o que ele realmente é.
Definir constantes de interface não é errado. Errado é definir uma interface de constantes. Tem diferença.
Uma constante de interface define uma característica imutável de um objeto. Mas um objeto não é feito apenas de características, mas também de comportamentos descritos pelos seus métodos.
Sendo assim, o anti-pattern existe em definir uma interface feita unicamente de constantes, sem métodos.
Uma ressalva que eu acho válida, no entanto, é uma interface composta apenas de constante MAS que estenda outra(s) interface(s) estas sim com um ou mais métodos.
Por exemplo, eu tenho um conjunto de classes de Validação e todos os algorítimos respeitam a interface Validate:
<?php
interface Validate {
/**
* Validates given Character Set
*
* @param string $data
* Data to Validate
*/
public function validate( $data );
}E essa é uma interface feita apenas de constantes, mas pelo fato de estender uma outra que possui ao menos um método, não caracteriza o anti-pattern.
A interface serve pra definir o comportamento de um objeto, logo não faz sentido, nem há motivo ter getters e settes nela.Quanto a private, serve para proteger dados internos da classe.