Ir para conteúdo

POWERED BY:

Arquivado

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

João Batista Neto

Refatoração O.C.P. - ShippingMethod

Recommended Posts

Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.

Muitos já devem ter ouvido falar sobre Open/Closed Principle. O que muitos não entendem, é o que, de fato, significa ter entidades de software abertas para extensão, mas fechadas para edição. Acredito que, talvez, o motivo para grande a maioria das confusões em relação a esse princípio, se dê por conta de uma falha na exemplificação de um caso de uso real ou da ausência das consequências causadas pela violação.

 

O.C.P. se trata, na verdade, de evolução. Assim como pessoas evoluem e mudam conforme o tempo, o software também muda. Além disso, da mesma forma como acontece com as pessoas, que evoluem para a vida adulta, sem apagar sua infância, um software também deve evoluir, sem que suas entidades sejam modificadas para isso.

 

Se há algo que não vai mudar durante o ciclo de vida de um software, é o fato de que todo software muda durante seu ciclo de vida. Isso significa que, quanto mais velho é um software, mais entidades dependem umas das outras, consequentemente, mais entidades serão afetadas pela mudança.

 

Modificar uma entidade de software significa que uma série de reações em cadeia acontecerão. Se você tem uma boa cobertura de testes em seu software, já deve ter se deparado com esse tipo de situação, onde a simples edição de uma entidade, ocasiona a falha de vários testes, de entidades muitas vezes não diretamente relacionadas, mas que dependem de uma dependência direta da entidade modificada.

 

Se é fato que o software muda, e O.C.P. estabelece que as entidades de software devem ser fechadas para modificação, como é, então, um software conforme O.C.P.?

 

O caso de uso - Calculando preço do frete e prazo de entrega

 

Vamos imaginar que tenhamos um E-Commerce que utiliza um serviço, inicialmente o da E.C.T. (Correios), para calcular o custo do frete para entregar uma encomenda em um endereço especificado pelo cliente.

 

O código origina

<?php
namespace Neto\Commerce;

class ShoppingCart implements \Countable, \IteratorAggregate
{
    private $items = array();
    private $quantities = array();

    public function addItem(Product $product, $quantity = 1)
    {
        $productId = $product->getProductId();

        if (isset($this->items[$productId])) {
            $quantity += $this->quantities[$productId];
        }

        $this->items[$productId] = $product;
        $this->quantities[$productId] = $quantity;
    }

    /**
     * @see \Countable::count()
     */
    public function count()
    {
        return count($this->items);
    }

    public function getItemAmount($productId)
    {
        if (!isset($this->items[$productId])) {
            throw new \UnexpectedValueException('Item not found');
        }

        $quantity = $this->quantities[$productId];

        return $quantity * $this->getItemPrice($productId);
    }

    public function getItemPrice($productId)
    {
        if (!isset($this->items[$productId])) {
            throw new \UnexpectedValueException('Item not found');
        }

        return $this->items[$productId]->getProductPrice();
    }

    public function getItemQuantity($productId)
    {
        if (!isset($this->quantities[$productId])) {
            return 0;
        }

        return $this->quantities[$productId];
    }

    public function getItemTotal()
    {
        $total = 0;

        foreach ($this->items as $productId => $item) {
            $total += $this->getItemAmount($productId);
        }

        return $total;
    }

    /**
     * @see \IteratorAggregate::getIterator()
     */
    public function getIterator()
    {
        return new \ArrayIterator($this->items);
    }

    public function getShippingAmount($shippingFrom, $shippingTo)
    {
        if ($this->count() == 0) {
            return 0;
        }

        $weight = 0;
        $height = 0;
        $width = 0;
        $length = 0;

        foreach ($this->items as $productId => $item) {
            $weight += $this->quantities[$productId] * $item->getProductWeight();
            $height += $this->quantities[$productId] * $item->getProductHeight();

            $currentWidth = $item->getProductWidth();
            $currentLength = $item->getProductLength();

            if ($currentWidth > $width) {
                $width = $currentWidth;
            }

            if ($currentLength > $length) {
                $length = $currentLength;
            }
        }

        $ect = new \Neto\Commerce\Shipping\ECT();

        return $ect->getShippingAmount($shippingFrom,
                                       $shippingTo,
                                       $weight,
                                       $height,
                                       $width,
                                       $length);
    }
}
O problema

 

Observando o método ShoppingCart::getShippingAmount(), veremos facilmente alguns problemas:

 

1. Existe um acoplamento muito alto com o participante \Neto\Commerce\Shipping\ECT.

2. A estratégia para o cálculo das dimensões da embalagem é muito frágil.

 

De fato, se observarmos atentamente, o acoplamento com o participante ECT é tão alto, que dificulta até os testes. Com o conhecimento específico sobre como mensurar o tamanho da embalagem e o participante ECT, o participante ShoppingCart toma decisões que tornarão a manutenção desse código extremamente difícil. De fato, se a empresa passar a fazer entregar com outro prestador de serviços, teremos que editar esse código.

 

O problema vai ficando maior, conforme novos prestadores de serviço de entrega vão sendo agregados à carteira de prestadores de serviço da empresa. Por exemplo, imaginem que, por uma questão de mercado, a loja passe a enviar os produtos com, além da ECT, também com a TRANSFOLHA ou outros serviços de logística. Se esse código não for refatorado rapidamente, sua rigidez pode causar edições semelhantes a:

public function getShippingAmount($shippingWith, $shippingFrom, $shippingTo)
{
    if ($this->count() == 0) {
        return 0;
    }

    switch ($shippingWith) {
        case 'ECT':
            $weight = 0;
            $height = 0;
            $width = 0;
            $length = 0;

            foreach ($this->items as $productId => $item) {
                $weight += $this->quantities[$productId] * $item->getProductWeight();
                $height += $this->quantities[$productId] * $item->getProductHeight();

                $currentWidth = $item->getProductWidth();
                $currentLength = $item->getProductLength();

                if ($currentWidth > $width) {
                    $width = $currentWidth;
                }

                if ($currentLength > $length) {
                    $length = $currentLength;
                }
            }

            $ect = new \Neto\Commerce\Shipping\ECT();

            return $ect->getShippingAmount($shippingFrom,
                                           $shippingTo,
                                           $weight,
                                           $height,
                                           $width,
                                           $length);
        case 'TransFolha':
            //código para calcular frete com a TransFolha
        case 'FreteFácil':
            //código para calcular frete com PayPal Frete Fácil
        case 'UPS':
            //código para calcular frete com UPS
    }
}
Isso é péssimo, primeiro pois a cada edição, um bug em potencial é adicionado ao código. Segundo, pois a dificuldade em testar o código nos deixa no escuro sobre os possíveis bugs. Além disso, a cada nova adição de um prestador de serviço, o método ShoppingCart::getShippingAmount() assume a responsabilidade sobre a lógica do cálculo das dimensões da embalagem e frete.

 

Como o princípio de design O.C.P. nos diz que ninguém deve ter autorização para editar o código, precisamos garantir que esse código não precise ser editado. Isso é até muito simples de se fazer. De fato, a simples refatoração para permitir que o código seja testável, já vai eliminar a necessidade de se editar esse código no futuro. Quando digo sobre permitir que o código seja testável, estou dizendo que precisamos, de alguma forma, poder ter um Mock para o participante responsável pelo cálculo do frete.

 

O teste

<?php
namespace Neto\Commerce;

class ShoppingCartTest extends \PHPUnit_Framework_TestCase
{
    /**
     * @testdox Initial value for ShoppingCart::count() will be zero
     */
    public function testInitialItemCountInTheCartIsZero()
    {
        $cart = new ShoppingCart();

        $this->assertCount(0, $cart);
    }

