1

I'm implementing a program where the user can 'create a musical scale' from a variety of notes (identified by their names), and the program will generate music from this scale.

I want to implement a class that takes a note and a length, and generates a SoundWave. So one option would be to expose the following method:

public SoundWave GenerateNote(string noteName, double length);

However, I think it would be best to try and avoid strings. So I can think of two additional options:

public SoundWave GenerateNote(Note note, double length);
// Note is a simple object with a string name and a double frequency.

And:

public void Scale { get; set; }
public SoundWave GenerateNote(int noteIndex, double length);

Thing is, I won't be able to avoid strings completely, because as I said the user will select notes identified by their names. So what would be the best option you can think of?

  • 2
    blog.codinghorror.com/new-programming-jargon - see stringly typed. – Zavior Jun 20 '15 at 11:03
  • The second method is completely missing the point - you'll get very little benefit in program clarity if you just replace magic strings with magic numbers. If you are weighting a string argument that must be part of a small set of strings against an integer argument where each possible number has it's own meaning - that's a textbook case for using enum. – Idan Arye Jun 21 '15 at 1:12
10

The point of avoiding strings is not that they are inherently bad and should never be used. If that were the case, we'd omit them from our programming languages.

What should be avoided is the use of strings to represent alternative values when the possible values are in fact a small set of predefined options, rather than any possible string. The danger that someone will forget which values are allowed and which aren't is just too great.

Therefore, mechanisms have been invented to support that common case better. In business logic, you should be using enums instead of strings; in GUIs, you should have drop-down lists rather than text input fields for such data. But nothing stops you from using strings for data that really are arbitrary, such as user names.

3

Thing is, I won't be able to avoid strings completely, because as I said the user will select notes identified by their names. So what would be the best option you can think of?

Is this going to be a command line program? If so, it's of course impossible to avoid dealing with string since that's what the user is typing in. I'd give the Note class a static parse(string s) method that produces a Note instance.

Once you have a GUI, though, you'll probably have the user choose from a selection of existing notes.

  • (Which will be displayed as... strings!) – AlexFoxGill Jun 20 '15 at 15:10
  • 1
    @AlexFoxGill: yeah, but in any sane GUI framwork, you should get the result of the user's choice as an object directly without having to parse a string. – Michael Borgwardt Jun 20 '15 at 18:04
  • Yep - just noting that it's practically impossible to completely avoid strings in the majority of applications, even with a UI – AlexFoxGill Jun 20 '15 at 18:58
1

Asumming there are a finite set of notes, e.g. A, C, D, E, …, it seems an enumerated type would fit your purpose best. An enumerated type (available in most programming languages) can take values from a small set, where the type definition contains all values which are allowed. So, for example, you could use values MON, TUE, WED, THU, FRI, SAT, SUN for a DayOfWeek enum type, or A, C, D, … for Note type.

Using enums gives your code improved readabilty and makes some kinds of errors impossible, such as passing the string xyz as your note name. With an enumerated type it is simply not possible to pass an illegal value.

  • The usual piano supports 88 different notes; there are some with more. A bit much for an enumeration. – gnasher729 Jun 20 '15 at 17:10
1

Do not confuse user interface issues with programming interface issues.

The fact that the user will enter notes by name is a user interface issue, and of absolutely no concern to your GenerateNote() function, which is part of a programming interface.

As a matter of fact, the user interface could, in theory, allow the user to select among different notations, (A, B, C...) or (Do, Re, Mi...) so clearly, the task of mapping strings entered by the user to actual note objects should be handled by the user interface, and these strings should never be allowed to propagate to other parts of your application. The fact that a Note object may contain its own name is not even necessarily pertinent to the mapping, since the user may enter "Do", and the user interface will have to map this to a Note object bearing the name "A". Similarly, when the user interface displays the Note objects back to the user, it needs to perform the reverse transformation, and again, it may need to show 'Do' while the object bears the name 'A'. So, the two are completely unrelated.

A noteIndex of type int is as bad as a string, because you are using a general purpose data type there where a more specific data type (an actual Note class) could be used instead. The general purpose data type is meaningless in and of itself, and its meaning is in fact whatever meaning is assigned to it at runtime (instead of compile-time) by code that handles it, so its existence constitutes a completely unnecessary and dangerous (see error prone) indirection. The best practice in Software Engineering is to use a special type for each different concept that your logic deals with. That's what "Strong Typing" is all about.

So, definitely go with a Note object.

Why not just use a double frequency and get it over with? Well, again, the answer is "Strong Typing". Take an example from a different application: suppose you are writing software that deals with automobiles, so you are dealing with things like "speed" and "fuel consumption". The proper thing to do is to have a "Speed" data type, which contains a double, and a "FuelConsumption" data type, which contains another double, so that you never pass doubles around, because at some point a double which was meant to stand for "Speed" will mistakenly be passed to a function which expected a double that was meant to stand for "FuelConsumption", and all kinds of funny things will happen then. Good luck with debugging a system built this way! With strong typing, the compiler makes sure that you can not do the wrong thing.

  • On one hand, I see what you mean. On the other hand, a note is merely a frequency; e.g., the note A4 is simply the frequency 440Hz. That means that a note can be represented by a simple double. So what exactly would my Note object contain? As you say, a note doesn't have exactly one name; 440 is both A4 and La. So if a Note doesn't contain a string name, it only contains the frequency, a double. So why not just use a double? – Aviv Cohn Jun 20 '15 at 15:47
  • In your opinion, should I prefer simplicity (a double) or, well I don't know what to call the other option (a Note)? – Aviv Cohn Jun 20 '15 at 15:48
  • @AvivCohn I amended my answer to address your question. – Mike Nakis Jun 20 '15 at 16:05
  • 1
    @AvivCohn: a note is much more than a single frequency; for one thing, consider different tunings (e.g. well temperament, Pythagorean tuning). Pitches (just one property of notes) can contain multiple frequencies. Notes, like color, are more psychoperceptual than physical. Depending on your (present and future) requirements, your design might need to take this into account. A separate Note type in the design should leave room for expanded functionality (e.g. different tunings; generating scores or MIDI sequences, rather than sound waves). – outis Jun 20 '15 at 19:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.