Fábrica (programação orientada a objetos)

Origem: Wikipédia, a enciclopédia livre.
Método  fábrica em: LePUS3

Na programação orientada a objeto (POO), uma fábrica ou Factory é um objeto para a criação de outros objetos – formalmente uma fábrica é uma função ou método que retorna os objetos de uma classe ou protótipo variável ,[1] a partir de uma chamada de método, o que é considerado "novo".[a] Mais amplamente, uma sub-rotina que retorna um objeto "novo" pode ser chamada de "fábrica", como no método de fábrica ou na função de fábrica. Este é um conceito básico em POO , e constitui a base para um número de padrões de design de software.

Motivação[editar | editar código-fonte]

Na programação baseada em classes, fábrica é uma abstração de um construtor de uma classe, enquanto no programação baseada em protótipo uma fábrica é uma abstração de um objeto protótipo. Um construtor é concreto quando ele cria objetos como instâncias de uma classe única, e por um processo especificado (instância da classe), enquanto que uma fábrica pode criar objetos por instanciar diversas classes, ou pelo uso de outros esquemas de alocação, tais como um conjunto de objetos. Um objeto protótipo é concreto quando é utilizado para criar objetos sendo clonado, enquanto que uma fábrica pode criar objetos clonando vários protótipos, ou por outros esquemas de alocação.

Fábricas podem ser invocado de diversas formas, na maioria das vezes, uma chamada de método (um método de fábrica), pode ser chamado como uma função, se a fábrica é um objeto de função. Em algumas linguagens, fábricas, são generalizações de construtores, o que significa construtores são próprias fábricas e estes são chamados da mesma maneira. Em outras linguagens fábricas e os construtores são chamados de forma diferente, por exemplo, usando a palavra-chave new para invocar construtores, mas uma chamada de método normal para invocar as fabricas; nestas linguagens fábricas são uma abstração de construtores, mas não é estritamente uma generalização, como os construtores não são as próprias fábricas.

Terminologia[editar | editar código-fonte]

A terminologia é diferente se o conceito de uma fábrica é em si um design pattern – no livro seminal Design Patterns não há um "padrão de fábrica", mas em vez disso, dois padrões (factory method pattern e abstract factory pattern) que usam fábricas. Algumas fontes referem-se ao conceito como o padrão de fábrica,[2][3] enquanto outros consideram o conceito em si um "programming idiom" ,[4] reservando-se o termo "padrão de fábrica" ou "padrões de fábrica" para padrões mais complicados que usam fábricas; neste contexto, o conceito de uma fábrica em si, pode ser referido como uma fábrica simples . Em outros contextos, particularmente a linguagem de programação Python, "fábrica" em si é usado, como neste artigo.[5] Mais amplamente, a "fábrica" pode ser aplicada não apenas a um objeto que retorna objetos a partir de algumas chamadas de método, mas sim em uma sub-rotina que retorna objetos, como em uma factory function (mesmo se as funções não são objetos) ou factory method.[6] Porque em muitas linguagens, fábricas são invocados pela chamada de um método, o conceito geral de uma fábrica é muitas vezes confundida com o factory method pattern.

Uso[editar | editar código-fonte]

POO fornece polimorfismo em objeto de uso pelo method dispatch, formalmente subtipo polimorfismo através de simples expedição de determinados pelo tipo de objeto no qual o método é chamado. No entanto, isso não funciona para os construtores, como construtores de criar um objeto de algum tipo, em vez de usar um objeto existente. Mais concretamente, quando um construtor é chamado, não há nenhum objeto ainda que para a expedição.[b]

Utilizar fábricas em vez de construtores ou protótipos permite usar polimorfismo para criação de objetos, não apenas para uso de objetos. Especificamente, o uso de fábricas fornece encapsulamento e significa que o código não está vinculado a classes ou objetos específicos e, portanto, a hierarquia de classes ou protótipos podem ser alterados ou refatorados sem a necessidade de alterar o código - eles abstraem da hierarquia de classes ou protótipos.

 Mais tecnicamente, em linguagens onde as fábricas generalizam construtores, as fábricas geralmente podem ser usadas em qualquer lugar que os construtores possam ser, o que significa que interfaces que aceitam um construtor também podem geralmente aceitar uma fábrica - geralmente só precisamos de algo que crie um objeto, em vez de precisar especificar uma aula e instanciação.

