O que é CDN ?

CDN (Content Delivery Network) são links disponibilizados, normalmente por grandes empresas de tecnologia, com arquivos de bibliotecas comuns de javascript ou css, Isso é extremamente útil para evitar desperdício de banda com download de arquivos comuns como Jquery, NodeJS entre outros. Empresas como Microsoft e Google possuem diversos Links disponibilizados com varias versões do JQuery.

As maiores vantagens em se utilizar CDN ao invés de colocar esses arquivos diretamente no projeto são:

  • Se o usuário acessou qualquer site que utilize o mesmo CDN que você, ele não precisará baixar esse arquivo js novamente.
  • Arquivo em diversos servidores, com isso o download dele é mais rápido
  • Não utiliza a banda do seu servidor para envio do arquivo js
  • Utilizar é de graça e não necessita de nenhum pagamento

Esse é um exemplo de como fica a referencia para um CDN em sua aplicação:

<script src="netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css"></script>

Algumas páginas com mais informações de CDN de determinadas empresas:

Anúncios

Princípio de Substituição de Liskov (Liskov substitution principle – LSP)

O principio de substituição de Liskov define :

“Os subtipos devem ser substituíveis pelos seus tipos base”

Esse é um principio q confunde muitos programadores, porém é bem simples depois que se entende ele. Um termo muito utilizado no aprendizado do conceito de herança é o termo “É-um”. Esse termo é muito amplo para definição de um subtipo. A melhor forma de definição para subtipo é substituível, quando a substituição é definida por uma interface explicitamente ou implicitamente.

Desing by contract é uma pratica que ajuda a seguir esse principio. Ela define para que não seja criado variáveis de classes concretas, deve ser criado as variáveis a partir de contratos explícitos.

Esse principio alerta com o uso incorreto de polimorfismo, é necessário ter uma visão muito correta para generalizar ou especificar uma propriedade ou um parâmetro de um método em uma classe. Para deixar os termos que estou usando mais claros:

  • Generalizar: Quando a variável em questão é um tipo base, uma interface, uma classe abstrata ou qualquer outro tipo de objeto que é possível passar um objeto derivado dele.
  • Especializar: Quando a variável em questão é o objeto concreto, só é possível passar um objeto daquele determinado tipo.

A parte mais importante para entender esse principio, é que não se utilizar uma classe base quando alguma classe derivada dele não pode ser substituível por ele. Caso a classe derivada remova alguma funcionalidade da classe base ou ocasione um erro nas funcionalidades da classe base.

Exemplo:Vou tentar mostrar em uma cenário bem bestinha, mas é para entender realmente o conceito. Voce tem uma classe pessoa e ela tem dois métodos CalcarPeDireito e CalcarPeEsquerdo. Os dois metodos recebem dois parametros um Sapato(classe abstrata) e uma Meia(classe abstrata), fazendo o uso de generalização.  A estrutura de classes do Sapato e da Meia são essas:

    /// <summary>
    /// Meia
    /// </summary>
    public abstract class Sock
    {
        protected Sock()
        {

        }
        public abstract string Side { get; }

    }

    public  class RightSock : Sock
    {
        public RightSock()
        {

        }
        public override string Side
        {
            get { return "Right"; }
        }
    }

    public  class LeftSock : Sock
    {
        public LeftSock()
        {

        }
        public override string Side
        {
            get { return "Left"; }
        }
    }

    /// <summary>
    /// Sapato
    /// </summary>
    public abstract class Shoe
    {
        public abstract string Side { get; }

        protected Shoe()
        {

        }
    }

    public class LeftShoe : Shoe
    {
        public LeftShoe()
        {

        }
        public override string Side
        {
            get { return "Left"; }
        }
    }

    public class RightShoe : Shoe
    {
        public RightShoe()
        {

        }
        public override string Side
        {
            get { return "Right"; }
        }
    }

A classe pessoa está desenvolvida assim:

    public static class Person
    {
        /// <summary>
        /// Calçar Pé esquerdo
        /// </summary>
        /// <param name="shoe"> Can't be any show only the left</param>
        /// <param name="sock"> There is no diference</param>
        public static void PuttingOnLeftFootBreakingLSP(Shoe shoe, Sock sock)
        {
            Console.WriteLine("Putting socks {0} on the left foot!", sock.Side);
            if (shoe.Side != "Left")
            {
                Console.WriteLine("Wrong foot!");
            }
            else
            {
                Console.WriteLine("Putting shoes {0} on the left foot!", shoe.Side);
            }
        }

        /// <summary>
        /// Calçar Pé Direito
        /// </summary>
        /// <param name="shoe"></param>
        /// <param name="sock"> There is no diference</param>
        public static void PuttingOnRightFootBreakingLSP(Shoe shoe, Sock sock)
        {
            Console.WriteLine("Putting socks {0} on the righ foot!", sock.Side);
            if (shoe.Side != "Right")
            {
                Console.WriteLine("Wrong foot!");
            }
            else
            {
                Console.WriteLine("Putting shoes {0} on the righ foot!", shoe.Side);
            }
        }
    }

Com isso nada impede de eu fazer isso no codigo:

     Person.PuttingOnLeftFootBreakingLSP(new RightShoe(), new RightSock());
     Person.PuttingOnRightFootBreakingLSP(new LeftShoe(), new RightSock());

Ok, agora para resolver esse problema e seguir o principio de Substituição de Liskov como podemos alterar isso? Muito simples, utilizaremos a generalização ainda é possível, no caso no paramento da meia, pois a meia não tem problema se colocar invertido. E usaremos a especialização onde é necessário, no caso no sapato, utilizaremos em em seus respectivos  métodos o sapato do lado correto, como mostra o código a seguir:

 public static class Person
    {
        /// <summary>
        /// Calçar Pé esquerdo
        /// </summary>
        /// <param name="shoe"></param>
        /// <param name="sock"></param>
        public static void PuttingOnLeftFootUsingLSP(LeftShoe shoe, Sock sock)
        {
            Console.WriteLine("Putting socks {0} on the left foot!", sock.Side);
            Console.WriteLine("Putting shoes {0} on the left foot!", shoe.Side);
        }

        /// <summary>
        /// Calçar Pé Direito
        /// </summary>
        /// <param name="shoe"></param>
        /// <param name="sock"></param>
        public static void PuttingOnRightFootUsingLSP(RightShoe shoe, Sock sock)
        {
            Console.WriteLine("Putting socks {0} on the righ foot!", sock.Side);
            Console.WriteLine("Putting shoes {0} on the righ foot!", shoe.Side);
        }

    }

Assim quando um desenvolvedor utilizar essa classe ele pode utilizar o polimorfismo com os parâmetros possíveis, mais os que não é possível generalizar ele é obrigado a passar a classe concreta corretamente, isso da muito mais segurança na codificação. Isso faz com que aquele código de forma incorreta na classe Pessoa não seja mais permitido.

Todo o código deste post está no github neste link.

Criar eventos ao adicionar item em List C#

Isso é algo realmente chato as vezes, você precisa fazer uma ação antes de que qualquer item seja inserido em determinada lista. Isso as vezes se torna bem chato se o sistema não tem um encapsulamento das propriedades bem organizado.

Tem um projeto no Github(HAL – Help a lot) exatamente com uma coleção que faz isso, eu iniciei esse projeto agrupar funções corriqueiras, que as vezes é necessário criar em diversos sistemas, ou cada empresa tem sua DLL mágica que tem as funções básicas. O código está totalmente aberto quem quiser olhar o código, tem alguma sugestão ou quer ajudar no desenvolvimento só entrar em contato comigo e pode começar a desenvolver.  Caso queria fazer o download Apenas  DLL.

