7 de setembro de 2010

Os benefícios intangíveis dos cursos de MBA

por Bianca Machado Branco*

A proliferação dos cursos de MBA (Master of Business Administration) é um fato. Porém, o que ocorre, ultimamente, é que o público dessa modalidade de pós-graduação também está se modificando, abrangendo não apenas a alta gerência como também profissionais do nível operacional.

Cada vez mais os cursos de MBA vêm sendo procurados por jovens profissionais, muitos recém-formados, com pouco ou nenhum tempo de carreira, porém com expectativas muito altas em relação ao retorno em termos financeiros que o curso trará para eles. Esses profissionais são a chamada geração Y, aqueles nascidos na década de 80, que possuem uma característica marcante: o imediatismo. Eles iniciam um MBA pouco tempo depois de concluída a graduação e esperam com isso uma inserção imediata no mercado de trabalho ou um reconhecimento rápido das empresas onde atuam - como um aumento salarial significativo, uma promoção de cargo ou outra forma de compensação financeira.

Entretanto, a especialização por si só não oferece garantias de sucesso. A experiência de um MBA pode representar valor agregado sob o ponto de vista das empresas, mas o retorno que o profissional trará à empresa demanda um longo tempo para que se possa medir. A companhia não recompensará um título por si só, mas os resultados que o profissional trouxer após aplicar no seu dia-a-dia o know-how absorvido no curso.

Expectativas financeiras à parte, um MBA também trás benefícios intangíveis, como:
Incremento da bagagem profissional, ampliada por meio de conhecimentos de várias áreas de negócios. Essa bagagem é o que ajudará o profissional a falar a língua do cliente e a se inserir em projetos mais desafiantes;

Aprender a pensar "fora da caixa". O currículo do MBA tradicional ensina a interpretar balanços contábeis, planos de marketing e elaborar um planejamento estratégico. Essas são ferramentas úteis para um entendimento de como se desenvolvem as operações de um departamento ou de uma empresa. Quanto melhor a compreensão por parte do profissional sobre os movimentos da empresa, melhor o seu posicionamento estratégico dentro dela;

Visão de oportunidades de negócios, que pode fazer com que o profissional ajude a alavancar o negócio da empresa (e com isso se destacar), ou até mesmo empreender um negócio por conta própria;

Networking, permitindo a troca de experiências com profissionais de várias áreas, trazendo visibilidade ao profissional no mercado e podendo, até mesmo, gerar futuras indicações;

Valorização do profissional no mercado, o que significa manutenção da empregabilidade.

Os benefícios tangíveis, como crescimento na carreira, serão consequências da experiência prática profissional, que vale mais que qualquer título. O que o MBA fará pelo profissional é muní-lo de um ferramental diferenciado, para que ele possa encontrar soluções que agreguem valor ao negócio da empresa.

*Bianca Machado Branco possui MBA em Gestão Empresarial e formação em Análise de Sistemas. Trabalha com tecnologia da informação desde 1998. Atualmente é coordenadora de projetos na ABC71.

3 de setembro de 2010

Criando Serviços que permitam várias instalações num mesmo computador

Como parte de sua solução de ERP Omega, a ABC71 entrega a seus Clientes um Serviço do Windows para execução de processos de forma agendada. Isto é, o Cliente dita a frequência e o horário mais apropriado e o Serviço, que pode ser instalado em outro computador, se encarrega da execução na data e hora indicada.

A maioria de nossos Clientes precisa gerenciar apenas um banco de dados, mesmo quando há mais de uma planta envolvida já que cada uma controla seu próprio banco. Essa característica permitiu ao Serviço descrito no parágrafo anterior atender muito bem a necessidade de agendamento desses Clientes. A situação mudou quando em alguns Clientes se fez necessário monitorar de forma centralizada as execuções em mais de um banco simultaneamente, cada um deles representando uma filial distinta do mesmo Cliente.

Um solução óbvia é fazer com que o mesmo Serviço do Windows seja instalado mais de uma vez e, então, cada instalação cuida de um banco de dados diferente.

