aspose file tools*
The moose likes Beginning Java and the fly likes Can (should) I make this more OO? Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login


Win a copy of Spring in Action this week in the Spring forum!
JavaRanch » Java Forums » Java » Beginning Java
Bookmark "Can (should) I make this more OO?" Watch "Can (should) I make this more OO?" New topic
Author

Can (should) I make this more OO?

Jeremy French
Greenhorn

Joined: May 11, 2005
Posts: 13
New to Java, though not to programming.

I have a Note (as in musical) class and a Chord class, which consists of 3 or more notes. However there are many types of chords, and a consumer can call the Chord constructor with the String name of the chord - such as "C7", which means the chord starts with C and is a 7th chord. The type will determine what notes are included in the chord.

My first instinct is to put a long if or switch statement in the constructor that populates a Note array and sets the instance variables,

but that feels not very OO. Should I be making a ChordType Class? or should I make a 7thChord class that extends Chord? Or is the way I have it just as good in terms of OOness?


There are 10 kinds of people in the world. Those that read binary and those that don't.
Rick Goldstein
Greenhorn

Joined: Oct 10, 2003
Posts: 21
I've been learning guitar for the past couple of years (nothing more painful than trying to learn your first musical instrument when you're near 40, let me tell you), so I've picked up a little about chord theory. Here's my take, for what it's worth.

The chord structure is transposable. In other words, a major chord always has the same chromatic spacing of the notes, which correspond to the I-III-V degrees of the 8 note major scale. For a minor chord, the third is flatted. You could define a Chord class with a type (that could be an enum, if you're using Java 5, or a typesafe enumeration pattern of standard classes, if Java 1.4 or earlier). The type could be some kind of patterned string, or an array of integers, representing the chromatic note numbers for the type. So for a major, it would be (1, 5, 8). Then you just need to provide the root note, and the Chord instance can compute the notes (using an indexed lookup, or something). Even better, provide the chord with a scale (major, minor, Ionian, whatever) and the degree pattern for the chord type, and the root note. Everything is computable from there, without a case statement.

You can do all of this with enums/constants. Never use a literal string in your code if you can help it.

Rick
Merrill Higginson
Ranch Hand

Joined: Feb 15, 2005
Posts: 4864
I'm sure there are a number of ways one could approach this, but I would think that there would be a better way than a long switch statement.

Here are a few thoughts:

I'd have an abstract base class of chord that defines a List of notes. That abstract class could also define an abstract constructor that takes an argument of a single note defining the root, since to my knowledge all conventional chords have roots. The abstract class could define other methods, such as transpose(interval), or play(), etc.

I'd then create subclasses identifying the various types of chords (major, minor, diminished, augmented, seventh (technically major/minor seventh) major seventh, minor seventh, ninth, 13th, etc)

Given the root, each subclass should be able to calculate the other notes in the chord.

In your example, you'd create a C7 chord as follows:

new SeventhChord("C");

The constructor would then create the other notes of the chord by making the first note the root, then calulating up a major 3rd, up a minor third, up a minor third.

I can already think of things this model doesn't cover such as altered notes, but I'll leave that to you. You get the idea.


Merrill
Consultant, Sima Solutions
Jeremy French
Greenhorn

Joined: May 11, 2005
Posts: 13
Yes, I believe you're both correct. Merrill, I considered that approach, but I'm not sure how I would then keep track of what subclasses were available to me. For example, say I wanted to choose a random (or by some other algoithm) Chord that contained a "C" (assuming the Chord has a hasNote(char note) method) - How could I iterate through a list of all available subclasses? Would I have to keep an updated list in the consumer that was doing the choosing? Isn't that basically just another long switch statment? That seems to violoate the rule of (i forget what it's called) not creating code that needs to be updated. Not to say that my first solution didn't also violate this rule, but it's what I'm trying to get away from.
[ May 11, 2005: Message edited by: Jeremy French ]
Stan James
(instanceof Sidekick)
Ranch Hand

Joined: Jan 29, 2003
Posts: 8791
Just thinking out loud ... maybe take the root name and construct a major triad. Then start analyzing the notation. Find a number like 7 or 9 and add chord parts to fill in. Find a "m" for minor, lower the third, find a +5 and augment the fifth, find "susp" and suspend the 3rd and so on. You could have little helpers to apply each modification: the "+" helper, the "m" helper, the "number" helper. Or maybe use the Decorator pattern to build the chord and modify it as you get the note array.

Hey, this sounds pretty fun. Lemme know if that's interesting or if you find another option.
[ May 11, 2005: Message edited by: Stan James ]

A good question is never answered. It is not a bolt to be tightened into place but a seed to be planted and to bear more seed toward the hope of greening the landscape of the idea. John Ciardi
Jeremy French
Greenhorn

Joined: May 11, 2005
Posts: 13
Yes, that's another approach, but it doesn't get me away from the long switch- or in that case, if- statement. It also means I still have to update the Chord class if I want to start basic and add more complex chord types in the future, as opposed to just creating additional classes.

I apologize if I posted in the wrong forum. I assumed this was a simple OO thing that I just wasn't used to. Didn't realize it would actually require thought to answer.
Jeremy French
Greenhorn

Joined: May 11, 2005
Posts: 13
Sorry, Stan. As I said, I'm new to Java, so I didn't fully understand your post. I had to go educate myself on the Decorator pattern to fully understand what you meant. I think you may be right. I have to think through it some more and consider it in the context of the larger application, but you've all given me some good ideas of where to go. Thank you.
Stan James
(instanceof Sidekick)
Ranch Hand

Joined: Jan 29, 2003
Posts: 8791
The Head First Design Patterns book has a great chapter on Decorator
.

The neat thing is you'd build a chain of modifiers from left to right but maybe let the process from right to left. So a "+9" notation would process the 9 first, add the 7th & 9th, then the + would modify the most recently added chord part. It could be tricky, but very cool.
[ May 12, 2005: Message edited by: Stan James ]
Merrill Higginson
Ranch Hand

Joined: Feb 15, 2005
Posts: 4864
I agree with you, Stan: This is kind of a fun model to work with. As I think about it more, and read the other posts, I'm more inclined toward the decorator pattern also. The decorator might have such methods as makeMinor() addMinorSeventh(), etc.

Also, I think the Note class might make a good candidate for a typeSafe enumeration. For example, to represent the note "C#" you'd write Note.CSharp. If you wanted to incorporate this model into a system that would actually play the chords by communicating with a sound card or synthesizer, you'd have to create another class (perhaps a subclass of note) called Pitch, which would be a combination of the note and the octave. It would also have a frequency property which could be passed to a synthesizer.

Anyway, thanks, Jeremy for providing an interesting example in "thinking OO".
Ilja Preuss
author
Sheriff

Joined: Jul 11, 2001
Posts: 14112
Originally posted by Stan James:
maybe take the root name and construct a major triad. Then start analyzing the notation. Find a number like 7 or 9 and add chord parts to fill in. Find a "m" for minor, lower the third, find a +5 and augment the fifth, find "susp" and suspend the 3rd and so on. You could have little helpers to apply each modification: the "+" helper, the "m" helper, the "number" helper. Or maybe use the Decorator pattern to build the chord and modify it as you get the note array.


Or use a Builder. Have a parser that instantiates the Builder with the root note, and then successively calls makeMinor() etc. as appropriate. When there is nothing more to parse, get the final Chord instance from the Builder. (Of course the Builder could use the Decorator pattern to construct the Chord... )


The soul is dyed the color of its thoughts. Think only on those things that are in line with your principles and can bear the light of day. The content of your character is your choice. Day by day, what you do is who you become. Your integrity is your destiny - it is the light that guides your way. - Heraclitus
Layne Lund
Ranch Hand

Joined: Dec 06, 2001
Posts: 3061

but that feels not very OO. Should I be making a ChordType Class? or should I make a 7thChord class that extends Chord? Or is the way I have it just as good in terms of OOness?[/qb]<hr></blockquote>

Although this doesn't have anything to do with your question, I'd like to point out that you must be VERY carful when comparing Objects, such as Strings. In the code above, I don't think


does what you think. This compares if type is the same String object as the literal "C7". It does NOT compare the contents of the type with the string literal. To compare the contents you should do this:

In fact, when you compare objects, you should usually use the equals() method.

