Desenvolvimento - Delphi
Review RemObjects Chrome
Chrome é o Object Pascal para Visual Studio. Ele pode ser instalado tanto no Visual Studio 2005 como no 2003 e permite desenvolver aplicativos .NET 1.1 e 2.0 usando uma linguagem fácil, dinâmica e extremamente poderosa.
por Erick SasseO que é Chrome?
Chrome é o Object Pascal para Visual Studio. Ele pode ser instalado tanto no Visual Studio 2005 como no 2003 e permite desenvolver aplicativos .NET 1.1 e 2.0 usando uma linguagem fácil, dinâmica e extremamente poderosa.
Ele está na versão 1.5, e já suporta nativamente todos os recursos do .NET Framework, incluindo os novos recursos da versão 2.0 como Generics, Iterators, Nullable Types, etc.
Por que o Chrome foi criado?
Segundo seus criadores, foram dois os principais motivos da criação do Chrome:
- As limitações técnicas das linguagens existentes
- Muitos desenvolvedores Delphi estavam escolhendo o Visual Studio para desenvolvimento em .NET e tinham poucas opções de linguagens (C# ou VB). Embora ambas fossem opções aceitáveis, certamente muitos desenvolvedores preferiam usar Object Pascal ao ter que aprender uma nova linguagem.
O Chrome foi criado com o objetivo de aumentar a produtividade dos desenvolvedores e garantir compatibilidade cross-plataform, além de suportar todos os recursos da CLR.
Mas em .NET a linguagem utilizada faz diferença?
Embora muitos acreditem que a linguagem não faz muita diferença, isso só é verdade se os recursos forem os mesmos de uma para outra.
No caso do Chrome, ele tem muitas facilidades que não estão presentes em C# ou em Delphi. E essas facilidades podem fazer diferença no dia-a-dia, na produtividade do desenvolvedor, e na legibilidade do código.
Estarei apresentando alguns dos recursos interessantes que Chrome nos proporciona.
Implicit Properties
Veja a classe abaixo em Delphi:
Pessoa = class
private
FIdade: Integer;
FNome: String;
public
property Nome : String read FNome write FNome;
property Idade: Integer read FIdade write FIdade;
end;
Em C# seria algo assim:
class Pessoa
{
private string nome;
public string Nome
{
get { return nome; }
set { nome = value; }
}
private int idade;
public int Idade
{
get { return idade; }
set { idade = value; }
}
}
Em Chrome, a mesma classe, tirando proveito do recurso de propriedades implícitas, poderia ser escrita assim:
Pessoa = class
public
property Nome : String;
property Idade: Integer;
end;
Ou seja, bem menos código que C# e Delphi. Segundo o pessoal do Chrome, a proporção de maneira geral é algo como o quadro abaixo:
Declaração Inline de Variáveis
Essa facilidade é mais interessante para os programadores Delphi. Quando você precisa fazer um loop, você precisa declarar a variável integer na seção var da implementação do método. Algo assim:
procedure MainForm.OnClick(Sender: Object; ea: EventArgs);
var
i: integer;
begin
for i := 0 to 1 do
MessageBox.Show(i.ToString);
end;
Em Chrome, isso fica mais fácil usando a declaração inline:
method MainForm.OnClick(Sender: Object; ea: EventArgs);
begin
for i: integer := 0 to 1 do
MessageBox.Show(i.ToString);
end;
Neste caso, além do código ficar menor e de mais fácil leitura, o escopo da variável é local ao loop. Se você tentar usar a variável fora do loop, ela não existe.
Type Inference
Para declarar e inicializar uma variável em Delphi, você precisa especificar o tipo acima do bloco e instanciar no inicio do bloco. Ficaria algo assim:
var
sb: StringBuilder;
begin
sb := StringBuilder.Create;
..
end;
Em C# você também precisa definir o tipo da variável e inicializá-la na sequência:
StringBuilder sb = new StringBuilder();
Em Chrome, usando o recurso de Type Inference, você pode declarar e instanciar a variável em qualquer lugar do código. Acima do bloco, como em Delphi, ou no meio do código, como em C#, e sem a necessidade de repetir mais de uma vez o tipo:
var sb = new StringBuilder;
Chrome consegue definir o tipo da variável automaticamente, apenas analisando o código de criação. Isso mostra o dinamismo da linguagem.
Fica ainda mais útil quando trabalhamos com generics (que ainda não é suportado pelo Delphi), que precisam de um pouco mais de código na especificação e inicialização das variáveis. Em C#, uma lista de doubles, você faria assim:
List<double> lista = new List<double>();
Em Chrome é mais simples:
var lista = new List<double>;
Alias, esta é a exata sintaxe que estará presente no C# 3.0, para suportar este recurso, inclusive utilizando o “var” antes da variável. É o C# emprestando coisas boas do Pascal.
Class Contracts
Class Contracts é um dos recursos mais interessantes do Chrome. É algo como “Design-By-Contract”, introduzido pela linguagem Eiffel e já planejado para o C# 4.0 (com o codinome Spec#), que deve demorar um pouco ainda.
Por que esperar? Você pode usar isso hoje no Chrome.
Class Contracts é algo como condições que sua classe deve confirmar para satisfazer as regras de uso da classe. É uma forma extremamente simples e poderosa de verificar seu código e o uso correto das classes.
Chrome usa dois conceitos para implementar Class Contracts: Pré e Post Conditions e Invariants.
Pre e Post Conditions
São condições que seu código deve atender antes ou ao final da execução de um método.
method Pessoa.DiasDeVida: Integer;
require
Idade > 0;
begin
result := Idade * 365;
end;
No exemplo acima, para que o método seja executado, é necessário que a propriedade Idade seja maior que zero, é um exemplo de pre conditions. Se ao entrar no método, as condições não forem satisfeitas, o aplicativo gera uma exceção (ou mais precisamente, uma asserção) para informar o uso indevido da classe. Isso é muito mais eficiente do que confiar na documentação da classe informando como ela deve ser usada ou ainda ter que ficar validando todas as entradas com ifs emitindo avisos ou exceções.
method MinhaLista.Add(aItem: Object);
begin
// ...
// lógica complexa para adicionar o item na lista
// ...
ensure
Count = old Count + 1;
Contains(aItem);
end;
No exemplo acima você pode ver o uso de post conditions, onde o método certifica-se de satisfazer as condições especificadas ao finalizar sua execução. Nesse exemplo, vemos um método para adicionar um objeto a uma lista. Ao final do método, checamos se a quantidade de itens da lista é igual a quantidade anterior mais um (Count = old Count + 1). Repare no comando old. Além disso, ele certifica-se que o item inserido agora faz parte da lista (Contains(aItem)). Sentiu o poder?
Invariants
Enquanto as pre e post conditions definem verificações de métodos, as invariants permitem definir condições para toda a classe. Por exemplo:
Pedido = public class
public
property Status: PedidoStatus;
property Itens: List<ItemPedido>;
public invariants
not ((Status = PedidoStatus.Entregue) and (Itens.Count = 0));
end;
Nesse exemplo você garante que nenhum pedido será marcado como “Entregue” se ele não contiver itens. Isso com apenas uma linha de código, nada mais.
Neste caso usamos invariants públicas, ou seja, se algum método público terminar sua execução e deixar a classe em um estado inválido, uma asserção será gerada. Chrome suporta dois níveis de invariants: públicas, que são verificadas após a execução de qualquer método público, ou privadas, que são verificadas na finalização de um método privado. Essa flexibilidade é útil, pois enquanto seus métodos internos estão sendo executados, a classe pode estar momentaneamente em um estado inválido, e isso pode ser perfeitamente correto.
Blocos Melhorados
Blocos “using”
O bloco using é algo que também não temos no Delphi, mas que tem grande utilidade. Ele permite que você crie e libere objetos .NET que implementem IDisposable. O exemplo abaixo cria um objeto FileStream e chama seu Dispose ao final do bloco. Esse tipo de objeto utiliza recursos não-gerenciados, portanto você deve liberar ao terminar de usar. No Delphi você precisaria usar um try/finally.
using f := new FileStream(...) do begin
...
f.Write(...);
...
end;
Blocos “with”
O with é odiado por muita gente no Object Pascal devido a dificultar a leitura do código, confundir durante o debug e uma série de outros motivos. O Chrome lhe oferece uma nova forma de utilizar o with. Ele permite criar blocos de código onde uma variável é introduzida e utilizada com escopo somente ao bloco with. Veja exemplo:
method MainForm.OnClick(Sender: Object; ea: EventArgs);
begin
with lFileInfo := new FileInfo("c:\temp\MyTest.txt") do begin
if lFileInfo.Exists then
lFileInfo.DoSomething
else
DoSomething;
end;
end;
Neste exemplo, o objeto lFileInfo fica disponível apenas dentro do with, e você é obrigado a informar a referência para acessar suas propriedades e métodos. Interpretando o código, vemos que se o arquivo existir, o método DoSomething do objeto lFileInfo seria executado, senão o método DoSomething do MainForm seria chamado. Observe que você não conseguiria acessar nenhuma propriedade/método de lFileInfo sem especificá-lo antes.
Blocos “locking”
Os blocos “locking” permitem que você escreva blocos de código que são executados de forma serial, ou seja, apenas uma thread por vez. O que o Chrome faz é envolver esse bloco em uma CriticalSection automaticamente. Exemplo:
locking self do begin
...
// algum código que não é thread-safe
...
end;
Case Melhorado
Outra melhoria significativa trazida pelo Chrome é o no case. Em Object Pascal, sempre que você precisar comparar uma string a vários valores, você precisaria algo assim:
function GetClassByID(aClassID: string): TMyCustomClass;
var
upperclassid: string;
begin
upperclassid := UpperCase(aClassID);
if upperclassid = "XYZ" then
result := TMyXYZClass
else if upperclassid = "ABC" then
result := TMyOtherClass
else raise Exception.Create("Invalid Class ID");
end;
O case do Chrome funciona de forma parecida com o switch do C#, então o mesmo código, em Chrome, ficaria assim:
method GetClassByID(aClassID: string): MyCustomClass;
begin
case aClassID.ToUpper of
"XYZ": result := TMyXYZClass;
"ABC": result := TMyOtherClass;
else raise new Exception("Invalid Class ID");
end;
end;
Mais simples e mais legível.
E se precisar comparar referências de classe executando alguma operação dependendo do tipo?
Em Object Pascal:
procedure PerformActionOnClass(aClass: TMyCustomClass);
begin
if aClass = TMyXYZClass then
TMyXYZClass(aClass).DoSomething
else if aClass = TMyOtherClass then
TMyOtherClass(aClass).DoSomethingElse
else raise Exception.Create("Invalid Class Reference");
end;
Em Chrome:
method PerformActionOnClass(aClass: MyCustomClass);
begin
case aClass type of
TMyXYZClass: TMyXYZClass(aClass).DoSomething;
TMyOtherClass: TMyOtherClass(aClass).DoSomethingElse;
else raise Exception.Create("Invalid Class Reference");
end;
end;
Métodos Assíncronos
Métodos assíncronos são usados para multi-threading, onde você pode executar várias operações em paralelo. Nisso o Chrome é simplesmente show de bola, facilitando absurdamente a vida do desenvolvedor.
A forma mais comum de se fazer muti-threading em linguagens .NET seria criar uma classe separada que usa uma combinação das classes Thread e ThreadStart do namespace System.Threading.
Um exemplo em C#:
public class UrlFetcher
{
private string url
public UrlFetcher (string url)
{
this.url = url;
}
public void Fetch()
{
// trata url aqui
}
}
//[... e em uma classe separada ...]
UrlFetcher fetcher = new UrlFetcher (myUrl);
new Thread (new ThreadStart (fetcher.Fetch)).Start();
Em Chrome, basta marcar o método como assíncrono e chamá-lo:
MyWinForm = public class
method Fetch(aURL: string); async;
end;
//[.. e ao chamar o método, ele automaticamente cria um a nova thread para executá-lo..]
Fetch("www.chromesville.com");
Muito mais simples, não?
Generics
Generics é uma daquelas coisas que você pensa: como ficamos sem isso até hoje? Este recurso foi introduzido na versão 2.0 do .NET Framework, e Chrome suporta todos os recursos da versão 2.0 nativamente, assim como generics. Esse recurso não é suportado pelo Delphi, pois ele ainda só suporta a versão 1.1 do Framework.
Não entrarei na explicação do que é generics, mas em Chrome você utilizada de forma praticamente idêntica a C#. Para declarar uma lista de doubles por exemplo, o código seria esse:
var lista = new List<double>;
Além de utilizar classes genéricas já existentes, naturalmente você também pode criar suas próprias classes genéricas com Chrome.
Suporte ao Mono
Desde o início, um dos compromissos da RemObjects é que o Chrome deveria suportar implementações alternativas da CLR, sendo a principal delas, o Mono. Portanto, os projetos construídos com o Chrome rodam no Mono aproveitando todos os recursos que ele oferece.
Além disso, o compilador de linha de comando do Chrome (que é free!) é um assembly gerenciado 100% compatível com Mono, de forma que você pode apenas copiá-lo para o qualquer plataforma suportada pelo Mono (Linux, MacOS, etc) e executá-lo:
Cross-Linking
Mais um recurso interessante do Chrome, o Cross-Linking permite que você gere seus assemblies utilizando diferentes versões do .NET Framework.
Se você estiver desenvolvendo para o Mono, por exemplo, basta apontar para o diretório onde se encontram os assemblies dele para poder utilizar suas classes. O mesmo acontece com o Compact Framework.
Para ter uma idéia melhor do poder do Cross-Linking, basta saber que com Chrome, você pode suar o Visual Studio 2003 e desenvolver para .NET 2.0. Legal, não?
Compromisso com Atualizações
A RemObjects mantém um compromisso de manter o Chrome sempre atualizado com as novidades que a Microsoft apresenta no .NET Framework. Já existem betas do Chrome com suporte ao Avalon, por exemplo. LINQ também já está sendo estudado pela equipe. Isso é importante para dar segurança e garantia para quem investir no Chrome.
Versões Express
As versões Express do Visual Studio não são extensíveis, portanto você não pode usar o Chrome nelas. Isso é uma limitação que a Microsoft impõe sobre as edições Express, que não permite que nenhum “addin” de terceiros seja instalado nelas. Não é uma limitação do Chrome.
O que precisa melhorar
Notei que o Intellisense do Chrome não é tão perfeito como o do C#.
Senti falta de duas coisas usando Chrome: formatação de código, e suporte ao designer e debugger para Compact Framework.
O Chrome ainda não suporta o designer nem o debugger da Compact Framework, ou seja, você pode desenvolver assemblies para CF sem problemas usando o Cross-Linking, mas não conseguirá usar o designer visual de forms nem o debugger. A justificativa da RemObjects para isso é que o designer da CF não suporta outras linguagens, ou seja, ele já foi criado de forma meio engessada pela própria Microsoft. E assim que a Microsoft permitir plugar novas linguagens no designer CF, Chrome poderá ser usado imediatamente.
Por enquanto, se você pretende usar Chrome em aplicativos CF, o ideal é fazer as classes em Chrome e colocá-las em um Class Library que é usada pela parte de UI em C#. Isso é bem tranqüilo.
A formatação de código automática que temos no C#, não temos no Chrome, assim como não temos no Delphi sem o uso de plug-ins. Porém, quem está acostumado a isso, como eu, sente muita falta.
Conclusão
Minha conclusão, como desenvolvedor Delphi há muitos anos, é que com o Chrome me senti em casa. É muito mais natural para qualquer desenvolvedor Delphi usar Chrome do que C# ou VB. É muito bom também ver nossa linguagem preferida ter tantos recursos fantásticos, que ainda sonhamos em ver no Delphi.
Chrome não deixa nada a desejar em termos de linguagem para C#, que é considerada a linguagem “oficial” do .NET. Pelo contrário, ele já apresenta recursos que devem aparecer no C# somente na versão 3 ou 4.
Se você é um desenvolvedor Delphi e pretende programar em .NET puro, ou seja, sem usar VCL.NET por exemplo, só vejo vantagens em usar Chrome. Ao contrário do Delphi, ele já suporta .NET 2.0 e todos seus novos recursos.
Com os recursos do Chrome, em minha opinião, o código fica mais legível e mais enxuto. Além dos recursos de Class Contracts, que se usados com inteligência, certamente refletirão em maior qualidade do código e por conseqüência, dos aplicativos desenvolvidos.
Chrome é uma linguagem de primeiro escalão, que deve certamente ser avaliada por desenvolvedores Delphi que desejam usar Visual Studio, além daqueles que querem usar Object Pascal e ter acesso aos últimos recursos do .NET Framework.
Referências
Chrome: http://www.chromesville.com/
RemObjects: http://www.remobjects.com/
Eiffel Design By Contract - http://archive.eiffel.com/doc/manuals/technology/contract/
Spec#: http://research.microsoft.com/specsharp/
Screenshots