Implementar essa solução num programa feito em C++ Builder ou Delphi é relativamente simples, exigindo duas alterações na forma de se registrar o serviço. Uma vez que o Windows não aceita a existência simultânea de serviços com um mesmo nome interno, a primeira modificação é fazer com que cada instalação seja feita com um nome diferente. A segunda modificação no programa diz respeito a como informar ao Serviço qual é o banco de dados que ele deve usar. A resposta curta para isso é passar a informação como um parâmetro na linha de comando registrada para o Serviço.

A linha de comando nada mais é que o nome do programa (com o caminho e a extensão EXE) seguido de valores separados por espaços em branco. Tais valores são repassados para o programa quando ele é executado, de forma que é possível lê-los e utilizá-los conforme a necessidade. A linha de comando de um serviço pode ser alterada após sua instalação através da função ChangeServiceConfig da API do Windows. Então, nós a chamaremos no evento AfterInstall do TService:
void __fastcall TSchedService::ServiceAfterInstall(TService *Sender)
{
SC_HANDLE mh = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (mh != NULL)
{
{ Abre o serviço com o Nome calculado para distinguir instâncias que apontem para bancos de dados diferentes. }
SC_HANDLE sh = OpenService(mh, Name.c_str(), SC_MANAGER_ALL_ACCESS);
if (sh != NULL)
{
{ Incluir como parâmetro de execução do serviço o nome da configuração que deve ser usada, conforme definido pelo Configurador do Scheduler }
AnsiString lParam = "USE_ALIAS=\"" + _CfgName + "\"";
AnsiString lPath = System::ParamStr (0) + " " + lParam;

ChangeServiceConfig(sh, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE,
SERVICE_NO_CHANGE, lPath.c_str(), NULL, NULL,
NULL, NULL, NULL, NULL);

CloseServiceHandle (sh);
}
CloseServiceHandle (mh);
}
};

A expressão System::ParamStr (0) dá acesso ao caminho completo do executável para o Serviço, isto é, do programa que está atualmente em execução. Veja que foi criado um par de valores no formato NOME=VALOR, que é acrescentado ao executável. Durante a execução do serviço eu consigo obter essa informação e extrair o valor. Segue o código para realizar essa extração em C++ Builder - no Delphi, varia apenas a sintaxe:
AnsiString __fastcall TSchedService::ExtractCfgName (void)
{
bool lOk = false;
AnsiString lNomeParam ("USE_ALIAS="), ret;
int i = 1, lCompr = lNomeParam.Length();

{ Percorre os parâmetros da linha de comando para encontra o que nos interessa }
while ((i <= System::ParamCount()) && (lOk == false))
{
ret = System::ParamStr (i);

if (ret.SubString (1, lCompr) == lNomeParam)
{
lOk = true;
{ Extrai apenas o valor após o sinal de igual }
ret = ret.SubString (lCompr+1, ret.Length ());
}
i ++;
}

if (! lOk)
ret = "";
return (ret);
};

No meu caso, o valor extraído representa uma chave do Registry onde gravei com antecedência toda a configuração para acessar o banco de dados correto mas poderia representar um nome de arquivo ou uma tag num XML, por exemplo. Em última análise, eu poderia ter passado todas as informações necessárias para a conexão com essa técnica mas isso tornaria mais complicado dar manutenção no programa, exigindo atenção especial à passagem dos parâmetros e ao limite de tamanho da linha de comando (normalmente, 256 caracteres).

Serviços do Windows têm um nome interno usado pelo sistema operacional como identificador único e um nome externo usado como uma descrição a ser apresentada ao usuário. Tanto o C++ Builder quanto o Delphi usam como nome interno do Serviço o valor da propriedade Name do componente TService. Por isso, é preciso modificar esse valor antes do programa ter a oportunidade de registrar o Serviço ou realizar qualquer outra operação que dependa do nome. Um bom lugar é o próprio construtor do TService.

