Ir para conteúdo

Arquivado

Este tópico foi arquivado e está fechado para novas respostas.

Prove Yourself

[Resolvido] Problema com análise

Recommended Posts

Tenho um site que contém perfis. Esses perfis exibem informações: títulos, fotos, etc. Surgiu uma ideia de vender alguns perfis. Então agora tenho a seguinte situação: Perfis públicos (ninguém paga por eles), perfis privados (empresas) pagos e perfis privados não pagos. Os perfis públicos exibirão todas as informações possíveis. Os perfis pagos são divididos em planos (A e B). O perfil do plano A exibe algumas informações, já o perfil do plano B exibe todas as informações, seria algo como perfil prata e ouro. Os perfis privados gratuitos exibem apenas o mínimo de informações possíveis. Vale lembrar que um perfil privado pago pode passar a ser gratuito e exibir apenas as informações dos perfis gratuitos. As informações do perfil não serão eliminidas, apenas não serão exibidas até o cliente pagar novamente.

 

No meu sistema atual, perfil é uma classe. Apenas uma entidade no sistema. Utilizo o ORM do framework Kohana e ele não permite herança.

 

O problema é como reestruturar bem esse sistema, para não ter problemas no futuro, ao adicionar novas funcionalidades. Alguém tem alguma sugestão?

 

Valeu.

Compartilhar este post


Link para o post
Compartilhar em outros sites

É bom pensar também que os clientes podem querer mudar de perfil a qualquer tempo, por exemplo, ir do plano B para o plano A. Portanto é preciso que haja flexibilidade.

 

Eu acho que o melhor seria armazenar todas as informações de todos os perfis numa única tabela e usar o pattern Composições para o Modelo.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Atualmente todos os perfis estão em apenas uma tabela. Você conhece algum material bom sobre este pattern?

Vou continuar pesquisando. Obrigado.

UPDATE: Pesquisei sobre o pattern (o nome original é composite ou você está falando de composição do OO?) mas não sei se ele se encaixa no meu problema. Ou talvez não entendi. Você poderia me explicar? Obrigado.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Exatamente, eu me refiro ao pattern Composite.

 

Você já deve ter achado um monte de material sobre esse pattern, mas caso contrário aí vai um exemplo:

 

<?php
class CustomerName
{
 public $first = "";
 public $middle = "";
 public $last = "";
}

class CustomerAddress
{
 public $line1 = "";
 public $line2 = "";
 public $city = "";
 public $state = "";
 public $zip = "";
}

class Customer
{
 public $id = null;
 public $name = null;
 public $address = null;

 public function Customer()
 {
   $this->name = new CustomerName();
   $this->address = new CustomerAddress();
 }

 public function Load( $id )
 {
   $this->id = $id;
   $this->name->first = "George";
   $this->name->middle = "W";
   $this->name->last = "Bush";
   $this->address->line1 = "1600 Pennsylvania Ave.";
   $this->address->line2 = "";
   $this->address->city = "Washington";
   $this->address->state = "DC";
   $this->address->zip = "20006";
 }

 public function Update()
 {
   // Update the record in the database
   // or insert the record if there is no id
 }

 public function __toString()
 {
   return $this->name->first." ".$this->name->last;
 }
}

$cust = new Customer();
$cust->Load( 1 );
print( $cust );
print( "\n" );
?>

 

 

Outros dois exemplos de Composite:

http://forum.imasters.com.br/topic/402287-vale-a-pena-usar-templates/page__p__1576365#entry1576365

http://forum.imasters.com.br/topic/384416-criando-arquivos-ini-com-php-usando-composite/

 

 

Abstract Factory

 

Eu citei o Composite porque ele pode servir ao seu problema e ainda ser fácil de implementar, mas uma opção melhor seria o pattern Abstract Factory, pois ele além de separar em vários objetos os elementoes que seriam de um só, também permite selecionar quais seriam ligados ao objeto principal, através de condições:

 

<?php
class Record
{
 public $id = null;
 public $first = null;
 public $last = null;

 public function __construct( $id, $first, $last )
 {
   $this->id = $id;
   $this->first = $first;
   $this->last = $last;
 }
}

class USRecord extends Record
{
 public $addr1 = null;
 public $addr2 = null;
 public $city = null;
 public $state = null;
 public $zip = null;

 public function __construct( $id, $first, $last,
   $addr1, $addr2, $city, $state, $zip )
 {
    parent::__construct( $id, $first, $last );
  $this->addr1 = $addr1;
  $this->addr2 = $addr2;
  $this->city = $city;
  $this->state = $state;
  $this->zip = $zip;
 }
}

