Ir para conteúdo

Arquivado

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

michaeldias

OOP - Relações, DI e Strategy

Recommended Posts

Estou um pouco confuso em relação à alguns conceitos, se alguém puder ajudar...

 

1º - Qual é a diferença, na teoria e na prática, entre Injeção de Dependência e a relação de Associação, em Orientação a Objetos? Injeção de Dependência contraria a idéia da relação de Composição de objetos?

 

2º - Qual é a diferença, na teoria e na prática, entre Injeção de Dependência e o pattern Strategy?

 

Compartilhar este post


Link para o post
Compartilhar em outros sites

1º - Qual é a diferença, na teoria e na prática, entre Injeção de Dependência e a relação de Associação, em Orientação a Objetos? Injeção de Dependência contraria a idéia da relação de Composição de objetos?

São conceitos completamente distintos.

 

Composição é uma das primitivas da Orientação a Objetos e indica uma relação onde um objeto A contém um objeto B.

 

Normalmente, esse relacionamento é representado assim:

image_thumb[20.png?imgmax=800]

O diagrama acima mostra que um objeto [inline]ClassA[/inline] contém um objeto [inline]ClassB[/inline]. Entretanto, existem algumas formas diferentes pra que essa relação ocorra.

 

Primeiramente, não há nesse diagrama indicação de cardinalidade, ou seja, quantos elementos [inline]ClassB[/inline] podem existir em um objeto [inline]ClassA[/inline] ou a quantos objetos [inline]ClassA[/inline] diferentes pode pertencer um objeto [inline]ClassB[/inline]. Normalmente, quando não há indicação, assumimos que a relação é 1:n, no sentido de que [inline]ClassA[/inline] possui um único objeto [inline]ClassB[/inline] e que um mesmo objeto [inline]ClassB[/inline] pode pertencer a n objetos [inline]ClassA[/inline].

 

Além disso, também não há nenhuma informação sobre como e quando o objeto do tipo [inline]ClassB[/inline] é criado, se ele é necessário para a existência de um objeto [inline]ClassA[/inline] ou apenas opcional.

 

Com isso em mente, em termos de código, podemos criar uma associação de composição da seguinte forma:

 

class ClassA {
    private $classBInstance;

    public function __construct() {
        $this->classBInstance = new ClassB();
    }
}

class ClassB {
    
}
Também é possível que seja dessa forma:

 

class ClassA {
    private $classBInstance;

    public function setClassBInstance(ClassB $instance) {
        $this->classBInstance = $instance;
    }
}

// ou ainda:

class ClassA {
    private $classBInstance;

    public function __construct(ClassB $instance) {
        $this->setClassBInstance($instance);
    }
    
    public function setClassBInstance(ClassB $instance) {
        $this->classBInstance = $instance;
    }
}
Temos assim 3 exemplos de possíveis implementações de uma relação de composição, mas apenas os dois últimos seguem (mais ou menos) o Princípio da Inversão de Depenpendências, através do qual as dependências de uma classe são injetadas no objeto ao invés de serem criadas dentro dele, como no primeiro exemplo.

 

Mas qual o problema de fazer como no primeiro exemplo???

A princípio, nenhum, certo?

 

O problema é quando essa dependência da [inline]ClasseA[/inline] precisar ser trocada. Suponha que você não quer mais usar um objeto [inline]ClasseB[/inline], mas sim um objeto [inline]ClasseC[/inline], que tem uma funcionalidade diferente. Como você faz?

 

Ah, é fácil, dá pra fazer assim ó:

class ClassA {
    private $classCInstance;

    public function __construct() {
        $this->classCInstance = new ClassC();
    }
}

 

Ok, jovem padawan, mas e se você precisar alterar de forma dinâmica entre objetos [inline]ClassB[/inline] e [inline]ClassC[/inline].

 

Aí o Princípio da Inversão de Dependências faz sua mágica.

 

Guarde este mantra:

Seus objetos devem depender sempre de abstrações, não de concreções (implementações), uma vez que mudanças na implementação são muito mais frequentes que mudanças na abstração.

Ao invés de brincar com [inline]ClassA[/inline], [inline]ClassB[/inline], [inline]ClassC[/inline], vou dar um exemplo mais concreto.

 