    /**
     * @testdox Adding an item to shopping cart with ShoppingCart::addItem(Product $product, $quantity = 1) will increase the item count.
     */
    public function testAddingAnItemToShoppingCartWillIncreaseTheItemCount()
    {
        $cart = new ShoppingCart();

        $this->assertCount(0, $cart);

        $cart->addItem(new Product(123, 'item 0', 100, 1, 2, 15, 30));

        $this->assertCount(1, $cart);

        $cart->addItem(new Product(234, 'item 1', 100, 1, 2, 15, 30));

        $this->assertCount(2, $cart);
    }

    /**
     * @testdox Adding the same item twice to shopping cart will not change the item count.
     */
    public function testAddingTheSameItemTwiceToShoppingCartWillNotChangeTheItemCount()
    {
        $cart = new ShoppingCart();

        $this->assertCount(0, $cart);

        $cart->addItem(new Product(123, 'item 0', 100, 1, 2, 15, 30));

        $this->assertCount(1, $cart);

        $cart->addItem(new Product(123, 'item 0', 100, 1, 2, 15, 30));

        $this->assertCount(1, $cart);
    }

    /**
     * @testdox ShoppingCart::getItemQuantity($productId) will return zero if the product was not added to the shopping cart.
     */
    public function testGetProductQuantityWillReturnZeroIfTheProductWasNotAddedToTheShoppingCart()
    {
        $cart = new ShoppingCart();

        $this->assertEquals(0, $cart->getItemQuantity(123));
    }

    /**
     * @testdox Adding the same item twice to shopping cart will increase its quantity
     */
    public function testAddingTheSameItemTwiceToShoppingCartWillIncreaseItsQuantity()
    {
        $product = new Product(123, 'item 0', 100, 1, 2, 15, 30);
        $productId = $product->getProductId();

        $cart = new ShoppingCart();

        $this->assertEquals(0, $cart->getItemQuantity($productId));

        $cart->addItem($product);

        $this->assertEquals(1, $cart->getItemQuantity($productId));

        $cart->addItem($product);

        $this->assertEquals(2, $cart->getItemQuantity($productId));
    }

    /**
     * @expectedException \UnexpectedValueException
     * @testdox ShoppingCart::getItemPrice($productId) will throw an exception if product was not found in the cart.
     */
    public function testGetItemPriceWillThrowAnExceptionIfProductWasNotFoundInTheCart()
    {
        $cart = new ShoppingCart();

        $cart->getItemPrice(123);
    }

    /**
     * @testdox ShoppingCart::getItemPrice($productId) will return the product price.
     */
    public function testGetItemPriceWillReturnTheProductPrice()
    {
        $productPrice = 100;
        $product = new Product(123, 'item 0', $productPrice, 1, 2, 15, 30);
        $productId = $product->getProductId();
        $cart = new ShoppingCart();

        $cart->addItem($product);

        $this->assertEquals($productPrice, $cart->getItemPrice($productId));
    }

    /**
     * @expectedException \UnexpectedValueException
     * @testdox ShoppingCart::getItemAmount($productId) will throw an exception if product was not found in the cart.
     */
    public function testGetItemAmountWillThrowAnExceptionIfProductWasNotFoundInTheCart()
    {
        $cart = new ShoppingCart();

        $cart->getItemAmount(123);
    }

    /**
     * @testdox ShoppingCart::getItemAmount($productId) will return the product price multiplied by its quantity.
     */
    public function testGetItemAmountWillReturnTheProductPriceMultipliedByItsQuantity()
    {
        $productPrice = 100;
        $quantity = 5;
        $product = new Product(123, 'item 0', $productPrice, 1, 2, 15, 30);
        $productId = $product->getProductId();
        $cart = new ShoppingCart();

        $cart->addItem($product, $quantity);

        $this->assertEquals($productPrice * $quantity,
                            $cart->getItemAmount($productId));
    }

    /**
     * @testdox ShoppingCart::getItemTotal() will return the sum of all product price multiplied by its quantity in the cart.
     */
    public function testGetItemTotalWillReturnTheSumOfAllItemsInTheCart()
    {
        $cart = new ShoppingCart();

        $this->assertEquals(0, $cart->getItemTotal());

        $cart->addItem(new Product(123, 'item 0', 100, 1, 2, 15, 30), 2);
        $cart->addItem(new Product(234, 'item 1', 100, 1, 2, 15, 30), 2);

        $this->assertEquals(400, $cart->getItemTotal());
    }