Uma implicação importante ao se usar esse método é que alterar o nome de um componente nos força a respeitar as regras de nomeação do ambiente. Ou seja, o nome não pode ser iniciado por um número, não pode apresentar espaços em branco nem caracteres que não existam na língua inglesa, como caracteres acentuados (agudo, circunflexo, til, trema, crase) ou o cê cidilha. Podemos usar o próprio nome da configuração obtido anteriormente para diferenciar cada instância instalada. Para que isso funcione, teremos que preparar o nome obtido para garantir sua validade, substituindo os caracateres que não forem permitidos. Veja um exemplo:
char __fastcall TWConfigSched::PrepChar (char AChar)
{
char lCh;
switch (Byte (AChar) )
{
case 192: case 193: case 194: case 195:
case 196: case 197:
lCh = 'A';break;
case 199:
lCh = 'C';break;
case 200: case 201: case 202: case 203:
lCh = 'E';break;
case 204: case 205: case 206: case 207:
lCh = 'I';break;
case 210: case 211: case 212: case 213:
case 214: case 216:
lCh = 'O';break;
case 217: case 218: case 219: case 220:
lCh = 'U';break;
}

{ Outros caracteres inválidos são substituídos pelo underscore }
if ( ! ( (lCh >= '0' && lCh <= '9') ||
(lCh >= 'a' && lCh <= 'z') ||
(lCh >= 'A' && lCh <= 'Z') ) )
{
lCh = '_';
}

return (lCh);
};

Como esta função faz a troca de um único caracter, é preciso percorrer todo o nome do Serviço e trocá-los um a um para compor o novo nome. O conjunto de substituições reproduzido acima não está completo - falta tratar caracteres minúsculos e aqueles usados por línguas como o espanhol e o alemão (N com til, por exemplo). No entanto, as letras que não se encaixarem na substituição direta são trocadas por um underscore.

Mais Informações API de Serviços do Windows, Criação de Serviços (parte 1 e parte 2)

31 de agosto de 2010

Design Patterns com Delphi : Interpreter - parte III

Neste post eu concluo o exemplo para o Design Pattern comportamental Interpreter. Conforme eu disse no post anterior, aqui eu falo sobre análise sintática de um texto para podermos trabalhar de fato com o Interpreter.

Embora o conceito estabelecido para o Interpreter nada diga a respeito, é imprescindível construir um analisador sintático para podermos utilizar este Pattern. Independentemente da complexidade da sintaxe válida estabelecida, é o analisador quem extrairá as partes que compõem uma expressão e montará a instância de classe que nos permitirá avaliar tal expressão em um contexto qualquer. Como as sintaxes podem ser muito diferentes entre si - pense num comando SQL comparado à notação polonesa, por exemplo - fica bastante difícil determinar uma fórmula única que sirva de guia para construir todas as possibilidades.

Para o exemplo do saldo de itens de estoque introduzido no post inicial sobre o Interpreter, a sintaxe é bastante simples. Ela consta de alguns poucos símbolos válidos e de apenas duas operações definidas. Formalmente, podemos resumir essa sintaxe como segue:

Expressão ::= { variável [ { + | - } Expressão] }
variável ::= { SALDO | EMFABRICACAO | PEDIDOS }
Ou seja, a expressão pode ser uma única das variáveis permitidas ou uma operação (soma ou subtração) entre a variável e uma outra expressão - isso mostra o nítido caráter recursivo dessa sintaxe. Os nomes de variáveis aceitas na expressão são os listados na segunda linha do quadro acima. O sinal | significa que se deve escolher um dos valores para que a expressão seja válida. O par [ ] indica um termo não obrigatório. Para finalizar a definição da sintaxe, ressalto que ela não é sensível ao caso; isto é, letras maiúsculas e minúsculas não são diferenciadas.

Montar uma instância de classe com base num texto que siga essa sintaxe exigirá preparativos para controlar chamadas recursivas, conforme constatamos anteriormente. Precisamos ter controle sobre qual é o próximo tipo de token esperado (variável ou operação) e também conhecer até que ponto o texto já foi analisado. Veja o código:
function TWMontaExpressao.CreateExprFrom (AStrExpr: String) : TWExpressaoAbstrata;
var lPos, lCompr : integer;
lTipoTokenEsperado : TWTipoToken;
lExpr: TWExpressaoAbstrata;
begin
{ Próximo caractere a ser lido no texto }
lPos := 1;
{ Ainda não há expressões criadas }
lExpr := Nil;
AStrExpr := Trim (AStrExpr);
{ Comprimento do texto analisado }
lCompr := Length (AStrExpr);
{ Inicia aguardando uma variável }
lTipoTokenEsperado := ttkVar;

repeat
try
{ Chama a função recursiva para montar a expressão a partir do texto }
lExpr := CreateProxExpr (AStrExpr, lPos, lTipoTokenEsperado, lExpr);
except
on Exception do begin
{ No caso de erro, devolver a memória usada }
FreeAndNil (lExpr);
Raise;
end;
end;
until (lPos > lCompr);

Result := lExpr;
end;
A função acima apenas prepara o ambiente para que a função CreateProxExpr seja invocada recursivamente. Ela, então, pode percorrer o texto, analisando e interpretando os termos da expressão associada.

Para isso funcionar, a declaração da função recursiva deve ser feita com passagem de parâmetros por referência (palavra chave VAR do Delphi) pois assim o avanço na análise pode ser devolvido em cada chamada à função, garantindo que nada será interpretado duas vezes e que uma chamada prosseguirá a partir do ponto onde a anterior parou. A função também recebe a instância da expressão montada até o momento para que possa utilizá-la na montagem das instâncias seguintes. A definição do cabeçalho da função fica assim:
function CreateProxExpr (var AStrExpr: String;
var APos: integer;
var ATipoTokenEsperado:TWTipoToken;
AExpr: TWExpressaoAbstrata)
: TWExpressaoAbstrata;
A codificação dessa função deve extrair o próximo token do texto e verificar se ele é do tipo esperado - variável ou operação. Se for do tipo variável, basta criar a instância correta de expressão para a variável. No caso de ser uma operação, a própria função será chamada recursivamente para obter o termo seguinte da expressão pois nossas operações sempre exigem dois termos. Com o segundo termo preparado, podemos então criar a instância de classe representando a operação em questão. Fica mais fácil vendo o código:
lToken := ProxToken (AStrExpr, APos, ATipoTokenEsperado);
lTipoToken := CalcTipoToken (lToken);

if (lTipoToken <> ttkVazio) then
begin
if (lTipoToken <> ATipoTokenEsperado) then
raise Exception.CreateFmt('Esperado símbolo do tipo "%s"',
[GetNameFromTipoToken(ATipoTokenEsperado)]);
if (lTipoToken = ttkVar) then
ATipoTokenEsperado := ttkOper { Próximo tem que ser uma operação }
else begin
ATipoTokenEsperado := ttkVar; { Próximo tem que ser uma das variáveis }
lExpr := CreateProxExpr (AStrExpr, APos, ATipoTokenEsperado, AExpr);

if (lExpr = Nil) then
raise Exception.CreateFmt('Fim inesperado da expressão - operador "%s"', [lToken]);
end;

Result := CriaInstancia (lToken, AExpr, lExpr);
end;
Embora não estejam retratadas, o código acima faz uso de outras funções importantes do analisador: a função ProxToken - que apenas percorre o texto a partir da posição atual e extrai dele um token - e a CriaInstancia, que é uma espécie de Factory para criação de instâncias de expressões.

O resultado da chamada à função CreateExprFrom é uma única instância de expressão que pode ser avaliada com qualquer item de estoque do tipo TWItem. Todas as outras instâncias construídas pelo analisador ficam internas a esta instância e é ela a responsável por devolver os recursos de memória quando não forem mais necessários.

O exemplo completo está disponível para download como um projeto Delphi 2005. Para obtê-lo, siga este link.

Mais Informações Design Patterns com Delphi : Interpreter - parte I e parte II, Download do projeto de exemplo em Delphi 2005

26 de agosto de 2010

Design Patterns com Delphi : Interpreter - parte II

Do ponto de vista da construção de classes e seus relacionamentos, a implementação do Design Pattern comportamental Interpreter (introduzido no post anterior) não apresenta grandes desafios.

Como podemos ver no diagrama reproduzido abaixo, há apenas heranças simples e classes acessando normalmente os membros umas das outras.
Diagrama UML para o padrão Interpreter
Para começar, precisamos definir uma classe abstrata para representar a base de todas as expressões. É nessa classe que estabelecemos a interface para a avaliação de expressões, isto é, o nome da função de avaliação, seus parâmetros e tipo de retorno. Essa definição é que torna a classe abstrata, forçando as heranças a providenciarem o funcionamento correto inerente a cada uma delas.

No nosso exemplo, a classe base é a TWExpressaoAbstrata, cuja função para avaliação das expressões chama-se Calcular, que calculará o saldo disponível do item informado no parâmetro. Segue a declaração em Delphi.
{ Expressão Abstrata }
TWExpressaoAbstrata = class
public
function Calcular (AItem: TWItem) : double;virtual;abstract;
end;

{ Expressão Não-Terminal }
TWExpressaoSoma = class(TWExpressaoAbstrata)
protected
_Expr1, _Expr2: TWExpressaoAbstrata;

public
constructor Create (AExpr1, AExpr2: TWExpressaoAbstrata);
destructor Destroy;override;

function Calcular (AItem: TWItem) : double;override;
end;

{ Expressão Terminal }
TWExpressaoSaldo = class(TWExpressaoAbstrata)
public
function Calcular (AItem: TWItem) : double;override;
end;

No trecho acima aparecem ainda as declarações de duas classes que herdam da TWExpressaoAbstrata e que são exemplos de codificação para os tipos de expressões existentes. Cada uma delas fornece uma implementação própria da função Calcular, dependendo do objetivo da classe. Uma delas é a TWExpressaoSaldo, implementando uma expressão terminal (aquela que não depende de outra expressão para poder ser avaliada). A outra classe é a TWExpressaoSoma, que implementa a expressão não-terminal relativa à operação de soma. Esse tipo de expressão depende de outras expressões para ter seu valor calculado, tornando necessária a presença das variáveis internas _Expr1 e _Expr2. Como essas variáveis internas são declaradas com o tipo base abstrato TWExpressaoAbstrata, elas podem receber qualquer tipo de expressão, tanto as terminais quanto outras não-terminais. Vejas a implementação das funções dessas classes :
{ Expressão não-terminal para a operação de Soma }
constructor TWExpressaoSoma.Create (AExpr1, AExpr2: TWExpressaoAbstrata);
begin
_Expr1 := AExpr1;
_Expr2 := AExpr2;
end;

destructor TWExpressaoSoma.Destroy;
begin
FreeAndNil (_Expr1);
FreeAndNil (_Expr2);

inherited;
end;

function TWExpressaoSoma.Calcular (AItem: TWItem) : double;
begin
Result := _Expr1.Calcular(AItem) + _Expr2.Calcular(AItem);
end;

{ Expressão terminal que apenas retorna o saldo físico atual do item }
function TWExpressaoSaldo.Calcular (AItem: TWItem) : double;
begin
Result := AItem._Saldo;
end;

Veja que a função Calcular na operação de soma exige que ambas as instâncias de expressões internas estejam presentes e sejam válidas, já que ambas têm que ser avaliadas antes de terem seus valores somados para produzir o resultado esperado.

Normalmente, seu programa terá referência apenas à expressão de nível mais alto - as demais, se houver, serão instâncias internas a esta de nível mais alto. Isso significa que se esta expressão é não-terminal, então ela é composta por outras expressões às quais a aplicação não tem acesso direto. É por isso que expressões não-terminais têm a responsabilidade de liberar a memória usada pelas expressões internas que as compõem, conforme vemos no destrutor mostrado no trecho de código acima.

A classe TWItem não possui nada de especial, podendo ser até mesmo uma estrutura simples, se for necessário. A única coisa a ressaltar é que as expressões definidas neste exemplo usam o TWItem como contexto, acarretando a obrigatoriedade de que essas expressões conheçam integralmente a definição do item. Com isso, as expressões ficam amarradas a esse contexto envolvendo itens.

No próximo post, eu mostro como codificar uma análise sintática básica a partir de uma expressão textual, criando no processo as instâncias de classes de expressão que resultarão numa única instância passível de ser avaliada com vários contextos distintos.