Calculadora "Bem OO"

15 respostas
aajjbb

Para testar um pouco dos meu ainda pouco conhecimento em java, resovi criar essa calculadora, gostaria de saber se minha “logica” esta bem orietanda a objetos ou não, eu acho q poderia usar interfaces na implementação das operações, mas nao soube como fazer em um primeiro momento, se alguem puder opinar, agradeceria.

package br.com.operacoes;

import java.util.Scanner;

public class Principal {
	public static void main(String[] args) {
		Scanner input = new Scanner(System.in);
		
		int escolha;

			System.out.println("Entre com a Operação Desejada.");
			
			System.out.println(" 1 para Adição \n 2 para Subtração \n 3 para Multiplicação \n 4 para divisão");
			escolha = input.nextInt();
				
				switch (escolha) {
				case 1:
					Adicao adicao = new Adicao();
					adicao.adiciona();
					
					break;
				case 2:
					Subtracao subtracao = new Subtracao();
					subtracao.subtrai();
					
					break;
				
				case 3:
					Multiplicacao multiplicacao = new Multiplicacao();
					multiplicacao.multiplica();
					
					break;
					
				case 4:
					Divisao divisao = new Divisao();
					divisao.divide();
				}
	}

}

Operações

import java.util.Scanner;

public class Adicao {

	public void adiciona() {
Scanner input = new Scanner(System.in);
		
		long number1;
		long number2;
		long result;
		
		System.out.println("Digite um primeiro Numero para Adição \n");
			number1 = input.nextLong();
		
		System.out.println("Digite um segundo Numero Para a Adição \n ");
			number2 = input.nextLong();
			
			result = number1 + number2;
			
			System.out.println("O resultado é " + result);
			
		/*			*/
			
			System.out.println("Digite 0 para sair");
				int choice = input.nextInt();
				
					if (choice == 0) {
						System.exit(0);
				}	else {
					System.out.println("");
			}

		
	}

}

______________________________________________

import java.util.Scanner;

public class Multiplicacao {

	public void multiplica() {
		Scanner input = new Scanner(System.in);
		
		long number1;
		long number2;
		long result;
		
		System.out.println("Digite um primeiro Numero para Multiplicacao \n ");
			number1 = input.nextLong();
		
		System.out.println("Digite um segundo Numero Para a Multiplicação \n ");
			number2 = input.nextLong();
			
			result = number1 * number2;
			
			System.out.println("O resultado é " + result);
			
		/*			*/
			
			System.out.println("Digite 0 para sair");
				int choice = input.nextInt();
				
					if (choice == 0) {
						System.exit(0);
				}	else {
					System.out.println("");
			}
		
	}

}
 ___________________________________

package br.com.operacoes;

import java.util.Scanner;

public class Divisao {

	public void divide() {
Scanner input = new Scanner(System.in);
		
		long number1;
		long number2;
		long result;
		
		System.out.println("Digite um primeiro Numero para Divisão \n ");
			number1 = input.nextLong();
		
		System.out.println("Digite um segundo Numero Para a Divisão \n ");
			number2 = input.nextLong();
			
				if(number2 == 0) {
					System.out.println("Não divida por 0");
					System.exit(0);
				} 
			
			result = number1 / number2;
						
			System.out.println("O resultado é " + result);
			
		/*			*/
			
			System.out.println("Digite 0 para sair");
				int choice = input.nextInt();
				
					if (choice == 0) {
						System.exit(0);
				}	else {
					System.out.println("");
			}

		
	}

}

15 Respostas

OneSr

Olha aajjbb, fato seja dito, você realmente está pensando corretamente em POO, más uma coisa poderia ser melhorada nessa sua maneira de pensar, digamos que no seu exemplo, você criou uma classe principal e varias classes com as 4 operações básicas, o mais indicado em relação a POO para você pensar seria a seguinte situação, criar uma classe principal exemplo ‘DriverCalulator’ e apenas uma unica classe ‘Calculadora’ com seus métodos, métodos esses que devem fazer as 4 operações básicas, e em relação a fazer isso com um toque gráfico poderia utilizar um JFrame ou em um primeiro momento bem básico a API JOptionPane, só mais uma coisa, não esqueça de criar um construtor para a classe, só para evitar a perda de informações caso a criação do objeto falhe por falta de um inicializador na memória.

Espero ter ajudado na opnião. :slight_smile:

renzonuccitelli

Na minha opinião está muito procedural isso. Leia esse tópico que acho que pode te dar uma idéia de como fazer isso de forma mais OO.

[]s

OneSr

renzonuccitelli:
Na minha opinião está muito procesural isso. Leia esse tópico que acho que pode te dar uma idéia de como fazer isso de forma mais OO.

[]s

realmente está muito procesural, mais dito que ele está pensando só como os objetos funcionam…acho isso um bom exercício antes de implementar isso diretamente na realidade do mundo, pelo menos para mim funcionou ‘antes de entrar no mundo real’ trabalhar com pequenos exemplos fora do mundo real em si, repare na frase que ele colocou logo no começo:

a lógica esta correta a não ser pelo fato que já esclareci anteriormente.

renzonuccitelli

OneSr:
renzonuccitelli:
Na minha opinião está muito procesural isso. Leia esse tópico que acho que pode te dar uma idéia de como fazer isso de forma mais OO.

[]s

realmente está muito procesural, mais dito que ele está pensando só como os objetos funcionam…acho isso um bom exercício antes de implementar isso diretamente na realidade do mundo, pelo menos para mim funcionou ‘antes de entrar no mundo real’ trabalhar com pequenos exemplos fora do mundo real em si.

É importante tentar aprender os conceitos OO desde o início. Eu tb programava de forma muito procedural no início e estou dando uma dica pro autor como eu gostaria que fizessem comigo. Programar em OO necessita o entendimento da importância da abstração e como implementá-la usando interfaces e classes abstratas. Além de isso te trazer a vantagem de maior reuso e desacoplamento, te ajuda a entender a arquitetura de muitos frameworks que existem.

Vou fazer um esboço de calculadora OO e colocar aqui, baseado no tópico que mandei o link.

Allan_Barcelos

Olha é melhor colocar tudo em uma classe só, tu pode continuar usando o Scanner para ler os dados

public class Calculadora{

public double soma(int x, int y){
return x + y;
}

public double divide(int x, int y){
return x / y;
}

public subtrai soma(int x, int y){
return x - y;
}

public multiplica soma(int x, int y){
return x * y;
}

public static void main(String[] args){

Scanner s = new Scanner(System.in);
int e = 0;

            System.out.println("Entre com a Operação Desejada.");  
             
            System.out.println(" 1 para Adição \n 2 para Subtração \n 3 para Multiplicação \n 4 para divisão");  

e = s.nextInt();
while(e < 1 || e > 4){
System.out.println("Vocês não digitou uma opção valida, escolha sua opção novamente.");
            System.out.println("Entre com a Operação Desejada.");  
             
            System.out.println(" 1 para Adição \n 2 para Subtração \n 3 para Multiplicação \n 4 para divisão");
e = s.nextInt();
}

else if(e == 1){

// pede e executa as operações usando um System.out.println();

}
else if(e == 2){

// pede e executa as operações usando um System.out.println();

}

else if(e == 3){

// pede e executa as operações usando um System.out.println();

}

else if(e == 4){

// pede e executa as operações usando um System.out.println();

}

}
}
renzonuccitelli

Olá, vou tentar colocar alguns conceitos de Poliforfismo e abstração nessa calculadora.

O que eu quero é que minha calculadora efetue cálculos. Sua responsabilidade vai ser de pegar os dados do usuário e ir imprimindo de acordo, executando operações que lhe são adicionadas. Então vai a interface que representa minha abstração:
public interface ICalculadora {
	void efetuarCalculo();
}

Feito isso eu pensei na abstração de minhas operações. Elas devem calcular argumentos, devem informar quantos argumentos espera e devem saber seu símbolo. Então vai minha abstração de Operação:

public interface IOperacao {
	public String getSimbolo();

	public int getNumeroDeArgumentos();

	public double calcular(double... args);
}
Com essas abstrações, implementei minha calculadora de forma que as inserções de dados se proceda igual nas calculadoras comuns (informa o primeiro argumento, escolhe operação, insere segundo argumento se for operador binário)
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

public class Calculadora implements ICalculadora {
	private Map<String, IOperacao> mapaDeOperacoes;
	private Scanner scanner = new Scanner(System.in);

	@Override
	public void efetuarCalculo() {
		System.out.println("Escolha um operando:");
		double primeiroArgumento = Double.parseDouble(scanner.nextLine());
		IOperacao op = mapaDeOperacoes.get(scanner.nextLine());
		if (op.getNumeroDeArgumentos() == 1) {
			System.out.println("=");
			System.out.println(op.calcular(primeiroArgumento));
		} else {
			double segundoArgumento = Double.parseDouble(scanner.nextLine());
			System.out.println("=");
			System.out.println(op.calcular(primeiroArgumento, segundoArgumento));
		}
	}

	public Calculadora(IOperacao... operacoesSuportadas) {
		mapaDeOperacoes = new HashMap<String, IOperacao>();
		for (IOperacao op : operacoesSuportadas) {
			mapaDeOperacoes.put(op.getSimbolo(), op);
		}
	}
}

Perceba o meu construtor. É nele que insiro as operações suportadas pela minha calculadora, passando como parametro. Isso vai ser importante daqui a pouco.
Também estou mapeando as operações de acordo com seu símbolo, de forma a acabar com o bloco switch. Blocos if else ou switch grande é mal cheiro. Pra dar manutenção é um saco.

Eu quero que minha calculadora execute uma adição, então vamos implementar a Adição, que é uma Operação:

public class Adicao implements IOperacao {
	@Override
	public double calcular(double... args) {
		return args[0] + args[1];
	}

	@Override
	public int getNumeroDeArgumentos() {
		return 2;
	}

	@Override
	public String getSimbolo() {
		return "+";
	}

	@Override
	public String toString() {
		return "Adição";
	}
}
Agora eu faço minha classe pra rodar a calculadora:
public class Main {
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		ICalculadora calculadora = new Calculadora(new Adicao());
		while (true) {
			calculadora.efetuarCalculo();
		}
	}
}

Legal, temos uma calculadora que faz adição. Mas vamos supor que queremos uma mais poderosa. Queremos uma com subtração.

Basta então criar a nova Operação:
public class Subtracao implements IOperacao {
	@Override
	public double calcular(double... args) {
		return args[0] - args[1];
	}

	@Override
	public int getNumeroDeArgumentos() {
		return 2;
	}

	@Override
	public String getSimbolo() {
		return "-";
	}

	@Override
	public String toString() {
		return "Subtração";
	}
}
E então adicionar na sua Calculadora:
public class Main {
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		ICalculadora calculadora = new Calculadora(new Adicao(), new Subtracao());
		while (true) {
			calculadora.efetuarCalculo();
		}
	}
}

Perceba que eu não mexi no código da calculadora para acrescentar a subtração. Então esse polimorfismo e abstração é um pulo do gato para reusar código. Para adicionar operações, basta seguir os passos iguais aos anteriores:

1) Extenda IOperacao, implementando sua operação.
2) Adicione a Operação na Calculadora, sem ter que mudar nada no código da mesma.

Um bom exércício seria implementar o fatorial, para ter um operador unário.

Espero ter conseguido ajudar, mesmo tendo feito a coisa meio rápido e sem tratar erros. O que importa é o conceito.

[]s

renzonuccitelli

Fiquei curioso pra ver se funcionava com operador unário.

primeiro inseri um código pra informar os simbolos válidos pro usuário:
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

public class Calculadora implements ICalculadora {
	private Map<String, IOperacao> mapaDeOperacoes;
	private Scanner scanner = new Scanner(System.in);

	@Override
	public void efetuarCalculo() {
		System.out.println("Escolha um operando:");
		double primeiroArgumento = Double.parseDouble(scanner.nextLine());
		System.out.println("Escolha o operador:");
		for (IOperacao o : mapaDeOperacoes.values()) {
			System.out.println(o.getSimbolo() + " para " + o);
		}
		IOperacao op = mapaDeOperacoes.get(scanner.nextLine());
		if (op.getNumeroDeArgumentos() == 1) {
			System.out.println("=");
			System.out.println(op.calcular(primeiroArgumento));
		} else {
			double segundoArgumento = Double.parseDouble(scanner.nextLine());
			System.out.println("=");
			System.out.println(op.calcular(primeiroArgumento, segundoArgumento));
		}
	}

	public Calculadora(IOperacao... operacoesSuportadas) {
		mapaDeOperacoes = new HashMap<String, IOperacao>();
		for (IOperacao op : operacoesSuportadas) {
			mapaDeOperacoes.put(op.getSimbolo(), op);
		}
	}
}
Aí implementei o Fatorial:
public class Fatorial implements IOperacao {
	@Override
	public double calcular(double... args) {
		int fat = (int) args[0];
		return calcularFatorial(fat);
	}

	private int calcularFatorial(int fat) {
		if (fat == 0) {
			return 1;
		}
		return fat * calcularFatorial(fat - 1);
	}

	@Override
	public String toString() {
		return "Fatorial";
	}

	@Override
	public int getNumeroDeArgumentos() {
		return 1;
	}

	@Override
	public String getSimbolo() {
		return "!";
	}
}

E funcionou a contento.

[]s

aajjbb

é, estou lendo e relendo varias vezes, percebi que estou pensando muito procedural mesmo, e esse exemplo do renzonuccitelli esta sendo um pouco dificil de assimilar, mas, em um geral, muito mais facil para manuntençao e adição de operações. esse metodo recursivo do fatorial tambem esta sendo um desafio, mas a compreenção ja aumentou.

aajjbb

mais uma duvida…

a classe calculadora na linha 18 se referencia a Interface IOpercao mesmo sem implementala, como isso nao gera erro?

renzonuccitelli

É normal ser dificil de entender no início. Procedural para mim tb era muito mais fácil de enxergar, parece mais com a forma que as idéias primeiramente vem à mente, numa passo a passo bem definido de instruções. Com o tempo vc chega lá. Eu dou curso de OO básico na Nuccitec e aprensento esses conceitos. Tentei colocar aqui um resumo, mas confesso que escrever de forma clara o conceito, sem interagir, realmente é mais difícil. Também usei algumas API

Quanto a interface IOperacao da linha 18 o que e interessa é eu saber sua assinatura, não sua implementação. Isso que o polimofismo que ensinam nos cursos básicos, só que muitos professores, principalmente de faculdade, ficam dando voltas e voltas para explicar um conceito razoalvelmente simples. Perceba que a calculadora não sabe quais operações ela faz, ela simplesmente permite a vc adicionar essas operações através do construtor. Essa característica que me permite adicionar novas operações sem alterar o código da calculadora.

Antes de conseguir abstrair interfaces, eu fazia um procedimento diferente. Eu criava a interface IOperacao sem nenhum método. Aí colocava a interface na calculadora e ia escrevendo o código. Durante a escrita da calculadora, as necessidades de métodos da Interface IOperacao vão nascendo junto com a implementação do código. Então pensar em OO acaba sendo responder a seguinte pergunta: Que informações minha interface deve me fornecer para eu completar minha tarefa? Inclusive é comum no início vc mudar várias vezes sua interface. E inclusive é isso que muitos frameworks fazem, mudando suas vesões quando alteram demais suas interfaces de forma que se tornam incompatíveis com versões anteriores.

Espero ter conseguido explicar um pouco minha visão de OO.

[]s

aajjbb

é, na verdade ainda esta sendo dificil digerir este codigo por completo, na verdade, ja tenho uma certa experiencia com java e outros frameworks da plataforma a um tempo, mas acho q minha mente ainda esta procedura, como voce disse, é sempra a primeira logica que vem a cabeça, estou usando os java tutorials para refrescar um pouco a memoria, mas, como sou muito pouco criativo, se você pudesse me dar outra ideia de “sistema” para testas e aprimorar meus conhecimentos em “java/OO” seria de uma grande ajuda… desde já, obrigado pela atenção.

renzonuccitelli

Eu programava há uns dois anos de forma procedural em Java. Só fui realmente abrir a mente quando desenvolvi o JColtrane junto com o Eduardo Guerra, editor chefe da MundoJ (antiga Mundo Java). Por mais que tenha muito material na net, trocar idéias com pessoas mais experientes é a melhor forma. Recomendaria para vc fazer um curso básico de OO com alguma pessoa que manje. Ver esse código pronto realmente é dificil de digerir, mas vc vendo a pessoa fazer, vendo o passo a passo, fica mais fácil. E não existe isso de ser criativo. Depois que vc entendo os conceitos básicos, vc passa a pensar nessa forma de programar naturalmente.

Perceba a semelhança nesse código que fiz da calculadora com o outro, do outro tópico onde fiz um cara que calculava área.

E se estiver na região do Vale, recomendo a www.nuccitec.com.br, onde dou aula.

Exercícios para colocar esse tipo de solução existem alguns até famosos:

Contrua um Zoologico que permita a vc adicionar animais. O zoologico deve ter um método fazer barulho onde cada animal faz o seu barulho especifico.

Construa uma pista de corrida onde vc pode adicionar veiculos, e ao executar a largada, cada veiculo acelera de acordo com seu motor.

Contrua um carro e troque o seu motor pra ele ficar mais pontente, acelerando mais rápido.

Em todos esses casos, vc pode usar o mesmo jeitão de solução que coloquei aki.

[]s

aajjbb

ok, otimos exemplos, ja estou começando a implementar agora, engraçado, como sou de pindamonhangaba, acho q é menos de 40 min de de SJC, alias, vou ai com frequencia rsrs.

aajjbb

alias, achei estranho o nome desse seu projeto, ja ia perguntar se havia alguma coisa haver com o saxofonista john coltrane, mas, ao entrar no site, respondeu minha pergunta…

renzonuccitelli

Pinda fica perto. Me mande uma mensagem privada que podemos conversar, caso deseje saber dos cursos.

[]s

Criado 4 de outubro de 2010
Ultima resposta 5 de out. de 2010
Respostas 15
Participantes 4