sexta-feira, 21 de março de 2008

Padrao de ETL

Refletindo um pouco sobre a quantidade de coisas que terao que se comportar como ferramentas de ETL (Extract, Transform and Load), pensei que seria interessante definir um padrao que otimize nossos esforcos em desenvolver tais ferramentas.

Talvez um modelo interessante seja o de se ter uma classe Processadora que contenha pelo menos 5 atributos

class ProcessadoraAbstract {
private $E = new ExtractorBase();
private $ETI;
private $T = new TransformerBase();
private $TLI;
private $L = new LoaderBase();

public function exec($parmExtract) { ... }
}


O objeto E e' responsavel pela carga dos dados. Quando o objeto processador for executado, devera ser passado como parametro o path ou os dados a se tratar; O processador invoca entao o E que faz o trabalho da carga e depois coloca o resultado na variave ETI (Extract Transform Interface), que ficara disponivel para o objeto T, que fara as transformacoes necessarias e coloca o resultado em TLI (Transform Load Interface), que faz a carga final dos dados no destino.
Organizando as coisas desse jeito, podemos criar, por exemplo, um driver especializado na leitura de um arquivo texto que esta em um servidor FTP, como uma instancia de E. Depois, sem alterar os demais processos, podemos fazer um driver que acesse um banco de dados SQLServer; Desde que se obedeca fielmente os padroes ETI e TLI, conseguimos isso facilmente.

Para o projeto Progem, isso sera especialmente util, uma vez que teremos que prever varias formas de importacao dos dados, e agindo assim podemos deixar as coisas mais preparadas para varios niveis de adaptacao.

Poderiamos ate pensar em adotar uma ferramenta ETL convencional ou ate comercial, mas o custo de adaptacao em relacao a complexidade dos nossos problemas nao me leva a crer que valha a pena. Alem do mais, se a complexidade aumentar, temos plenas condicoes de alterar as classes e adequa-las para novas realidades, o que poderia ser mais dificil se pensar em ferramentas de terceiros.

quinta-feira, 20 de março de 2008

Uma visão da nova arquitetura


Pensando e repensando em nossos objetivos, em se adotando a tal da arquitetura em 3 camadas, vi que existem algumas questões que precisam ficar bem esclarecidas. Só que agora o foco não está no nível puramente conceitual, mas sim na aplicação prática do modelo.

Por isso decidi postar este artigo, descrevendo um pouco mais a arquitetura e com isso, ajudar a defini-la o mais rápido possível.




Conceitos básicos

Action (link - também conhecido como padrão Command)
Responsável pelas interações com o usuário, seja ele um navegador, um dispositivo móvel ou qualquer outro "robô". Recebe uma requisição de uma operação, conceitualmente atômica, que deverá ser realizada pelo sistema. Ele invoca quem sabe realizar a tal tarefa, e depois formata o resultado da forma mais conveniente.
As responsabilidade dos Actions são:
  • Receber a requisição de execução de uma operação;
  • Receber e tratar os dados enviados;
  • Delegar a execução a quem de direito;
  • Obter o resultado das operações;
  • Formatar os dados para exibição;
  • Enviar as respostas ao solicitante;

Business Objects (BO) (link)
Um objeto de negócio é uma classe que geralmente está no modelo de domínio dos processos de negócio. Ele pode ser persistente ou não, mas possui tanto os atributos quanto os métodos de negócio.
Ele não deve ser entendida como um simples mapeamento para a camada de dados, uma vez que quem faz isso é o Active Record (AR).
O BO concentra os métodos de acesso aos atributos (getters e setters) e os de negócio, onde estão encapsuladas as regras de negócio.
O ato de persisitir um BO consiste em um conjunto de operações de instanciação de objetos AR, atribuição de valores, testes e efetivação da gravação. Um BO pode ter mais de um AR associado, e por isso o processo pode ser complexo.