class ForeignRecord extends Record
{
 public $addr1 = null;
 public $addr2 = null;
 public $city = null;
 public $state = null;
 public $postal = null;
 public $country = null;

 public function __construct( $id, $first, $last,
   $addr1, $addr2, $city, $state, $postal, $country )
 {
    parent::__construct( $id, $first, $last );
  $this->addr1 = $addr1;
  $this->addr2 = $addr2;
  $this->city = $city;
  $this->state = $state;
  $this->postal = $postal;
  $this->country = $country;
 }
}

class RecordFactory
{
 public static function createRecord( $id, $first, $last,
   $addr1, $addr2, $city, $state, $postal, $country )
 {
 if ( strlen( $country ) > 0 && $country != "USA" )
     return new ForeignRecord( $id, $first, $last,
       $addr1, $addr2, $city, $state, $postal, $country );
 else
     return new USRecord( $id, $first, $last,
       $addr1, $addr2, $city, $state, $postal );
 }
}

function readRecords()
{
 $records = array();

 $records []= RecordFactory::createRecord(
  1, "Jack", "Herrington", "4250 San Jaquin Dr.", "",
  "Los Angeles", "CA", "90210", ""
 );
 $records []= RecordFactory::createRecord(
  1, "Megan", "Cunningham", "2220 Toorak Rd.", "",
  "Toorak", "VIC", "3121", "Australia"
 );

 return $records;
}

$records = readRecords();
foreach( $records as $r )
{
 $class = new ReflectionClass( $r );
 print $class->getName()." - ".$r->id." - ".$r->first." - ".$r->last."\n";
}
?>

 

Os exemplos acima foram retirados do livro "PHP Hacks", de Jack Henrington (O'REILLY)

Compartilhar este post


Link para o post
Compartilhar em outros sites

Exatamente, eu me refiro ao pattern Composite.

 

Você já deve ter achado um monte de material sobre esse pattern, mas caso contrário aí vai um exemplo:

 

Cuidado mangakah, o exemplo que você postou refere-se à composition, não Composite.

 

composition e aggregation são formas de associação entre objetos:

 

gallery_94216_5_5547.png

Um funcionário está associado à uma empresa.

Uma roda agrega um carro.

Uma cabeça compõe uma pessoa.

 

Se o funcionário sair da empresa, ela continuará funcionando.

Se você remover uma roda de um carro, você terá dificuldades para andar, mas o carro não deixará de existir.

Se você cortar a cabeça de uma pessoa..., você sabe o que acontece.

 

Já um Composite é um padrão estrutural que permite que os clientes tratem a composição ou os objetos da composição de uma forma uniforme:

 

gallery_94216_25_12129.png

 

Não conheço o livro que você citou, mas se ele afirma que o exemplo que você postou se trata de Composite, então existe um engano e uma grande confusão.

 

De qualquer forma, o problema do tópico não é resolvido com Composite, primeiro porque o Prove disse que o ORM que ele trabalha não permite herança (que é a base do Composite), segundo porquê simplesmente não existe uma estrutura de entidades que necessite disso.

 

De fato, o problema descrito pelo Prove está mais para Decorator do que para Composite.

 

Prove, se achar interessante, posso descrever Decorator aqui, talvez seja a solução para seu problema.

 

;)

Compartilhar este post


Link para o post
Compartilhar em outros sites

Decorator :seta: Estrutural

 

Conhecido também como:

Wrapper

 

Intenção:

Dar responsabilidades adicionais a um objeto dinamicamente.

Motivação:

Algumas vezes é necessário dar responsabilidades à um objeto específico, não para toda a classe. Seu caso, por exemplo:

 

Um perfil contém algumas informações e um indicador de visibilidade (público e privado), porém, independente de ser público ou privado, você pode querer vendê-lo.

 

Mas, o fato de um perfil específico ser vendável, não significa que qualquer perfil público ou privado também o seja. De fato, apenas alguns perfis são vendáveis.

 

Uma forma de se adicionar responsabilidades a um objeto é através de herança, mas você já afirmou que existe a limitação quanto à herança, mas também existe a situação de que, utilizando-se herança, qualquer instância da sub-classe será vendável também; Isso não é flexível e, se você tiver outras situações, você terá uma família de objetos muito grande e, possivelmente, difícil de se manter.

 

Uma outra forma de se adicionar a responsabilidade é encapsular um objeto dentro de um outro objeto. O objeto que encapsula o primeiro objeto chama-se
decorator
e possui a mesma interface do objeto que será decorado mais a interface do novo
comportamento/estado
que ele adiciona ao primeiro objeto.

 

Como o decorator possui a mesma interface do objeto que ele encapsula, os clientes que utilizarão esse objeto sequer saberão que ele está ali.

 

Uma analogia que podemos fazer com Decorator é o car tuning:

 

TuningFuscaTartaruga_thumb1.jpg

Se você utilizasse herança, qualquer instância de fusca que você criasse com essa sub-classe seria uma tartaruga e esse, definitivamente, não é o objetivo.

 

 

TuningFuscaTartaruga_thumb1.jpg

Aplicabilidade:

  • Adicionar responsabilidades para objetos específicos de forma transparente, ou seja, sem afetar outros objetos que já utilizam determinado objeto.
  • Quando herança é inviável ou impraticável ou seja, quando o número de combinações que se teria causaria uma explosão combinatória que tornaria impossível manter o código.

Estrutura:

gallery_94216_25_16756.png

 

Participantes:

Component
: (carro)

Define a interface dos objetos que podem ter responsabilidades anexadas a ele dinamicamente.

 

ConcreteComponent
: (fusca)

Implementa a interface do Component.

 

Decorator
:

Mantém uma referência do objeto Component e define uma interface que é compatível com a interface do Component.

 

ConcreteDecorator
: (tuning tartaruga)

Adiciona responsabilidades ao Component.

Bom, vejamos:

 

A interface de um perfil:

 

Profile.php


<?php
interface Profile {
public function getInfo();
public function getPhoto();
public function getTitle();
public function show();
}

 

Agora a entidade que o seu ORM deve lhe entregar:

 

ConcreteProfile.php


<?php
class ConcreteProfile implements Profile {
private $title;
private $photo;
private $info;

public function __construct( $info , $photo , $title ) {
$this->info = $info;
$this->photo = $photo;
$this->title = $title;
}

public function getInfo() {
return $this->info;
}

public function getPhoto() {
return $this->photo;
}

public function getTitle() {
return $this->title;
}

public function show() {
echo $this->getInfo(), PHP_EOL;
echo $this->getPhoto(), PHP_EOL;
echo $this->getTitle(), PHP_EOL;
echo "-----------------------\n";
}
}

 

Bom, como pode ver, a entidade é o perfil público que mostra todas as informações.

 

ProfileDecorator.php


<?php
abstract class ProfileDecorator implements Profile {
protected $profile;

public function __construct( Profile $profile ) {
$this->profile = $profile;
}

public function getInfo() {
return $this->profile->getInfo();
}

public function getPhoto() {
return $this->profile->getPhoto();
}

public function getTitle() {
return $this->profile->getTitle();
}
}

 

Com a interface do decorator vamos ver algumas variações:

 

PrivateProfile.php


<?php
class PrivateProfile extends ProfileDecorator {
public function show() {
echo $this->getTitle() , PHP_EOL;
echo "-----------------------\n";
}
}

 

NegotiableProfile.php


<?php
class NegotiableProfile extends ProfileDecorator {
public function getSaleInfo() {
echo 'Esse perfil é vendável por R$ #,##' , PHP_EOL;
}

public function show() {
$this->getSaleInfo();
$this->profile->show();
}
}

 

Agora o client que usa sua entidade:

 

Client.php


<?php
class Client {
public function test( Profile $profile ) {
$profile->show();
}
}

 

Bom, o Client espera um Profile, vamos ver o que acontece:

 


<?php
$profile = new ConcreteProfile( 'Informação do perfil' , 'Foto do perfil' , 'Um título qualquer' );
$client = new Client();

$client->test( $profile );

 

Seu ORM lhe entregou um Profile que já é utilizado pelo seu Client, a saída será:

 

Informação do perfil

Foto do perfil

Um título qualquer

-----------------------

 

Mas ai você resolveu que um perfil privado só exibe algumas informações, vejamos:

 


<?php
$profile = new ConcreteProfile( 'Informação do perfil' , 'Foto do perfil' , 'Um título qualquer' );
$privateProfile = new PrivateProfile( $profile );
$client = new Client();

$client->test( $privateProfile );

 

Como pode ver, o cliente continua recebendo um Profile, a saída será:

 

Um título qualquer

-----------------------

 

E se tivermos um perfil que pode ser vendido ?

 

Simples, podemos pegar um perfil público ou privado e decorá-lo com a funcionalidade da venda:

 


