php-brasil / shopping

Object oriented design analysis of a shopping cart and related objects
13 stars 1 forks source link

Design do participante Shopping\Order #1

Open netojoaobatista opened 10 years ago

netojoaobatista commented 10 years ago

Durante a navegação, o usuário adiciona diversos Shopping\Product ao Shopping\Cart, com suas respectivas quantidades. Ao concluir a escolha e adição dos produtos, existe a opção por finalizar a compra, transformando um Shopping\Cart em um Shopping\Order.

Enquanto um Shopping\Cart é apenas um agregador de Shopping\Product, um Shopping\Order envolve alguns comportamentos adicionais, como total da compra, descontos, taxas, etc. O design de um Shopping\Order deve ser de tal forma que esse participante possa reaproveitar as implementações para iteração dos itens do carrinho, adicionando os comportamentos específicos relacionados com o pedido e sua finalização.

Como deve ser, então, o design de um Shopping\Order?

Como implementar as possíveis opções de pagamento, entrega de mercadoria, descontos, etc, e como esses participantes devem interagir com o Shopping\Order?

danizord commented 10 years ago

Podemos extrair as implementações de iteração do carrinho para uma ProductCollection? Assim será possível reaproveitar essa implementação sem que o Shopping\Order dependa de uma instância de Shopping\Cart. Afinal, quando uma Shopping\Order é criada, o Shopping\Cart deveria deixar de existir, correto?

netojoaobatista commented 10 years ago

Semanticamente falando, @danizord, um Shopping\Order sempre vai depender de um Shopping\Cart. Afinal, o que é um pedido, senão a finalização de um carrinho?

Veja, não estou dizendo que um ProductCollection não é adequado. Só estou dizendo que a dependência entre Shopping\Order e Shopping\Cart existe por definição.

danizord commented 10 years ago

@netojoaobatista, ah sim, me expressei mal, minha intenção não é remover a dependência em sí, mas a composição.

O Shopping\Cart é mutável, por isso eu acho que ele deveria ser descartado no momento da criação da Shopping\Order. Depois que a compra foi finalizada, não podemos mais adicionar/remover produtos da compra.

netojoaobatista commented 10 years ago

Okay,

Agora, é mesmo necessário um ProductCollection? Não seria mais simples, seShopping\Order apenas encapsular Shopping\Cart?

danizord commented 10 years ago

@netojoaobatista A função da ProductCollection seria desacoplar a Shopping\Order do Shopping\Cart, de forma que qualquer alteração no estado do Shopping\Cart não seja refletida no estado do Shopping\Order.

$cart = new Cart();
$cart->addItem(new Product(), 1);
$cart->addItem(new Product(), 1);

$order = new Order($cart);
$price = $order->getPrice();

$cart->addItem(new Product(), 1);

$this->assertEquals($price, $order->getPrice());
netojoaobatista commented 10 years ago

A pergunta permanece, @danizord. É mesmo necessário um ProductCollection?

Uma vez que trabalhamos com referências para as instâncias, modificações em Shopping\Cart afetarão a ProductCollection que, por sua vez, afetará Shopping\Order.

Vejo duas formas de se evitar esse problema:

  1. Prototype - Se criarmos uma instância controlada, copiando o protótipo de Shopping\Cart, conseguimos evitar que mudanças no carrinho, ou em um ProductCollection, afetem o Shopping\Order.
  2. Observer - Se Shopping\Order observar Shopping\Cart por mudanças, podemos disparar um \DomainException, caso exista a tentativa de se modificar o estado do carrinho após criação de Shopping\Order.
danizord commented 10 years ago

@netojoaobatista Shopping\Cart e Shopping\Order não compartilhariam a mesma instância de ProductCollection, cada um teria a sua própria coleção de produtos encapsulada. Porém, nós precisamos da mesma implementação de iterator nos dois lugares, daí a necessidade de uma ProductCollection.

gabrielsch commented 10 years ago

class Order
{
    private $products;

    public function __construct(Cart $cart)
    {
        $this->products = clone $cart->getProducts();
    }
}

é isso, @danizord?

danizord commented 10 years ago

@gabrielsch sim sim, essa é uma das formas que o @netojoaobatista propôs para resolver o problema, que na minha opinião é a melhor forma, com prototype.