This week's book giveaways are in the AI and JavaScript forums.
We're giving away four copies each of GANs in Action and WebAssembly in Action and have the authors on-line!
See this thread and this one for details.
Win a copy of GANs in ActionE this week in the AI forum
or WebAssembly in Action in the JavaScript forum!
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Bear Bibeault
  • Paul Clapham
  • Jeanne Boyarsky
  • Knute Snortum
Sheriffs:
  • Liutauras Vilda
  • Tim Cooke
  • Junilu Lacar
Saloon Keepers:
  • Ron McLeod
  • Stephan van Hulst
  • Tim Moores
  • Tim Holloway
  • Carey Brown
Bartenders:
  • Joe Ess
  • salvin francis
  • fred rosenberger

ComplexNumber class

 
Ranch Hand
Posts: 3061
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I am creating a complex number class. The problem that I have encountered is that I want to be able to have at least two types of complex numbers: one with int values and one with double values. Initiallay, I was thinking of creating a ComplexNumber base class with two subclasses: ComplexInteger and ComplexDouble. All complex numbers have a real portion and an imaginary portion, so this could logically go in the base class. However, the type for these two data fields is different for the two subclasses.
A similar problem arises when I try to create functions to manipulate ComplexNumbers. For now, I want to just do basic mathematical operations: add(), subtract(), multiply(), and divide(). To reduce the amount of code, these should go in the base class. The algorithm is the same no matter the numerical type of the data. Again, the problem comes back to the type. These operations can be declared to deal with ComplexNumbers, both as the return type and as the parameters, however the concrete object to return needs to be created somewhere.
If these operations are in the base class, how can I easily determine which concrete object to create? I would rather not use a chain of if statements with the instanceof operator. That seems to defeat the purpose of having the operation in the base class in the first place. Is there a design pattern that address this kind of problem? I've read the main design patter book (I forget the authors), but I haven't delved into Java specific design patterns, yet.
Any input to this problem will be very helpful.
Thanks,
Layne
 
Greenhorn
Posts: 18
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hi Layne,
I don't know of a pattern that applies directly to this, although the State pattern seems closest. Isn't this better approached as a tricky bit of polymorphism?
I've played around with this for a time, & I have a solution that has three classes: a base ComplexNumber class, and then 2 subclasses, ComplexInteger and ComplexDouble, which define their data members as ints and doubles respectively. The problem I ran into was that I couldn't find a way to have a getReal() or a getImaginary() method in the common class, because there is no return type that will allow ComplexInteger to return an int and ComplexDouble to return a double, and have the value recognized as an int. You can define a method <code> public double getInt() </CODE> but then the return value is always a double, even if it came from a ComplexInteger.
So, what I ended up doing was defining methods getRealInt() and getRealDouble() (and the same for the imaginary part), and having ComplexDouble throw an UnsupportedOperationException if it was asked for an int.
Then the code for add() looks like this:
so the client doesn't have to worry about whether they have a ComplexInteger or a ComplexDouble.
I'm not really happy with this approach, though - it seems little better than using the instanceOf operator to me.
Hope this is helpful
Dave
[ June 14, 2003: Message edited by: Dave Mulligan ]
 
author
Posts: 14112
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I think this would be a match for a slightly modified double dispatch pattern. (Warning: uncompiled/untested code following...)

Well, it probably will need some slight modifications depending on your requirements, but you get the idea...
As an aside, notice that I used "plus" as the method name instead of "add" - "add" seems to imply to me that the original object gets changed...
[ June 15, 2003: Message edited by: Ilja Preuss ]
 
Dave Mulligan
Greenhorn
Posts: 18
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I tried something like that (without knowing the name - "double dispatch", must remember that, thanks ) But I ran into a problem that I couldn't get around using that approach: if you add a ComplexDouble to a ComplexInteger, the result should be a ComplexDouble, whereas if you add two ComplexIntegers together, the result should be another ComplexInteger.
In other words, the ComplexInteger class needs the "plus" (I agree - better than "add") method to be overloaded to return either a ComplexInteger or a ComplexDouble, but you can't overload a method by just modifying the return type. And, you can't define two "plus" methods, one that takes a ComplexInteger and one that takes a ComplexDouble, because then the client can't just have a generic ComplexNumber as the parameter in their code.
Am I missing something basic, or do we actually need "instanceOf" in this, ermm, instance?
 
Ranch Hand
Posts: 84
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
'int's and 'double's are not objects, and it is difficult/convoluted to apply Object-Oriented design patterns here. If you use their object wrappers (Integer, Double) instead, this is an excellent candidate for Strategy.
You can even have ComplexFloat, ComplexShort if has any meaning.
The Visitor pattern has too many disadvantages to be used casually.
-GB.
 
Layne Lund
Ranch Hand
Posts: 3061
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Originally posted by Dave Mulligan:
(snip...)
Then the code for add() looks like this:
so the client doesn't have to worry about whether they have a ComplexInteger or a ComplexDouble.
I'm not really happy with this approach, though - it seems little better than using the instanceOf operator to me.
[ June 14, 2003: Message edited by: Dave Mulligan ]


I'm not entirely convinced this is a good approach, either. It seems to me that this would be much clearer by putting an add() method in each of the sub-classes, thus supporting the type for its own class locally. This approach and the above code have the same problem, especially when it comes to more complicated operations like division: the operations are exactly the same and repeated several times. For just two subclasses, this is fine, but what if I want to add ShortComplex, ByteComplex, and FloatComplex? The repitition here seems ridiculous.
I haven't looked closely at the other posts, so I'll get back about those later. I would really like to find a way to eliminate the need to duplicate the code so much. The algorithm to add two complex numbers is exactly the same, no matter the types used for the underlying data. This seems to be one of the few instances that I've encountered where strong type checking is actually a pain in the ass.
 
