Na primeira parte do artigo expliquei que inverti a ordem mais comum de artigos sobre Windows Services, criando primeiro as funcionalidades de negócio para depois criar a infra-estrutura necessária para executar os processos em background. Depois de ter implementado o monitor de diretórios, irei implementar a execução periódica de um processo, iniciando sem a intervenção do usuário.
Um processo periódico precisa apenas de algumas operações e informações básicas para realizar seu trabalho. Ele deve oferecer uma forma de ser iniciado e parado e receber a informação do período de “hibernação”. Utilizando um timer do .Net Framework a tarefa é bem fácil.
A Listagem 1 mostra a implementação bem simples da classe que abstrai a API. Logo no construtor o BackgroundRunner recebe o seu tempo de descanso, em milissegundos, e também uma instância de um ITask. Através da interface consigo abstrair a regra de negócio que será executada, minha classe somente entende de contar o tempo e executar a tarefa solicitada na hora certa. Eu poderia ter optado pela estratégia do delegate, utilizada no monitor de diretórios, mas quis apresentar uma estratégia nova.
Listagem 1: Implementando o BackgroundRunner
public class BackgroundRunner
{
private readonly Timer timer;
private readonly ITask _task;
public BackgroundRunner(double interval, ITask task)
{
_task = task;
timer = new Timer(interval);
timer.Elapsed += Elapsed;
timer.AutoReset = true;
timer.Enabled = true;
}
public void Start()
{
timer.Start();
}
public void Stop()
{
timer.Stop();
}
void Elapsed(object sender, ElapsedEventArgs e)
{
_task.Execute();
}
}
OBS: Existem três tipos de Timers no .Net Framework, para saber os detalhes de funcionamento de cada, leia este artigo.
O teste para o BackgroundRunner também é simples. Desta vez estou utilizando o Rhino Mocks para criar um mock da interface ITask e definir quantas vezes a tarefa será chamada. Brincando com esta variável, com o tempo de espera, definido na chamada do Wait() e o intervalo, definido no BackgroundRunner, é possível verificar se a tarefa está sendo executada periodicamente e no tempo certo.
Listagem 2: Testando o BackgroundRunner
namespace UtilsTest
{
[TestFixture]
public class BackgroundRunnerTest
{
[Test]
public void TestPeriodicRun()
{
MockRepository mockery = new MockRepository();
ITask mockedTask = mockery.CreateMock<ITask>();
Expect.Call(mockedTask.Execute).Repeat.Times(2);
mockery.ReplayAll();
BackgroundRunner runner = new BackgroundRunner(500, mockedTask);
runner.Start();
TestHelper.Wait(1100);
mockery.VerifyAll();
}
}
}
Outros cenários ainda precisam ser testados. Além disto, esta classe pode ser melhorada com algumas funcionalidades como receber mais de uma ITask para executar. Mas por enquanto vou ficar por aqui.
Até o próximo capítulo.
Código-fonte: