sábado, 5 de julho de 2014

Compreender a “Dependency Injection” (II)

Introdução

Esta é a segunda parte de uma série de artigos dedicados ao tema da Dependency Injection (Injecção de Dependência).

Parte I – Apresentação e Conceitos Base
Parte II – Introdução prática da DI (este artigo)
Parte III – Boa programação da DI
Parte IV – Contentores IoC – Exemplos de Aplicação

Depois de termos feito uma apresentação teórica dos conceitos base, neste artigo vamos iniciar a exploração da DI. Começamos com um exemplo simples que levanta uma série de questões relacionadas com as dependências do código e depois vamos analisando, passo-a-passo, a evolução das soluções para este problema, até introduzirmos o conceito de DI. 

Conteúdo

O problema do acoplamento

Vamos analisar um exemplo muito simples para percebermos esta questão do acoplamento do código e por que razão o alto acoplamento representa um problema.

No nosso exemplo, necessitamos criar uma classe que represente uma pessoa, classe essa que deve implementar uma funcionalidade que permita à pessoa cumprimentar um amigo, através do envio de um email.

public class Pessoa
{
    public void CumprimentaAmigo()
    {
        // cdigo com mecanismo para enviar email
    }
}

Esta primeira versão da nossa classe não respeita o Princípio da Responsabilidade Única. A nossa classe, para além da responsabilidade de implementar as funcionalidades relacionadas com a entidade Pessoa, também tem a responsabilidade de implementar um mecanismo de envio de email.

Para resolvermos esta questão, vamos separar estas responsabilidades em duas classes distintas:

public class ServicoEmail
{
    public void EnviaEmail(string assunto, string msg)
    {
        // cdigo com mecanismo para enviar email
    }
}
public class Pessoa
{
    private ServicoEmail email = new ServicoEmail();
    public void CumprimentaAmigo()
    {
        email.EnviaEmail("Ola", "Ola amigo, como vai isso?");
    }
}

E pronto! Resolvemos o problema do Princípio da Responsabilidade Única. Criámos uma nova classe ServicoEmail que trata do envio das mensagens de Email e a nossa classe Pessoa cria uma instância dessa classe para implementar  correctamente o método CumprimentaAmigo.

Todos concordamos que este é um exemplo muito simples e directo, mas que enferma de algumas limitações, conforme passamos a descrever:

  • A classe Pessoa depende da classe ServicoEmail. Existe uma forte conexão (alto acoplamento) entre estas duas classes.
  • Vamos imaginar que temos uma nova versão melhorada da classe de envio de email, ServicoEmailRapido. A única forma de utilizarmos esta nova classe será alterarmos a classe Pessoa.
  • Digamos que resolvemos inserir um parâmetro no construtor da classe ServicoEmail. Mais uma vez, somos obrigados a alterar a classe Pessoa.
  • Por uma decisão de design, a classe ServicoEmail passa a ser singleton. Lá temos que alterar a classe Pessoa
  • De forma a melhorar o sistema de notificações, decide-se criar novos sistemas de envio de mensagens, como SMS ou Twiter. A classe Pessoa tem que ser modificada para poder usar estas novas implementações.
  • Outro programador necessita utilizar a classe Pessoa, mas quer usar outro sistema de mensagens. Isto não pode ser feito com a versão actual da classe Pessoa, porque está agarrada à classe ServicoEmail. O que acontece normalmente é este programador duplicar a classe Pessoa e fazer as alterações de que necessita. O projecto acaba com duas versões da classe Pessoa.
  • Temos estado a analisar cenários em que ocorrem alterações de código. Todas as alterações devem ser testadas. Como podemos testar a classe Pessoa sem incluir a classe ServicoEmail? Como podemos criar testes unitários automatizados (com NUnit, p.ex.), neste caso?

Essas limitações podem ser melhoradas se alterarmos a nossa forma de pensar e recriarmos o código de uma forma mais modular. Isto é importante, mas é independente da DI, como veremos na próxima secção.

Programar baseado em Abstrações

O que pretendemos é eliminar a dependência entre a classe Pessoa e a classe ServicoEmail, que está na origem das limitações atrás descritas. Esta dependência está expressa na linha de código:

    private ServicoEmail email = new ServicoEmail();

O que necessitamos é remover a referência explícita à classe ServicoEmail, que causa a dependência. Fazendo uma pequena análise a esta situação, o que a classe Pessoa necessita, no método CumprimentaAmigo, é de um serviço genérico, que lhe permita enviar mensagens. Estamos aqui a introduzir um conceito de abstração, que pode ser implementado através da criação e utilização dum Interface.

A alteração da forma de pensar mencionada na secção anterior, passa então por aplicar o Princípio da Abstração, através da utilização de interfaces. No entanto, muitos programadores não usam interfaces porque os veem como código adicional, não necessário. Como em tudo, temos de ver as coisas dentro do contexto e haverá casos em que não necessitamos de interfaces (o caso do “Hello World”, p.ex.). No entanto, a programação com interfaces permite produzir um código muito mais modular e extensível, como ilustrado nos exemplos a seguir. Esta abordagem também melhora a testabilidade. O exemplo discutido na seção anterior foi bastante simples, mas incluiu várias armadilhas que podem ser facilmente evitadas se usarmos interfaces em vez de classes concretas para definir os serviços.

A correcta utilização dos interfaces envolve as três etapas seguintes:

1. Definir o Interface

Começamos por definir o interface IServicoMensagens que inclui a definição da assinatura do método EnviaMensagem.

public interface IServicoMensagens
{
    void EnviaMenssagem(string assunto, string msg);
}

2. Implementar o Interface
Na nossa lista de limitações, mencionámos várias formas de enviar mensagens. Vamos criar uma classe para cada uma delas, que implementa o novo interface.

public class ServicoEmail : IServicoMensagens
{
    public void EnviaMenssagem(string assunto, string msg)
    {
        // cdigo com mecanismo para enviar email
    }
}
public class ServicoEmailRapido : IServicoMensagens
{
    public void EnviaMenssagem(string assunto, string msg)
    {
        // cdigo com mecanismo para enviar email
    }
}

public class ServicoSms : IServicoMensagens
{
    public void EnviaMenssagem(string assunto, string msg)
    {
        // cdigo com mecanismo para enviar sms
    }
}

public class ServicoTwiter : IServicoMensagens
{
    public void EnviaMenssagem(string assunto, string msg)
    {
        // cdigo com mecanismo para enviar tweet
    }
}

3. Usar o Interface
Finalmente, em vez de usarmos classes, utilizamos interfaces. Na classe Pessoa, substituímos o campo email pelo o interface IServicoMensagens conforme indicado abaixo.

public class Pessoa
{
    private IServicoMensagens servicoMsg;

    public void CumprimentaAmigo()
    {
        servicoMsg.EnviaMenssagem("Ola", "Ola amigo, como vai isso?");
    }
}

Com a introdução do interface, criámos um nível de abstração que nos permitiu remover a dependência da classe Pessoa sobre as classes de serviço de envio de mensagens.

Todavia, o código acima ainda tem um problema. Declarámos uma variável que representa o serviço, mas esse serviço nunca é instanciado:

    private IServicoMensagens servicoMsg;

Como não podemos instanciar serviços directamente, temos que instanciar uma classe. Mas se instanciarmos uma classe, voltamos a criar uma dependência, como antes:

    private IServicoMensagens servicoMsg = new ServicoEmail();

