sexta-feira, 23 de outubro de 2009

Modelo anêmico realmente é ruim?

Eu sempre costumo dizer para o pessoal que se você não enxerga o problema não tem por que aplicar uma solução.

O mesmo vale para o modelo anêmico, se você não consegue enxergar os problemas dessa modelagem com certeza você não vê valor nenhum nas outras formas de modelar o domínio.

Com certeza você vai dizer que conhece aplicações desenvolvidas com modelo anêmico e que funcionam muito bem, e com certeza isso é verdade, da mesma forma que conheço aplicações procedurais que funcionam muito bem. Então o que nos motiva a não ter modelos anêmicos?

Quando você decide pelo modelo anêmico você tem que estar ciente que você esta abrindo mão de vários benefícios que o OO pode lhe proporcionar. Fowler diz o seguinte:

“The fundamental horror of this anti-pattern is that it's so contrary to the basic idea of object-oriented design; which is to combine data and process together. The anemic domain model is really just a procedural style design, exactly the kind of thing that object bigots like me (and Eric) have been fighting since our early days in Smalltalk. What's worse, many people think that anemic objects are real objects, and thus completely miss the point of what object-oriented design is all about.”

http://martinfowler.com/bliki/AnemicDomainModel.html

Ele reforça a idéia de que o modelo anêmico não é OO, podemos ver essa afirmação na prática trazendo o clássico exemplo do carro para o mundo do modelo anêmico. Quando você modela um carro geralmente você faz algo assim:


public class Carro
{
private int _posicaoAtual;

public void Andar()
{
this._posicaoAtual += 10;
}
}

Agora no mundo anêmico o carro ficaria assim:

public class Carro
{
private int _posicaoAtual;

public int PosicaoAtual
{
get{ return this._posicaoAtual;}
set{ this._posicaoAtual = value;}
}
}

public class BLCarro
{
publicvoid Andar(Carro carro)
{
carro.PosicaoAtual += 10;
}
}


Perceba que agora criamos um “andador de carro” isso faz algum sentido? Creio que não.
Mas se o modelo anêmico é um anti-padrão, por que temos o DTO (Data Transfer Object Pattern)?

Modelo anêmico não tem nada haver com DTO, eu falo que DTO é um mal necessário, ele foi criado para realizar transporte de dados, não para transporte entre as camadas lógicas como se tem feito com os modelos anêmicos.

Um fator que motiva a utilização do modelo anêmico é o acesso a dados, mas acredite esse problema tem solução, você pode utilizar o Repository ou Active Record.

Active Record with Castle
http://www.castleproject.org/activerecord/index.html

Using Repository and Unit of Work patterns with Entity Framework 4.0

http://blogs.msdn.com/adonet/archive/2009/06/16/using-repository-and-unit-of-work-patterns-with-entity-framework-4-0.aspx

NHibernate and the Unit of Work Pattern
http://blogs.hibernatingrhinos.com/nhibernate/archive/2008/04/10/nhibernate-and-the-unit-of-work-pattern.aspx

Outro fator que confunde um pouco a cabeça do pessoal é que se tem um caminho fixo para se chegar ao banco de dados com o modelo anêmico sendo UI > BL > DL utilizando o modelo o anêmico para trafegar os dados entre a camada, com uma entidade como fica esse caminho?

Pra não fugir do costume vou soltar a frase “cada caso é um caso” você pode por exemplo utilizar um façade para ter acesso ao domínio. Ex.: UI > Façade > Domínio.

sexta-feira, 9 de outubro de 2009

Personalizando o toString em enums

Os enumerados são, sem dúvida, grandes ferramentas para auxiliar e padronizar o desenvolvimento de aplicativos. Eu, por exemplo, o considero excelente para sumir com os famosos números mágicos (magic numbers) dentro das entidades, ex:


//Antes
productionOrder.State = 3;
//Depois
productionOrder.State = ProductionOrderState.Canceled;

Porém, uma desvantagem do enum é quando você quer exibir o texto do valor contido no enum. Ex.:
ProductionOrderState productionOrderState = ProductionOrderState.Canceled;
Console.Writeline(productionOrderState);
//Print: Canceled