Active Record (AR) (link)
O Active Record é um objeto que mapeia a tabela no banco, permitindo o fácil acesso à base, através de uma camada de abstração de banco de dados.
Os nomes dos atributos devem ser iguais às das colunas nas tabelas do banco de dados, e a classe possui apenas os métodos getters e setters.
A importância do AR está na sua simplicidade, visto que seu papel é apenas um: transportar dados entre a camada de negócios e a de persistência.

Data Access Object (DAO) (link)
O Data Access Object é responsável pelo trânsito de dados entre o sistema e o "mundo externo". O mundo externo pode ser outros sistemas, serviços, arquivos hospedados em servidores FTP, HTTP, webservices, etc. É um objeto que realiza serviços de integração. Seu uso depende do tipo de sistema e das necessidades de integração. Alguns tipos de integração permitem que um serviço rode periodicamente e realize a integração entre os sistemas; já outros precisam que a integração seja imediata. O uso do DAO deve ser visto com muito cuidado, e exige um bom planejamento.

Perguntas e Respostas

P. Posso usar operações com lógica de negócio embutida, em classes AR?
R. Até poderiamos fazer isso, mas a complexidade dos ARs poderia dificultar as manutenções. Além disso, o AR é o mapeamento fiel ao modelo de dados, e nem sempre teremos classes de negócio que reflitam exatamente o modelo persistente. Assim, o melhor será separar o controle (BO) da persistência (AR), e colocar nos BOs a lógica de negócio.

P. Um BO pode acessar mais de um AR?
R. Sim. Embora na maioria dos casos o BO será um espelho de um AR, podem haver casos em que um BO tenha que recuperar um AR e atualizar outro. Além disso, por questões de performance, podemos criar um BO que tenha atributos de ARs diferentes. Por fim, o BO é um modelo de classe de negócio, e por isso, poderá conter elementos como listas e objetos, coisa que um AR não terá.

P. Por que 'burocratizar' as operações simples, como a pesquisa de um registro no banco, tendo que obrigatoriamente passar pelo BO?
R. Embora o acesso direto seja mais prático e rápido de se implementar, o uso de etapas e responsabilidades em camadas dá um isolamento entre estas, que permite uma manutenção com pouco impacto final nas demais. A inclusão de um campo, ou mudança de tipo, pode afetar menos um projeto grande do que se o mesmo fosse todo feito usando acessos diretos.

P. Como faço para recuperar um objeto que é atributo de um BO ?
R. Quando um atributo de um BO for na verdade um outro objeto BO, ao invés de um inteiro simples que contenha o ObjectID, recomenda-se manter o objeto e quando na recuperação do AR do banco de dados, instanciar o BO e atribuir sua chave. Quando for acessar algum outro atributo deste BO, então invoca-se o método de carga e ele é completado. Estou estudando uma forma de implementar o padrão lazy-load, como no Hibernate do Java.

P. Um Action pode acessar diretamente um AR, para fazer gravações simples?
R. Não. Se começarmos a simplificar nesse ponto, teremos problemas mais tarde. Imagine quando, por exemplo, for necessário dar manutenção em um sistema, onde deverá ser inserido um tratamento de verificação de dados antes de ser gravado; Se os actions acessarem diretamente o AR em questão, será necessário ir de action em action fazendo a manutenção, enquanto que se for centralizado no BO, o ponto de manutenção é um só.

P. Pode haver um BO que não seja persistido?
R. Sim. Os BOs que realizam operações de negócio, dentro do escopo do sistema, como a geração de relatórios específicos e cálculos , por exemplo, além daqueles que possuem apenas regras de negócio, funcionando como helpers.

P. Quando usar DAO?
R. O DAO deve ser usado quando for necessário realizar alguma comunicação de algum componente do sistema com o mundo externo, como a consulta de dados disponíveis em uma página na web, por exemplo. Outros exemplos seriam a leitura ou gravação de arquivos em servidores de FTP, acesso a bancos de dados de terceiros, importação e parsing de arquivos CSV. Se a integração for assíncrona, recomenda-se registrar o serviço em uma instância do PHPCron, por exemplo; já se for necessário realizar o sincronismo no momento das operações de persistência, recomenda-se usar os métodos do DAO a partir dos BOs - nestes casos, cria-se métodos do tipo 'preInserir', 'preAtualizar', 'preExcluir', 'posInserir', etc., e neles coloca-se as chamadas aos métodos dos DAOs correspondentes;