Vamos então recapitular as questões que nos surgiram:

  • Ao referenciarmos directamente as classes de serviço, criámos uma dependência.
  • Removemos a dependência, utilizando um interface para a declaração da variável do serviço.
  • Mas não podemos instanciar interfaces, temos sempre que instanciar uma classe. Todavia, se instanciarmos uma classe, voltamos a ter a dependência.

Vamos fazer uma pequena modificação no código da classe Pessoa, que nos permita ultrapassar estas duas questões: remover a dependência e ter um objecto instanciado que implemente o interface do serviço.

public class Pessoa
{
    private IServicoMensagens servicoMsg;

    public Pessoa(IServicoMensagens servico)
    {
        this.servicoMsg = servico;
    }

    public void CumprimentaAmigo()
    {
        servicoMsg.EnviaMenssagem("Ola", "Ola amigo, como vai isso?");
    }
}

Note-se que a classe Pessoa não está a inicializar o serviço, mas espera por ele como um parâmetro do seu construtor. Este é um elemento-chave no design, que melhora a modularidade, extensibilidade e testabilidade. A classe Pessoa não é dependente de qualquer implementação, mas apenas de um serviço definido por um interface. Isso significa que podemos usar a classe Pessoa, sem nos preocuparmos com a implementação subjacente do serviço de mensagens. Além disso, diferentes instâncias da classe Pessoa podem ser criadas utilizando diferentes serviços de mensagens.

Vamos então criar uma pequena classe que instancie objectos da classe Pessoa e que injecte as dependências nos respectivos construtores.

public class Sistema
{
    public void main()
    {
        IServicoMensagens servico = new ServicoEmail();
        Pessoa socio = new Pessoa(servico);
        socio.CumprimentaAmigo();
    }
}

Acabámos de introduzir o conceito de Dependency Injection. Uma classe não depende directamente de outras classes, mas apenas de abstrações, representadas por interfaces. Os objectos com instâncias concretas que implementam os interfaces, são “injectados” em runtime. No exemplo anterior, estamos a injectar no construtor, mas esta não é a única forma de realizar a DI.

Na abordagem inicial, a classe definia exactamente e controlava as suas dependências. As instruções para instanciar as dependências estavam na própria classe. Com esta abordagem, não é a classe que decide quem são os objectos que implementam as suas dependências, mas terá que ser outra entidade a tomar essa decisão antes de instanciar a própria classe. Temos assim o conceito de Inversion of Control (IoC).

A ajuda da DI

Podemos facilmente verificar que a DI nos permite resolver as limitações indicadas nas secções anteriores. Com a injecção da dependência no construtor da classe Pessoa, esta deixa de ser afectada por qualquer alteração nas classes dos serviços (desde que não se altere o interface, claro). A questão de podermos ter diferentes instâncias da classe Pessoa a usarem diferentes serviços, também é fácil de resolver.

    public void Central()
    {
        ServicoEmail mail = new ServicoEmail();
        ServicoSms sms = new ServicoSms();
        ServicoTwiter tweet = new ServicoTwiter();

        List<Pessoa> socios = new List<Pessoa>();
        socios.Add(new Pessoa(mail));
        socios.Add(new Pessoa(sms));
        socios.Add(new Pessoa(tweet));
        socios.Add(new Pessoa(mail));

        foreach (Pessoa s in socios)
            s.CumprimentaAmigo();
    }

Começamos por criar três instâncias de serviços de tipos distintos, mas cada uma delas implementa o interface IServicoMensagens. Depois criamos uma lista de objectos da classe Pessoa e injectamos serviços diferentes em elementos diferentes. Finalmente percorremos todos os elementos da lista e executamos o método CumprimentaAmigo. Neste exemplo, o primeiro e o quarto sócios enviam uma mensagem de email, o segundo uma mensagem SMS e o terceiro uma mensagem do Twiter.

Queremos ainda fazer uma breve referência à questão dos testes unitários (serão o tema de outro artigo). Vamos imaginar que necessitamos testar se o método CumprimentaAmigo da classe Pessoa está correctamente implementado. Nem sempre é possível garantir toda a infraestrutura de envio de mensagens de Email, SMS ou Twiter. Normalmente isto estará presente no ambiente de produção, mas não nos computadores dos programadores. Então vejamos como a DI pode ajudar os nossos testes.

    public class ServicoFake: IServicoMensagens
    {
        public string assunto;
        public string msg;

        public void EnviaMenssagem(string assunto, string msg)
        {
            this.assunto = assunto;
            this.msg = msg;
        }
    }

    [TestClass]
    public class TestesPessoa
    {
        [TestMethod]
        public void PessoaPodeCumprimentar()
        {
            // set
            ServicoFake servico = new ServicoFake();
            Pessoa pessoa = new Pessoa(servico);

            // act
            pessoa.CumprimentaAmigo();

            // assert
            Assert.AreEqual(servico.assunto, "Ola");
            Assert.AreEqual(servico.msg, "Ola amigo, como vai isso?");

        }
    }

Como a classe Pessoa só depende do interface IServicoMensagens, para a testarmos, basta criar um serviço mock ou fake, que implemente o interface, que vamos usar com valores pré-determinados, para validarmos o funcionamento do método CumprimentaAmigo.

Algumas conclusões

Acabámos de ver, através de pequenos exemplos, alguns dos benefícios da DI. Permite-nos uma clara separação de conceitos, a garantia de respeitarmos os princípios SOLID e a construção de código modular, extensível e facilmente testável.

Pode-se argumentar que a introdução da DI tornou a classe Pessoa mais complexa de instanciar uma vez que o construtor requer parâmetros. Este é um aspecto a ter conta, a DI acrescenta complexidade à solução. Já na introdução do primeiro artigo tínhamos mencionado que a DI em sistemas simples pode ser mais prejudicial do que benéfica. Há que analisar sempre o contexto, dimensão e complexidade do sistema.

De qualquer forma, hoje em dia existem várias frameworks de IoC que tornam a implementação e configuração da DI numa tarefa simples e directa. Vamos ver alguns exemplos de utilização destas frameworks noutro artigo.

 

Notas:

- Neste artigo optou-se por explorar o tema da DI através de pequenos exemplos de código, muito simples, cujo objectivo é apenas fazer uma introdução e aguçar o apetite do(a) leitor(a).

- Os exemplos de código apresentados foram desenvolvidos em linguagem C#. Poderiam estar em C++, Java, VB.NET ou qualquer outra linguagem OO. a opção pelo C# deveu-se simplesmente a uma maior facilidade pessoal e abrangência em termos de documentação disponível para consulta.

- Ainda nos exemplos, optou-se por usar uma nomenclatura de classes, métodos e variáveis em língua Portuguesa, para melhor clarificação e compreensão do tema. Convenções e regras de nomenclatura recomendadas serão tema de outro artigo.

terça-feira, 1 de julho de 2014

Torne-se um bom Programador em 6 Etapas Muito Difíceis

Introdução

Em primeiro lugar, saibam que Peter Norvig, Director de Investigação da Google, compreendeu isto há muito tempo: leva 10 anos a aprender a ser um programador. Depois há um excesso de livros do tipo "Aprender X em algum pequeno número de dias", lá fora; há hordas de artigos de blog sobre "Como melhorar a sua programação em poucas maneiras fáceis"; e, em geral, um grande número de pessoas procuram conselhos sobre como se tornar um génio com o mínimo esforço.

Neste artigo, vamos mudar um pouco o passo. Em vez de “Cinco maneiras fáceis de tornar o seu código surpreendente em 21 dias”, vamos descrever como é na realidade. Vamos tentar fazê-lo de forma ligeira, por vezes a parecer brincadeira, mas na realidade é mesmo assim. Bem-vindo ao Como se tornar um bom programador em seis etapas muito dificies.

Passo 1

Entre no tema a longo prazo ou dedique-se à ornitologia

Claro que pode sempre mexer-se e fazer bons scripts de shell e talvez um pequeno jogo ou outro; se está satisfeito com um conjunto de competências limitadas, siga de imediato a rota do fácil-e-rápido. Não estamos a tentar diminuir a legitimidade dessa opção, de todo - algumas pessoas não têm tempo (ou mesmo o desejo) para se tornarem um programador mestre. Se não lhe agrada a ideia de praticar este ofício durante dez anos antes de se tornar bom nisso, então não se preocupe - mas não se iluda, você vai ser sempre limitado no que pode fazer e no que pode fazer bem. Se isso é uma situação razoável para si, óptimo! Não precisa terminar este artigo.

Para o resto de nós, porém, há algo sedutor sobre conseguir ser Realmente Bom em programação. Nós queremos ser especialistas, ninjas, gurus – qualquer que seja o substantivo superlativo de mestria que seja a sua fantasia. Para nós, 10 anos parece ser um investimento razoável. Talvez um pouco pesado, mas se vale a pena fazer, então vale a pena fazer certo... certo?

Assim, o primeiro passo para ser um bom programador é engolir a pílula. Aceitar que este não é apenas um processo de 10 anos, mas um processo para a vida. E como Norvig observa muito justamente, deve fazê-lo, porque quer. Ninguém se torna excepcionalmente bom a fazer algo que preferia não estar a fazer; os recordistas mundiais não entram nos livros da história, porque meio acidentalmente comeram a maior quantidade sempre de cachorros-quentes de uma assentada, mas naquele dia não chegaram a sentir fome.

Passo 2

Escrever muito código

Não tem que ser bom código. Não vai ser bom código, durante algum tempo. Apenas deite material cá para fora. Sempre que encontrar um pequeno incómodo na sua vida diária com o computador, pense sobre como pode escrever um programa para ajudar a resolver esse problema. Sempre que encontrar alguma coisa interessante que quiser experimentar, faça-o. Jogue com novos conceitos, ferramentas e linguagens, tanto quanto possível.

O processo de aprendizagem nunca vai parar. Então se abordar esta questão com a atitude de que você absorve tanto mais conhecimento e experiência quanto maior for a aprendizagem que pode fazer num dia, então vai longe. Entre na mentalidade de que um dia / semana / mês em que ainda não aprendeu algo interessante é um fracasso. Há tanto material lá fora, que certamente pode aprender alguma coisa útil todos os dias; isto torna-se mais difícil passados 15 anos, mais ou menos, mas ainda é perfeitamente possível. Nenhum mortal pode assimilar todo o conhecimento que existe no mundo sobre programação, por isso, se alguma vez sentir que já não encontra mais coisas para aprender, procure um novo projeto e escreva mais código.

Enquanto estiver a fazer isto, esteja atento. Procure por padrões - coisas que faz muitas vezes que possam ser úteis de automatizar, ou coisas que escreve muitas vezes no seu código e que possa ser capaz de separar em bibliotecas partilhadas ou outros locais centralizados. Procure linguagens que tornem determinadas tarefas fáceis. Depois procure outras linguagens que não são tão boas para aquelas mesmas tarefas e tente descobrir por que uma é mais produtiva que a outra.

Mas acima de tudo, escreva código. Todos os dias, mesmo que seja apenas uma expressão regular para pesquisar no seu histórico de email. Faça algo de programação tão frequentemente quanto puder. E lembre-se, se isto deixar de ser divertido, vá fazer outra coisa qualquer; não há nenhum objectivo nisto se não se estiver a divertir.

Passo 3

Ler ainda mais código

Depois de ter acumulado um pequeno conjunto de projectos, comece a ler o código de outras pessoas. No início, será difícil; elas vão fazer coisas que ainda não tinha visto antes, ou usar estilos a que não está habituado, ou mesmo linguagens que ainda não aprendeu. Se você encontrar algo interessante, leia o seu código, se possível. Não se preocupe em analisar profundamente um determinado projecto, pelo menos de início; pode ser um trabalho a tempo inteiro apenas para entender algumas bases de código existentes, como as de muitos grandes projetos. Escolha uma ou duas coisas que desejaria conseguir aprender a fazer, e descubra como elas foram feitas.

A leitura de novo código vai expô-lo a novas formas de pensar e isso ajuda a “esticar” o seu cérebro. Este esticar é vital para se manter o progresso e vai ajudar a garantir que à medida que avança, vai continuando a descobrir coisas novas para aprender.

Não se esqueça de falar com outros programadores também. Pergunte como e por que eles fizeram determinadas coisas. Pergunte se eles iriam fazer as coisas de forma diferente se voltassem atrás. Pergunte se eles têm alguma sugestão para o seu código. (mas seja educado; a maioria dos programadores alto perfil são extremamente ocupados e não têm necessariamente tempo ou inclinação para navegar através do trabalho de outras pessoas, de graça. O respeito leva-o por um longo caminho; esta é uma indústria muito compacta e a reputação significa muito.)

Passo 4

Aprenda muitas linguagens. Domine um par delas

Não irá conseguir ter um excedente prático de tempo, pelo menos não o suficiente para dominar várias linguagens simultaneamente, a não ser que seja extremamente sortudo. Portanto, aprenda tantas linguagens quanto possível a um nível superficial - o suficiente para saber o que as faz mover, o que as torna boas nos seus trabalhos específicos e quais as suas fraquezas. O esticar do cérebro é importante aqui; não se limite apenas a linguagens imperativas, como o C, ou orientada a objetos linguagens, como o Java; expanda-se para linguagens funcionais e linguagens declarativas também.

Aprenda um dialeto do Lisp. Isto não vai fazer nada pela sua programação do dia-a-dia, porque não vai usá-lo, mas vai fazer de si um melhor pensador e vai-lhe dar uma compreensão profunda da beleza dos sistemas recursivos. Agarre-se ao Lisp até que o momento "aha!" ocorra; até lá, vai parecer uma sopa de sintaxe estranho e convenções bizarras. Depois disso, ele permanecerá consigo durante a sua carreira, como um dos mais incrivelmente elegantes conceitos já concebidos pela humanidade.

Então, aprenda uma linguagem puramente funcional. Recomenda-se o Haskell, porque o obriga a ser puro de uma forma que outras linguagens funcionais (incluindo a maioria dos dialetos Lisp) não fazem. Vai ter que dobrar um pouco a sua mente, mas uma vez que o inevitável momento "aha!" ocorra (por volta da altura em que entenda o propósito dos monads) vai voltar a aumentar um nível no seu pensamento e na capacidade de projectar sistemas elegantes.

Finalmente, aprenda uma linguagem declarativa. O SQL conta, apesar de ser um pouco sem-saborão aprender apenas SQL. O Prolog é muitas vezes recomendado, mas não é necessariamente acessível. No âmbito da prática, XAML, XSLT e XQuery são boas ferramentas para conhecer e vão apresentá-lo aos conceitos por trás da programação

declarativa. (Em poucas palavras, dizemos ao computador o “que” queremos que ele faça e ele descobre como fazê-lo; isto está em contraste direto com a programação imperativa, onde dizemos ao computador como fazer as coisas e esperamos que ele faça o “que" de forma correcta e com a programação funcional, na qual descrevemos as transformações de tipos e dados.)

Como bónus, aprender ferramentas baseadas em XML depois de aprender um dialeto Lisp tornará dolorosamente óbvio o quão desesperadamente o XML está a tentar reinventar as expressões –s, e quão pobre é o trabalho que ele faz.

Passo 5

Crie uma linguagem

A linguagem não tem que ser complexa, rica, ou sofisticada, ou mesmo particularmente elegante. Nem sequer tem que ser original; É costume sugerir-se escrever um interpretador de Lisp (pontos de bónus se fizer isto num dialeto Lisp!) como uma boa maneira de aprender as bases. Em essência, quer ter uma sensação dos fundamentos do design de programação de computador: análise léxica (lexing), análise sintática (parsing), compilação, interpretação, máquinas virtuais e as inúmeras maneiras como as decisões básicas de projecto de uma linguagem afetam a forma como ela é útil para várias tarefas.

Isto irá permitir-lhe realizar três coisas:

  • Vai ganhar uma compreensão mais profunda de como as suas ferramentas de eleição funcionam, o que lhe dará a capacidade de ser mais eficaz com elas.
  • Vai começar a perceber as razões por trás das decisões de design das principais linguagens e ferramentas, que tanto o fascinar como irritar (se o fizer de forma correcta). Essa percepção vai ajuda-lo a escolher as suas ferramentas de forma mais eficaz, quando iniciar novos projetos.
  • Vai vislumbrar as possibilidades inexploradas que ainda existem para a criação de ferramentas e linguagens, e, esperamos, vai abrir os seus horizontes para uma série de novas oportunidades de coisas interessantes para aprender e experimentar.

Passo 6

Aprenda algo que ainda ninguém aprendeu

Este é o passo mais difícil e o final. Até agora, tem estado principalmente a aprender coisas que já são conhecidas; coisas que pode descobrir através da leitura do código de outras pessoas, ou livros, ou trabalhos académicos; coisas que são boas de saber, mas que não são novas.

Agora está na altura de se libertar dos limites, e realmente ascender à mestria. Agora está na altura de abrir um caminho num território em que ninguém se tenha aventurado ainda.

Não se engane; isto é algo que não vai querer enfrentar até bem depois do seu "décimo ano" de experiência, principalmente porque até então será muito mais provável ter estado apenas a reinventar a roda do que realmente a fazer pesquisa nova e original. Mas assim que tenha um bom entendimento sobre o campo, não é exatamente difícil encontrar as deslumbrantes fronteiras do conhecimento inexplorado por que a ciência da computação aguarda.

O mais provável é que isto irá levar mais dez anos, se não para sempre. Não desista; lembre-se que isto ainda deve ser divertido. Se a qualquer momento você parar de apreciar esta actividade, vá fazer outra coisa. A vida é muito curta para perder tempo a tentar fazer algo que já não quer fazer.

Nem toda agente vai ter sucesso neste passo, mas quem fizer uma tentativa será recompensado pelo esforço. Não deixe que as probabilidades o deitem abaixo. Mesmo que nunca ganhe o Prémio Turing, o seu crescimento contínuo como programador e a sua jornada rumo à mestria final dependem de esticar constantemente o cérebro - e, para o efeito, problemas não resolvidos são a melhor maneira de esticar.

Parabéns! Agora é um bom programador!

Oh espere ... na verdade, você acabou de morrer de velhice. Desculpe. Melhor sorte na próxima vida!

Com toda a seriedade, porém, não espere terminar nunca. No momento em que parar de fazer progressos na sua jornada, começa a morrer e a tornar-se irrelevante. Alguns dos falhanços mais tristes que já se viram no mundo da programação, derivam de pessoas que percorreram uma certa parte do caminho e decidiram que tinham terminado a aprendizagem; hoje são completamente irrelevantes no mundo do software e, provavelmente, nunca irão além da sua situação atual - a menos, é claro, que decidam começar a aprender de novo.

 

Agora, vá em frente e crie código! Talvez um dia, quando você for um grande programador, nos possa dizer como o conseguiu. Nós adoraríamos aprender.

 

Tradução livre do artigo “Become a Good Programmer in Six Really Hard Steps”

Nota: Na tradução optei por usar o masculino, não com qualquer intenção de discriminação, mas apenas como facilidade de linguagem.

Algumas “Leis” do Desenvolvimento de Software

Introdução

Apesar de todos os avanços nas ferramentas de software, parece haver algumas verdades que persistem sobre o desenvolvimento de software. Ao compreendermos essas "leis” podemos remover alguns dos mistérios do processo. Neste artigo vamos discutir algumas das regras práticas que parecem aplicar-se em geral à arte da programação.

Conteúdo

Numa grande percentagem de empreendimentos humanos existem princípios fundamentais, leis em alguns casos, que podemos usar para entender todos os tipos de fenómenos, comportamentos e resultados. Nós, humanos temos vindo a desenvolver software há cerca de 50 anos. Durante este tempo algumas verdades básicas, ou mesmo leis, têm surgido; estar ciente destas verdades pode fornecer algum conhecimento e discernimento adicionais. O conhecimento dessas "leis" permite que alguns eventos e projectos deixam de ser um puzzle para compreender.

As primeiras 3 “leis” que vamos enunciar foram discutidas no prefácio do livro “Exploring Scrum: The Fundamentals”, por Jeff Sutherland (co-criador do Scrum): Lei de Ziv, Lei de Humphrey e Lei de Conway. Para além destas, vamos enunciar mais algumas “leis”, que foram aparecendo ao longo dos anos e que parecem ser razoavelmente constantes e confiáveis.

Lei de Ziv

O desenvolvimento de software é imprevisível e as especificações nunca serão totalmente entendidas. Se concordarmos que a natureza fundamental do desenvolvimento de software é essencialmente o desenho de novos produtos, esta lei parece válida. Quando estamos a criar algo novo, independentemente da semelhança que possa ter com o que já foi criado antes, é impossível ter a certeza de todas as necessidades e restrições. Podemos prever com diferentes graus de precisão e devemos ressalvar todas as previsões com um certo grau de certeza, mas não conseguimos realmente prever com exactamente todos os requisitos funcionais e não-funcionais. O tema da incerteza também é abordada na lei de Humphrey.

Lei de Humphrey

O utilizador nunca vai saber o que quer até ver o software a funcionar (talvez nem mesmo assim).

Muitas vezes ouvimos o utilizador final a dizer “Eu saberei o que é, quando o vir” (“I’ll Know It When I See It – IKIWISI”). À medida que os utilizadores interagem com um novo software vão-se tornando mais conscientes sobre o potencial do sistema e as suas funcionalidades. O processo de revisão e sugestão iterativo leva a novas descobertas imprevisíveis. Essas novas descobertas são, para muitos, a razão para que a utilização de abordagens tradicionais de gestão de projetos no desenvolvimento de software não seja uma boa opção, não é tão eficaz como outras abordagens. As abordagens tradicionais assumem, incorretamente, que os utilizadores sabem o que querem antes do software ser desenvolvido.

Lei de Conway

Um sistema de software irá refletir a estrutura organizacional da equipa de desenvolvimento desse software, ou dito de outra forma, as organizações que projectam sistemas são compelidas a produzir projectos que são cópias das estruturas de comunicação da própria organização. Se a equipa de desenvolvimento não comunica bem, então os componentes de software do sistema, provavelmente, também não vão comunicar bem. Isto é compreensível quando se considera que o desenvolvimento de software é um processo humano muito intensivo, com uma infinidade de decisões e interações necessárias. A lei de Conway já existe há muitos anos, remontando à década de 1960.

Lei de Noel

As pessoas vão-se lembrar de prazos falhados, mas não de funcionalidades em falta. Se cumprir o prazo de entrega, mesmo que uma versão não inclua todas as funcionalidades prometidas, há menos probabilidade de ter problemas do que se tivesse entregue todas as funcionalidades, mas numa data posterior ao prazo de entrega.

Al Noel afirma esta "lei" é uma das razões que as abordagens Agile são, por vezes, eficazes. Todas as abordagens ágeis exigem entregas regulares com uma periodicidade não superior a uma semana. Se entregarmos regularmente código para produção nas datas prometidas, os clientes e utilizadores finais tendem a ser permissivos, mesmo que algumas funcionalidades e correções planeadas não sejam entregues na mesma data. Isto é especialmente verdade se os utilizadores estiverem acostumados a receber a maioria das entregas fora de prazo e se tiverem experimentado adiamentos frequentes na programação. Para alguns utilizadores finais, receber alguma coisa, numa base regular, e não necessariamente tudo, pode deixá-los satisfeitos.

Lei de Gilb

Se não atacar os riscos, os riscos vão atacá-lo a si. Novamente, se considerarmos que o desenvolvimento de software é essencialmente a criação de um novo produto, é inevitável haver uma certa quantidade de risco em cada projecto. Além disso, ignorar os riscos não os faz desaparecer, simplesmente eles não vão ser levados em consideração e não vão ser geridos. Se estes riscos não são controlados, podem evoluir para problemas reais que são susceptíveis de o "atacar". Quando algo tem riscos, estes riscos não devem pôr em causa a continuidade do projeto, nem se deve prosseguir o projecto ignorando os riscos e que eles se podem tornar.

Lei de Bradley

O software evolui ou morre. Ao longo do tempo, verifica-se que esta afirmação é verdadeira em muitos casos. Basta pensarmos em todas aquelas ferramentas de software que usámos e que não foram significativamente modificadas e actualizadas, para perceber que já não estão disponíveis. Outras, por sua vez, foram revistas e actualizadas e ainda estão presentes no mercado. Da mesma forma, sistemas de gestão de negócio caíram em desuso porque não foram melhorados ou estendidos. Esta “lei” é mais uma ferramenta para entender o software e o desenvolvimento de software.

Lei de Carriço

O bom software é aquele que funciona. Apesar da lei de Noel, que diz que os utilizadores são mais permissivos com funcionalidades em falta do que com prazos falhados, as funcionalidades disponibilizadas não devem ter bugs. Quando fazemos uma entrega, para além do esforço no cumprimento dos prazos, devemos ter especial atenção aos testes, de forma a garantir que o sistema funcione.

Esta “lei” parece bastante óbvia, mas somente quando a ouvi, numa disciplina do curso de Engª Multimédia, no ISTEC, da boca do Prof. Rui Carriço (um grande bem haja) é que sistematizei o conceito, apesar de, na altura, já contar com mais de 10 anos de experiência de análise e desenvolvimento de software.

 

Sem qualquer pretensão especial, estas são algumas regras práticas, que podemos apelidar de “leis”, que nos ajudam a entender melhor o trabalho de desenvolvimento de software. Espero que lhes consigam retirar alguma utilidade e aplicação prática.

Baseado no artigo “Some «Laws» of Software Development”, de Al Noel.

segunda-feira, 30 de junho de 2014

Os Princípios S.O.L.I.D. da POO

Introdução

Hoje em dia, quando falamos de programação de software, inevitavelmente estamos a falar de Programação Orientada a Objectos (POO). Torna-se pertinente perguntar o que é o design orientado a objectos (OO)? É sobre o quê? Quais os seus benefícios? Quais os seus custos? Apesar de virtualmente todos os programadores de software utilizarem uma linguagem Orientada a Objectos (OO), de alguma forma, muitos de nós utiliza estas linguagens sem saber porquê ou sem saber como retirar o máximo benefício delas.

De todas as revoluções que ocorreram na indústria do desenvolvimento de software, duas tiveram tanto sucesso que moldaram a nossa mentalidade de modo que as tomamos como um dado adquirido: Programação Estruturada e Programação Orientada a Objectos. Todas as principais linguagens de programação que usamos hoje em dia são fortemente influenciadas por estas duas disciplinas.

Mas, o que é necessário para fazermos Programação Orientada a Objectos?

Bem, podemos começar por dizer que temos que usar uma linguagem Orientada a Objectos (OO), como, por exemplo, Java, C#, C++, VB.NET. Mas só porque estamos a usar uma linguagem OO, só porque estamos a programar com classes, não quer dizer que estejamos a fazer POO. Ou pelo menos, não quer dizer que o estejamos a fazer da forma correcta. Muitas vezes os programadores não têm conhecimento dos princípios e fundamentos que são a base das disciplinas de que a linguagem de programação que estão a usar derivou.

Este artigo é assim sobre as bases da POO, sobre os Princípios da Programação Orientada a Objectos. Em específico, vamos analisar os princípios S.O.L.I.D., definidos por Robert C. Martin.

Conteúdo

Estes princípios focam-se nos aspectos de gestão de dependências da POO, i.e. a gestão do Acoplamento entre os módulos do software. O alto acoplamento é um problema que a maioria de nós já enfrentou. Sempre que temos que analisar algum pedaço de código legacy, somos confrontados com má gestão de dependências, resultando em software que é difícil de alterar, frágil e não reutilizável. Por outro lado, quando as dependências são bem geridas, resulta código com baixo acoplamento e o software é flexível, robusto e reutilizável. Então a gestão de dependências e este princípios, estão na fundação das características e facilidades que os programadores pretendem que o software tenha.

The Single Responsability Principle (Princípio da Responsabilidade Única)

Uma classe deve ter uma e apenas uma razão para mudar. Basicamente, isto significa que cada classe deve ter uma única responsabilidade e que a responsabilidade deve estar totalmente encapsulada pela classe. Todos os seus serviços devem estar estreitamente alinhados com essa responsabilidade.

Por exemplo, se criarmos uma classe que represente uma Encomenda, não queremos que essa classe faça a persistência dos dados para BD e que os exporte também para XML. Porquê? Porque se, mais tarde, quisermos mudar de BD ou alterar o esquema do XML, estamos a possibilitar que a alteração de uma responsabilidade obrigue à alteração de outra responsabilidade.

The Open/Closed Principle (Princípio do Aberto/Fechado)

As entidades de software (classes, módulos, funções, etc.) devem estar abertas para extensão, mas fechadas para modificação ou, dito de outra maneira, devemos ser capazes de estender o comportamento de uma classe, sem a modificar.

Em princípio, isto parece contraditório: como podemos fazer um objeto comportar-se de uma forma diferente  sem o modificar? A resposta: usando abstrações, ou colocando o comportamento (responsabilidade) em classes derivadas. Por outras palavras, ao criarmos classes base com funções “overridable” (que permitem substituição), podemos criar novas classes derivadas, que fazem a mesma coisa de forma diferente, sem alterar a funcionalidade base. Mais, se as propriedades da classe abstracta necessitam ser comparadas ou organizadas em conjunto, uma outra abstração deve lidar com isso. Esta é a base do argumento "manter privadas todas as variáveis ​​do objeto" (Encapsulamento).

The Liskov Substitution Principle (Princípio da Substituição de Liskov)

As classes derivadas devem ser substituíveis pelas suas respectivas classes base. Por outras palavras, métodos que usem referências a classes base, têm que ser capazes de usar objectos de classes derivadas, sem o saber.

Quando se faz a sobreposição de um método duma classe abstracta, este tem que ser implementado de forma correcta, na classe derivada. Ou, “quando se usa um objecto através do interface da sua classe base, o objecto derivado não deve esperar que essa utilização obedeça a pré-condições que sejam mais fortes do que aquelas requeridas na classe base". A ilustração sempre popular desta caraterística é o exemplo quadrado-rectângulo.

Nota: este princípio deve o seu nome a Barbara Liskov, que o enunciou inicialmente numa conferência em 1987.

The Interface Segregation Principle (Princípio da Segregação de Interfaces)

Devem-se refinar e criar interfaces específicos para cada cliente, ou, dito de outra forma, os clientes não devem ser forçados a depender de interfaces que não usam. Quando um cliente depende de uma classe que implementa interfaces que o cliente não usa, mas que são usados por outros clientes, então esse cliente vai ser afectado pelas mudanças que os outros clientes possam forçar na classe.

The Dependency Inversion Principle (Princípio da Inversão da Dependência)

Devemos depender de abstrações e não de concretizações. Abstrações não devem depender de detalhes. Os detalhes é que devem depender de abstrações. Isto está intimamente relacionado com o Princípio Aberto/Fechado, discutido anteriormente. Ao passar dependências (tais como conectores a dispositivos de armazenamento) para as classes, como abstrações, removemos a necessidade de programar para uma dependência específica.

A utilização de Dependency Injection é uma forma de seguir este princípio.

Este  artigos apresenta apenas uma breve introdução os princípios SOLID, pretendendo destacar a utilidade e vantagens da sua aplicação. Para um conhecimento mais aprofundado sobre o tema, recomenda-se a pesquisa e leitura da extensa bibliografia online.

domingo, 29 de junho de 2014

Quando a solução é o problema

Como evitar o excesso de engenharia e funcionalidades inúteis no software

Se só tiver um martelo, tudo se vai parecer com um prego.

Os engenheiros de software e designers acreditam que para criar e melhorar o software, basta exercer seu ofício. O produto sofre de baixo envolvimento dos utilizadores ou interrupções? Eles dizem: "Vamos para uma sala projectar uma solução no quadro branco". Existe uma crença ingénua e generalizada nesta área. A crença é que basta projecto e engenharia para resolver problemas. Esta crença mascara um ponto perverso: cada acto de projecto e engenharia, longe de ser uma de solução, é uma "intervenção numa situação complexa" [Baumer & Silberman, 2011].

Criar software é como a cirurgia

O software é subtil e complexo; miríades de componentes interagem num frágil equilíbrio de design, engenharia, marketing e utilizadores do mundo real. Na sua complexidade e inter-relação, o software é como o corpo humano. E apesar de todos os esforços de fazer a criação de software primitiva e matemática, é muito mais parecido com sangue e cirurgia intestinal. Risco, consequências imprevisíveis, e complicações ensombram cada procedimento.

A medicina moderna classifica este risco como "iatrogenia" – o dano não intencional que surge a partir do tratamento médico. Hoje em dia, a iatrogenia mata mais pessoas do que o cancro. Nos Estados Unidos, a iatrogenia mata entre 3 a 10 vezes mais pessoas do que os acidentes de viação. Mais, por mais estranho que pareça, só nos últimos tempos é que a medicina começou a salvar mais vidas do que aquelas que tira.

Em comparação com a disciplina da medicina, a engenharia de software ainda é jovem e imatura. Não é, portanto, nenhuma surpresa que o software moderno esteja cheio de funcionalidades e correções que fazem mais mal do que bem. Lembra-se do OpenSSL v1.0.1, ou a última actualização do sistema operativo que bloqueou o seu smartphone? A cura foi pior que a doença. Está na altura de se reconhecer a iatrogenia no software. Está na altura dos profissionais de software estudarem e prevenirem os efeitos nocivos do projecto e engenharia.

Não existe nenhum martelo

Os profissionais sofrem de deformação de domínio (deformação profissional), ou da “Lei do Instrumento”. Quando confrontados com um problema, tendemos a responder com o martelo que temos nas nossas mãos. Raramente nos questionamos se o problema beneficiariam ou não com as marteladas. Raramente nos questionamos se estamos sequer diante de um prego ou não. Como resultado dessa falta de questionar, a chamada "resolução do problema" é muitas vezes a "exacerbação do problema." Para superar a Lei do Instrumento devemos mudar a nossa mentalidade. A chave para essa mudança vem num soberbo e conciso artigo, “Quando a implicação é Não ao Projecto”:

"Em vez de imaginarmos que o projecto em tecnologia oferece soluções para os problemas de insustentabilidade, sugerimos pensar no projecto como uma intervenção em uma situação complexa." - Baumer & Silberman, 2011

Como parar com o excesso de engenharia

Faça três perguntas

Agora que entendemos projecto e engenharia como uma intervenção numa situação complexa, como podemos evitar o imprevisto e consequências não intencionais da intervenção? A resposta é que temos que fazer perguntas antes de desenhar um único pixel ou escrever uma única linha de código. Baumer e Silberman oferecem as seguintes três questões:

  1. "Poderia a tecnologia de ser substituída por uma abordagem low-tech ou não-tecnológica, igualmente viável a situação? “
  2. “Será que a intervenção tecnológica resulta em mais problemas ou danos do que a situação que está a abordar?”
  3. “Será que a tecnologia resolve uma transformação tratável computacionalmente
    um problema em vez de o problema em si? "
    (ou seja, é a tecnologia de um substituto pálido para a solução real)
Mude a cultura da sua organização

Se quiser evitar o excesso de engenharia e o empilhar de funcionalidades, os seguintes valores culturais serão construtivos:

  1. Valorize não fazer coisas. Inicie um debate na sua organização acerca de "menos é mais" [REWORK]. Evangelize esta mensagem numa forma que as equipas de software a possam apreciar.
  2. Explore a “extravenção”. É isso mesmo, explore que funcionalidades pode remover para melhorar o produto. Antoine de Saint Exupéry capturou a extravenção numa linha brilhante “a perfeição é finalmente atingida não quando não há mais nada a acrescentar, mas quando não há mais nada a retirar” (Se pretender uma abordagem mais formal à extravenção, consulte o artigo How to Cut Features and Enjoy it — 20+ questions to find the simplest design).
  3. Documente os caminhos não tomados. A fim de superar a Lei do Instrumento e evitar revisitar o mesmo terreno no futuro, documente o que não está a fazer e por que não está a fazê-lo. Coloque os caminhos não tomados no wiki, mesmo se todos os seus instintos lhe dizem para colocá-los numa gaveta de arquivo. Assim como a deformação de domínio nos encoraja a começar a martelar sem pensar, a deformação de publicação desencoraja-nos a documentar as coisas que nós não fazemos (a deformação de publicação é poderosa o suficiente para cegar a própria história. Consegue citar uma pessoa, viva ou morta, que seja celebrada por não ter feito algo?).

Continue a criar

Este artigo não defende o ludismo. Engenheiros e designers dão o seu melhor quando criam em território desconhecido. Este artigo defende uma mudança na perspectiva. Projeto e engenharia são intervenções em situações complexas. Praticar isto é praticar a humildade e precisão. O software, as pessoas que o utilizam e as situações que o criam são mais complexas do que pensamos que sejam. Espere resultados surpreendentes, não lineares, de cada intervenção. Lembrar-se disto é evitar iatrogenia no software. Primum non nocere, “primeiro não causar dano” é uma máxima global da medicina. Também se devia se tornar no grito de guerra de profissionais de software.

Bibliografia recomendada

Taleb, Nassim Nicholas, Antifragile: Things That Gain From Disorder, EUA, Random House, 2012

Este livro fornece um modelo de sistemas complexos que o ajuda a ver os contornos antes de intervir. "Em suma, qualquer coisa em que há intervencionismo ingénuo, ou melhor, mesmo apenas intervenção, terá iatrogenias." - Nassim Taleb

 

Tradução livre do artigo “When the solution is the problem”, de Aneesh Karve.

10 sugestões para melhorar a gestão do Tempo

De vez em quando, depara-mo-nos com programadores que se queixam de como é difícil arranjar tempo, na sua agenda diária, para aprender coisas novas. Apesar da vida moderna ser bastante movimentada, o mais frequente é as pessoas não possuírem vontade, energia e competências de gestão de tempo para lidar com a situação. Na maioria das vezes, esta falta de tempo deve-se ao facto das pessoas gerirem mal o seu tempo, de não terem qualquer estratégia para o gerir de forma eficaz.

Aqui estão 10 sugestões que nos podem ajudar a gerir o tempo de uma melhor forma.

Se aplicar estas sugestões, poderá gerir o seu tempo de uma maneira melhor do que antes. Não vamos ver quaisquer gráficos ou figuras extravagantes, que mais não fazem do que ter uma boa aparência no papel. Vamos ver simplesmente algumas sugestões práticas que poderá começar a usar de imediato.

1. Não aceite coisas maiores que a sua capacidade

Sejamos honestos – todos nós temos recursos limitados em termos de tempo, energia e capacidade para completar uma tarefa. Mesmo que queiramos completar mais tarefas, vai haver um limite para o que conseguimos realizar num dia.

Em vez de tentar terminar muitas tarefas, que não vão caber num só dia, tente apenas aquelas que cabem no seu “prato”. Se tentar realizar muitas tarefas de uma só vez, o mais provável é não conseguir termina-las todas no mesmo dia. E então este "fracasso" vai aumentar a pressão para completar as tarefas à pressa, no dia seguinte. Isto, por sua vez, pode causar mais erros do que o esperado e adiar ainda mais a conclusão das mesmas.

Assim, é importante identificar e começar apenas aquilo que conseguir terminar no  mesmo dia. Seja realista ao fazê-lo. Aplique bom senso e experiência prévia para concluir essas tarefas. Com alguma experiência, pode determinar o limite que é o ideal para si - não muito baixo (preguiça não permitida), nem muito alto (fadiga não permitida).

2. Planeie um dia de cada vez 

Um erro comum que algumas pessoas cometem é o de planear um longo período de tempo. Quanto maior o período, maior a probabilidade surgirem obstáculos inesperados.

Não planeie uma semana, por exemplo, porque algo inesperado pode surgir. Para começar, planeie apenas um dia (ou mesmo menos) de cada vez. Uma vez criado o plano, cinja-se a esse plano sob qualquer das formas. Muitas pessoas mudam de plano com muita frequência. Seja firme. Depois de gizar um plano, não o altere sob qualquer circunstância. Ao fim de algum tempo vai aprender a introduzir alguma flexibilidade na sua programação diária. Mas até lá seja rígido sobre o seu plano diário. Aprenda a dizer NÃO a tudo o que não esteja planeado.

3. Estabeleça prioridades para as tarefas

Simplesmente escolher tarefas para terminar num só dia não é suficiente. Também é necessário estabelecer prioridades. Nem todas as tarefas são igualmente importantes, o seu nível de urgência também é diferente. Baseado na importância e urgência é preciso definir alguma prioridade para cada tarefa.

Pode-se definir a prioridade de várias formas diferentes - numa escala de 1 a 10, ou de Baixa, Abaixo do Normal, Normal, Acima do Normal e Alta (valores tirados a partir da enumeração CacheItemPriority, do ASP.NET – Low, BelowNormal, Normal, AboveNormal, High). Lembre-se que estas prioridades são para as tarefas a serem concluídas num único dia.

Uma vez estabelecidas as prioridades, comece a executar as tarefas por ordem, da de maior prioridade para a de menor. Se não conseguir completar uma tarefa de baixa prioridade nesse dia, ela poderá passar para o dia seguinte e, eventualmente, subir na na ordem de prioridades.

4. Evite distrações

Deve aprender a evitar todos os tipos de distrações. Se estiver envolvido em algo urgente ou importante, diga às pessoas à sua volta para não o perturbar. Não pode esperar que elas tenham noção da sua urgência a menos que lhes diga especificamente. Aprenda a desligar o telemóvel. Além disso, desligue as notificações instantâneas de email e qualquer outro tipo de alerta. Não esteja a verificar o email a cada momento. Se está a fazer algo, não interrompa a tarefa em curso para responder a um email. Não ligue os headphones ao ouvido quando está a fazer algo importante.

Estas e muitas outras distrações causam perda de tempo. Embora possam ser apenas alguns segundos ou minutos, quando se acumulam, podem resultar numa considerável quantidade de tempo. O pior ainda, é que sua mente se afasta a cada distração e precisa de mais tempo para se voltar a concentrar na tarefa a ser concluída.

5. Faça uma melhor gestão do seu tempo online

Como programador de software, estará conectado à Internet durante uma grande parte do seu dia de trabalho. No entanto, só porque tem uma ligação à Internet não significa que deva permanecer sempre online. Muitas pessoas têm o hábito de verificar os emails com frequência e de fazer log-in no Facebook ou Twitter de vez em quando. Isto é desnecessário. Crie o hábito estrito de aceder a emails e redes sociais numa altura específica do dia. Defina também a frequência de verificar estes recursos. A jornada de trabalho tem, normalmente, um "horário nobre", onde o seu nível de energia e produtividade é mais alto. Não gaste este horário nobre na verificação de emails ou nas redes sociais. Algumas coisas que podem ajudar incluem:

  • Só porque um email chegou à sua caixa, não quer dizer que lhe deva responder imediatamente. Não ponha de lado a tarefa que está a executar, agende a resposta ao email para mais tarde.
  • Decida a prioridade e importância para os emails. Isto é especialmente importante se receber dezenas de mensagens todos os dias. Use filtros de email para guardá-los em pastas separadas e depois olhe para eles de acordo com o horário programado.
  • Aprenda a limpar os seus feeds de notícias em sites de redes sociais, como o Facebook.
  • Utilize algum plugin de redes sociais para melhorar a sua experiência no Facebook e Twitter.
  • Faça uma pequena lista de sites/blogs que são importantes e úteis para o seu trabalho diário ou para actualizar o seus conhecimentos. Em vez ter que ler centenas de sites diferentes, mantenha um olho nas actualizações destes sites selecionados.
  • Desligue todos os alertas ou notificações instantâneas. Isso inclui email e sites de redes sociais.
  • Durante a leitura de um conteúdo online é fácil dispersar-se perante a pilha de informação. Mantenha-se focado. Faça questão de se manter no tópico original que estava a ler/pesquisar.

6. Faça uma melhor gestão do seu tempo em viagem

Se viver numa cidade como Luanda (ou Londres, ou Lisboa, …), o mais provável é que gaste uma quantidade considerável de tempo a viajar – da sua casa para o escritório e de volta para casa. Este tempo é pouco produtivo, mas pode utilizá-lo de uma forma mais eficaz. Escolha um meio de locomoção que seja confortável. Por exemplo, em vez de ir em viatura própria, opte por um autocarro de carreira, climatizado e apanhe-o numa paragem onde consiga ir sentado. Porquê? Uma vez que está numa posição confortável, pode pensar nalgumas formas criativas de utilizar o tempo de viagem. Aqui estão algumas sugestões:

  • Leve um leitor multimédia portátil para ouvir/ver áudio, vídeos e podcasts no caminho.
  • Dê uma vista de olhos a um livro ou revista técnica, que adicione novos conhecimentos.
  • Leve um bloco de notas e uma caneta. Anote, desenhe, visualize conceitos relacionados com o seu trabalho ou coisas novas que viu. Isto vai permitir-lhe rever o que aprendeu.
  • Durma. Não é brincadeira. Há muitos programadores que não dormem o suficiente à noite, devido a várias razões (porque vão tarde do escritório para casa, antecedentes familiares, meio ambiente, etc.). Se não pode fazer mais nada para utilizar o tempo de viagem, aproveite para induzir relaxamento e descanso no seu corpo. Use um pouco de técnicas de yoga, para relaxar durante este tempo. O corpo recargado é mais eficiente na realização de tarefas, uma vez chegado ao escritório ou a casa.

7. Estabeleça as expectativas das pessoas à sua volta

As pessoas à sua volta, no escritório ou em casa, têm certas expectativas sobre si. Essas expectativas são definidas com base na forma como interage com elas diariamente. Por exemplo, uma pessoa que está sempre a ajudar os outros, cria essa mesma expectativa nas pessoas ao seu redor. Então, se um dia essa pessoa não ajudar, as outras pessoas acham estranho e podem mesmo desenvolver sentimentos negativos sobre ela. Elas costumam facilmente esquecer todas as ocasiões em que essa pessoa as ajudou e lembrar-se apenas desta ocasião, quando não prestou a sua ajuda.

Se o seu comportamento indica que está sempre disponível quando as pessoas precisam de alguma coisa (seja importante ou não, não interessa), então elas vão esperar sempre o mesmo comportamento. Tem que os avisar que não gosta de ser perturbado quando está a fazer algo importante. Não pode assumir que as pessoas vão saber o significado da tarefa que está a fazer, automaticamente. É necessário transmitir especificamente a necessidade de estar sossegado e também dizer-lhes porque que é que a atividade é importante para si. Por exemplo, em vez de simplesmente dizer aos seus familiares para não o perturbar, pode explicar-lhes como o conseguir terminar a tarefa que tem mãos o vai ajudar e à sua família (digamos para uma promoção, ou aumento salarial, ou um novo emprego). Aqui estão algumas coisas a ter em consideração:

  • Aprenda a dizer NÃO.
  • Aprenda a delegar tarefas.
  • Se lhe estão a ser atribuídas muitas responsabilidades, avise as suas chefias, de forma adequada.
  • Nas relações profissionais, é bem aceite recusar-se educadamente a participar em eventos pessoais. Se estiver a tornar-se mais íntimo de uma dada pessoa, pode achar que é difícil dizer-lhe NÃO.
  • Não permita que os outros o pressionem ou forcem a participar em algo que vai afetar os seus horário e a sua agenda, previamente planeada.
  • Transmita a importância de terminar atempadamente uma tarefa que tenha em mãos, aos seus familiares. Depois de completar a tarefa, passe algum tempo de qualidade com eles.

8. Evite sonhar acordado e conversas sem importância

Quando estiver a realizar uma tarefa não permita que a sua atenção se desvie. Se tem que pensar sobre algo não relacionado com a tarefa atual, primeiro termine a tarefa e, em seguida, passe algum tempo a pensar sobre o assunto, antes de se concentrar de novo no seu trabalho. Faça questão de evitar conversa desnecessária ou de circunstância. Aprenda a desligar o seu telemóvel enquanto completa alguma tarefa importante. Muitos programadores costumam manter alguma aplicação de mensagens instantâneas (Skype, Google ou Facebook Messenger) a correr em segundo plano, enquanto fazem o seu trabalho. De vez em quando, mudam para essa aplicação, para participar em alguma conversa. Aprenda a evitar essas distrações.

9. Aumente o seu nível de energia

A gestão de tempo não é apenas a criação de horas disponíveis para fazer algum trabalho. É igualdade importante possuir a energia necessária para completar as tarefas. De fato, se o seu nível de energia é alto, pode realizar muito mais trabalho num determinado período de tempo. Para aumentar o seu nível de energia três coisas são importantes - dieta, sono e respiração (prana). Faça uma dieta saudável, coma alimentos naturais e que consiga digerir facilmente. Durma o bastante. Se possível durma uma pequena sesta retemperadora, durante a tarde. Por exemplo, o Yoga tem muitos tipos de exercícios de respiração (Pranayama), para revitalizar todo o sistema de energia no corpo. Aprenda-os a partir de algum especialista e torne-os parte de sua rotina diária.

10. Mantenha-se em forma

Isto pode soar como não relacionado com o tema do artigo. No entanto, a sua aptidão física e mental desempenham um papel vital na gestão do tempo. Um equipamento mente-corpo não saudável não se pode concentrar na tarefa em mãos. Ele não consegue gerar bons níveis de energia e, como resultado, você acaba por não cumprir os prazos. Exercitar-se diariamente e manter-se em forma é um aspecto importante de uma vida em plenitude. Exercícios como caminhada rápida, jogging, corrida, natação ou yoga são todos adequados a aptidão física geral. Se está adoentado, consulte um médico especialista imediatamente e elimine a raiz do problema o mais breve possível. Um indivíduo saudável é capaz de realizar muito maior quantidade de trabalho, num determinado período de tempo, em comparação com uma pessoa não saudável ou em baixa forma física.

Tradução livre do artigo “10 Tips For Better Time Management”, de Bipin Joshi.

Nota: Na tradução optei por usar o masculino, não com qualquer intenção de discriminação, mas apenas como facilidade de linguagem.