Como fazer para imprimir "Production Order is Canceled" no lugar de Canceled?
Abaixo vai uma dica bem simples de como fazer isso. Primeiro copie o código abaixo:
public struct Described<T> where T : struct
{

private T _value;

public Described(T value)
{
_value = value;
}

public override string ToString()
{
string text = _value.ToString();
object[] attr = typeof(T).GetField(text).GetCustomAttributes(typeof(DescriptionAttribute), false);

if (attr.Length == 1)
text = ((DescriptionAttribute)attr[0]).Description;

return text;
}

public static implicit operator Described<T>(T value)
{
return new Described<T>(value);
}

public static implicit operator T(Described<T> value)
{
return value._value;
}
}
Fonte: Post StackOverFlow (http://stackoverflow.com/questions/796607/how-do-i-override-tostring-in-c-enums)
Agora no seu enum implemente o atributo Description, do namespace System.ComponentModel em cada item do enum:
public enum ProductionOrderState
{
[Description("Production Order is Open")]
Open,
[Description("Production Order is Canceled")]
Canceled
}

Para obter a descrição do valor do enum faça o seguinte:
Described<ProductionOrderState> status = ProductionOrderState.Open;
Console.WriteLine(status);
//Print: Production Order is Open

Veja que a classe Described não influencia em nada o funcionamento do enum, veja o teste abaixo:

Described<ProductionOrderState> status = ProductionOrderState.Open;
Console.WriteLine(status);
ProductionOrderState other = status;
Console.WriteLine(other);

Mas ainda não é o suficiente certo? A descrição esta hard code como vamos fazer quando tiver que alterar a descrição? E para multi idioma como ficaria?

Para resolver esse problema crie um Resource no seu projeto e coloque nele as descrições que precisa para o seu enum.

resource

Para obter o valor do Resource nos items do enum podemos utilizar uma classe disponível no Enterprise Library (http://msdn.microsoft.com/en-us/library/cc467894.aspx), faça referência no seu projeto a dll Microsoft.Practices.EnterpriseLibrary.Configuration.Design, dentro dela você contará com o atributo SRDescription.

Troque no seu enum o attribute Description pelo SRDescription informando o nome do Resource que você criou e o nome da chave do resource que você criou, ex:
public enum ProductionOrderState
{
[SRDescription("Sts_Open", typeof(Resources))]
Open,
[SRDescription("Sts_Canceled", typeof(Resources))]
Canceled
}

Prontinho, agora temos disponível um meio prático de obter e personalizar as descrições dos nossos enums.

DDD: Como validar entidades de negócio

Eu respondi uma pergunta sobre como é realizada as validações de entidade de negócio no DDD.
No mesmo post eu coloquei um exemplo obtido no livro do Tim McCarthy.

No exemplo abaixo é gerado uma exceção caso a entidade não esteja válida.


public sealed class Address
{
private string street;
private string city;
private string state;
private string postalCode;

public Address(string street, string city, string state, string postalCode)
: this(street, city, state, postalCode, true)
{
}

private Address(string street, string city, string state, string postalCode,
bool validate)
{
this.street = street;
this.city = city;
this.state = state;
this.postalCode = postalCode;
if (validate)
{
this.Validate();
}
}

private void Validate()
{
if (string.IsNullOrEmpty(this.street)
string.IsNullOrEmpty(this.city)
string.IsNullOrEmpty(this.state)
string.IsNullOrEmpty(this.postalCode))
{
throw new InvalidOperationException("Invalid address.");
}
}
}

Já no exemplo abaixo é verificado se a lista de regras quebradas esta preenchida, caso ela esteja vazia a entidade esta ok.
public class Company : EntityBase
{
private string name;
private string abbreviation;
private Address headquartersAddress;
private List<Address> addresses;
private string phoneNumber;
private string faxNumber;
private string url;
private string remarks;

public Company()
: this(null)
{
}

public Company(object key)
: base(key)
{
this.name = string.Empty;
this.abbreviation = string.Empty;
this.headquartersAddress = null;
this.addresses = new List<Address>();
this.phoneNumber = string.Empty;
this.faxNumber = string.Empty;
this.url = string.Empty;
this.remarks = string.Empty;
}

public string Name
{
get { return this.name; }
set { this.name = value; }
}

protected override void Validate()
{
// The Company must have a name
if (!string.IsNullOrEmpty(this.name))
{
this.BrokenRules.Add(new BrokenRule("", ""));
}
}
public ReadOnlyCollection<BrokenRule> GetBrokenRules()
{
this.Validate();
return this.brokenRules.AsReadOnly
}
}

Mas os exemplos acima demonstram uma forma mais "genérica" de como realizar essa validação. Por que genérica? Por que podemos ter vários tipos de validações para uma mesma entidade dentro de processos específicos.

Imagine por exemplo que para cadastrar um cliente você não precise do CPF, mas para finalizar um processo de compra essa informação passe a ser necessário, se você chamar o método "Validate" da classe cliente com o CPF em branco ele vai dizer que esta válido, mas para o processo de compra isso não é verdade.

As validações colocadas na própria entidade de negócio, geralmente são utilizadas nos processos de CRUD para garantir que todos os campos obrigatórios estejam preenchidos e de forma adequada. Mas cada processo de negócio necessita de informações específicas de cada entidade, por tanto, ele deve se preocupar em garantir essas informações.

Na prática o que eu quero dizer com isso? Que se o processo precisa do campo CPF do cliente, o processo (ou um serviço referente ao processo) é responsável em verificar se o CPF esta preenchido e não o cliente.

Conclusão, nossos métodos referentes a processos como o de compra, por exemplo, devem fazer mais do que simplesmente persistência de banco de dados (Insert e Update) mas também
garantir com que as entidades estejam com estado adequado.

quinta-feira, 10 de setembro de 2009

DDD: Enviar e-mail, onde vai esse negócio?

Já me deparei com essa pergunta em vários lugares e por isso mereceu um post no meu blog.

A resposta a essa pergunta é: Por que você precisa enviar e-mail?
Eu sei que é muito feio responder uma pergunta com outra pergunta mas nesse caso isso se faz necessário por que ela tem relação direta na solução do problema.

Geralmente eu sugiru como solução a criação de um serviço, mas eu percebo que as pessoas ainda não sabem direito como seria esse serviço e isso acontece por que a primeira coisa que vem a mente são os serviços de domínio;

No capítulo sobre “Services” do livro Domain-Driven Design: Tackling Complexity in the Heart of Software do Evans ele define os serviços da seguinte forma:

“This pattern is focused on those SERVICES that have an important meaning in the domain in their own right, but of course SERVICES are not used only in the domain layer. It takes care to distinguish SERVICES that belong to the domain layer from those of other layers, and to factor responsibilities to keep that distinction sharp.”

Ele deixa claro que podemos ter serviços em outras camadas fora a camada do domínio, mas ele ressalta a importância de se distinguir exatamente a atividade necessária para decidir o melhor local para o serviço.

Por exemplo, se para o seu envio de e-mail tiver envolvendo regras de negócio você provavelmente terá um serviço de negócio interagindo com um serviço de infraestrutura.

No livro Evans cita como exemplo o processo de transferêcia bancária. Após a camada de aplicação solicitar para a camada de domínio a transferência e obter confirmação que o processo foi realizado com sucesso, ela invoca um serviço da camada de infraestrutura para notificar os usuários envolvidos.

Resumindo, enviar e-mail é uma atividade de infraestrutura mas o motivo, ou negócio, influência diretamente na solução adotada.

quarta-feira, 9 de setembro de 2009

Boas práticas também serve para camada de interface sabia?

Quando falamos em padrões e boas práticas é muito comum pensarmos nas camadas de persistência, negócio, infraestrutura etc. Mas será que aplicamos esses mesmos princípios na camada de interface?
Alguns exemplos simples de como deixamos essas práticas de lado quando estamos trabalhando na camada de interface:


1) Preciso popular um ComboBox com uma lista de cidades. O procedimento mais comum nesse caso é o de "arrastarmos" o controle na tela, chamamos o método apropriado para trazer as cidades e preenchemos o controle com as cidades. Só que se tivermos 10 telas que precisamos exibir um ComboBox com cidades nós iremos repetir essa operação 10 vezes. Não teremos duplicidade de código? E se a forma de popular o ComboBox com as cidade por qualquer motivo tiver que ser alterada? Quanto tempo vamos ter que desprender para fazer essa alteração?

2) Muitas vezes precisamos gerar uma quantidade de código muito grande para tratar um determinado controle (TextBox, ComboBox, Button, etc...) e todo esse código fica misturado com o código de processo da tela (Cadastrar Cliente, Cadastrar Usuário, etc...). Se isolássimos o controle como um novo controle, será que não seria mais fácil a manutenção?

Com os recursos de interface rica, estamos produzindo cada vez mais código nas interfaces para proporcionar uma melhor experiência do usuário com o nosso sistema, e com isso também temos uma maior dificuldade em gerenciar esse código todo, por tanto, é cada vez maior a necessidade de se aplicar todos esses princípios e boas práticas na camada de interface.

Você já parou para analisar quanto tempo você desprende só para construir uma interface? Você já parou para analisar o quanto das alterações solicitadas pelo usuário estão diretamente ligadas a recursos de interface?

Utilizar MVC e criar controles personalizados são excelentes ferramentas para nos ajudar a gerenciar essa complexa estrutura que é a camada de interface, pense nisso quando estiver construindo suas interfaces.

quarta-feira, 17 de junho de 2009

MVC ou MVP?

Eu conheço uma galera interessada em padrões de projeto mas como acontece com todo mundo (inclusive comigo) algumas dúvidas e confuções acabam surgindo.

Foi me passado o seguinte trecho do livro Patterns of Enterprise Application Architecture do Fowler. (Se você não leu, deveria!)

“The view is only about display of information; any changes to the information are handled by the third member of the MVC trinity: the controller. The controller takes user input, manipulates the model, and causes the view to update appropriately”

Combinado com a definição dada no livro o seguinte exemplo de MVC foi passado:

