Descoberta do Conhecimento em Bancos de Dados e a Gestão Estratégica

Data-mining sob o ponto de vista de negócios:

BUSINESS INTELLIGENCE

INTRODUÇÃO

As instituições governamentais, organizações de interesse público ou privado, impulsionadas pela tecnologia e abrangência da INTERNET, disponibilizam imensos volumes de dados e informações, porém, de difícil visualização, interpretação e respectiva aplicação e capitalização do conhecimento.

Ver o post original 1.217 mais palavras

Arquitetura de armazenamento: HBase e MongoDB

Seguem os slides de uma breve apresentação sobre a arquitetura de armazenamento nos data-stores HBase e MongoDB. Essa apresentação faz parte de um ciclo de seminários de uma disciplina do curso de mestrado em Ciência da Computação da UFU. Esse é um estudo preliminar dos bancos NoSQL de maneira geral, temos a expectativa de que a aplicação desses conceitos possa ajudar a escalar o processamento de dados massivos, como dados metereológicos, por exemplo. Segue a apresentação:

Evitando distrações com a Internet e Redes Sociais

Confesso que acho o tema controverso. Há quem defenda que é possível trabalhar, escutar música e twittar tudo ao mesmo tempo. Por outro lado, há quem diga que só é possível ser produtivo e fazer um bom trabalho se você estiver com a cara enfiada no monitor. Do lado das empresas, há aquelas que bloqueiam qualquer acesso à Internet, algumas bloqueiam redes sociais, sites de notícias e há ainda aqueles que oferecem liberdade completa ao funcionário.

Sem a pretensão de oferecer uma solução definitiva, ou defender um ponto de vista, posso apenas fazer um relato pessoal. Pessoalmente, consigo compenetrar facilmente em tarefas de programação, matemática, ou ainda redação. Porém, aquele build all, aquele restart no servidor de aplicação e outras tarefas simples, mas que nos interrompem por uns 3 ou 4 minutos (é pouco, mas quando se está em um estado de fluxo, isso é uma eternidade) são o suficiente pra dar aquela escapada pro Facebook, Twitter ou fórum preferido. O problema dessas escapadas, é que elas podem tomar um tempo um pouco maior do que os 3 ou 4 minutos do build, e que acumulados podem representar uma perda de tempo mais significativa.

Para quem se sente prejudicado com esse tipo de situação, fica a dica de 2 plugins, um para o Chrome e outro para o Firefox. Eles permitem que você forneça uma lista de sites para serem bloqueados durante um certo período de tempo. Assim, quando der aquela coceira para acessar um deles, o plugin não vai permitir:

  • Nanny for Google Chrome (TM): o plugin Nanny está disponível na Chrome Web Store, basta um clique para adicioná-lo;
  • LeechBlock: o LeechBlock está disponível para o Firefox, muito simples de instalar também;

Os dois plugins funcionam de maneira bastante semelhante. Você fornece uma lista de sites, e estipula ainda um intervalo de tempo no qual deseja que os sites sejam bloqueados. O intervalo não precisa ser único. Por exemplo, você definir que quer o bloqueio somente das 8h às 12h e das 14h às 18h. Você pode ainda estipular uma tolerância de alguns minutos antes dos sites serem bloqueados. Enfim, fica ao gosto do freguês.

Tutorial: escrevendo seu próprio servidor de transferência arquivos (parte2)

Bom, na segunda parte do tutorial vou mostrar como aceitar conexões de um cliente através de um socket e enviar as respostas.
Para criar um servidor precisamos, antes de mais nada, abrir um ServerSocket. Dessa maneira vamos abrir uma porta TCP para aceitar conexões de outros hosts. Durante os testes, é importante certificar-se de que o firewall vai deixar as conexões entrarem.
O código abaixo é a estrutura básica do servidor. Ele contém um ServerSocket dentro de um laço para aceitar as conexões. Ao aceitar uma conexão, o servidor delega a conexão para uma nova thread. Dessa maneira, o ServerSocket pode aceitar novas conexões sem precisar esperar o processamento da conexão anterior:

public class Servidor {
	private ServerSocket	server;
	private boolean started = false;

	public void start() throws Exception{
		started = true;
		
		int count = 1;
                server = new ServerSocket(3000);
		while(started){
			final Socket socket = server.accept();
			
			Thread thread = new Thread(new Runnable(){
				@Override
				public void run() {
					service(socket);
				}
			}, "service-" + count++);
		}
	}
	

	public void stop() throws IOException{
		started = false;
		server.close();
	}
}

Agora, resta escrever a lógica do tratamento das requisições do cliente. Pode até não parecer, mas esta parte é a mais “chata” de se escrever. Isso porque a transmissão de dados através de um stream tem algumas peculiaridades que quando não são observadas, podem custar algumas boas horas de debug e xingamento. Primeiro, vou começar escrevendo como NÃO ler dados de um stream, sabendo que é a entrada de um socket:

private void service(Socket socket) throws IOException{
		BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
		
		byte [] buf = new byte[256];
		int count = 0;
		while((count = bis.read(buf)) > -1){ //só retorna -1 quando o socket do outro lado é fechado
			//...
		}
	}

No código acima, o método lê bytes do stream de entrada do socket até que o método read do stream retorne -1. Pois bem, o valor -1 somente é retornado quando o stream encontra a marcação EOF nos dados, e isso somente acontece quando a conexão é fechada. Assim, se você estiver escrevendo uma lógica do tipo request-response, com essa abordagem é possível que você leia toda a request e mesmo assim ficar com a thread bloqueada, pois você ainda não recebeu aquele tão esperado -1.
Um outro erro seria usar o método available():

private void service(Socket socket) throws IOException{
		BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
		
		byte [] buf = new byte[256];
		int count = 0;
		while(bis.available() > 0){
			//...
		}

A pegadinha nesse caso é a seguinte: o método available() retorna o número de bytes disponíveis para no buffer. Isso significa que o método pode retornar 0 antes de terminar a leitura de toda a entrada, fazendo com que a thread continue com dados incompletos.
Mas então, qual a solução para este dilema ? É simples, basta trocar mensagens com tamanho fixo ou com algum tipo de delimitador.
O jeito mais fácil que eu encontrei é trocar objetos serializáveis, pois assim a própria JVM se encarrega de inserir delimitadores e fixar os tamanhos dos campos:

class Message implements Serializable {
	private static final long	serialVersionUID	= 1L;

	public int command;
	public String[] args;
	public String[] result;
	public boolean disconnect;
	public boolean fileToSend;
	public String fileName;
	public byte[] fileData;
}

A classe acima é um exemplo de como pode ser um objeto para troca de dados entre o cliente e o servidor. Praticamente, todos os campos que podem ser utilizados na comunicação são colocados em uma única estrutura. Vejamos agora um exemplo de como ficaria a nossa lógica, no servidor:

private void service(Socket socket) throws IOException, ClassNotFoundException {
		ObjectInputStream entrada = new ObjectInputStream(socket.getInputStream());
		ObjectOutputStream saida = new ObjectOutputStream(socket.getOutputStream());
		
		while(true){
			Message request = (Message) entrada.readObject();
			
			if(request.disconnect){
				socket.close();
				break;
			}
			
			Message response = new Message();
			Command cmd = new Command();
			
			response.result = cmd.execute(request.command, request.args);
			
			saida.writeObject(response);
		}
	}

Bom, espero que nesses 2 posts eu tenha conseguido colocar os principais pontos que devem ser levados em consideração na hora de escrever esse tipo de aplicação.

Tutorial: escrevendo seu próprio servidor de transferência arquivos (parte1)

Uma das primeiras aplicações de rede que surgiram foram os servidores de transferência de arquivos.
Hoje o protocolo mais usado para esse fim é o FTP
(File Transfer Protocol), e pode ser uma boa fonte de referências para o assunto.
Vamos ao que interessa. Acho que o passo mais simples é começar implementando os comandos, sem pensar
na comunicação em rede por enquanto. Um passo de cada vez.
O código abaixo é um exemplo simples de como criar um diretório:

class Command{
	public void criarDiretorio(String nomeDiretorio){
		File dir = new File(nomeDiretorio);
		
		if(dir.exists()){
			if(dir.isDirectory()){
				System.out.println("O diretório " + nomeDiretorio + " já existe!");
			}else if(dir.isFile()){
				System.out.println("O caminho " + nomeDiretorio + " é um arquivo!");
			}
		}else{
			dir.mkdir();
		}
	}
}

Reparemos que a execução do comando já está separada no seu próprio método. Isolar as partes do seu programa é um pouco mais trabalhoso, mais acreditem, deixa o desenvolvimento muito mais consistente. Bom, com essa estratégia podemos escrever cada comando em seu próprio método, e ainda, um método controlador que executa o comando apropriado de acordo com um código ou nome recebido:

class Command {
	public static final int	CRIAR_DIR	= 1;
	public static final int	LISTAR_DIR	= 2;

	public String[] executar(int codigoComando, String[] args) {
		switch (codigoComando) {
			case CRIAR_DIR:
				return criarDiretorio(args[0]);

			case LISTAR_DIR:
				return listarConteudo(args[0]);

			default:
				return null;
		}
	}

	private String[] criarDiretorio(String nomeDiretorio) {
		File dir = new File(nomeDiretorio);

		String[] result = null;

		if (dir.exists()) {
			if (dir.isDirectory()) {
				result = new String[] { "O diretório " + nomeDiretorio + " já existe!" };
			} else if (dir.isFile()) {
				result = new String[] { "O caminho " + nomeDiretorio + " é um arquivo!" };
			}
		} else {
			dir.mkdir();
		}

		return result;
	}

	private String[] listarConteudo(String nomeDiretorio) {
		File dir = new File(nomeDiretorio);

		String[] result = null;

		if (!dir.exists()) {
			result = new String[] { "O diretório " + nomeDiretorio + " não existe!" };
		} else if (dir.isFile()) {
			result = new String[] { "O caminho " + nomeDiretorio + " é um arquivo!" };
		} else {
			result = dir.list();
		}

		return result;
	}
}

Com essa estrutura, já é possível escrever diversos comandos, cada um com seu próprio código. Reparem que comando que seleciona o comando a ser executado recebe um array de Strings, para receber um número variável de argumentos. As saídas dos comandos deixaram de ser enviadas para saída padrão para serem retornadas em arrays também. Assim, a execução dos comandos fica isolada da exibição dos resultados, de forma que os resultados podem ser exibidos no console, em uma janela ou transmitidos pela rede.

Testando sua interface em Swing

A prática de escrever testes automatizados vem sendo amplamente adotada por equipes de todo o mundo. Embora a maior parte do material encontrado na internet trata sobre testes unitários, também é possível escrever testes de integração automatizados, testes do banco de dados, etc. Enfim, é possível automatizar qualquer tipo de teste, uma vez que a variedade de ferramentas para este propósito é grande.
Nesse post, eu quero mostrar um exemplo simples de como é possível escrever um teste automatizado para uma interface em Swing, usando apenas o framework JUnit. O nosso exemplo será uma simples calculadora, que realiza apenas a soma, para resumir o código, vejamos:

public class TestCalculadora {
	@Test
	public void testSomar() {
		Calculadora calculadora = new Calculadora();
		
		Integer numero1 = 10;
		Integer numero2 = 5;
		
		calculadora.textFieldNumero1.setText(numero1.toString());
		calculadora.textFieldNumero2.setText(numero2.toString());
		
		calculadora.buttonSomar.doClick(); //simula um click programaticamente
		
		Integer resultado = numero1 + numero2;
		
		Assert.assertEquals(resultado.toString(), calculadora.textFieldResultado.getText());
	}
}

O teste acima especifica que nossa calculadora deve receber os números a serem somados em campos de texto, e após clicar no botão de soma, o resultado será exibido em um terceiro campo de texto. Reparem, que o método doClick() simula programaticamente o click no botão.
A implementação de Calculadora é o código suficiente para passar nesse primeiro teste:

public class Calculadora {
	JTextField	textFieldResultado;
	JTextField	textFieldNumero1;
	JTextField	textFieldNumero2;
	JButton		buttonSomar;

	public Calculadora() {
		this.textFieldResultado = new JTextField();
		this.textFieldNumero1 = new JTextField();
		this.textFieldNumero2 = new JTextField();
		this.buttonSomar = new JButton();
		
		this.buttonSomar.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				somar();
			}
		});
	}
	
	private void somar(){
		int numero1 = Integer.valueOf(textFieldNumero1.getText());
		int numero2 = Integer.valueOf(textFieldNumero2.getText());
		
		textFieldResultado.setText(String.valueOf((numero1 + numero2)));
	}
}

Como podem reparar, esse classe não é uma interface gráfica propriamente dita, pois não estende nenhum componente gráfico. Mas isso é resolvido facilmente:

public class Calculadora extends JFrame {
	JTextField	textFieldResultado;
	JTextField	textFieldNumero1;
	JTextField	textFieldNumero2;
	JButton		buttonSomar;

	public Calculadora() {
		this.setSize(150, 200);
		this.textFieldResultado = new JTextField(12);
		this.textFieldResultado.setEnabled(false);
		this.textFieldNumero1 = new JTextField(12);
		this.textFieldNumero2 = new JTextField(12);
		this.buttonSomar = new JButton("Somar");

		this.buttonSomar.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				somar();
			}
		});

		getContentPane().setLayout(new BorderLayout());

		JPanel panel = new JPanel(new FlowLayout(FlowLayout.CENTER));

		panel.add(textFieldNumero1);
		panel.add(textFieldNumero2);
		panel.add(buttonSomar);
		panel.add(textFieldResultado);

		getContentPane().add(panel, BorderLayout.CENTER);
	}

	private void somar() {
		int numero1 = Integer.valueOf(textFieldNumero1.getText());
		int numero2 = Integer.valueOf(textFieldNumero2.getText());

		textFieldResultado.setText(String.valueOf((numero1 + numero2)));
	}

	public static void main(String[] args) {
		EventQueue.invokeLater(new Runnable() {
			@Override
			public void run() {
				Calculadora calculadora = new Calculadora();
				calculadora.setVisible(true);
			}
		});
	}
}

Assim, com o teste testSoma() passando, a funcionalidade da tela está pronta, restando adicionar o código relativo à aparência propriamente dito. Percebemos, que o teste automatizado não substitui um teste manual, afinal de contas, requisitos como layout, estética da tela, usabilidade e etc. dependem da percepção do usuário. Porém, auxilia bastante no desenvolvimento das funcionalidades da tela. A desvantagem nesse caso, é a necessidade de expor os componentes da tela para a classe de teste. Nesse caso, coloquei a interface e o teste no mesmo pacote e declarei os componentes da tela com acesso default. As alternativas para manter estes campos private seria criar getters para os componentes, criar métodos que delegam o set das propriedades, ou ainda, acessar os campos por reflection.

Você sabe o que é metacognição ?

Você já parou para pensar como o seu cérebro funciona ? Já pensou como aquele CDF da cadeira na frente consegue guardar tanta informação ? Ou como sua mente retém conhecimento ? Pois bem, são desses assuntos que trata a metacognição.
A metacognição é o estudo sobre como o cérebro humano retém informação, sobre como é este processo. Ela reúne basicamente temas sobre memória, aprendizado, tipos de inteligência, etc. Conhecendo um pouco melhor o funcionamento do nosso cérebro, é possível desenvolver algumas técnicas simples, mas que fazem uma diferença absurda no processo de aprendizado.
Antes de mais nada é preciso conhecer algumas regiões do cérebro para entender porque essas técnicas funcionam. Basicamente são 2 partes que nos interessam: a memória temporária e a memória permanente. A nossa vivência diária vai sendo armazenada na região da memória temporária, até que, durante uma boa noite de sono parte da informação temporária é transferida para a região de memória permanente. Mas o que vai para a memória permanente e o que não vai ? Bom, quem decide isso é o cérebro, e o truque da coisa é, de alguma maneira, tentar dizer para ele que aquilo que você leu ou estudou é importante o suficiente para ser armazenado. Mas como eu vou dizer para o meu cérebro o que é importante ? Bom, para isso precisamos conhecer mais um pouco o funcionamento do cérebro. Basicamente, a nossa memória é associativa. Dificilmente lembramos de fatos isolados, e algumas vezes, as informações vem acompanhadas até de alguma carga emocional.
Com isso em mente, podemos estabelecer um roteiro básico de estudo para otimizar o aprendizado, com 2 passos básicos:

  • estabelecer o máximo de associações sobre o tema estudado
  • dormir bem no dia

O segundo passo é auto-explicativo, penso eu. Mas e quanto ao primeiro, o que seria afinal estabelecer o máximo de associações sobre o tema estudado ? É bastante simples também. Vou falar sobre a minha experiência, basicamente, a de um programador estudando. Passo fundamental: ler. Para aprender sobre um assunto é preciso ler sobre ele. Juntar código achado na Internet para “fazer funcionar” definitivamente não é uma boa maneira de aprender. Você só sabe o que está fazendo se for capaz de explicá-lo para outra pessoa. Mas ler não basta, a experiência da leitura precisa ser enriquecida: resumos, desenhos, diagramação, ou seja, recursos que o ajudem a visualizar o que você está lendo. E por fim, um pouco de código para ver a coisa funcionar.
Em resumo é isso do que trata a metacognição. Uma ótima referência são os livros da série Head First, ou Use a Cabeça, pois são todos trabalhados em cima destes conceitos. No Brasil, um dos pioneiros neste estudo é o Prof. Pierluigi Piazzi, que já escreveu alguns livros sobre o tema e ministrou diversas palestras pelo país.


Categorias

Atualizações Twitter