Sorry if I'm breaking the purpose of this particular thread. However, this paradigm shows up often enough that I wanted to help you avoid making the same mistake in the future.

Keep Coding!

Layne
[ May 12, 2005: Message edited by: Layne Lund ]

Java API Documentation
The Java Tutorial
Stan James
(instanceof Sidekick)
Ranch Hand

Joined: Jan 29, 2003
Posts: 8791
As I think about the number of modifiers needed, there aren't that many: m,M,+,-,'dim','susp'. Any others? A Builder with methods instead of modifier decorators would be a fine start. You can see if anything is growing to the point it ought to be its own class.

I'd look at starting with the chord name, then parsing the rest backwards. For example:

C7+5

Start with C, build the C-E-G
Find the 5, already have a G, no effect.
Find the +, augment the last named note, change G to G#
Find the 7, don't have a Bb yet, add it

I don't know how you build in more complex smarts. For example I'd never voice C13 as C-E-G-Bb-D-F-A, but maybe C-Bb-E-A-D
Jeremy French
Greenhorn

Joined: May 11, 2005
Posts: 13
Although this doesn't have anything to do with your question, I'd like to point out that you must be VERY carful when comparing Objects, such as Strings. In the code above, I don't think


if (type=="C7")


does what you think. This compares if type is the same String object as the literal "C7". It does NOT compare the contents of the type with the string literal. To compare the contents you should do this:

code:


if ("C7".equals(type))


In fact, when you compare objects, you should usually use the equals() method.

Sorry if I'm breaking the purpose of this particular thread. However, this paradigm shows up often enough that I wanted to help you avoid making the same mistake in the future.


Actually, because of the immutability of Strings and String pool, this does compare the contents of the string. Try it yourself.


but your point is valid toward normal objects, and I'm aware of the difference.
Ilja Preuss
author
Sheriff

Joined: Jul 11, 2001
Posts: 14112
Originally posted by Jeremy French:



This only works because all Strings are compile time constants. If you have Strings that are computed at runtime or read from external resources, it breaks.
Ilja Preuss
author
Sheriff

Joined: Jul 11, 2001
Posts: 14112
Originally posted by Stan James:
As I think about the number of modifiers needed, there aren't that many: m,M,+,-,'dim','susp'. Any others? A Builder with methods instead of modifier decorators would be a fine start.


Good point - a Builder is a good match because the number of modifications is limited. If you were likely to want to add more later, Decorators or something would be better, to conform to the Open Closed Principle.
Jeremy French
Greenhorn

Joined: May 11, 2005
Posts: 13

This only works because all Strings are compile time constants. If you have Strings that are computed at runtime or read from external resources, it breaks.


I stand corrected.

Interestingly enough, it was HeadFirst Java that introduced me to this misunderstanding. In Appendix B it specifically states that creating a new String in Java that is identical to a previously created String refers the new object reference to the previously-existing String object. Just to be sure I went back and checked to make sure I wasn't misreading it, but it's there in black and white. I know the authors hang around here occasionally. If anyone knows how to get in touch with them, perhaps they would like to know they're distributing misleading or possibly incorrect information.
Stan James
(instanceof Sidekick)
Ranch Hand

Joined: Jan 29, 2003
Posts: 8791
Hey, nobody says you can't decorate your builder!
Layne Lund
Ranch Hand

Joined: Dec 06, 2001
Posts: 3061
Originally posted by Jeremy French:


I stand corrected.

Interestingly enough, it was HeadFirst Java that introduced me to this misunderstanding. In Appendix B it specifically states that creating a new String in Java that is identical to a previously created String refers the new object reference to the previously-existing String object. Just to be sure I went back and checked to make sure I wasn't misreading it, but it's there in black and white. I know the authors hang around here occasionally. If anyone knows how to get in touch with them, perhaps they would like to know they're distributing misleading or possibly incorrect information.


There are several ways to create String objects. Try this for example:

Before running this, you should see if you can figure out the output yourself. I'm willing to bet you will be surprised. If you need more help figuring this out, you should probably start another thread since we are getting off of the main topic for this one.

Layne
 
I agree. Here's the link: http://aspose.com/file-tools
 
subject: Can (should) I make this more OO?