http://www.codeproject.com/KB/tips/ModelViewController.aspx?fid=1194034&select=2878520#xx2878520xx

Bom, qual o problema?
O problema é que o exemplo passado no link não é MVC, pelo menos não na versão purista, mas sim MVP.

Mas como eu sei a diferença?

Analise comigo o trecho do código no exemplo da classe CalcController:

public void OnClick( int number )
{
    view.Total = model.SetInput(number).ToString();
}

No exemplo, a controller além de atualizar a Model ainda informa a View da alteração que a Model sofreu.

Apesar do trecho citado do livro do Fowler não ser incisivo em relação a interação entre a Model e a View, no MVC a Model notifica a View através de eventos quando seu estado é alterado.

O exemplo na verdade utiliza uma versão adaptada do MVC conhecida como MVP (Model View Presenter), cuja principal diferença é a controller alterando a Model e notificando a View das alterações feitas.

Veja o exemplo apresentado no site da SUN sobre MVC:
http://java.sun.com/developer/technicalArticles/javase/mvc/

Mas existe algum problema com isso?
Tecnicamente não, os dois funcionam muito bem, o problema esta na fato de estar usando um padrão achando que esta usando outro.

Eu particularmente prefiro utilizar o MVP, principalmente quando se pensa em DDD eu procuro deixar somente código referente a regras de negócio no domínio.

Fica esse alerta pra quem esta começando a utilizar esses padrões, principalmente por que outros estão surgindo como MVVM por exemplo, já pensou se a gente não saber a diferença entre eles?

sexta-feira, 5 de junho de 2009

Definições – Assunto importante

 

Olá pessoal,
Já faz algum tempo que fico sem postar no meu blog, minha volta para o Coral Kemuel tem tomado bastante tempo :)

Se você estuda ou estudou em qualquer lugar com certeza você já usufruiu da maravilha que é pesquisar assuntos no Google.

Um professor amigo meu chamado Renato (vulgo “Presuntinho”) me dizia que a internet é uma grande fonte de informação mas não de conhecimento, pode ter certeza, isso é verdade.
Quando você resolve aproveitar conteúdo de internet para seus trabalhos, acadêmicos ou não, você deve tomar muito cuidado.

Uma situação muito comum para consultar o “pai” Google é para se pegar definições, pois bem, eu me deparei com algumas que particularmente considero bizarras. Não vou colocar QUEM escreveu, isso não é o importante, o importante é o QUE.

“O flex é um jquery com recursos de transferencia assycornima de dados, suporte a chat sem a necessidade de um código fonte pesado, inumeros controles não disponiveis em ajax, de código aberto e compativel com asp.net (e java, e php, e ruby).”

Pois é, no texto acima o cara tentou dar uma definição do adobe flex. Abaixo segue a definição dada pela própria adobe:

“O Flex é uma estrutura de código aberto altamente produtiva e gratuita para a criação e manutenção de aplicativos Web expressivos implantados de maneira consistente nos principais navegadores, desktops e sistemas operacionais.”
Fonte: http://www.adobe.com/br/products/flex/?promoid=BOZRZ

Agora vamos ver uma definição que foi dada para OOP:

“O OOP é uma técnica de programação de forma estruturada com a finalidade de reaproveitar o código existente e implementar novas classes quando necessário.”

Usar o termo “programação estruturada” na definição de programação orientada a objetos na minha opinião é um equívoco, veja o que eu peguei do wikipéia:

“(A programação estruturada) Foi a forma dominante na criação de software entre a programação linear e a programação orientada por objetos.” Fonte: http://pt.wikipedia.org/wiki/Programa%C3%A7%C3%A3o_estruturada

Agora imagina o “campeão” apresentando um trabalho na faculdade na frente “do professor” dando definição de OOP e no meio na frase ele solta “programação estruturada”. O que será que ele vai dizer?

Minha recomendação para evitar esses problemas? Leia, leia muito. Leia livros cite referências, autores …

Comunidade .Net precisamos evoluir nesse sentido leiam as discussões no GUJ sobre DDD e veja como eles se preocupam com definições.

segunda-feira, 11 de maio de 2009

DDD: Repositório Genéricos

 

A utilização de repositórios genéricos em sistemas que aplicam Domain Driven Design é uma prática que vem sendo bastante utilizada.

As vantagens e desvantagens dessa prática foram levantadas pelo Greg Young com um post em seu blog.

O que realmente gostaria de descatar do blog é sobre a relação entre o repositório e o DDD. Greg diz o seguinte.

“Looking back to [DDD, Evans] one will see that it is to represent a series of objects as if they were a collection in memory so that the domain can be freed of persistence concerns. In other words the goal is to put collection semantics on the objects in persistence.”

O repositório deve representar os dados como se estivessem em memória sem se preocupar com um armazenador de dados específico.

Quando utilizamos o repositório como um contrato entre todos os armazenadores de dados, garantimos que, independente da forma que o mesmo se utilizará para realizar a ação, os dados serão manipulados da forma esperada.

O trecho de código exemplifica exatamente a diferença entre uma ação genérica e uma específica:

Repository<T>.FindAllMatching(QueryObject o);
CustomerRepository.FindCustomerByFirstName(string);

Quanto mais genérico for uma “ação” maiores serão suas dificuldades em manter o “contrato” entre o seu domínio e armazenadores de dados.

Mas mesmo assim, Greg Young demostra como ter um bom reaproveitamento de código através de composição utilizando repositório genéricos conforme trecho abaixo:

Public class CustomerRepository {
    private Repository<Customer> internalGenericRepository;
    Public IEnumerable<Customer> GetCustomersWithFirstNameOf(string _Name) {
         internalGenericRepository.FetchByQueryObject(new CustomerFirstNameOfQuery(_Name)); //could be hql or whatever
     }
}

Este post é apenas um resumo do post do Greg das coisas que me chamaram mais a atenção, recomendo que você leia o post original e principalmente os comentários do pessoal.

segunda-feira, 4 de maio de 2009

VO (Value Object) vs DTO (Data Transfer Object)

Eu percebo que várias pessoas têm confundido o Value Objects com Data Transfer Objects, pra muita gente tudo isso quer dizer uma coisa, mas na prática não são, e isso é independente se você esta utilizando DDD.


Eu não quero ousar ficar dando definições para esses caras, por tanto, vou basear as minhas explicações nas definições do Martin Fowler:


Value Object
“A small simple object, like money or a date range, whose equality isn't based on identity.”


Um exemplo muito comum de Value Object que utilizamos no nosso dia-a-dia é a classe DateTime, perceba que a classe por si só não nos diz nada, na verdade as propriedades que a constituem é que são realmente importantes (dia, mês, ano, etc...).


Um exemplo muito comum em que se utiliza o Value Object é no caso de endereço, imagine aplicar as informações de endereço para Cliente e Fornecedor:


public class Cliente
{
        private string logradouro;
        private string bairro;
        private string cidade;
        private string estado;


        public string Logradouro
        {
                get{ return logradouro;}
                set{ logradouro = value;}
        }


        public string Bairro
        {
                get{ return bairro;}
                set{ bairro = value;}
        }


        public string Cidade
        {
                get{ return cidade;}
                set{ cidade = value;}
        }


        public string Estado
        {
                get{ return estado;}
                set{ estado = value;}
        }
}


public class Fornecedor
{
        private string logradouro;
        private string bairro;
        private string cidade;
        private string estado;

        public string Logradouro
        {
                get{ return logradouro;}
                set{ logradouro = value;}
        }

