PHP: Padrões O padrão Observer

Introdução

O padrão Observer é talvez o mais frequentemente encontrados em gráfica de usuário tradicional
aplicações de interface de desktop. É uma excelente maneira de garantir que disparate
componentes do visor refletir as mudanças no núcleo de um sistema. Como veremos, porém,
continua a ser uma técnica poderosa em um ambiente Web-oriented.

Neste artigo eu mostrar-lhe como usar o padrão Observer para construir uma flexível
transmissão estilo de relação entre um componente central e os objetos que
se preocupam com isso.

Primeiro, porém, em preparação para um exemplo de execução, eu dou uma olhada no objeto
zombando de uma grande técnica para teste de condução de código.

Exemplo de código e um bônus inesperado

Neste artigo, como você poderia esperar, eu cozinhei um exemplo para ilustrar
meus pontos. Esta é sempre uma tarefa difícil. O exemplo deve ser credível
o suficiente para que um leitor poderia imaginar que lá fora no mundo real. Por outro
Por outro lado, é preciso muito sacrifício que seria essencial no código implantado no
selvagem. O primeiro a ir é geralmente verificação de erros. Ninguém quer através de arado
moitas de try / catch cláusulas para chegar ao ponto simples enterrado dentro. O
mesmo é verdade para a funcionalidade incidental. Em um artigo sobre a maneira que
objetos e classes interagem, o processo de falar com um banco de dados ou de
análise de um documento XML é muitas vezes irrelevantes. Essa funcionalidade pode, muitas vezes
ser substituída por declarações de impressão simples (‘update db’, ‘ler documento XML’).

Você pode achar um aviso deste tipo, em qualquer uma das centenas de técnico
artigos. Tutorial código é voltada para compreensão e não a implantação.
Neste caso, porém, ela serve para ilustrar um ponto. Isto é: interface
deve outrank aplicação em programação orientada a objeto. Na concepção de
seu código, você pode tentar criar objetos que não fazem nada em tudo, mas imprimir
suas ações se destinam. Concentre-se nas classes em seu sistema e seus
relacionamentos. Em seguida, concentrar-se sobre as acções que devem ser definidas. Tire essa
direito, e código de implementação, muitas vezes, caem no lugar naturalmente.

Outro benefício importante que você pode derivar dessa abordagem está em testes.
O teste de unidade em si é um assunto para outro artigo. No entanto, você deve
considerar estratégias para código de construção que automatiza testes de classe. De todos
os problemas que podem aparecer em testar um componente, indiscutivelmente maiores do
é contexto. Um objeto raramente vive em isolamento, e ele pode precisar de trabalhar com
outros que falam a bases de dados, o sistema de arquivos ou solicitações HTTP. Como fazer
de reproduzir essas condições em um teste? Uma maneira é a de assegurar que uma real
Requisição HTTP toma lugar, e um banco de dados real é na mão, abastecido com
dados válidos. Isto sela aproximação você com a sobrecarga de recriar um verdadeiro
contexto mundial para a classe que você está testando, arrastando o seu foco na direção errada.

Se você está de codificação para uma interface, no entanto, uma outra opção se abre. Você pode
criar objetos falsificados. Se o código que se comunica com seu banco de dados é muito bem
encapsulado por trás de uma interface clara, então não deve haver nada que impeça
você de criar uma classe que compartilha a mesma origem como a sua classe de banco de dados,
mas serve hard-coded dados para fins de teste.

O exemplo para este artigo diz respeito uploads de arquivos. Uma classe, UploadManager ,
trabalha com UploadFile objetos. Aqui está o UploadFile abstrato
superclasse e uma implementação simplificada:


abstract class UploadFile {
 

tamanho função abstrata ();
nome da função abstrata ();
abstrata função com erros ();
abstrata função moveTo ($ str);
}

UploadFileException classe extends Exception {}