    /**
     * @testdox ShoppingCart::getIterator() will return an \Iterator with all products in the cart.
     */
    public function testGetIteratorWillReturnAnIteratorWithAllProductsInTheCart()
    {
        $products = array(
            new Product(123, 'item 1', 100, 1, 2, 15, 30),
            new Product(456, 'item 2', 100, 1, 2, 15, 30),
            new Product(789, 'item 3', 100, 1, 2, 15, 30)
        );

        $cart = new ShoppingCart();

        foreach ($products as $product) {
            $cart->addItem($product);
        }

        $iterator = $cart->getIterator();
        $iterator->rewind();

        $this->assertInstanceOf('\Iterator', $iterator);

        for ($i = 0, $t = count($products); $i < $t; ++$i, $iterator->next()) {
            $this->assertSame($products[$i], $iterator->current());
        }

        $this->assertFalse($iterator->valid());
    }
}
Como podemos ver, não existe um teste para o cálculo do frete. Mesmo porque, daquela forma, não há como testá-lo. Vamos começar a refatoração, garantindo que ele seja testável. Isso será feito através da injeção da dependência:
<?php
namespace Neto\Commerce;

class ShoppingCartTest extends \PHPUnit_Framework_TestCase
{
    //...

    /**
     * @testdox ShoppingCart::getShippingAmount() will return zero if cart is empty.
     */
    public function testGetShippingAmountWillReturnZeroIfCartIfEmpty()
    {
        $shippingMethod = $this->getMock('\Neto\Commerce\Shipping\ShippingMethod',
                                         array('getShippingAmount'));

        $cart = new ShoppingCart();

        $this->assertEquals(0, $cart->getShippingAmount($shippingMethod,
                                                        '14400000',
                                                        '01000000'));
    }
}
A partir de agora, já começamos a ter o código testável. Com a inversão da dependência, nosso ShoppingCart não será mais responsável nem pelo cálculo das dimensões da embalagem, nem pela criação da instância da ECT. O interessante dessa primeira refatoração, é que o teste vai passar, mesmo não tendo refatorado ainda o código do ShoppingCart. Para finalizar o carrinho, tudo o que precisamos é verificar se o carrinho está fazendo a chamada corretamente:
<?php
namespace Neto\Commerce;

class ShoppingCartTest extends \PHPUnit_Framework_TestCase
{
    //...

    /**
     * @testdox ShoppingCart::getShippingAmount() will return zero if cart is empty.
     */
    public function testGetShippingAmountWillReturnZeroIfCartIfEmpty()
    {
        $shippingMethod = $this->getMock('\Neto\Commerce\Shipping\ShippingMethod',
                                         array('getShippingAmount'));

        $cart = new ShoppingCart();

        $this->assertEquals(0, $cart->getShippingAmount($shippingMethod,
                                                        '14400000',
                                                        '01000000'));
    }

    /**
     * @testdox ShoppingCart::getShippingAmount() will call ShippingMethod::getShippingAmount() to calculates the shipping amount.
     */
    public function testGetShippingAmountWillCallShippingMethodToCalculatesTheShippingAmount()
    {
        $cart = new ShoppingCart();
        $cart->addItem(new Product(123, 'item 0', 100, 1, 2, 15, 30), 2);

        $shippingFrom = '14400000';
        $shippingTo = '01000000';

        $shippingMethod = $this->getMock('\Neto\Commerce\Shipping\ShippingMethod',
                                         array('getShippingAmount'));

        $shippingMethod->expects($this->at(0))
                       ->method('getShippingAmount')
                       ->with($cart, $shippingFrom, $shippingTo);

        $cart->getShippingAmount($shippingMethod,
                                 $shippingFrom,
                                 $shippingTo);
    }
}
Vamos refatorar o carrinho agora:
<?php
namespace Neto\Commerce;