Por exemplo, em Python, a classe collections.defaultdict [7] tem um construtor que cria um objeto do tipo defaultdict[c] , cujos valores padrão são produzidos através da invocação de uma fábrica. A fábrica é passada como um argumento para o construtor, e pode ser um construtor, ou qualquer coisa que se comporta como um construtor – um objeto que retorna um objeto, por exemplo, de uma fábrica. Por exemplo, usando o  list constructor de listas:

# collections.defaultdict([default_factory[, ...]])
d = defaultdict(list)

Criação do objeto[editar | editar código-fonte]

Objetos de fábrica são usados ​​em situações em que se apossar de um objeto de um tipo particular é um processo mais complexo do que simplesmente criar um novo objeto, especialmente se alocação ou inicialização complexas forem desejadas. Alguns dos processos necessários na criação de um objeto incluem a determinação de qual objeto criar, o gerenciamento da vida útil do objeto e o gerenciamento de preocupações específicas de build-up e tear-down do objeto. O objeto de fábrica pode decidir criar dinamicamente a classe do objeto (se aplicável), retorná-la de um pool de objetos, fazer configurações complexas no objeto ou outras coisas. Da mesma forma, usando essa definição, um singleton implementado pelo padrão singleton é uma fábrica formal - ele retorna um objeto, mas não cria novos objetos além da instância única.

Exemplos[editar | editar código-fonte]

O exemplo mais simples de uma fábrica é uma simples fábrica de função, que apenas invoca o construtor e retorna o resultado. Em Python, uma fábrica função f que instância uma classe de Um pode ser implementada como:

def f():
    return A()

Uma simples fábrica função de implementação do padrão singleton é:

def f():
    if f.obj is None:
        f.obj = A()
    return f.obj

f.obj = None

Isto irá criar um objeto quando for chamado, e sempre retorna o mesmo objeto a partir de então.

Sintaxe[editar | editar código-fonte]

As fábricas podem ser invocadas de várias maneiras, na maioria das vezes uma chamada de método (um método de fábrica), às vezes sendo chamada como uma função se a fábrica for um objeto que pode ser chamado (uma função de fábrica). Em algumas linguagens, construtores e fábricas têm sintaxe idêntica, enquanto em outros, os construtores têm uma sintaxe especial. Em linguagens onde construtores e fábricas têm sintaxe idêntica, como Python, Perl, Ruby, Object Pascal e F #,[d]construtores podem ser substituídos de maneira transparente por fábricas. Em linguagens onde elas diferem, é preciso distingui-las em interfaces, e alternar entre construtores e fábricas exige mudar as chamadas.

Semântica[editar | editar código-fonte]

Em linguagens onde os objetos são alocados dinamicamente, como em Java ou Python, as fábricas são semanticamente equivalentes aos construtores. No entanto, em linguagens como C ++ que permitem que alguns objetos sejam alocados estaticamente, as fábricas são diferentes de construtores para classes alocadas estaticamente, pois o último pode ter alocação de memória determinada em tempo de compilação, enquanto a alocação dos valores de retorno de fábricas deve ser determinada em tempo de execução. Se um construtor pode ser passado como um argumento para uma função, então a invocação do construtor e a alocação do valor de retorno devem ser feitas dinamicamente no tempo de execução e, assim, ter uma semântica similar ou idêntica para invocar uma fábrica.

Design patterns[editar | editar código-fonte]

As fábricas são usadas em vários padrões de design, especificamente em padrões criacionais, como a biblioteca de objetos padrão de design. Receitas específicas foram desenvolvidas para implementá-las em vários idiomas. Por exemplo, vários "GoF patterns", como o "Factory method pattern", o "Construtor" ou até mesmo o "Singleton" são implementações desse conceito. O "padrão abstrato de fábrica" é um método para construir coleções de fábricas. Em alguns padrões de design, um objeto de fábrica tem um método para cada tipo de objeto que é capaz de criar. Esses métodos aceitam opcionalmente parâmetros definindo como o objeto é criado e, em seguida, retornam o objeto criado.

Em alguns padrões de design, um objeto de fábrica tem um método para cada tipo de objeto que é capaz de criar. Esses método aceitam opcionalmente parâmetros definindo como o objeto é criado e, em seguida, retornam o objeto criado.

Aplicações[editar | editar código-fonte]