P. Quando devo usar o BO?
R. Sempre. Todo Action deve reponder com o resultado do processamento do sistema - e neste caso, a ponte é o Action, e a "cabeceira" é o BO. A menos em um protótipo visual que apenas simule o funcionamento do sistema, qualquer sistema deverá ter seus BOs implementados. A política de agrupar funcionalidades em um mesmo BO só deve ser usada quando o mesmo é uma entidade abstrata do modelo de domínio.

P. Quando devo usar o AR?
R. O AR deve ser usado sempre para mapear uma tabela do banco de dados no sistema. Eventualmente podemos pensar em ARs associados aos DAOs - um DAO pode retornar uma lista de ARs.

P. Quando devo usar o Action?
R. O Action é a interface entre o sistema e o mundo real (frontend), que disponibiliza um serviço, onde o cliente faz uma solicitação e recebe uma resposta. Então, sempre que for necessária uma interface com o cliente, esta será um Action.

P. Em algumas arquitetura os elementos de persistência (AR) possuem um método bind, onde passo um vetor associativo e o objeto "se vira" para atribuir os valores. Como funcionará na nova arquitetura?
R. O bind provavelmente será implementado, mas com algumas restrições: já que não estamos mapeando fielmente os campos dos formulários para tabelas, ou mesmo para objetos intermediários (BOs), o método deverá ser sobrecarregado nas classes finais. Assim, um BO terá um método bind que saberá que campos vindos do formulário vão ser atribuídos aos seus atributos; O AR poderá ter a mesma coisa. Embora haja um trabalho um pouco maior, isso dá uma certa segurança haja visto que todos os atributos que forem aceitos no bind serão de conhecimento do desenvolvedor.

P. Na nova arquitetura, o que preciso fazer para criar um novo "serviço" disponibilizado por um dado Action?
R. No construtor do Action em questão, o método deverá ser registrado, para evitar que um hacker subverta o sistema solicitando um método não-registrado. Depois, implemente o método normalmente. Os links que darão acesso ao novo método deverão conter o parâmetro 'acao' com o valor do nome do novo método.

quinta-feira, 13 de março de 2008

Ferramentas de desenvolvimento rápido... As Concorrentes

Na busca pela minimização dos custos com desenvolvedores de software, do tempo de desenvolvimento de soluções contratadas, e na redução dos erros de programação, adotam-se ferramentas de apoio ao desenvolvimento. Hoje por acaso achei uma de tantas ferramentas que prometem o desenvolvimento rápido de sistemas web, independente de linguagem, 100% visual, independente de banco de dados, multi-camadas, etc.

Claro que fui dar uma espiada no dito-cujo, e quem vê se impressiona. Uma ferramenta onde você pode especificar até o layout dos formulários com uma IDE similar ao Delphi. Você chega ao ponto de descrever o fluxo de funcionamento da aplicação com um fluxograma!!! Que maravilha!

- Bom, eu não acho
.

Dentre alguns dos pontos que precisam ser observados quando se usa uma ferramenta de apoio ao desenvolvimento, na minha opinião, é a manutenção. A manutenção é a correção de erros ou evolução da solução de software.

O que ocorre quando usamos uma ferramenta de geração de código é a simples troca de um editor de texto, onde o desenvolvedor digita seu código, por um conjunto de menus e formulários, que uma vez preenchidos, darão origem a um conjunto de blocos de código. Em vez de o desenvolvedor se especializar na linguagem, se especializará na ferramenta - o que traz alguns riscos, como a dependência e a extrema necessidade de um contrato de suporte.

