Congrats on making the switch.
For a trivial calculator, you have applied polymorphism and solved the problem elgantly. The bigger problem is with your latter question, on how to allow arbitrary values.
You could create a child class and override it's calculate() and setValues() to allow arbitrary values but IMHO you would be better off solving the problem implementing an expression evaluator, although in PHP you could just pass the equation to eval() for pedagogical reasons, that probably isn't what you after.
The problem with solving an expression like you are doing, using nothing but objects, is that the interface (and interaction) does not really allow for complex expression evaluation, as you are experiencing.
Complex math equations are best described using a DSL (domain specific language) in the form of infix, not chaining or piping objets togather.
If the excersize is for the developer only and no user would ever need to enter equations, than you could probably get away with implementing a calculator using objects. I'm not sure how best to implement an arbitrary number evaluator but it seems deriving from a child class and overriding the calculate() and setValues() would do the basic trick.
Code: Select all
class Average extend BaseCalculator{
function setValues($arr)
{
}
}
You could maybe use the objects to emulate an infix equation like:
Code: Select all
$res = new Div(10, new Sub(5, new Add(1, 2)));
This would essentially be the same as:
Each object would need to check whether an argument was a constant or evaluation result. If the object model supported operator overloading you could emulate infix almost indentically, but you essentially would be re-inventing the wheel.
As you have experienced, expressing an mathematical equation using a programming interface is quite challenging (whether you use objects or just fucntions) as an alternative, you could implement a calculator using the reverse polish notation.
http://en.wikipedia.org/wiki/Reverse_Polish_notation
It 'was' (and maybe still is) a commonly used technique to easily implement calculators, as it's far better suited for a push/pop stack based calculator and less CPU/memory intensive as there is no parsing of the expression required (ie: infix).
Calculators are always bad examples of the flexing power of OO but then again, they make a whole lot more sense than duck and car analogies.
Truth be told, if you follow OOD principles/practices:
http://ootips.org/ood-principles.html
Inheritence will rarely be used and it is only a small part of the overall OO paradigm. As a developer just coming onto OOP you will go through 3 phases:
1. Begin wrapping everything up in classes as a form of functions on steriods (ie: namespace emulation). The GD API is a good example as each function requires a global resource handle, which is an excellent candidate for a private member variable.
2. Start applying inheritence to solve every problem (try and avoid this). The difference between private, public and protected will click and you will develop codebases with deep, fragile hierarchies.
3. Frustrated by problems you begin to learn and truly appreciate the OOD principles and applying them to assist in solving problems. Inter-object dependencies are minimized or removed outright, dependency injection is favoured over static composition, etc.
You will begin to see problems as a series of objects, not algorithms, you are more concerned with interface and interaction, and not so much the implementation.
Cheers,
Alex