        public string Bairro
        {
                get{ return bairro;}
                set{ bairro = value;}
        }

        public string Cidade
        {
                get{ return cidade;}
                set{ cidade = value;}
        }

        public string Estado
        {
                get{ return estado;}
                set{ estado = value;}
        }
}


Agora veja uma outra forme de disponibilizar as informações de endereço para as classes:

public class Cliente
{
        private Endereco endereco;
        public Endereco Endereco
        {
                get{ return endereco;}
                set{ endereco = value;}
        }
}


public class Fornecedor
{
        private Endereco endereco;


        public Endereco Endereco
        {
                get{ return endereco;}
                set{ endereco = value;}
        }
}


public struct Endereco
{
        private string logradouro;
        private string bairro;
        private string cidade;
        private string estado;


        public string Logradouro
        {
                get{ return logradouro;}
                set{ logradouro = value;}
        }


        public string Bairro
        {
                get{ return bairro;}
                set{ bairro = value;}
        }


        public string Cidade
        {
                get{ return cidade;}
                set{ cidade = value;}
        }


        public string Estado
        {
                get{ return estado;}
                set{ estado = value;}
        }
}


Pronto, agora temos um Value Object de negócio aplicado em nosso sistema, mas existem mais detalhes que precisamos nos preocupar quando utilizar um Value Object.
Um Value Object tem características importantes que o diferenciam:


1) Construção:
No .Net geralmente o Value Object é criado como um struct, mas tome cuidado quando você estiver pensando em DDD, nem toda classe struct é um Value Object.

2) Determinar igualdade:
No mundo real quando lidamos com uma classe cliente para saber se um cliente é igual a outro podemos simplesmente comparar a o campo referente a chave primário do nosso banco de dados (ex.: cliente1.Id == cliente2.Id), isso já seria o suficiente para determinar se uma entidade é igual, mas na comparação entre value object é necessário comparar todas as propriedades que o compõe, no caso do DateTime, por exemplo, para saber se uma pessoa nasceu no mesmo dia de outra é necessário comparar o Dia, mês e ano dos objetos.

3) Contexto:
Apesar de ter usado a classe Endereço como um exemplo de Value Object de negócio, o contexto é um fator diferencial para determinar se um object deve ser tratado como um Value Object ou não, no caso dos Correios, por exemplo, cada endereço é tratado de forma única existe um identificador único para ele, pois é necessário calcular taxas como o sedex, nesse cenário o Endereço não será um Value Object e sim Entity.


Data Transfer Object
“An object that carries data between processes in order to reduce the number of method calls.”


Redução de chamadas é palavra chave na definição do DTO.
Na empresa em que trabalho nós utilizamos .Net com Adobe Flex, imagine um exemplo de uma classe simples de pedido:


namespace Domain.Entities
{
    public enum TipoEntrega
    {
        CorreioSedex,
        CorreioNormal,
        Transportadora
    }


    public class Pedido
    {
        private List<IItemPedido> items;
        private Cliente cliente;
        private Nullable<TipoEntrega> tipoEntrega;
        private Endereco destinoEntrega;
        private bool freteCalculado;
        private decimal frete;
        private decimal desconto;
        private decimal total;


        public Pedido()
            : this(null)
        {
        }


        public Pedido(Nullable<decimal> frete)
        {
            items = new List<IItemPedido>();
            if (frete != null)
            {
                this.frete = (decimal)frete;
                this.freteCalculado = true;
            }
        }


        public int AdicionarProduto(Produto produto)
        {
            return this.AdicionarProduto(produto, 1);
        }


        public int AdicionarProduto(Produto produto, int quantidade)
        {
            if (produto == null)
                throw new ArgumentNullException("produto");
            if (quantidade < 1)
                throw new ArgumentException("Não pode ser adicionar um produto cuja quantidade seja inferior a 1");
            if (items.Exists(delegate(IItemPedido item) { return item.Nome.Equals(produto.Nome); }))
                throw new InvalidOperationException("O produto já se encontra no pedido");
            IItemPedido novoItem = new ItemPedido(produto, quantidade);
            items.Add(novoItem);
            //retorna o indice do produto adicionado
            return items.Count - 1;
        }


        public ReadOnlyCollection<IItemPedido> Items
        {
            get { return items.AsReadOnly(); }
        }


        public Cliente Cliente
        {
            get { return cliente; }
            set { cliente = value; }
        }