Fábrica de objetos são comuns em toolkits e frameworks onde a biblioteca precisa do código para criar objetos do tipo que pode ser uma subclasse por aplicações utilizando o framework. Eles também são usados em desenvolvimento orientado a testes para permitir classes para ser colocado em teste.

 As fábricas determinam o tipo concreto real de objeto proa ser criado e é aqui que o objeto é realmente criado. Como a fábrica só retorna uma interface abstrata para o objeto, o código do cliente não sabe - e não está sobrecarregado com - o tipo concreto real do objeto que acabou de ser criado. No entanto, o tipo de objeto concreto é conhecido pela fábrica abstrata. Em particular, isso significa:

  • O código do cliente tem qualquer conhecimento de que o concreto do tipo, não precisa incluir qualquer arquivos de cabeçalho ou de classe declarações relativas ao tipo concreto. O código do cliente lida apenas com o resumo do tipo. Objetos de um tipo concreto são, na verdade, criada pela fábrica, mas o código do cliente acessa tais objetos de somente através da interface abstrata.
  • A adição de novos tipos de concreto é feito modificando o código do cliente para usar uma outra fábrica, uma modificação, que é normalmente uma linha em um arquivo. Isso é significativamente mais fácil do que modificar o código do cliente para instanciar um novo tipo, que exigem mudança de cada local no código onde um novo objeto é criado.

Aplicabilidade[editar | editar código-fonte]

Factories podem ser usadas quando:

  1. A criação de um objeto torna impossível reutilizar significativas, sem duplicação de código.
  2. A criação de um objeto requer acesso a informações ou recursos que não deve ser contido dentro da composição de classe.
  3. O gerenciamento do tempo de vida dos objetos gerados devem ser centralizados, para garantir um comportamento consistente na aplicação.

Fábricas, especificamente os métodos da fábrica, são comuns em toolkits e frameworks, onde a biblioteca precisa do código para criar objetos de tipos que podem ser uma subclasse por aplicações utilizando o framework.

Hierarquias de classes paralelas geralmente exigem objetos de uma hierarquia para poder criar objetos apropriados de outra.

Métodos das factories são utilizados no desenvolvimento orientado a testes para permitir classes serem colocadas em teste.[8] Se uma classe Foo cria outro objeto Dangerous que não pode ser colocado sob testes de unidade automatizados (talvez ele se comunique com um banco de dados de produção que nem sempre está disponível) e, em seguida, a criação de Dangerous objetos é colocado no virtual método de fábrica createDangerous na classe Foo. Para o teste, TestFoo (uma subclasse de Foo) em seguida, é criado, com a fábrica virtual método createDangerous substituído para criar e retornar FakeDangerous, um falso objeto. Testes de unidade, em seguida, usar TestFoo para testar a funcionalidade do Foo sem considerar o efeito colateral do uso de um real Dangerous objeto.

Benefícios e variantes[editar | editar código-fonte]

Além do uso em padrões de projeto, fábricas, especialmente métodos de fábrica, têm vários benefícios e variações.

Nomes descritivos[editar | editar código-fonte]

Um método de fábrica tem um nome distinto. Em muitas linguagens orientadas a objeto, construtores devem ter o mesmo nome da classe em que estão, o que pode levar à ambiguidade, se existe mais de uma maneira para criar um objeto (ver sobrecarga). Métodos de fábrica, não têm esta restrição e que podem ter nomes descritivos; estes são por vezes conhecidas como alternativa de construtores. Como um exemplo, quando os números complexos são criados a partir de dois números reais números reais pode ser interpretado como Cartesianas ou em coordenadas polares, mas usando métodos de fábrica, o que significa, é claro, como ilustrado pelo seguinte exemplo em C#.

    public class Complex
    {
        public double real;
        public double imaginary;

        public static Complex FromCartesian(double real, double imaginary)
        {
            return new Complex(real, imaginary);
        }

        public static Complex FromPolar(double modulus, double angle)
        {
            return new Complex(modulus * Math.Cos(angle), modulus * Math.Sin(angle));
        }

        private Complex(double real, double imaginary)
        {
            this.real = real;
            this.imaginary = imaginary;
        }
    }

Complex product = Complex.FromPolar(1, Math.PI);

Quando os métodos de fábrica são usados para desambiguação, os construtores brutos são geralmente tornados privados para forçar os clientes a usar os métodos de fábrica.

Encapsulamento[editar | editar código-fonte]

Os métodos de fábrica encapsulam a criação de objetos. Isso pode ser útil se o processo de criação for muito complexo; por exemplo, se depender das configurações nos arquivos de configuração ou na entrada do usuário..

