Em uma recente conversa no Reddit, alguns de nós tomamos uma tangente no assunto sobre o padrão de projetos de software conhecido como factory. Mantive então, e ainda mantenho agora, a ideia de que um “factory” sempre e somente retorna uma nova instância. Se você tem um “factory” que retorna algo diferente de uma nova instância, não é um factory sozinho. No caso de métodos do factory, é um factory + um assessor; no caso de objetos do factory, é um factory + registro.
Um “factory” (seja um método ou um objeto dele) é uma maneira de separar a criação do objeto do uso do objeto.
Vamos dizer que nós precisamos criar um objeto para fazer algum trabalho em um método de classe:
class Example { public function __construct($db) { $this->db = $db; } public function doSomething($itemId) { $data = $this->db->fetchOne( "SELECT * FROM items WHERE id = :id", ['id' => $itemId] ); $item = new Item($data); // do some other work with the item, // the return the results of that work. return $results; } }
A criação do item é misturada com o uso do produto. O que nós queremos fazer é separar a criação do uso. Uma maneira de fazer isso é usar um método do factory, ou um objeto dele, para lidar com a criação do objeto para nós. Aqui está um exemplo de um método do factory:
class Example { public function __construct($db) { $this->db = $db; } public function doSomething($itemId) { $data = $this->db->fetchOne( "SELECT * FROM items WHERE id = ?", ['id' => $id] ); $item = $this->item($itemId); // do some other work with the item, // the return the results of that work. return $results; } public function item($data) // factory method { return new Item($data); } }
Aqui está um exemplo de um objeto factory injetado:
class Example { public function __construct($db, $factory) { $this->db = $db; $this->factory = $factory; } public function doSomethingWithItem($id) { $data = $this->db->fetchOne( "SELECT * FROM items WHERE id = ?", ['id' => $id] ); $item = $this->factory->item($data); // do some other work with the item, // the return the results of that work. return $results; } } class Factory // factory object { public function item($data) // factory method { return new Item($data); } }
Havia algumas pessoas na lista de discussão do Reddit que acreditam que um factory pode ainda reter a instância criada para reutilização. Por exemplo, vamos dizer que queremos criar e reutilizar um objeto colaborador de algum tipo:
class Example { public function doSomething() { $collab = $this->collab(); // do stuff, then: return $result; } protected function collab() // factory method? { if (! $this->collab) { $this->collab = new Collab(); } return $this->collab; } }
Até onde eu posso dizer, isso não é apenas um factory. Há três coisas acontecendo lá: inicializar uma propriedade, criar um objeto e acessar uma propriedade. Para dividir as preocupações, seriam necessários dois métodos:
class Example { public function doSomething() { $collab = $this->getCollab(); // do stuff, then: return $result; } protected function getCollab() // initialize + access { if (! $this->collab) { $this->collab = $this->newCollab(); } return $this->collab; } protected function newCollab() // factory { return new Collab(); } }
Agora, a preocupação de criar o objeto é representada pelo método newCollab(), e as preocupações de inicialização e acesso da propriedade são representadas pelo método getCollab(). Na verdade, dividir um método do factory nesse tipo de situação é a recomendação exata do livro GOF Design Patterns na página 113; esse é o livro que primeiro definiu o termo “método de factory”, por isso parece autoritário sobre esse ponto.
O que acontece quando você adiciona retenção para o que seria um objeto do factory? Por exemplo, o seguinte objeto é apenas um método do factory, ou ele faz mais do que apenas criar e retornar novas instâncias?
class WhatIsThatThing { public function getService() { if (! $this->service) { $this->service = $this->newService(); } return $this->service; } public function newService() { return new Service(); } }
Ele tanto cria como mantém os objetos criados. Isso faz com que ele seja mais do que apenas um factory; e mais parece um container nesse momento.
Finalmente, em relação aos nomes dos métodos, parece que o melhor é indicar o que esperar como resultado de retorno. O padrão é newInstance() em objetos do factory que lidam com apenas uma única classe, e new<Type>() quando o objeto do factory cria muitas classes diferentes. Usar get<Type>() indica que uma instância pré-existente (provavelmente memorizada em uma propriedade) está sendo devolvida.
***
Paul M. Jones faz parte do time de colunistas internacionais do iMasters. A tradução do artigo é feita pela redação iMasters, com autorização do autor, e você pode acompanhar o artigo em inglês no link: http://paul-m-jones.com/archives/6161
Mensagem do anunciante:
Infraestrutura financeira descomplicada é com a iugu! Tão simples que parece brincadeira! Clique aqui e dê um up nos negócios!
Powered by WPeMatico