        public Nullable<TipoEntrega> TipoEntrega
        {
            get { return tipoEntrega; }
            set { tipoEntrega = value; }
        }


        public decimal Frete
        {
            get
            {
                if (!freteCalculado)
                {
                    CalculadorFreteFactory factory = CalculadorFreteFactory.GetInstance();
                    CalculadorFrete calculador = factory.ObterCalculador(this.tipoEntrega);
                    frete = calculador.CacularFrete(destinoEntrega.Cidade);
                    freteCalculado = true;
                }
                return frete;
            }
        }


        public Endereco DestinoEntrega
        {
            get { return destinoEntrega; }
            set { destinoEntrega = value; }
        }


        public decimal Desconto
        {
            get { return desconto; }
            set { desconto = value; }
        }


        public decimal Total
        {
            get
            {
                if (total == 0)
                {
                    foreach (IItemPedido item in items)
                    {
                        total += item.ValorParcial;
                    }
                    total += Frete - desconto;
                }
                return total;
            }
        }


        private class ItemPedido : IItemPedido
        {
            private Produto produto;
            private int quantidade;
            public ItemPedido(Produto produto)
                : this(produto, default(int))
            { }


            public ItemPedido(Produto produto, int quantidade)
            {
                this.produto = produto;
                this.quantidade = quantidade;
            }


            public int Quantidade
            {
                get { return quantidade; }
                set { quantidade = value; }
            }


            public string Nome
            {
                get { return produto.Nome; }
            }


            public decimal ValorUnitario
            {
                get { return produto.ValorUnitario; }
            }


            public decimal ValorParcial
            {
                get { return produto.ValorUnitario * quantidade; }
            }
        }
    }


    public class CalculadorFreteFactory
    {
        private static CalculadorFreteFactory uniqueInstance = new CalculadorFreteFactory();


        private CalculadorFreteFactory()
        { }


        public static CalculadorFreteFactory GetInstance()
        {
            return uniqueInstance;
        }


        public CalculadorFrete ObterCalculador(Nullable<TipoEntrega> tipoEntrega)
        {
            if (tipoEntrega == null)
                throw new ArgumentNullException("tipoEntrega");
            CalculadorFrete retorno;
            if (tipoEntrega == TipoEntrega.CorreioNormal)
                retorno = new CalculadorCorreioNormal();
            else if (tipoEntrega == TipoEntrega.CorreioSedex)
                retorno = new CalculadorFreteSedex();
            else
                retorno = new CalculadorFreteTransportadora();
            return retorno;
        }
    }


    public interface IItemPedido
    {
        string Nome { get;}
        decimal ValorUnitario { get; }
        decimal ValorParcial { get; }
        int Quantidade { get; set; }
    }


    public class Produto
    {
        private string nome;
        private decimal valorUnitario;


        public string Nome
        {
            get { return nome; }
            set { nome = value; }
        }


        public decimal ValorUnitario
        {
            get { return valorUnitario; }
            set { valorUnitario = value; }
        }
    }


    public class Cliente
    {
        private int id;
        private InformacoesPessoais informacoesPessoais;
        private Endereco endereco;


        public InformacoesPessoais InformacoesPessoais
        {
            get { return informacoesPessoais; }
            set { informacoesPessoais = value; }
        }


        public int Id
        {
            get { return id; }
            set { id = value; }
        }


        public Endereco Endereco
        {
            get { return endereco; }
            set { endereco = value; }
        }
    }


    public struct InformacoesPessoais
    {
        private string nome;
        private string sobreNome;
        private DateTime dataNascimento;


        public InformacoesPessoais(string nome)
            : this(nome, String.Empty, default(DateTime))
        { }


        public InformacoesPessoais(string nome, string sobreNome)
            : this(nome, sobreNome, default(DateTime))
        { }


        public InformacoesPessoais(string nome, string sobreNome, DateTime dataNascimento)
        {
            this.nome = nome;
            this.sobreNome = sobreNome;
            this.dataNascimento = dataNascimento;
        }


        public string Nome
        {
            get { return nome; }
            set { nome = value; }
        }


        public string SobreNome
        {
            get { return sobreNome; }
            set { sobreNome = value; }
        }


        public string NomeCompleto
        {
            get { return nome + " " + sobreNome; }
        }


        public DateTime DataNascimento
        {
            get { return dataNascimento; }
            set { dataNascimento = value; }
        }