<?php
$profile = new ConcreteProfile( 'Informação do perfil' , 'Foto do perfil' , 'Um título qualquer' );
$negotiableProfile = new NegotiableProfile( $profile );
$client = new Client();

$client->test( $negotiableProfile );

 

A saída:

 

Esse perfil é vendável por R$ #,##

Informação do perfil

Foto do perfil

Um título qualquer

-----------------------

 

Hummmm, e se quisermos vender um perfil privado ???

 


<?php
$profile = new ConcreteProfile( 'Informação do perfil' , 'Foto do perfil' , 'Um título qualquer' );
$privateProfile = new PrivateProfile( $profile );
$negotiableProfile = new NegotiableProfile( $privateProfile );
$client = new Client();

$client->test( $negotiableProfile );

 

A saída:

 

Esse perfil é vendável por R$ #,##

Um título qualquer

-----------------------

 

Como pode ver, fizemos um tuning no nosso objeto e o Client que utilizava esse objeto nem percebeu o que aconteceu.

 

;)

Compartilhar este post


Link para o post
Compartilhar em outros sites

Nossa, excelente didática (e que carro bizarro! Eu ri com essa foto =), parabéns e muito obrigado!

Eu acredito ter entendido, só acho que é muita informação para minha cabeça. Até assimilar tudo ... :)

Se eu colocar um método show no meu Model, não estarei "quebrando" o MVC? O model deve conhecer a view que usa os seus dados?

Obrigado mais uma vez.

Compartilhar este post


Link para o post
Compartilhar em outros sites

mt bom exemplo :)

 

o show ali foi tipo um "debug" para você ver qual profile cada tipo de classe esta assumindo

Compartilhar este post


Link para o post
Compartilhar em outros sites

Nossa, excelente didática (e que carro bizarro! Eu ri com essa foto =), parabéns e muito obrigado!

 

:D

 

Se eu colocar um método show no meu Model, não estarei "quebrando" o MVC?

Obrigado mais uma vez.

 

Você não deve ter um "show" no seu Model.

 

O model deve conhecer a view que usa os seus dados?

 

A View conhece a Model, o contrário não é verdadeiro.

 

Sua camada de negócios entregará entidades, essas entidades poderão ou não estar encapsuladas dentro de um Decorator.

 

O exemplo que eu postei foi meramente ilustrativo; Se sua entidade tiver um método getInfo(), sua View poderá utilizá-lo para recuperar o estado do objeto e, assim, exibir a informação adequada.

 

mt bom exemplo

 

;)

 

o show ali foi tipo um "debug" para você ver qual profile cada tipo de classe esta assumindo

 

Exatamente.

 

:joia:

Compartilhar este post


Link para o post
Compartilhar em outros sites

Eu acho que entendi. Não sei pq isso está tão complicado na minha cabeça :)

Tá. Agora imaginem a seguinte situação.

 

- O usuário digita: http://www.site.com.br/perfil/1

- O controller recebe a requisição e obtem o perfil com id 1.

- A view correspondente ao perfil (bronze (gratuito), prata, ouro (ouro e público usam mesma view) é carregada e exibida.

 

Como saber qual view será carregada? Ou melhor, como saber qual dos decorators utilizar (instanciar)?

Obrigado e desculpa perguntar tanto.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Explicação mais perfeita que esta não tem, estou aguardando o seu livro! Certeza que seria ementa de todos as disciplinas de PHP do Brasil! :clap: :clap: :clap: :clap: :clap:

Compartilhar este post


Link para o post
Compartilhar em outros sites

Excelente. Resolvi meu problema.

 

Criei um decorator com o método abstrato view. Este método retorna uma view com os dados populados. Minhas classes (perfil ouro, prata e bronze) extendem o decorator e implementam o método view de acordo com o que é preciso.

 

Acima, perguntei: "Como saber qual view será carregada? Ou melhor, como saber qual dos decorators utilizar (instanciar)?". Para resolver este problema criei uma classe Factory que instancia o objeto de acordo uma condição (switch). Não sei se é a melhor forma mas funciona.

 

Eu precisei de uma noite de sono para colocar a cabeça no lugar e resolver o problema. Não sei como não consegui antes pois a explicação do João está bem clara. Com toda ceteza, se você lançar um livro João, eu comprarei!

 

Um abraço a todos e muito obrigado!

Compartilhar este post


Link para o post
Compartilhar em outros sites

×

Informação importante

Ao usar o fórum, você concorda com nossos Termos e condições.