UploadFileImpl classe extends UploadFile {

private $ formFieldName;
private $ fsPath = null;

construção da função __ ($ formFieldName) {

if (! isset ($ _FILES [$ formFieldName])) {
throw new UploadFileException (“formFieldName $ desconhecida”);
}
$ This -> formFieldName = $ formFieldName;
}

tamanho function () {
retornar $ _FILES [$ this -> formFieldName] [‘size’];
}

nome function () {
retornar $ _FILES [$ this -> formFieldName] [‘name’];
}

função com erros () {
retornar $ _FILES [$ this -> formFieldName] [‘error’];
}

caminho function () {
return $ this -> fsPath;
}

função moveTo ($ perm_loc) {

if (! is_null ($ this -> fsPath)) {
return null;
}

if ( is_dir ($ perm_loc)) {
$ Perm_loc = DIRECTORY_SEPARATOR $ this -> name ()..;
}

move_uploaded_file ($ _FILES [$ this -> formFieldName] [‘tmp_name’], $ perm_loc);

if ( is_file ($ perm_loc)) {
$ This -> fsPath = realpath ($ perm_loc);
return $ this -> fsPath;
}

return null;
}
}

UploadFileImpl classe é um wrapper simples em torno de um elemento de
$_FILES matriz que é preenchido automaticamente pelo mecanismo de PHP
quando um upload HTTP ocorre. A classe fornece informações sobre o arquivo carregado,
e suporta movê-lo de seu local de armazenamento temporário para um mais permanente
casa no sistema de arquivos.

Esta classe é simples e compacta o suficiente para usar nos exemplos, mas sofre de
uma grande desvantagem quando usado em testes e demonstrações. Ele requer um arquivo de upload
ter ocorrido. O fato de que o UploadFileImpl classe estende
uma superclasse abstrata fornece-nos uma oportunidade. Podemos criar um boneco
upload de classe que estende o mesmo pai. Qualquer cliente deve ser capaz de usar este
alternadamente com a coisa real. Aulas fictícias são conhecidas como as simulações.
Aqui está MockUploadFile :


class MockUploadFile extends UploadFile {
 

$ tamanho privada = 3000;
private $ fspath = null;
$ name privado = ‘mock.gif’;
private $ errorCode = null;
$ privado moveToResult = true;

construção da função __ ($ name) {
$ This -> nome = $ nome;
}

setSize função ($ tamanho) {
$ This -> size = $ tamanho;
}

tamanho function () {
return $ this -> tamanho;
}

setName função ($ name) {
$ This -> nome = $ nome;
}

nome function () {
return $ this -> nome;
}

setErrorCode função ($ code) {
$ This -> errorCode code = $;
}

função com erros () {
return $ this -> errorCode;
}

caminho function () {
return $ this -> fsPath;
}

setMoveToFail function () {
$ This -> moveToFail = true;
}

função moveTo ($ path) {

if ($ this -> com erros () | |! is_null ($ this -> fsPath)) {
return null;
}

return $ this -> fsPath path = $;
}
}

Observe que esta classe de simulação é configurável. Podemos definir os valores-chave, que pode até causar
operações para falhar se queremos. Porque eles podem ser configurados para criar condições de erro,
tais classes pode agir como espiões ou provocadores agentes no campo inimigo. Podemos enviar
-los para causar problemas, então sentar e ver o caos que se seguiu desdobrar.

A Figura 1 mostra o UploadFile tipo em conjunto com um cliente insuspeito
classe. O cliente é passado um UploadFile objeto e continua alegremente
desconhece a implementação em particular com o qual trabalha. É por isso que é
geralmente uma boa idéia para passar objetos em torno de um sistema em vez de tê-los instanciado
diretamente pelas classes que os utilizam. Claro que os objetos devem ser instanciados em algum lugar,
e de acordo com alguma lógica. Vamos olhar para algumas destas estratégias em um artigo posterior.

A Figura 1

O padrão Observer

Descrito pelo Gang of Four (Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides)
em seu catálogo seminal, Design Patterns. Elementos de Software Orientada a Objetos reutilizáveis
(Addison Wesley, 1995), este padrão é um exemplo fantástico de dissociação em ação.

Um dos grandes objetivos do programador orientado a objeto é criar ortogonal
componentes. Isso significa que a coesão elevada (cada componente está ligado a si mesmo e um núcleo
propósito) e acoplamento fraco (os componentes são independentes de seu contexto possível).
Esta compra nos componentes que podem ser reutilizados facilmente, e talvez mais importante que pode
levar a sistemas que são flexíveis e menos propenso a erros. Quando, em contrapartida o maior
sistema é permitida a enraizar-se em um componente, também há uma teia de interdependência.Se
um componente é muito acolhedor, com seu contexto, não pode facilmente ser puxado para longe dele.
Além disso, fazer uma mudança em uma parte de um sistema pode levar a não intencional
conseqüências em outros lugares.