        public int Idade
        {
            get
            {
                TimeSpan diff = DateTime.Now - dataNascimento;
                return diff.Days / 364;
            }
        }
    }


    public struct Endereco
    {
        private string logradouro;
        private string numero;
        private string bairro;
        private string cidade;
        private string estado;
        private string cep;


        public string Logradouro
        {
            get { return logradouro; }
            set { logradouro = value; }
        }


        public string Numero
        {
            get { return numero; }
            set { numero = value; }
        }


        public string Bairro
        {
            get { return bairro; }
            set { bairro = value; }
        }


        public string Cidade
        {
            get { return cidade; }
            set { cidade = value; }
        }


        public string Estado
        {
            get { return estado; }
            set { estado = value; }
        }


        public string Cep
        {
            get { return cep; }
            set { cep = value; }
        }
    }
}


namespace Domain.Services
{
    public abstract class CalculadorFrete
    {
        public abstract decimal CacularFrete(string cidadeDestino);
    }


    public class CalculadorFreteSedex : CalculadorFrete
    {
        public override decimal CacularFrete(string cidadeDestino)
        {
            if (cidadeDestino.Equals("Sorocaba"))
                return 15;
            else
                return 25;
        }
    }


    public class CalculadorCorreioNormal : CalculadorFrete
    {
        public override decimal CacularFrete(string cidadeDestino)
        {
            if (cidadeDestino.Equals("Sorocaba"))
                return 10;
            else
                return 25;
        }
    }


    public class CalculadorFreteTransportadora : CalculadorFrete
    {
        public override decimal CacularFrete(string cidadeDestino)
        {
            if (cidadeDestino.Equals("Sorocaba"))
                return 30;
            else
                return 35;
        }
    }
}


No momento de realizar a comunicação com o Adobe Flex teremos vários problemas:

  • Não podemos replicar as regras de negócio que estão contidas nelas para o Adobe Flex;
  • Existem algumas características nas classes que são complicadas de serem implementadas no Flex (Ex.: classes tipos enum e aplicação de Lazy Loading);

Nesse caso uma solução simples para realizar a comunicação seria criar uma classe DTO somente para realizar a comunicação entre o .Net e Adobe Flex:


namespace ComunicacaoAdobeFlex.ValueObjects
{
public class Pedido
{
private Cliente cliente;
private decimal frete;
private decimal desconto;
private decimal total;


public Cliente Cliente
{
get { return cliente; }
set { cliente = value; }
}


public decimal Frete
{
get { return frete; }
set { frete = value; }
}


public decimal Desconto
{
get { return desconto; }
set { desconto = value; }
}


public decimal Total
{
get { return total; }
set { total = value; }
}
}


public class Cliente
{
private string nome;
private string sobreNome;
private DateTime dataNascimento;
private int idade;


public string Nome
{
get { return nome; }
set { nome = value; }
}


public string SobreNome
{
get { return sobreNome; }
set { sobreNome = value; }
}


public DateTime DataNascimento
{
get { return dataNascimento; }
set { dataNascimento = value; }
}


public int Idade
{
get { return idade; }
set { idade = value; }
}
}
}


Perceba que no exemplo acima, além de criar uma classe mais simples, ela contém somente as informações que o Adobe Flex irá utilizar (isso foi de propósito).


Com certeza existem outras características que podem ser elencadas para diferenciar Value Object e Data Transfer Object, mas espero que as características levantadas possam iluminar o seu caminho.

terça-feira, 28 de abril de 2009

Navegação de pastas em FTP

Você por acaso já se deparou com o seguinte problema?
Você tem que se conectar em uma determinada pasta de FTP (ex.: ftp://myfp.com.br/myfolder/test) só que quando você se conecta no servidor de FTP a pasta myfolder não esta lá.

Pois bem, um dos motivos desse problema é por que a pasta default do usuário utilizado na conexão não é a pasta raiz do servidor (ex.: ftp://myftp.com.br/users/myuser).

Agora, como fazer para acessar a pasta raiz (ex.: ftp://myftp.com.br) e depois acessar a pasta desejada (ex.: ftp://myftp.com.br/myfolder/test)?

O truque é simples, basta colocar os caracteres %2F/ na quantidade necessário até localizar a raiz e depois colocar o endereço da pasta desejada

(ex.: ftp://myftp.com.br/%2F/%2F/myfolder/test)