use Neto\Commerce\Shipping\ShippingMethod;

class ShoppingCart implements \Countable, \IteratorAggregate
{
    //...

    public function getShippingAmount(ShippingMethod $shippingMethod,
                                      $shippingFrom,
                                      $shippingTo)
    {
        if ($this->count() == 0) {
            return 0;
        }

        return $shippingMethod->getShippingAmount($this,
                                                  $shippingFrom,
                                                  $shippingTo);
    }
}
Como podemos ver, o código ficou muito mais elegante agora. Não existe conhecimento específico nenhum. Tudo o que o método ShoppingCart::getShippingAmount() faz, é delegar o cálculo para um outro participante. Ainda, para evitar qualquer tipo de acoplamento, o carrinho passa a si mesmo, para o participante ShippingMethod, para que as decisões sobre como calcular o frete sejam feitas exclusivamente pelo novo participante. Com o teste passando, podemos criar a interface ShippingMethod:
<?php
namespace Neto\Commerce\Shipping;

use Neto\Commerce\ShoppingCart;

interface ShippingMethod
{
    public function getShippingAmount(ShoppingCart $shoppingCart,
                                      $shippingFrom,
                                      $shippingTo);
}
Agora, podemos implementar o participante ECT, ou qualquer um outro:
<?php
namespace Neto\Commerce\Shipping;

use Neto\Commerce\ShoppingCart;

class ECT implements ShippingMethod
{
    const ENDPOINT = 'http://ws.correios.com.br/calculador/CalcPrecoPrazo.asmx';
    const SEDEX = 40010;

    private $weight = 0;
    private $height = 0;
    private $width = 0;
    private $length = 0;

    private function calcPackageDimensions(ShoppingCart $shoppingCart)
    {
        foreach ($shoppingCart as $productId => $item) {
            $quantity = $shoppingCart->getItemQuantity($productId);

            $this->weight += $quantity * $item->getProductWeight();
            $this->height += $quantity * $item->getProductHeight();

            $currentWidth = $item->getProductWidth();
            $currentLength = $item->getProductLength();

            if ($currentWidth > $this->width) {
                $this->width = $currentWidth;
            }

            if ($currentLength > $this->length) {
                $this->length = $currentLength;
            }
        }
    }

    public function createSoapClient()
    {
        return new \SoapClient(static::ENDPOINT. '?wsdl',
                               array('trace' => true,
                                     'exceptions' => true,
                                     'style' => SOAP_DOCUMENT,
                                     'use' => SOAP_LITERAL,
                                     'soap_version' => SOAP_1_1,
                                     'encoding' => 'UTF-8'));
    }

    public function getShippingAmount(ShoppingCart $shoppingCart,
                                      $shippingFrom,
                                      $shippingTo)
    {
        $this->calcPackageDimensions($shoppingCart);

        $request = new \stdClass();
        $request->nCdEmpresa = '';
        $request->sDsSenha = '';
        $request->sCepOrigem = $shippingFrom;
        $request->sCepDestino = $shippingTo;
        $request->nVlPeso = $this->weight;
        $request->nCdFormato = 1;
        $request->nVlComprimento = $this->length;
        $request->nVlAltura = $this->height;
        $request->nVlLargura = $this->width;
        $request->sCdMaoPropria = 'n';
        $request->nVlValorDeclarado = 0;
        $request->sCdAvisoRecebimento = 'n';
        $request->nCdServico = ECT::SEDEX;
        $request->nVlDiametro = 0;

        $response = $this->createSoapClient()->CalcPrecoPrazo($request);
        $cServico = $response->CalcPrecoPrazoResult->Servicos->cServico;

        if (isset($cServico->Erro) && $cServico->Erro != 0) {
            throw new \RuntimeException($cServico->MsgErro, $cServico->Erro);
        }

        return $cServico->Valor;
    }
}
Tudo se resume em abstração

É claro que haverão casos que não serão tão simples como esse do exemplo, mas de forma geral, Abstração é a chave para solucionar muitos dos problemas de design que possamos ter. Excesso de conhecimento causa um acoplamento alto. Quanto maior o acoplamento, maior é a dificuldade em se testar o código. Quanto maior a dificuldade em se testar o código, maior a probabilidade de não se testar o código e, consequentemente, maior a probabilidade de bugs.

 

Outro ponto importante, é que apesar do artigo se tratar de O.C.P., acabamos abordando S.R.P. ao remover a responsabilidade da estratégia de cálculo da embalagem, do participante ShoppingCart. Abordamos também um outro princípio importante, D.I.P., que além de garantir que o código fosse testável, ainda eliminou, pelo menos para esse caso, a necessidade de edição do ShoppingCart.

 

Entre todas as dicas dadas aqui, existe uma que deve sempre ser colocada no topo de qualquer lista de prioridades: Garanta que seu código seja testável, isso facilitará muito qualquer refatoração que seja necessária.

 

O código ilustrado nesse artigo, assim como os respectivos testes, estão disponíveis no GitHub.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Entendi 5% (o que é muito bom... hahaha). Excelente artigo! :clap:

 

No mais:

 

testGetProductQuantityWillReturnZeroIfTheProductWasNotAddedToTheShoppingCart()

 

PORR@! hauehauheuahuheau

Compartilhar este post


Link para o post
Compartilhar em outros sites

Excelente artigo. Mais uma coisa que eu sabia e não sabia que sabia. :wacko:

 

Só não entendi o porquê se você ter usado static para acessar a constante se você não precisa a atrasar a resolução do escopo.

 

 

 

PORR@! hauehauheuahuheau

 

Isso porque pelo que lembro do hangout, o PHPUnit converte esse métodos gigantescos em frases humanamente legíveis.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Só não entendi o porquê se você ter usado static para acessar a constante se você não precisa a atrasar a resolução do escopo.

Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.

Isso porque pelo que lembro do hangout, o PHPUnit converte esse métodos gigantescos em frases humanamente legíveis.

Exato,

 

:seta: http://en.wikipedia.org/wiki/TestDox

 

A saída dos testes será alguma coisa semelhante a:

Neto\Commerce\Product
 [x] Product::setProductId($productId) will throw an InvalidArgumentException if id is not an integer
 [x] Product::getProductId() will return the id setted with Product::setProductId()
 [x] Product::setProductName($productName) will throw an InvalidArgumentException if name is not scalar
 [x] Product::getProductName() will return the name setted with Product::setProductName()
 [x] Product::setProductPrice($productPrice) will throw an InvalidArgumentException if price is not numeric
 [x] Product::getProductPrice() will return the price setted with Product::setProductPrice()
 [x] Product::getProductPrice() will always return a float
 [x] Product::setProductWeight($productWeight) will throw an InvalidArgumentException if weight is not numeric
 [x] Product::getProductWeight() will return the weight setted with Product::setProductWeight()
 [x] Product::setProductHeight($productHeight) will throw an InvalidArgumentException if height is not numeric
 [x] Product::getProductHeight() will return the height setted with Product::setProductHeight()
 [x] Product::setProductWidth($productWidth) will throw an InvalidArgumentException if width is not numeric
 [x] Product::getProductWidth() will return the width setted with Product::setProductWidth()
 [x] Product::setProductLength($productLength) will throw an InvalidArgumentException if length is not numeric
 [x] Product::getProductLength() will return the width setted with Product::setProductLength()
 [x] Product::__construct($id, $name, $price, $weight, $height, $width, $length) will set ID, name, price, weight and product dimensions

Neto\Commerce\Shipping\ECT
 [x] ECT::createSoapClient() will return an instance of \SoapClient
 [x] ECT::getShippingAmount() will call the SOAP method CalcPrecoPrazo.
 [x] ECT::getShippingAmount() will send the expected Soap Request to ECT service.
 [x] ECT::getShippingAmount() will return the calculated shipping amount.
 [x] ECT::getShippingAmount() will throw an Exception in case of errors.