Todos nós já encontramos sistemas que as declarações de pimenta SQL em todos os componentes.Tal
codificação promíscuo é tentador. É mais fácil de consultar um banco de dados como e quando você precisar
para que a elaborar uma estratégia centralizada. O salário do pecado, no entanto, são trabalhar e
erro. A mudança de esquema simples de banco de dados pode exigir mudanças para a direita em todo o sistema,
com o risco inevitável que uma afirmação obscura ou dois é perdido. Perdida, isto é,
até a manhã de apresentar a sua fantástica aplicação nova para um cliente ou um investidor.

A dissociação, então, é o processo pelo qual você separar componentes de seus contextos,
eo padrão Observer é uma estratégia que você deve ter em sua caixa de ferramentas dissociação.

Visão global

O padrão Observer define um mecanismo através do qual os componentes podem optar por receber
mensagens quando um objeto alvo (confusamente conhecido como o assunto) altera a sua
Estado. O sujeito precisa ter nenhum conhecimento de ‘observar’ os componentes-lo para eventos,
além do fato de que eles estão lá e apoiar uma interface para notificação.

O Problema

Imagine que você está construindo um site de comunidade de música que permite aos usuários fazer upload de seu trabalho
para o compartilhamento e avaliação. Para este efeito, você criou o UploadFile
aulas já vimos, e agora precisamos gerenciar o upload. Há alguns
questões legítimas que um UploadManager classe deve atender. Ele deve:

  • verificar se um usuário não tiver preenchido sua cota de espaço
  • invocar UploadFile::moveTo()
  • verificar se há erros de upload
  • atualizar as informações do usuário espaço em disco

Aqui está uma implementação:


class UploadManager {
 

$ user privado;
private $ repDir;
$ status private = 0;

const UM_UPLOAD_ERROR = 1;
const UM_QUOTA_ERROR = 2;

function __ construct (Usuário $ user, $ repositório) {
$ This -> user = $ user;
$ This -> repDir repositório = $;
}

função processFile (UploadFile $ arquivo) {

$ This -> afirmar (($ file -> size () <$ this -> user -> disponível ()), ‘quota do usuário excedida “,self :: UM_QUOTA_ERROR);

$ File -> moveTo ($ this -> repDir);

$ This -> afirmar (($ errorcode = $ file -> com erros ()), “Arquivo de erro de upload: $ errorcode!”,Self :: UM_UPLOAD_ERROR);

$ This – status> = 0;
$ This -> user -> setStored ($ this -> user -> armazenado () + $ arquivo -> size ());
return true;
}

funcionar assert ($ condition, $ msg, $ code) {

if (! $ condition) {
$ This -> Status code = $;
jogar UploadFileException novo ($ msg, $ code);
}
}

estado function () {
return $ this -> Estado;
}
}

O coração desta classe é o processFile() método. Ele verifica disponível
cota de espaço, as tentativas para instalar o arquivo carregado, verifica os erros e atualizações
informações de armazenamento do usuário.

Aqui está um simples User de classe que trabalha com UploadManager :


class User {
 

quota $ privado;
$ privadas armazenadas = 0;

function __ construct ($ quota = 50000) {
$ This -> quota quota = $;
}

quota function () {
return $ this – quota>;
}

função disponível () {
return ($ this -> quota – $ this -> armazenados);
}

função setStored ($ quantidade) {
$ This -> armazenada quantidade = $;
}

função armazenada () {
return $ this -> armazenado;
}
}

Como você pode ver, esta classe foi cortar até o osso para que ele só suporta arquivos
funcionalidade de armazenamento. Podemos consultar um User objeto para descobrir mais sobre sua
quota, ea quantidade de armazenamento que tem em uso e disponíveis. UploadManager
chama esses métodos para assegurar que o utilizador actual tem um espaço adequado para armazenar o
submetidos clipe.

Aqui está um código que coloca as classes com seus ritmos:


$user = new User ( 4000 );
$mgr = new UploadManager ( $user , '/tmp' );
$file = new MockUploadFile ( "test.gif" );
 

if ($ mgr -> processFile ($ arquivo)) {
print “OK  n”;
}