Considere como exemplo um programa que lê arquivos de imagem. O programa suporta diferentes formatos de imagem, representados por uma classe de leitura para cada formato.

Cada vez que o programa lê uma imagem, ele precisa criar um leitor do tipo apropriado com base em algumas informações do arquivo. Essa lógica pode ser encapsulada em um método de fábrica. Essa abordagem também foi chamada de Fábrica Simples.

Java[editar | editar código-fonte]

public class ImageReaderFactory {
    public static ImageReader createImageReader(ImageInputStreamProcessor iisp) {
        if (iisp.isGIF()) {
            return new GifReader(iisp.getInputStream());
        }
        else if (iisp.isJPEG()) {
            return new JpegReader(iisp.getInputStream());
        }
        else {
            throw new IllegalArgumentException("Unknown image type.");
        }
    }
}

PHP[editar | editar código-fonte]

class Factory
{
    public static function build($type: string): FormatInterface
    {
        $class = "Format" . $type;
        if (!class_exists($class)) {
            throw new Exception("Missing format class.");
        }
        return new $class;
    }
}

interface FormatInterface {}

class FormatString implements FormatInterface {}
class FormatNumber implements FormatInterface {}

try {
    $string = Factory::build("String");
} catch (Exception $e) {
    echo $e->getMessage();
}

try {
    $number = Factory::build("Number");
} catch (Exception $e) {
    echo $e->getMessage();
}

Limitações[editar | editar código-fonte]

Existem três limitações associadas ao uso do método de fábrica. A primeira diz respeito à refatoração do código existente; os outros dois se relacionam com a extensão de uma classe.

  • A primeira limitação é que refatorar uma classe existente para usar fábricas interrompe os clientes existentes. Por exemplo, se a classe Complex for uma classe padrão, ela poderá ter vários clientes com código como:
    Complex c = new Complex(-1, 0);
    
Quando percebemos que são necessárias duas fábricas diferentes, mudamos a classe (para o código mostrado anteriormente). Mas como o construtor agora é privado, o código do cliente existente não é mais compilado.
  • A segunda limitação é que, como o padrão depende do uso de um construtor privado, a classe não pode ser estendida. Qualquer subclasse deve invocar o construtor herdado, mas isso não pode ser feito se esse construtor for privado.
  • A terceira limitação é que, se a classe fosse estendida (por exemplo, fazendo com que o construtor protegido isso é arriscado, mas viável), a subclasse deve fornecer sua própria reimplementação de todos os métodos de fábrica com exatamente as mesmas assinaturas. Por exemplo, se a classe StrangeComplex estende-se Complexa, em seguida, a menos que StrangeComplex oferece sua própria versão de todos os métodos de fábrica, a chamada produzirá uma instância de Complex (superclasse), em vez da instância da subclasse. Os recursos de reflexão de algumas linguagens podem evitar esse problema.

Os três problemas que poderiam ser atenuados através da alteração subjacente linguagem de programação para fazer fábricas de primeira classe, classe de membros (ver também a classe Virtual).

Notas[editar | editar código-fonte]

  1. Interface-wise, any object that returns an object can be used as a factory, but semantically a factory returns either a newly created object, like a class instance or copy of a prototype, or an object that looks new, like a re-initialized object from an object pool.
  2. In languages where constructors are themselves methods on a class object (class methods), there is an existing object, and constructors are special cases of factory methods, with polymorphic creation being a special case of polymorphic method dispatch. In other languages there is a sharp distinction between constructors and methods.
  3. This class is a subclass of dict, the built-in Python implementation of mappings or dictionaries.
  4. If optional keyword new is omitted.

Referências

  1. Gamma, Erich (1994). Design Patterns. [S.l.]: Addison-Wesley. pp. 18–19. ISBN 9780321700698 
  2. "Factory Pattern", OODesign.com
  3. Factory Pattern, WikiWikiWeb
  4. Chapter 4. The Factory Pattern: Baking with OO Goodness: The Simple Factory defined
  5. "30.8 Classes Are Objects: Generic Object Factories", Learning Python, by Mark Lutz, 4th edition, O'Reilly Media, Inc.,
  6. Factory Method, WikiWikiWeb
  7. defaultdict objects
  8. Feathers, Michael (outubro de 2004), Working Effectively with Legacy Code, ISBN 978-0-13-117705-5, Upper Saddle River, NJ: Prentice Hall Professional Technical Reference