Ilja Preuss
author
Posts: 14112
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Originally posted by Dave Mulligan:
if you add a ComplexDouble to a ComplexInteger, the result should be a ComplexDouble, whereas if you add two ComplexIntegers together, the result should be another ComplexInteger.


Yes. The only solution to this is to have ComplexNumber as its return type.

And, you can't define two "plus" methods, one that takes a ComplexInteger and one that takes a ComplexDouble, because then the client can't just have a generic ComplexNumber as the parameter in their code.


Yes. That is why ComplexInteger.getSum is dispatching to addend.getSumWithInt, so that the addend can decide which actual class to instantiate when added to a complex integer. And that second dispatch, which is actually giving you polymorphism on the method parameter, is what is giving "Double Dispatch" its name.
For "full" Double Dispatch, you would also have a getSumWithDouble method on both classes, to which ComplexDouble.getSum would dispatch. But that's not necessary in this case, as both implementations would be identical anyway.
Did that help?
 
Layne Lund
Ranch Hand
Posts: 3061
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
It looks like Double Dispatch is exactly what I am looking for. Where can I find more information about this topic? It doesn't appear in the Design Patterns book anywhere.
 
Layne Lund
Ranch Hand
Posts: 3061
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
ooo...look at that! There's another thread about Double Dispatch. I'll have to check it out.
p.s. Is there a convenient way to print out a given thread? When I print the HTML version, the ends of lines get chopped off. It looks like the web page is wider than a printed page...
 
Dave Mulligan
Greenhorn
Posts: 18
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Ahh, all becomes clear...
I had missed that ComplexInteger called the addSumWithInt method of addend, rather than it's own addSumWithInt method.
Thanks for the explanation.
 
Dave Mulligan
Greenhorn
Posts: 18
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Not that I'm one to worry a topic to death, or anything :roll: , but I've become interested in this particular issue, and have a follow-up question, to which Layne may or may not need the answer.
Here it is: sticking with the example of just adding two ComplexNumbers, there is a small but undeniable chance that the result of adding two ComplexDoubles will in fact result in a complex number whose real and imaginary parts are both integers (little "i"). In that case, this would better be returned as a ComplexInteger. Is there a generally accepted method or pattern for applying a cast in these sort of cases, or do we now have to resort to something like:

in the ComplexDouble class, and its inverse in the ComplexInteger class (as the result of dividing one ComplexInteger by another will probably not be a ComplexInteger)?
Incidentally, Layne, you may want to re-think the idea of using doubles as your data members, since they only give approximate answers, so a / b * b will not necessarily give you a again. BigDecimal might be the way to go.
[ June 16, 2003: Message edited by: Dave Mulligan ]
 
Ilja Preuss
author
Posts: 14112
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Originally posted by Dave Mulligan:
I had missed that ComplexInteger called the addSumWithInt method of addend, rather than it's own addSumWithInt method.


I suspected something like that...


Thanks for the explanation.


You're welcome!
 
Layne Lund
Ranch Hand
Posts: 3061
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Originally posted by Dave Mulligan:
(snip...)
sticking with the example of just adding two ComplexNumbers, there is a small but undeniable chance that the result of adding two ComplexDoubles will in fact result in a complex number whose real and imaginary parts are both integers (little "i"). In that case, this would better be returned as a ComplexInteger. Is there a generally accepted method or pattern for applying a cast in these sort of cases, or do we now have to resort to something like:

in the ComplexDouble class, and its inverse in the ComplexInteger class (as the result of dividing one ComplexInteger by another will probably not be a ComplexInteger)?
Incidentally, Layne, you may want to re-think the idea of using doubles as your data members, since they only give approximate answers, so a / b * b will not necessarily give you a again. BigDecimal might be the way to go.
[ June 16, 2003: Message edited by: Dave Mulligan ]


I think keeping the number in the given type will actually be better. Let's suppose the method returns a ComplexInteger when appropriate. If the next operation adds another ComplexDouble to it, then a "cast" operation is needed again. To me, this seems to be more trouble than it's worth.
As for using BigDecimal or other classes, that will be in the hierarchy, too. I simply used double and int to illustrate two different types of ComplexNumber. If designed correctly, you should be able to extend ComplexNumber to use any type for the underlying number, be it other primitive types like short, long, or float or numeric classes like BigInteger and BigDecimal.
 
Ilja Preuss
author
Posts: 14112
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Originally posted by Dave Mulligan:
sticking with the example of just adding two ComplexNumbers, there is a small but undeniable chance that the result of adding two ComplexDoubles will in fact result in a complex number whose real and imaginary parts are both integers (little "i"). In that case, this would better be returned as a ComplexInteger. Is there a generally accepted method or pattern for applying a cast in these sort of cases, or do we now have to resort to something like:
[...]


I think you had to resort to something like that, yes. Would that be bad???


Incidentally, Layne, you may want to re-think the idea of using doubles as your data members, since they only give approximate answers, so a / b * b will not necessarily give you a again. BigDecimal might be the way to go.


Well, that's the way computers work in most cases. Most often this isn't an issue - I would only use BigDecimal if I *really had to* (until now, I didn't).
 
The glass is neither half full or half empty. It is too big. But this tiny ad is just right:
Java file APIs (DOC, XLS, PDF, and many more)
https://products.aspose.com/total/java
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
Boost this thread!