Criamos um novo User objeto com uma cota de armazenamento de 4000 e passá-lo para o
UploadManager método construtor. Finalmente criamos um objeto de arquivo e invocar
UploadManager::processFile() . Esta amostra simplesmente gera ‘OK’. Nós podemos fazer
as coisas mais interessantes, embora por envenenar nosso MockUploadFile objeto. Este
define o tamanho do arquivo para além do contingente do usuário:


//...
$file -> setSize ( 5000 ); // TOO HIGH!!
 

if ($ mgr -> processFile ($ arquivo)) {
print “OK  n”;
}

/ / Output:
/ / Erro Fatal: Exceção Uncaught
/ / ‘UploadFileException’ com mensagem
/ / ‘Quota do usuário excedida’ dentro ..

Aqui nós fingimos um erro de upload de arquivos:


$file -> setErrorCode ( UPLOAD_ERR_PARTIAL );
 

if ($ mgr -> processFile ($ arquivo)) {
print “OK  n”;
}

/ / Output: Fatal error: Uncaught exceção
/ / ‘UploadFileException’ com mensagem
/ / ‘File upload de erro: 3’ polegadas ..

Até agora, as principais classes do exemplo ter permanecido razoavelmente centrada na sua
próprias preocupações. Talvez UploadManager pode delegar um pouco útil
mais para User , mas isso é uma mudança fácil de fazer a qualquer momento. Acoplamento
está solto, em que nenhum componente instancia outro, deixando ampla oportunidade para
mudança de classe por trás claras e interfaces bem definidas. A Figura 2 ilustra o
relações de uso.

A Figura 2

Agora vamos introduzir algumas moscas no ungüento. O grupo empresarial encontrar mais dinheiro
para o projeto e estender seu curta. A partir de agora, todos os envios devem ser registrados.
Notificação de transações falhas devem ser enviadas por e-mail para o pessoal designado.
Uploads de sucesso deve agendar uma mensagem a ser enviada através de serviços Web a um terço
parceiro do partido, mas somente enquanto o acordo de parceria continua.

É muitas vezes novas funcionalidades que matar um projeto. UploadManager é certamente
um lugar útil para pendurar todas estas funções, mas o que faz esta mágica para o sistema
como um todo? Logging pode ser algo que você poderia esperar objetos de negócios para
conhecemos, mas os detalhes em constante mudança de detalhes de parceria corporativa pode empurrar
as coisas um pouco. É o seu trabalho para acomodar esta lógica, mas como você pode fazê-lo
flexível? Aqui está uma versão alterada do principais métodos da classe:


function processFile ( UploadFile $file ) {
 

$ File -> moveTo ($ this -> repDir);

$ This -> afirmar (($ file -> size () <$ this -> user -> disponível ()), ‘quota do usuário excedida “,self :: UM_QUOTA_ERROR);

$ This -> afirmar (($ errorcode = $ file -> com erros ()), “Arquivo de erro de upload: $ errorcode!”,Self :: UM_UPLOAD_ERROR);

if (PARTNERSHIP_DEAL) {
$ Relay = new PartnershipRelay ();
$ Relé -> informar ($ file, $ user);
}

Logger :: instance () -> log ($ file -> name (). “: carregado”);

$ This – status> = 0;
$ This -> user -> setStored ($ this -> user -> armazenado () + $ arquivo -> size ());
return true;
}

funcionar assert ($ condition, $ msg, $ code) {

if (! $ condition) {
$ This -> Status code = $;
Logger :: instance () -> erro ($ msg, $ code);
$ Informante = new Informer ();
$ Informante -> mailwarning ($ msg, $ code);
jogar UploadFileException novo ($ msg, $ code);
}
}

Como você pode ver, o UploadManager classe tornou-se atolada em seu contexto. Ele
é confiar num constante, PARTNERSHIP_DEAL , bem como fazendo algum directa
instanciações. Poderíamos corrigir esses problemas por ter objetos passados, ou servido
por métodos de fábrica (mais sobre isso em artigos futuros), mas uma questão central continuará a ser:
UploadManager sabe demais. A classe tornou-se específica site. Nós
não poderia facilmente buscá-lo e soltá-lo em outro sistema similar, pois ele agora
baseia-se explicitamente sobre muitas classes e métodos que são incidentais ao seu núcleo
finalidade. A Figura 3 ilustra o problema.

 

http://devzone.zend.com/362/php-patterns_the-observer-pattern/

Leave a Reply

Your email address will not be published. Required fields are marked *