Digamos que você tem uma aplicação que é um repositório de ideias, pensamentos em geral. De início, por simplicidade, você optou por armazenar tudo em arquivos de texto planos. Você possui então 2 classes: uma que trata os dados e outra que faz o armazenamento.

 

class IdeaRepository  {
    private $storage;

    public function __construct() {
        $storage = new FileStorage();
    }
    public function insert($text) {
        // faz algum processamento...
        $storage->insert($text);
    }

    public function update($line, $text) {
        // faz algum processamento...
        $storage->update($line, $text);
    }

    public function delete($line) {
        // faz algum processamento...
        $storage->delete($line);
    }
}

class FileStorage {
    private $fileObject;
    public function __construct(SplFileObject $fileObject) {
        $this->fileObject = $fileObject;
    }

    private function open() { 
        // ...
    }

    private function close() { 
        // ...
    }

    public function insert($data) {
        $this->open();
        // escreve os dados
        $this->close();
    }

    // demais métodos omitidos por brevidade
}
Acontece que você é um cara muito inspirado, começa a ter muitas ideias e começa a achar meio difícil organizá-las em arquivos de texto comum e resolve usar um banco de dados. E aí, como você faz?

 

Da forma como está, você tem criar uma classe para realizar as operações sobre o banco de dados ([inline]DbStorage[/inline], por exemplo) e alterar a classe [inline]IdeaRepository[/inline] e fazer com que ela funcione com essa nova classe que você criou.

 

Sentiu um cheirinho aí? Meio complicado fazer essa mudança não?

 

Então como faz?

Lembra do papo de depender de uma abstração e não de uma concreção? Então... Temos que criar uma abstração para o armazenamento de ideias, que eu vou chamar de [inline]IdeaStorage[/inline]:

 

interface IdeaStorage {
    public function insert($idea);
    public function update($identifier, $idea);
    public function delete($identifier);
    public function get($identifier); // retorna uma ideia específica
    public function getAll(); // retorna todas as ideias
}
Essa interface é o "contrato" que uma implementação para o armazenamento de ideias deve seguir, ela não define como esse armazenamento será feito porque isso não importa.

 

Agora podemos modificar nosso repositório de ideias:

 

class IdeaRepository  {
    private $storage;

    // Repare que agora estamos INJETANDO a dependência no construtor
    public function __construct(IdeaStorage $storage) {
        $this->storage = $storage;
    }

    // o restante permanece inalterado
}
Legal, agora nosso repositório sabe que depende de algum objeto que vai fazer o armazenamento dos dados para ele, mas a forma com que isso será feito pouco importa, desde que a o objeto cumpra seu contrato (realize a interface requerida).

 

Agora fazemos assim:

 

class FileStorage implements IdeaStorage {
    private $fileObject;
    public function __construct(SplFileObject $fileObject) {
        $this->fileObject = $fileObject;
    }

    // implementação omitida por brevidade
    public function insert($idea) {}
    public function update($identifier, $idea){}
    public function delete($identifier){}
    public function get($identifier){}
    public function getAll(){}
}
No contexto dessa classe, [inline]$identifier[/inline] representa o número da linha no qual a ideia se encontra no texto.

 

Pra utilizarmos um banco de dados, podemos fazer assim:

 

class DbStorage implements IdeaStorage {
    private $driver;
    public function __construct(DbDriver $driver) {
        $this->driver = $driver;
    }

    // implementação omitida por brevidade
    public function insert($idea) {}
    public function update($identifier, $idea){}
    public function delete($identifier){}
    public function get($identifier){}
    public function getAll(){}
}
[inline]DbDriver[/inline] aqui é a camada mais baixa de interação com o banco de dados, sua implementação é irrelevante para o exemplo. Poderia ser um objeto [inline]PDO[/inline], [inline]MySQLi[/inline] ou similar.

 

No contexto dessa classe, [inline]$identifier[/inline] seria o campo identificador do registro no banco de dados, por exemplo.

 

Para utilizar nossa classe [inline]IdeaRepository[/inline], fazemos assim:

 