Neto\Commerce\ShoppingCart
 [x] Initial value for ShoppingCart::count() will be zero
 [x] Adding an item to shopping cart with ShoppingCart::addItem(Product $product, $quantity = 1) will increase the item count.
 [x] Adding the same item twice to shopping cart will not change the item count.
 [x] ShoppingCart::getItemQuantity($productId) will return zero if the product was not added to the shopping cart.
 [x] Adding the same item twice to shopping cart will increase its quantity
 [x] ShoppingCart::getItemPrice($productId) will throw an exception if product was not found in the cart.
 [x] ShoppingCart::getItemPrice($productId) will return the product price.
 [x] ShoppingCart::getItemAmount($productId) will throw an exception if product was not found in the cart.
 [x] ShoppingCart::getItemAmount($productId) will return the product price multiplied by its quantity.
 [x] ShoppingCart::getItemTotal() will return the sum of all product price multiplied by its quantity in the cart.
 [x] ShoppingCart::getIterator() will return an \Iterator with all products in the cart.
 [x] ShoppingCart::getShippingAmount() will return zero if cart is empty.
 [x] ShoppingCart::getShippingAmount() will call ShippingMethod::getShippingAmount() to calculates the shipping amount.

Dica:

$ git clone git@github.com:netojoaobatista/ocp.git
$ cd ocp
$ ant test

Depois vá até o diretório build e veja o arquivo testdox.

 

;)

Compartilhar este post


Link para o post
Compartilhar em outros sites

eu juro que tentei ler mas não rolo.

to igual o amigo li, entendi 10% por ai..

 

mas é excelente por que me abre os horizontes e isso pode me dar uma direção pra onde eu tenho que ir a partir de agora.

Compartilhar este post


Link para o post
Compartilhar em outros sites

sem querer postou no fórum principal do PHP

Não foi "sem querer". Todos os meus movimentos são friamente calculados.

 

elchapulincolorado.jpg

 

Depois faço a movimentação para o fórum de artigos.

 

;)

Compartilhar este post


Link para o post
Compartilhar em outros sites

Depois faço a movimentação para o fórum de artigos.

 

Já era. :P

 

Você citou e negritou o motivo de ter usado static ali (e eu quase não percebi) mas... quê mais que pode se estender numa constante? Constante é constante. Elas são, ou pelo menos deveriam ser, imutáveis.

Compartilhar este post


Link para o post
Compartilhar em outros sites

quê mais que pode se estender numa constante? Constante é constante. Elas são, ou pelo menos deveriam ser, imutáveis.

Excelente argumento.

 

;)

Compartilhar este post


Link para o post
Compartilhar em outros sites

Concordo com o Bruno, mas em termos de linguagem, constantes podem ser estendidas.

 

Exemplo:

<?php

class X
{
    const FOO = 1;

    public function y()
    {
        return static::FOO;
    }
}

class Y extends X
{
    const FOO = 2;
}

$x = new X;
var_dump($x->y()); // int(1) 

$y = new Y;
var_dump($y->y()); // int(2)

Compartilhar este post


Link para o post
Compartilhar em outros sites

Não foi "sem querer". Todos os meus movimentos são friamente calculados.

Saquei a propaganda na vez que vi... :assobiando:

 

 

Quanto o uso de constante me surge uma duvida de uns tempos atras, pra que diabos serve constantes em interface ?

Compartilhar este post


Link para o post
Compartilhar em outros sites

pra que diabos serve constantes em interface ?

Constantes, estejam elas definidas onde estiverem, servem para armazenar algum valor simples e evitar o uso de literais no código.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Eu acho uma baita falha de conceito permitir sobrescrever constantes.

Compartilhar este post


Link para o post
Compartilhar em outros sites

É realmente uma falha, de meu ponto de vista. constante é constante, como o próprio nome já diz.

 

Conheço o PHPUnit, esses dias atrás andei dando uma lida sobre, é excelente. João o Artigo ficou mui legal.

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.