Bom vamos ao problema, precisamos de uma lista que precisa interceptar a ação dos métodos Add, Insert, Remove e RemoveAt de uma lista para poder colocar o evento antes ou depois dessas ações. O que vou fazer é criar uma classe genérica que implementa IList<T> e aplicar o padrão de projetos Proxy para interceptar as ações necessárias. O código ficara assim:

    public class HalList<T> : IList<T>
    {
        public delegate void AddItemDelegate(T item);
        public delegate void RemoveItemDelegate(T item);

        public delegate void InsertItemDelegate(int index, T item);
        public delegate void RemoveAtItemDelegate(int index, T item);

        public event InsertItemDelegate BeforeInsertItem;
        public event InsertItemDelegate AfterInsertItem;

        public event RemoveAtItemDelegate BeforeRemoveAtItem;
        public event RemoveAtItemDelegate AfterRemoveAtItem;

        public event AddItemDelegate BeforeAddItem;
        public event AddItemDelegate AfterAddItem;

        public event RemoveItemDelegate BeforeRemoveItem;
        public event RemoveItemDelegate AfterRemoveItem;

        private IList<T> _list;

        public HalList()
        {
            _list = new List<T>();
        }

        public IEnumerator<T> GetEnumerator()
        {
            return _list.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        public void Add(T item)
        {
            if (BeforeAddItem != null)
                BeforeAddItem(item);

            _list.Add(item);

            if (AfterAddItem != null)
                AfterAddItem(item);
        }

        public void Clear()
        {
            _list.Clear();
        }

        public bool Contains(T item)
        {
            return _list.Contains(item);
        }

        public void CopyTo(T[] array, int arrayIndex)
        {
            _list.CopyTo(array, arrayIndex);
        }

        public bool Remove(T item)
        {
            if (BeforeRemoveItem != null)
                BeforeRemoveItem(item);

            bool ret = _list.Remove(item);

            if (AfterRemoveItem != null)
                AfterRemoveItem(item);
            return ret;
        }

        public int Count
        {
            get { return _list.Count(); }
        }

        public bool IsReadOnly
        {
            get { return false; }
        }

        public int IndexOf(T item)
        {
            return _list.IndexOf(item);
        }

        public void Insert(int index, T item)
        {
            if (BeforeInsertItem != null)
                BeforeInsertItem(index, item);

            _list.Insert(index, item);

            if (AfterInsertItem != null)
                AfterInsertItem(index, item);
        }

        public void RemoveAt(int index)
        {

            if (_list.Count <= index)
                throw new HalException("Index out of range on the List!");

            T item = _list[index];

            if (BeforeRemoveAtItem != null)
                BeforeRemoveAtItem(index, item);

            _list.RemoveAt(index);

            if (AfterRemoveAtItem != null)
                AfterRemoveAtItem(index, item);
        }

        public T this[int index]
        {
            get { return _list[index]; }
            set { _list[index] = value; }
        }
    }

Pode ser questionado o motivo de deixar tudo separa, mas estou ainda pensando se vou implementar alguma coisa diferente para cada um ou colocar em todos, esse ainda é o código inicial no projeto e pode ser alterado, mas esse código já está testado.

Princípio de Segregação de Interface(Interface segregation principle – ISP)

O principio de segregação de interface e:

“Os clientes não devem ser obrigados a depender de métodos

que não utilizam

Esse principio ajuda a evitar a criação de fat interfaces(interfaces gordas), termo utilizado para interfaces com mais funcionalidades do que o necessário. Classes que implementam uma interface assim não são coesas. As interfaces podem ser divididas em grupos de métodos, e cada grupo  atende uma conjunto diferentes de classes, cada classe pode implementar apenas as funcionalidades que fazem sentido;

Uma dos indicadores para identificar a quebra deste principio é no seguinte cenário você ter uma interface com 4 funcionalidades porém ao implementar essa interface em uma classe faz sentido todas os métodos. Porém em outra classes uma funcionalidade ou outra não faz sentido ser implementada.

Uma boa dica para evitar a quebra desse principio é sempre que adicionar um método em uma interface, analise quem implementa essa interface e se aquele método faz sentido para todas as classes que implementam. Se tiver sentido adicione nessa interface sem nenhum problema, caso não faça sentido crie um outra interface(ou verifique se faz sentido em outra interface já existente) para adicionar o método que você precisa implementar.

Indicadores de quebra do principio:

  • Métodos de classes, implementados com base em uma interface, retornando valores padrões ou jogando exceções
  • Implementações de métodos que não fazem sentido para a classe
  • Pouco sentido nas interfaces que existem no sistema
  • Muita alterações no código para adicionar um novo método na interface.
  • Ao chamar um método em uma classe não ter certeza se ele foi realmente implementado(isso acontece e bastante)

Exemplo: Digamos que você tenha uma interface IVeiculo e uma classe Carro, como descrito abaixo e agora é necessário implementar no sistema um objeto moto. Nesse exemplo a criação do objeto moto deve  implementar a interface IVeiculo. O Exemplo que quebra o principio ISP é assim:

    public interface IVehicleBreakingISP
    {
        void TurnOn();
        void TurnOff();

        void OpenDoor();
        void CloseDoor();
    }

    public class CarBreakingISP : IVehicleBreakingISP
    {

        public void TurnOn()
        {
            Console.WriteLine("Turn ON the car!!!!");
        }

        public void OpenDoor()
        {
            Console.WriteLine("Door is OPEN!!!!");
        }

        public void CloseDoor()
        {
            Console.WriteLine("Door is CLOSE!!!!");

        }

        public void TurnOff()
        {
            Console.WriteLine("Turn OFF the car!!!!");
        }
    }

    public class MotorcycleBreakingISP : IVehicleBreakingISP
    {
        public void TurnOn()
        {
            Console.WriteLine("Turn ON the Motorcycle!!!!");
        }

        public void TurnOff()
        {
            Console.WriteLine("Turn OFF the Motorcycle!!!!");

        }

        /// <summary>
        /// Do Not Call this
        /// </summary>
        public void OpenDoor()
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Do Not Call this
        /// </summary>
        public void CloseDoor()
        {
            throw new NotImplementedException();
        }
    }

Uma refatoração viável para esse necessário ficar de acordo com o principio é assim:

    public interface IHasDoors
    {
        void OpenDoor();
        void CloseDoor();
    }
    public interface IVehicle
    {
        void TurnOn();
        void TurnOff();
    }

    public class Car : IVehicle, IHasDoors
    {

        public void TurnOn()
        {
            Console.WriteLine("Turn ON the car!!!!");
        }

        public void OpenDoor()
        {
            Console.WriteLine("Door is OPEN!!!!");
        }

        public void CloseDoor()
        {
            Console.WriteLine("Door is CLOSE!!!!");

        }

        public void TurnOff()
        {
            Console.WriteLine("Turn OFF the car!!!!");
        }
    }

    public class Motorcycle : IVehicle
    {
        public void TurnOn()
        {
            Console.WriteLine("Turn ON the Motorcycle!!!!");
        }

        public void TurnOff()
        {
            Console.WriteLine("Turn OFF the Motorcycle!!!!");

        }

    }

Foi separado o grupo de responsabilidades de veículos e veículos com portas, assim o veiculo moto apenas implementa a interface de IVeiculo, enquanto que o carro precisa implementar a interface IVeiculo e ITemPortas, pois faz sentido.

Caso tenha curiosidade de como pensar assim separando as interfaces, tente implementar essas duas funcionalidades:

  • Quero poder abrir o porta malas no meu objeto carro.
  • Quero que seja implantando outro veiculo com nome de avião e colocado as devidas responsabilidades nele a partir de interface.

Apenas uma dica para o uso desse principio, ele ajuda muito a organizar o código, porém é necessário fazer o máximo para programar para a interface. Isso quer dizer que você não deve declarar uma variável com o tipo final e sempre tentar usar as interfaces para diminuir o acoplamento do código. Isso deve ser feito quando possível, tem casos que isso pode não parecer valido.

Todo o código deste post está no github neste link.

Princípio de Aberto e Fechado(Open/closed principle – OCP)

O Princípio de Aberto e Fechado define:

“As entidades de software(classes, módulos, funções etc) devem

ser abertas para ampliação, mas fechadas para modificação”

A palavra chave deste princípio é a abstração quer qualquer linguagem de programação orientada a objetos possui esse recurso.Imagine o seguinte cenário você tem dois sistemas o Sistema A e o Sistema B, eles possuem uma integração aonde o Sistema A chama um determinado método de um determinado objeto do Sistema B. Se existir uma abstração dessa integração(interface), é possível implementar uma nova funcionalidade no Sistema B sem que seja alterado a chamada do Sistema A, pois a alteração feita no sistema B irá seguir a mesma interface de comunicação.

Um sistema que quebra esse princípio se torna um sistema dificilmente modificado, pois ao alterar uma parte do sistema pode afetar outras partes, isso mostra muita rigidez nas relações do sistema.

É necessário muito cuidado com ao seguir esse princípio, muitas classes devem sofrer uma refatoração e deverão ser alteradas para uma abstração do problema, mas não é recomendável aplicar abstração desenfreada em todas as partes do aplicativo. É necessário muita dedicação para aplicar abstração só nas partes do sistema que exibem alterações frequentes.Segundo o Robert C Martin “Resistir à abstração precipitada é tão importante quando a abstração em si”.

Sintomas mais aparentes da quebra deste principio:

  • Dificuldade nas alterações
  • Relações feitas sem abstração
  • Alterações em código funcionado para implementação de novas funcionalidades
  • Rigidez do sistema

Exemplos de casos e sua refatoração:

Exemplo: Digamos que tenha uma funcionalidade que desenha formas geométricas em uma aplicação console,  e já existe um código porém ele está difícil de adicionar novas formas para serem desenhadas assim é necessário uma refatoração para ficar de forma mais fácil a implementação de novas formas para desenhas. O código atual esta assim:

public class DrawingShapesWithoutOcp
    {
        public static void DrawShape(string shape)
        {
            switch (shape)
            {
                case "square":
                    Console.WriteLine("SQUARE");
                    Console.WriteLine(" -----------");
                    Console.WriteLine("|           |");
                    Console.WriteLine("|           |");
                    Console.WriteLine("|           |");
                    Console.WriteLine("|           |");
                    Console.WriteLine(" -----------");
                    break;

                case "rectangle":
                    Console.WriteLine("RETANGLE");
                    Console.WriteLine("     -------------");
                    Console.WriteLine("    /              \\");
                    Console.WriteLine("  /                  \\");
                    Console.WriteLine("/                      \\");
                    Console.WriteLine("------------------------");
                    break;
            }
        }
    }

Um código dessa forma que tem previsão de crescimento é muito importante que seja refatorado para nao sofrer dificuldade ou problemas no futuro, isso um exemplo que acontece muito em sistemas existentes. Isso é realmente um indicio para iniciar a refatorar o sistema!
Esse código até pode parecer simples rápido e pode ser facilmente implementado novas formas, porém imagina você criar mais de 100 formas com essa estrutura de código, ai se torna inviável manter um código assim. Esse código também quebra totalmente o principio de aberto e fechado, pois ele esta fechado para extensão, todas vez é necessário alterar o código que já esta funcionando para adicionar um novo comportamento ou funcionalidade.
Primeiro vamos separar o comportamento então criaremos uma abstração do comportamento que queremos com a interface IShape e depois criaremos as implementações dessa interface para cada forma que precisamos que precisamos:

    public interface IShape
    {
        //behavior
        void DrawShape();
    }

    public class RectangleShape : IShape
    {
        public void DrawShape()
        {
            Console.WriteLine("RETANGLE");
            Console.WriteLine("     -------------");
            Console.WriteLine("    /              \\");
            Console.WriteLine("  /                  \\");
            Console.WriteLine("/                      \\");
            Console.WriteLine("------------------------");
        }
    }

    public class SquareShape : IShape
    {
        public void DrawShape()
        {
            Console.WriteLine("SQUARE");
            Console.WriteLine(" -----------");
            Console.WriteLine("|           |");
            Console.WriteLine("|           |");
            Console.WriteLine("|           |");
            Console.WriteLine("|           |");
            Console.WriteLine(" -----------");
        }
    }
Depois disso vamos criar uma classe para executar esse comportamento, ela irá utilizar a interface para garantir uma abstração do problema, assim deve ficar a classe.
    public class DrawingShapes
    {
        IList<IShape> _shapes = new List<IShape>();

        public void AddShape(IShape shape)
        {
            _shapes.Add(shape);
        }
        public void RemoveShape(IShape shape)
        {
            _shapes.Remove(shape);
        }

        public void DrawImages()
        {
            foreach (var shape in _shapes)
            {
                shape.DrawShape();
            }
        }
    }
Apos isso você pode utilizar a classe para ver como ficaria a chamada como mostrado nesse código:
 static void Main(string[] args)
        {
            SampleOfOCP();
        }

        private static void SampleOfOCP()
        {
            //Create shapes
            IShape rectangle = new RectangleShape();
            IShape square = new SquareShape();
            DrawingShapes d = new DrawingShapes();

            //add feature to draw rectangle
            d.AddShape(rectangle);
            //drawing images
            d.DrawImages();

            Console.WriteLine("Press Any key to Contunue the sample of OCP");
            Console.ReadKey();

            //add feature to draw square
            d.AddShape(square);
            d.DrawImages();
            Console.ReadKey();
        }

Isso não traz uma complexidade muito alta de forma desnecessária?

Não, por dois motivos, mantendo o código da mesma forma, efetuar testes automatizados se torna realmente difícil efetuar testes e também é necessário alterar código em uma classe que esta funcionando para adicionar nova funcionalidade.

Se quiser fazer algo para entender melhor, implemente outras formas nos dois códigos para entender as dificuldade em uma forma e outra.

Todo o código deste post está no github neste link.

Princípio de Responsabilidade Única(Single responsibility principle – SRP)

O Princípio de Responsabilidade Única define:

“Uma classe deve ter apenas um motivo para mudar”

Em muitas literaturas é estudado a coesão de uma classe, isso define todos os elementos de uma classes que possui responsabilidades únicas tendem a se relacionar, esse termo é bem utilizado e abordado atualmente, porém é uma outra visão para o mesmo problema de colocar muita responsabilidade em apenas uma classe.

Em uma classe cada responsabilidade dela é uma eixo de mudança. Quando um requisito sofre mudança as mudanças afetam as responsabilidades das classes.Se uma classe possui mais de uma responsabilidade implementada, ela possui mais de uma razão para sofrer mudanças.

Quando uma classe possui mais de uma responsabilidade, suas responsabilidades se tornam acopladas. Mudanças em uma responsabilidade podem ocasionar a capacidade da classe de cumprir suas outras responsabilidades.Isso torna um projeto frágil e que ocorre problemas inesperados.

Sintomas mais aparentes da quebra deste principio:

  • Classes muito extensas
  • Alteração de um requisito ocasiona a quebra de uma outra responsabilidade ou requisito
  • Trecho do sistema com Dono
  • Camadas “mágicas” no sistema(sistemas que tem apenas uma camada para fazer tudo)

Exemplos de casos e sua refatoração:

Exemplo: A persistência de dados junta

Vamos ao cenário: Criar uma entidade de empregado com os campos( nome, idade, valor_hora) e persistir na base de dados esses campos. O calculo do salario do empregado é feito ao final do mês, por isso é necessário ter uma forma de passar a quantidade de horas trabalhadas do funcionário e retornar  o valor do salário que deve ser pago. (Vou utilizar todas os valores como inteiros e não vou atentar a arquitetura ou qualquer outra boa pratica apenas quero exemplificar esse princípio)

Algumas pessoas já pensam em uma classe mais ou menos assim:

         public class EmployeeWithoutSRP
         {
             private int _id;
             private string _name;
             private int _age;
             private int _hourlyPay;
             private SqlConnection _connection;

             public EmployeeWithoutSRP()
             {

             }

             public EmployeeWithoutSRP(string name, int age, int hourlyPay)
             {
                 Name = name;
                 Age = age;
                 HourlyPay = hourlyPay;
             }

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

             /// <summary>
             /// Nome do Empregado
             /// </summary>
             public string Name
             {
                 get { return _name; }
                 set { _name = value; }
             }

             /// <summary>
             /// Idade
             /// </summary>
             public int Age
             {
                 get { return _age; }
                 set { _age = value; }
             }

             /// <summary>
             /// Valor hora
             /// </summary>
             public int HourlyPay
             {
                 get { return _hourlyPay; }
                 set { _hourlyPay = value; }
             }

             /// <summary>
             /// Regra de negocio
             /// </summary>
             /// <returns></returns>
             public int CalculatePayment(int hoursWorkedInMonth)
             {
                 return HourlyPay * hoursWorkedInMonth;
             }

             public void Save()
             {
                 using (_connection = new SqlConnection())
                 {
                     _connection.Open();
                     SqlCommand cmd = _connection.CreateCommand();
                     if (Id <= 0)//Insert
                     {
                         cmd.CommandText = "INSERT INTO EMPLOYEE (DE_NAME, NU_AGE, NU_HOURLY_PAY) VALUES (@NAME, @AGE, @HOURLY_PAY); SELECT @@IDENTITY;";

                         cmd.Parameters.Add(new SqlParameter("@NAME", Name));
                         cmd.Parameters.Add(new SqlParameter("@AGE", Age));
                         cmd.Parameters.Add(new SqlParameter("@HOURLY_PAY", HourlyPay));
                         int idEmployee = (int)cmd.ExecuteScalar();
                         Id = idEmployee;
                     }
                     else //Update
                     {
                         cmd.CommandText = "UPDATE EMPLOYEE set DE_NAME = @NAME, NU_AGE= @AGE, NU_HOURLY_PAY = @HOURLY_PAY  WHERE id = @ID";

                         cmd.Parameters.Add(new SqlParameter("@NAME", Name));
                         cmd.Parameters.Add(new SqlParameter("@AGE", Age));
                         cmd.Parameters.Add(new SqlParameter("@HOURLY_PAY", HourlyPay));
                         cmd.Parameters.Add(new SqlParameter("@ID", Id));
                         cmd.ExecuteNonQuery();
                     }
                 }
             }
         }

Porém essa classe quebra o principio de responsabilidade unica, pois ela possui a responsabilidade de fazer o calculo de pagamento do empregado e também de persistir seus dados em um SQL Server. Uma forma que muitas gente pensa é que fazendo isso adiciona comportamento para a classe, pois é natural pegar uma classe e chamar o método Save para persistir. Adicionar comportamento a uma classe é fazer um método para que dentro dele as propriedades ou campos sejam alterados da forma que aquele comportamento necessite. Por exemplo Em uma classe Usuário colocar o método Ativar() é um comportamento, ele altera uma propriedade para falar que está ativo, porém o método salvar não altera nada apenas persiste isso é uma responsabilidade.

Bom já falamos demais disso agora vamos ao código refatorado:

         public class Employee
         {
             private int _id;
             private string _name;
             private int _age;
             private int _hourlyPay;

             public Employee()
             {

             }
             public Employee(string name, int age, int hourlyPay)
             {
                 Name = name;
                 Age = age;
                 HourlyPay = hourlyPay;
             }

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

             /// <summary>
             /// Nome do Empregado
             /// </summary>
             public string Name
             {
                 get { return _name; }
                 set { _name = value; }
             }

             /// <summary>
             /// Idade
             /// </summary>
             public int Age
             {
                 get { return _age; }
                 set { _age = value; }
             }

             /// <summary>
             /// Valor hora
             /// </summary>
             public int HourlyPay
             {
                 get { return _hourlyPay; }
                 set { _hourlyPay = value; }
             }

             /// <summary>
             /// Regra de negocio
             /// </summary>
             /// <returns></returns>
             public int CalculatePayment(int hoursWorkedInMonth)
             {
                 return HourlyPay * hoursWorkedInMonth;
             }
         }

     public class RepEmployee
         {
             private SqlConnection _connection;

             public RepEmployee()
             {

             }

             public void Save(Employee employee)
             {
                 using (_connection = new SqlConnection())
                 {
                     _connection.Open();
                     SqlCommand cmd = _connection.CreateCommand();
                     if (employee.Id <= 0)//Insert
                     {
                         cmd.CommandText = "INSERT INTO EMPLOYEE (DE_NAME, NU_AGE, NU_HOURLY_PAY) VALUES (@NAME, @AGE, @HOURLY_PAY); SELECT @@IDENTITY;";

                         cmd.Parameters.Add(new SqlParameter("@NAME", employee.Name));
                         cmd.Parameters.Add(new SqlParameter("@AGE", employee.Age));
                         cmd.Parameters.Add(new SqlParameter("@HOURLY_PAY", employee.HourlyPay));
                         int idEmployee = (int) cmd.ExecuteScalar();
                         employee.Id = idEmployee;
                     }
                     else //Update
                     {
                         cmd.CommandText = "UPDATE EMPLOYEE set DE_NAME = @NAME, NU_AGE= @AGE, NU_HOURLY_PAY = @HOURLY_PAY  WHERE id = @ID";

                         cmd.Parameters.Add(new SqlParameter("@NAME", employee.Name));
                         cmd.Parameters.Add(new SqlParameter("@AGE", employee.Age));
                         cmd.Parameters.Add(new SqlParameter("@HOURLY_PAY", employee.HourlyPay));
                         cmd.Parameters.Add(new SqlParameter("@ID", employee.Id));
                         cmd.ExecuteNonQuery();
                     }
                 }
             }

         }

Sim eu refatorei mais o método de calcular pagamento ainda está na classe empregado isso não seria uma quebra do princípio?
Não, pois a responsabilidade da classe empregado é gerenciar as informações do empregado a regra de negocio faz parte disso, o que se deve tomar cuidado para não inchar essas classes, a ideia é manter classes pequenas e métodos bem pequenos também. Esse principio é facilmente entendido ao praticar TDD(Test Driven Development).
Todo o código deste post está no github neste link.

Implementando um tipo de Criteria

Quando é necessário fazer a extensão de uma classe e não se tem o fonte pode ser utilizado o padrão de projeto Proxy.

Um caso que pode ser utilizado é quando se está utilizando NHibernate no sistema e é necessário efetuar uma extensão das funcionalidades dele, no meu caso isso serviu para implementar um campo de Deletado em algumas tabelas e sempre que executar uma query nesta tabela não buscar o que já estiver deletado.

Para implementar um novo tipo de criteria você precisa criar uma classe herdando da interface ICriteria, porém essa interface solicita a implementação de vários métodos, ai você pensa, vou acabar tentando “reinventar a roda” para implementar todos esses métodos, porém não é necessário isso, você pode criar uma variável e “copiar” todo o comportamento da implementação que já existe e alterar os pontos que é necessário, uma implementação básica seria mais ou menos assim:


public class ExemploCriteria : ICriteria
{
private CriteriaImpl _instance;
public ExemploCriteria(System.Type persistentClass, ISessionImplementor session)
{ _instance = new CriteriaImpl(persistentClass, session); }

public ExemploCriteria(System.Type persistentClass, string alias, ISessionImplementor session)
{
_instance = new CriteriaImpl(persistentClass, alias, session);
}

public ExemploCriteria(string entityOrClassName, ISessionImplementor session)
{ _instance = new CriteriaImpl(entityOrClassName, session);}

public ExemploCriteria(string entityOrClassName, string alias, ISessionImplementor session) { _instance = new CriteriaImpl(entityOrClassName, alias, session); }
public object Clone() { return _instance.Clone(); }public ICriteria SetProjection(params IProjection[] projection) { _instance.SetProjection(projection); return this; }


public ICriteria Add(ICriterion expression) { _instance.Add(expression); return this; }
public ICriteria AddOrder(Order order)
{
_instance.AddOrder(order); return this;
}


public ICriteria SetFetchMode(string associationPath, FetchMode mode) {
_instance.SetFetchMode(associationPath, mode);
return this;
}

public ICriteria SetLockMode(LockMode lockMode) { _instance.SetLockMode(lockMode); return this;}
public ICriteria SetLockMode(string alias, LockMode lockMode)
{
_instance.SetLockMode(alias, lockMode);
return this;
}
public ICriteria CreateAlias(string associationPath, string alias)
{
_instance.CreateAlias(associationPath, alias);
return this;
}

public ICriteria CreateAlias(string associationPath, string alias, JoinType joinType)
{
_instance.CreateAlias(associationPath, alias, joinType);
return this;
}
public ICriteria CreateCriteria(string associationPath)
{
_instance.CreateCriteria(associationPath);
return this;
}

public ICriteria CreateCriteria(string associationPath, JoinType joinType)
{
_instance.CreateCriteria(associationPath, joinType);
return this;
}

public ICriteria CreateCriteria(string associationPath, string alias)
{
_instance.CreateCriteria(associationPath, alias);
return this;
}

public ICriteria CreateCriteria(string associationPath, string alias, JoinType joinType)
{
_instance.CreateCriteria(associationPath, alias, joinType);
return this;
}
public ICriteria SetResultTransformer(IResultTransformer resultTransformer)
{
_instance.SetResultTransformer(resultTransformer);
return this;
}

public ICriteria SetMaxResults(int maxResults)
{
_instance.SetMaxResults(maxResults);
return this;
}

public ICriteria SetFirstResult(int firstResult)
{
_instance.SetFirstResult(firstResult);
return this;
}

public ICriteria SetFetchSize(int fetchSize)
{
_instance.SetFetchSize(fetchSize);
return this;
}

public ICriteria SetTimeout(int timeout)
{
_instance.SetTimeout(timeout);
return this;
}


public ICriteria SetCacheable(bool cacheable)
{
_instance.SetCacheable(cacheable);
return this;
}

public ICriteria SetCacheRegion(string cacheRegion)
{
_instance.SetCacheRegion(cacheRegion);
return this;
}

public ICriteria SetComment(string comment)
{
_instance.SetComment(comment);
return this;
}

public ICriteria SetFlushMode(FlushMode flushMode)
{
_instance.SetFlushMode(flushMode);
return this;
}

public ICriteria SetCacheMode(CacheMode cacheMode)
{
_instance.SetCacheMode(cacheMode);
return this;
}

public IList List()
{
return _instance.List();
}

public object UniqueResult()
{
return _instance.UniqueResult();
}

public IEnumerable Future<T>()
{
return _instance.Future<T>;();
}

public IFutureValue<T> FutureValue<T>()
{
return _instance.FutureValue<T>();
}

public void List(IList results)
{
_instance.List(results);
}

public IList<T> List<T>()
{
return _instance.List<T>();
}

public T UniqueResult<T>()
{
return _instance.UniqueResult<T>();
}

public void ClearOrders()
{
_instance.ClearOrders();
}
public ICriteria GetCriteriaByPath(string path)
{
_instance.GetCriteriaByPath(path);
return this;
}

public ICriteria GetCriteriaByAlias(string alias)
{
_instance.GetCriteriaByAlias(alias);
return this;
}

public Type GetRootEntityTypeIfAvailable()
{
return _instance.GetRootEntityTypeIfAvailable();
}

public string Alias { get { return _instance.Alias; } }
}
Essa seria a implementação mais básica para a implementação de um tipo especifico para a criteria, a partir disso dá para imaginar varias coisas possíveis para se fazer antes de determinado método de uma criteria em seu sistema, porém é necessário muito cuidado pois dependendo da arquitetura do sistema pois o seu tipo de criteria pode ser utilizada no sistema inteiro para execução de querys, assim alterando algo em uma classe dessas você pode afetar o sistema inteiro, é como o sábio Ben Parker diz “Grandes poderes trazem grandes responsabilidades!
Eu utilizei isso para quando utilizado uma entidade que estivesse com uma determinada interface(INaoDeletarFisicamente) ele buscava apenas o que não estava deletado, em um próximo post irei mostrar como fazer essa implementação disso com NHibernate.