简体   繁体   中英

Code style: “hiding” data types (Java)

I'm working on a set of classes to represent musical notes, bars, rhythms and so on. Naturally, I'll have to deal with time signatures that are best represented by a fraction (like 4/4, 3/4 and so on).

I'm wondering what is better style. Having a constructor for a Bar contain a Fraction-object or just two ints as a time signature.

public Bar(Fraction timeSignature) {
    this.timeSignature = timeSignature;
}

or:

public Bar (int num, int den) {
    timeSignature = new Fraction(num, den);
}

I mean… i could have both but which one should I go for in my application? Is there a "better" style?

Thank you for your thoughts!

Personally, I'd go w/ the first approach. If you know you need/want a Fraction object, why have the Bar not just take that? The person invoking the Bar class will need to understand the Fraction either way (to pass you the ints), so it's just cleaner.

I'd use the first one and provide a factory method in the fraction class to be able to call something like:

new Bar(Fraction.of(4, 4));

best of both world ;-)

First, simple APIs are nice, yes, but if you expand the constructor later to take more parameters (say, BPM), using primitives rather than objects makes the calling code harder to understand. ie:

Bar myBar = new Bar(4, 4) // Okay
Bar myBar = new Bar(4, 4, 120) // ??
Bar myBar = new Bar(new TimeSignature(4, 4), 120) // Much clearer

When in doubt, explicit is better.

Secondly, I would say don't store the time signature in a Fraction object -- while they are often written as though they are fractions, time signatures are not. 3/4 and 6/8 are equivalent as fractions, but are different time signatures that produce a different sound as a result. TimeSignature is within your program's domain, it warrants a custom type.

You may want to consider making time signature an enum in order to prevent user input error. Since you may have a finite set of time signatures(2/4, 6/8, 4/4) you can control the time signatures a user creates by doing the following:

//example
new Bar(TimeSignature.DUPLE_2_2)

public enum TimeSignature{

DUPLE_2_2(2,2), QUADRUPLE_2_4(2,4), TRIPLE_3_4(3,4);

private final int numerator;
private final int denominator;

private TimeSignature(int numerator, int denominator) {
this.numerator = numerator;
this.denominator = denominator;
}


}

Good Luck

As with every design question, this depends. In this case, you should consider how your class is going to be used.

In general, I prefer simpler APIs. Compare this:

Bar myBar = new Bar(4, 4);

to this:

Bar myBar = new Bar(new Fraction(4,4));

The latter alternative is more explicit (which is good), but might become tedious if you instantiate a lot of bars with literal arguments. On the other hand, if your Code needs to handle Fractions anyway, the first syntax might be better.

Another aspect to consider is the overhead of introducing the new class Fraction . Creating an additional class just to store two int s means a lot of overhead for little use. However, if Fraction provides other services as well (such as simplification of fractions), the class earns its right to exist.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM