Hello, I'm rececently getting into java and OOP, and after reading some articles on this website I started to write a little exercise to apply the concepts.
Basically I'm writing an 'Expression' object that holds operands and operators.
Since the object 'Expression' is itself an operand, it is possible to pass it as an operand to another espression. In this way it is possible to simulate the behaviour of the various nested sub-expression within the parenthesis that appear in a written-on-paper expression...
first of all, here are the classes I defined (the question is at the end):
1) two abstract classes for the two kinds of object that my Expression object will handle: ------------------------------------------------------------------------------- --------
2) 4 instances of the operators "sum, difference, multiply and division) --------------------------------------------------------------------
(other to come, but for learning purpose it's enough)
class Odivide extends Operator {
privatefinalstatic String symbol ="/";
publicvoid write() {
System.out.print(symbol);
}
}
class Omultiply extends Operator {
privatefinalstatic String symbol ="*";
publicvoid write() {
System.out.print(symbol);
}
}
class Osubtract extends Operator {
privatefinalstatic String symbol = "-";
publicvoid write() {
System.out.print(symbol);
}
}
class Osum extends Operator {
privatefinalstatic String symbol ="+";
publicvoid write() {
System.out.print(symbol);
}
}
3) instances of operands: an integer , a float and a string (4 chars). ----------------------------------------------------------------------
class Integer4 extends Operand {
private Integer val;
public Integer4(Integer i) {
val = i;
}
publicvoid write() {
System.out.print(val.intValue());
}
}
class Float4 extends Operand {
private Float val;
public Float4(Float f) {
val = f;
}
publicvoid write() {
System.out.print(val.floatValue());
}
}
class String4 extends Operand {
private String val;
public String4(String s) {
val = s.concat("....").substring(0,4);
}
publicvoid write() {
System.out.print("'");
System.out.print(val);
System.out.print("'");
}
}
4) the expression object (the real focus!) -----------------------------------------
NOTE: it's an operand itself!!
class Expression extends Operand {
Operand op1;
Operand op2;
Operator ope;
public Expression( Operand o1, Operand o2, Operator op) {
op1 = o1;
op2 = o2;
ope = op;
}
publicvoid write() {
System.out.print("(");
op1.write();
ope.write();
op2.write();
System.out.print(")");
}
publicvoid writeWithoutParenthesis() {
op1.write();
ope.write();
op2.write();
}
// -- just a placeholder , not used
public Number calculate() {
returnnew Integer(1);
}
}
5) the main program object. Just for testing.
publicclass parsemath {
publicstaticvoid main (String[] args) {
/*
* espr. (((3+2) * (4+5))/(4.2/(5-3)))
* --- --- ---
* A B C
* ----------- --------
* D E
* ---------------------------
* F
*/
// A
Operand op32_1 = new Integer4( new Integer(3));
Operand op32_2 = new Integer4( new Integer(2));
Operator ope32_1 = new Osum();
Expression e32 = new Expression( op32_1, op32_2, ope32_1);
// B
Operand op45_1 = new Integer4( new Integer(4));
Operand op45_2 = new Integer4( new Integer(5));
Operator ope45_1 = new Osum();
Expression e45 = new Expression(op45_1, op45_2, ope45_1);
// C
Operand op53_1 = new Integer4( new Integer(5));
Operand op53_2 = new Integer4( new Integer(3));
Operator ope53_1 = new Osubtract();
Expression e53 = new Expression( op53_1, op53_2, ope53_1);
// D
Operator ope3245 = new Omultiply();
Expression e3245 = new Expression( e32, e45, ope3245);
// E
Operand op4_1 = new Float4( new Float(4.2));
Operator ope453 = new Odivide();
Expression e453 = new Expression( op4_1, e53, ope453);
Operator opeTot = new Odivide();
Expression eTot = new Expression( e3245, e453,opeTot);
System.out.println("expression with numbers");
eTot.writeWithoutParenthesis();
System.out.println();
/*
* espr. "Hell" + "o wo" + "rld!"
* ---- ---- ----
* A B C
* -----------
* D
* ----------------------
* E
*/
// A
Operand opA = new String4("Helloooo");
// B
Operand opB = new String4("o world");
// C
Operand opC = new String4("rld!");
// D
Operator opAB = new Osum();
Expression eAB = new Expression( opA, opB, opAB);
// E
Operator opABC = new Osum();
Expression eABC = new Expression( eAB, opC, opABC);
System.out.println("espression with strings");
eABC.writeWithoutParenthesis();
System.out.println();
System.out.println("OK.");
}
}
If you compile and run all the classes, you can have an output of the main expression with all the correctly nested parenthess. (the string expression is here just to evidentiate that you can define many different operands, not necessarily numeric. For the same reason, you could image different kinds of operators, not necessarily arithmetic)
This is possible due to the 'write()' method available in all the operand-type and operator-type classes.
But what if I vanted a similar behaviour that instead of printing the expression calculates it?
I imagine a method 'calculate()' that, called for each sub-expression, do all the calculation returning a value.
Obviously somewhere there has to be written some code to manage the different types of values carried by the 'operands' It is not the same thing to multiply two integers or an integer and a float... and how has to be treatd a multiplication between an integer and a String? Is it impossible? Can return a string repeated n times?
And here is the main question: What is the best design solution to achieve the calculation without having to reply code in all the various classes?
I thought about a 'calculator' object that receives operands and operators, with all the if-then-else instructions needed to manage the different object types. As a new kind of operand arises, it's the calculator that has to be modified to manage it, or to throw an 'UnknownOperand' or 'UnknownOperator' exception...
is this the right way to procede? is there a better design implementation? can we in some way put and manage some "intelligence" in the operator objects, so data conversion and calculation is made easier?
Since this is a thing to understand OO design, i need design suggestion more than a ready-to-go solution (well, if you have it, I'won't throw it away! :) )
I hope not to have been boring with this large post. Sorry if my english is confusing.
One option is to use the Visitor pattern to walk the expression tree and at each operator take the operands and perform the appropriate action.
A good example is supplied with the JavaCC package which builds a parse tree given a grammar. You can nominate that you want the Visitor interface generated and then just implement the appropriate actions.