$fileStorage = new FileStorage(new SplFileObject('/path/to/db.txt'));
$dbStorage = new DbStorage(new DbDriver(...));

$repository = new IdeaRepository($fileStorage); // ou $dbStorage, tanto faz...
Perceba que a única mudança necessária para trocar o meio de armazenamento é na instância do objeto passado como parâmetro para o (injetado no) construtor de [inline]IdeaRepository[/inline].

 

Esse é o Princípio da Inversão de Dependências! Ele nos dá uma forma mais eficiente -- em termos de mutabilidade -- de criar uma associação de composição.

 

_____________________________________________________________________

 

2º - Qual é a diferença, na teoria e na prática, entre Injeção de Dependência e o pattern Strategy?

Padrões (ou patterns) são formas reutilizáveis de se resolver problemas recorrentes. Injeção de Dependência é uma "ferramenta" para atingir o Princípio da Inversão de Dependências (que é um dos 5 princípios SOLID).

 

Princípios são guias para a construção de um software melhor; padrões são aplicações desses princípios.

 

O padrão Strategy representa uma solução para um problema onde existem algoritmos (sequências de ações) distintos para a resolução de um mesmo problema. Você encapsula tais algoritmos, facilitando a permutação nos objetos que deles dependem. A permutação entre os algoritmos é feita via injeção de dependência.

 

No exemplo que eu dei sobre o repositório de ideias, [inline]FileStorage[/inline] e [inline]DbStorage[/inline] podem ser consideradas diferentes estratégias para o armazenamento de ideias, constituindo assim uma implementação do padrão Strategy.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Haha, Henrique deu uma aula aqui...rs.

 

Muito boa, a primeira vez que vi esses conceitos, achava que era maior viagem!!!

 

Agora as coisas estão começando a fazer sentido!!

 

Muito bom!!

Compartilhar este post


Link para o post
Compartilhar em outros sites

A relação de composição, em orientação a objetos, descreve uma situação em que um objeto é parte intrínseca de outro, revelando características como:

  • Se um objeto A contém o objeto B, B não pode pertencer a mais ninguém.
  • Se um objeto A contém o objeto B, ao remover o objeto A da memória, B também será removido.

Por exemplo:

<?php
class Funcionario
{
    public $nome;
    public $telefone;

    public function __construct($nome, $telefone)
    {
        $this->nome     = $nome;
        $this->telefone = new Telefone($telefone);
    }
}

$pedro = new Funcionario('Pedro', '6868-8686');
$maria = new Funcionario('Maria', '9696-6969');

O objeto pedro e maria possuem as instâncias da classe Telefone.

 

A partir do momento que a instância de um objeto não pertence a outro, minha relação não é mais de composição.

 

Exemplo:

<?php
class Telefone
{
    public $numero;

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

class Funcionario
{
    public $nome;
    public $telefone;

    public function __construct($nome, Telefone $telefone)
    {
        $this->nome     = $nome;
        $this->telefone = $telefone;
    }
}

$telefone = new Telefone('6868-8686');

$pedro = new Funcionario('Pedro', $telefone);
$maria = new Funcionario('Maria', $telefone);

O exemplo acima não é mais uma composição, e sim uma associação.

Mas porque, na prática, não posso dizer que nesse exemplo eu tenho uma injeção de dependência? Pelo simples fato de o Type Hint Telefone ser uma classe concreta, e não uma abstrata ou uma interface? Ou injeção de dependência é mais abrangente do que simplesmente isso?

Compartilhar este post


Link para o post
Compartilhar em outros sites

Mas porque, na prática, não posso dizer que nesse exemplo eu tenho uma injeção de dependência? Pelo simples fato de o Type Hint Telefone ser uma classe concreta, e não uma abstrata ou uma interface? Ou injeção de dependência é mais abrangente do que simplesmente isso?

Você entendeu errado. Você de fato tem uma Injeção de Dependência no seu segundo exemplo, só não está de acordo o Princípio de InVERSÃO de Dependências, que é um conceito mais abrangente.

 

Note que para esse caso específico, talve nem seja viável seguir esse princípio, uma vez que você tem um Plain-Old Object que dificilmente terá variações na implementação.

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.