Quando criamos um software, (muito) provavelmente teremos que entrar nas suas entranhas e mexer nele. Nem sempre conseguimos atender aos requisitos de negócio com os recursos da IDE, uma vez que precisamos das especificidades das linguagens para criar algoritmos muito específicos (e olha que as vezes temos dificuldades nisso). Como vou fazer isso em uma IDE 100% visual? Aí você pode dizer: "ok, vamos gerar o código e depois mexemos nele". Ótimo, mas daí surgem dois probleminhas:
  • Quando o código for mexido, o modelo do projeto será sincronizado? Como?
  • Como é o código gerado? O desenvolvedor conhece a estrutura do mesmo? O desenvolvedor terá que conhecer a linguagem e o padrão, e as vezes, ser um profissional mais qualificado do que um "desenvolvedor normal", que sabe desenvolver o mesmo software "na mão", pois terá que conhecer uma série de recursos e estruturas geradas pela IDE .
    • Isso leva a outra questão: se o código for gerado, a linguagem terá que ser especificada - ou seja, deixou de ser independente de linguagem, pois a mesma terá que ser do conhecimento da equipe.

Outra questão é o processo, que é o fluxo de atividades que, uma vez seguido, guiará o projeto até o produto completo. A equipe de desenvolvimento terá que se adequar ao processo ditado pela IDE.

Na minha opinião, a melhor forma de se desenvolver usando uma ferramenta CASE, é estudando e definindo os processos internos, selecionando a linguagem-alvo, e a estrutura de como será gerado, mantido, documentado e sincronizado o código entre o ambiente de desenvolvimento e a ferramenta. A profundidade do uso da ferramenta CASE deve também ser pesada - quanto mais fundo se for nela, mais detalhes de negócio serão descritos e gerados por ela, e isso pode demandar mais tempo do analista, que terá que fazer um fluxograma que pode ser resumido em algumas linhas de código pelo desenvolvedor.

Muito mais do que focar nas atividades de especificação e codificação, a análise do contexto da empresa, envolvendo o perfil dos profissionais e o processo adotado, pode ser o fator de sucesso para a adoção de uma ferramenta como essa. E quem consegue fazer isso, pode criar sua própria ferramenta, customizada para sua realidade. Mas quem não pode, compra uma CASE, ou usa o notepad.

Referência: http://www.softwell.com.br/web/

quarta-feira, 12 de março de 2008

Patterns

Pattern é definido por Christopher Alexander como sendo a descrição da solução de um problema que ocorre frequentemente em nosso ambiente.

Então, podemos definir patterns apenas observando a repetição de problemas, abstraindo-os e sintetizando padrões de soluções. Embora isso soe meio teórico demais, é o que na prática fazemos, muitas vezes sem pensar. Depois de muito tempo resolvendo problemas similares, é natural que encontremos soluções mais interessantes, seja pela simplificação ou pela adaptação das mesmas prevendo problemas futuros. Assim compreendemos melhor, na prática, o que vem a ser efetivamente patterns.

Agora, voltando à nossa realidade, cabe a nós identificar os problemas recorrentes e definir estratégias de solução. O mercado está cheio de patterns que atendem alguns casos, mas nem sempre existem soluções prontas.

Um autor muito interessante é Martin Fowler, que além de trabalhar na área de processos de desenvolvimento, também entra muito nessa área de padrões de projetos de software.
Alguns exemplos muito interessantes que ele traz são:
  • Money, que representa um valor monetário;
  • Value Object, usado para transitar objetos transientes entre camadas de uma aplicação (estilo DTO)
  • Special Case, que nada mais é do que uma exceção à regra, trabalhada a nível de objeto ao invés de atributo null
  • etc.

O uso dos patterns está mais ligado a praticidade pretendida do que a necessidade de engolir o que o mercado (ou o meio acadêmico) diz que é bom. Se pensar no alto custo de se manter um software de grande porte, o interessante é a redução de retrabalho ou mesmo prever situações de manutenção, onde as intervenções sejam as mais rápidas possível. Outro ponto a se considerar é que a manutenção nem sempre será feito pelo mesmo profissional que desenvolveu o software - a documentação na medida certa aliada ao uso de padrões entendidos pelos profissionais envolvidos tanto no desenvolvimento quanto na manutenção são duas estratégias muito importantes em empresas de software.


Referências
ALEXANDER, Christopher; ISHIKAWA, Sara; SILVERSTEIN, Murray; JACOBSON, Max; FIKSDAHL-KING, Ingrid, and ANGEL, Shlomo. A Pattern Language. Oxford University Press, New York, 1977.

FOWLER, Martin; Patterns of Enterprise Application Architecture, June 2002

Acessando diretamente Métodos e Constantes de Classes

Quando queremos usar um método de uma classe (ou melhor, de um objeto de classe), geralmente o fluxo normal é:
  1. Instancia um objeto de classe;
  2. Invoca o método;

É uma questão de projeto, o uso ou não de métodos de classe. Os métodos de classe são aqueles que não acessam dados do objeto (não fazem referência ao $this). Isso é especialmente útil a economia de memória e para organização do código.

Para usar o método de uma classe, basta usar o nome da classe, seguido de :: e o nome do método.
Ex.:
// deve calcular o dígito verificador do parâmetro passado.
Pessoa::calculaDVCPF('12345')

terça-feira, 11 de março de 2008

Interface e "class_implements" - recursos interessantes

Quando queremos fazer um projeto onde não sabemos ao certo que objetos receberemos como parâmetros, podemos 'forçar' a lógica para não permitir que objetos de outras classes que não as que esperamos sejam rejeitadas. Mas algumas vezes isso pode não ser muito produtivo, especialmente se o que esperamos possua comportamentos agregados de várias classes diferentes - como não temos (e nem queremos ter) os recursos de herança múltipla, lançamos mão de um recurso muito interessante: as interfaces.

Meu primeiro contato com interface foi em Java, e vi que vale muito a pena, especialmente em projetos grandes e/ou quando na construção de frameworks e bibliotecas de suporte.
Enfim, interfaces são como "esqueletos" de classes, com os métodos que devem ser implementados nas classes que as usam. Se eu tenho um método que recebe um objeto com dados de alguma fonte, e penso que pode haver algum erro nestes, posso criar uma interface 'erro_possivel', com o método 'imprimeErro'; Se eu forçar para que todos os objetos que passem para esta minha função implementem esta interface, então quando houver um erro posso ter a certeza de que ele tem este método implementado... mas peraí, como faço isso?

Na criação da classe que será passada como parâmetro, uso o implements e o nome da interface; será exigido que você implemente os métodos declarados na interface na sua classe. Já no seu método, você terá que usar a função 'class_implements' passando como parâmetro o objeto, que será retornado um array com a lista de interfaces que este implementa - aí você verifica se nesta lista está contido o nome da interface esperada...

Carregar arquivos de classes automaticamente...

Como costume, separamos as classes em arquivos diferentes, e isso por um lado é bom, pois facilita a organização. Por outro lado, a proliferação de arquivos pode gerar dificuldade na carga das classes necessárias para alguns fins. Este problema ocorre especialmente quando se usa o esquema de instanciação de classes dinâmicas, ou seja, quando criamos objetos de classes especificadas em tempo de execução, como em esquemas de plugins.

O PHP possui um recurso muito interesante, e pouco explorado, que é a carga automática de arquivos sob demanda. Na prática é a carga de um arquivo quando uma classe não-carregada é instanciada - isso na tentativa de carregar o arquivo que contém a definição da classe em questão. É claro que neste arquivo a dita-cuja não estiver presente, será emitido um erro.
O recurso é o __autoload(). Atenção: lembre-se que os métodos que iniciam com dois underscores são chamadas de 'funções mágicas').

A __autoload() (bem descrita no http://br2.php.net/manual/pt_BR/language.oop5.autoload.php), é definido pelo usuário e recebe como parâmetro o nome da classe que está sendo instanciada. Dentro desta função, você pode fazer o que for necessário para achar o arquivo. Com isso você tem a liberdade de procurar em vários diretórios o arquivo que bata com o padrão de nome definido.

Três problemas que vejo no uso do __autoload() são:
  • Em primeiro lugar, o fato de não explicitar a inclusão do arquivo abre precedentes de risco de segurança, uma vez que alguém pode criar um arquivo no padrão em um diretório que será procurado pelo script antes de onde realmente reside o referido, e com isso comprometer a integridade do sistema.
    • Mas isso é difícil de acontecer
  • O segundo motivo é a dificuldade de manter um código que usa uma classe que só-Deus-sabe onde está localizada.
    • Para evitar isso, é necessário uma boa organização do código e da localização das classes.
  • Por fim, o terceiro e mais importante, na minha opinião, é que se for necessário o uso do __autoload() mais de uma vez no mesmo projeto, o interpretador emitirá um erro fatal - ou seja, ele não vai funcionar. Isso pode ocorrer especialmente quando se usa frameworks de apoio de terceiros, onde eles podem ter feito uso do __autoload().
    • Para solucionar este problema, foi criada a função 'spl_autoload_register()', que funciona igual ao '__autoload()', só que permite o 'empilhamento' das funções que serão invocadas quando na instanciação de uma classe não-carregada.
Veja maiores detalhes em http://br2.php.net/manual/pt_BR/function.spl-autoload-register.php , e/ou o capítulo 7, seção 7.1 do livro 'PHP5 in Practice'

Globals... nunca mais!!!

Até pouco tempo atrás eu usava de vez em quando a famigerada palavra-chave 'global' do PHP, dentro das funções que precisavam acessar alguma variável externa... Isso em um primeiro momento parece não ter problemas e, pelo contrário, parece ainda simplificar muito a vida do desenvolvedor.

Ledo engano...

Em primeiro lugar, o uso de variáveis globais gera um sério problema: quando eu alterar seu conteúdo, quem será afetado? Quem alterou o conteúdo de tal variável? Estas são algumas perguntinhas que podem ocorrer a quem estiver mantendo um sistema em PHP.
Além de gerar dificuldades de manutenção, o código fica poluído e confuso - quem for dar manutenção no sistema pode se perder.

Ok, mas então como podemos dar uma solução para esse problema?
Vou aqui citar dois casos típicos, que devem servir de base para a grande maioria das situações:

1. A variável que está fora da minha função, está dentro do escopo do meu script.

Entendamos da seguinte forma: várias funções desta página acessam diretamente uma determinada variável, declarando-a como global.
Se for possível, crie uma classe com esta (e outras) variáveis ditas globais, e junte com os métodos que as acessam. Se usar o PHP5, torne as variáveis private, e se possível, encapsule-as, criando seus getters e setters. Assim você mantém a integridade das variáveis garantindo que apenas os métodos desta classe podem alterar seus valores.
Se não for usar a classe, pelo passe a variável como parâmetro na função - não esqueça de que se você deseja alterar seu conteúdo de dentro da função, então passe por referência ao invés de passar por valor (use o caractere '&').

2. A variável está fora do escopo da minha aplicação, mas dentro de um arquivo importado.

Este caso é mais complexo, pois pode ser que não se tenha acesso direto ao código onde está a tal variável global, e não se possa alterá-lo. Assim, a idéia da classe permanece, com a diferença de que um atributo interno seja na verdade, uma referência à dita-cuja. Faça o atributo normalmente na classe, e em seu setter passe a variável 'global' por referência - assim, de dentro da classe você estará acessando indiretamente a tal variável.

Ordenando arrays com comparações definidas pelo usuário

Geralmente usamos as funções típicas de ordenação do PHP, como o sort(), usort() e ksort(). Isso é natural, considerando que na maioria dos casos estas funções atendem plenamente. Entretanto, quando temos um array mais complexo, onde sua ordenação exige mais do que estas funções fazem, precisamos de outra saída. Aqui entram as funções de ordenação "parametrizáveis". São elas: usort(), uasort() e uksort().

Para a referência e exemplos de uso, a melhor fonte que encontrei foi o manual do PHP (http://br2.php.net/manual/pt_BR/function.usort.php).

Note que a diferença fundamental do uasort() para o uksort() é que enquanto este classifica as chaves (claro que alterando junto seus valores), aquele apenas classifica os valores, deixando as chaves inalteradas.

Um exemplo prático do uso destas funções é quando queremos classificar uma tabela sem usar os recursos do banco de dados, se a lógica de classificação for complexa, podemos pegar a tabela em um array multidimensional e usar as funções para gerar um array ordenado;

Desenvolvendo em camadas

Desde a faculdade ouvi falar nas tais 3-camadas e em como elas ajudariam no desenvolvimento de sistemas, sejam eles escritos em Java, Delphi, PHP, ou qualquer outra. Depois de quebrar a cabeça tentando entender, me foram apresentados os tais Design Patterns do GoF , do Martin Fowler, e de tantos outros... Enfim, piorou tudo quando vi a lista de "camadas" adotadas no J2EE. Era tudo muito estranho - eu não conseguia ver aplicação naquilo tudo.

Quando comecei efetivamente a trabalhar com projeto e implementação orientados a objetos, percebi que algumas coisas podiam ser bem aproveitadas. Debulhei uma série de artigos e projetos desenvolvidos em cima de frameworks orientados a objetos, e consegui assimilar algumas coisas interessantes:

As minhas principais motivações no uso de divisão em camadas são:
  • Aumento na manutenibilidade - se o código for bem projetado, as manutenções corretivas e evolutivas tendem a ser mais ágeis e menos confusas.
  • Especialização - posso definir um especialista para fazer o trabalho em uma camada, aumentando a produtividade local (naquela camada)
  • Diminuição da dependência entre as camadas - posso criar uma camada em PHP, a outra em Java e outra em qualquer SGBD.
  • Desacoplamento - Quanto menos acoplados forem os componentes melhor, uma vez que as amarras entre estes podem dificultar as manutenções.
É evidente que isso traz um contraponto, a saber:
  • É exigido mais da equipe de projeto - a análise e projeto são mais exigidas, e a formalização dos artefatos produzidos é maior. Na prática isso demanda um pouco mais de trabalho.
  • Ferramental e processo - é necessário dar o devido suporte ao projeto, usando ferramentas de apoio; além disso, um processo é desejável, pois aumenta-se o volume de esforço acumulado, e é bom ter um passo-a-passo.
  • Comunicação - É um fator essencial: a comunicação entre todos os envolvidos dos projetos;
  • Gerência - diferente dos projetos individuais ad-hoc, projetos com a divisão de camadas geralmente exigem um certo grau de gerência para manter as partes coesas.

Removendo espaços do PHP

Lendo o primeiro capítulo do livro PHP 5 in Practice, de Elliot White III, vi uma seção interessante de ser postada.

As vezes queremos eliminar espaços em branco das nossas strings, e frequentemente usamos a função trim() . Só que as vezes queremos eliminar espaços dentro das strings, e quebramos a cabeça para fazer. Usando expressões regulares é fácil. Veja o exemplo a seguir:
$str = " This line  contains\tliberal \r\n use of   whitespace.\n\n";

// First remove the leading/trailing whitespace
$str = trim($str);

// Now remove any doubled-up whitespace
$str = preg_replace('/\s(?=\s)/', '', $str);

// Finally, replace any non-space whitespace, with a space
$str = preg_replace('/[\n\r\t]/', ' ', $str);

// Echo out: 'This line contains liberal use of whitespace.'
echo "
{$str}
";

?>
O exemplo acima funciona da seguinte forma:
  • Primeiro usa o trim() para limpar os espaços do início e do final da string.
  • Depois usa o preg_replace() para remover os espaços duplicados.
    • O caracter de escape \s significa "qualquer espaço em branco".
    • A notação (?=) é usada para realizar pesquisa adiante. Na prática ele significa "qualquer espaço que é seguido por outro espaço". Isso remove os duplicados, deixando apenas um espaço em branco.
  • Finalmente, usamos outra expressão regular [\n\r\t] para encontrar as quebras de linha (\n), "retornos de carro" (\r) ou tabulações (\t), e substituímos tudo isso por um espaço em branco.

Extraído da seção 1.2.1 do livro PHP 5 in Practice