Calculate distance using speed of sound I've been working on learning Java and this was a challenge as part of a chapter on data types:


  Create a program that computes how far away, in feet, a listener is
  from a sound. Sound travels approximately 1,100 feet per second
  through air.
  
  You can also compute the distance to a large object, such as a rock
  wall, by timing the echo. For example, if you clap your hands and time
  how long it takes for you to hear the echo, then you know the total
  round-trip time.
  
  Java: A Beginner's Guide, Sixth Edition - by Herbert Schildt · McGraw-Hill Education (Chapter 2)


I'm super-new to OOP and am trying to nip bad habits in the bud, so-to-speak. I've divided the logic in one class as much as I think is possible (at my current skill level). 

SoundSpeed.java

public class SoundSpeed {
    private final double SOUND_TRAVEL_SPEED = 1100;
    public double timeInSeconds;
    public boolean isEcho;
    private int distanceDivisor;

    double getResult() {
        if(isEcho) {
            distanceDivisor = 2;
        } else {
            distanceDivisor = 1;
        }
        return (timeInSeconds * SOUND_TRAVEL_SPEED) / distanceDivisor;
    }
}


SoundSpeedCalc.java

class SoundSpeedCalc {
    public static void main(String args[]) {
        SoundSpeed soundSpeed = new SoundSpeed();
        soundSpeed.timeInSeconds = 35.079;
        soundSpeed.isEcho = true;
        String verb;

        if(soundSpeed.isEcho) {
            verb = "echo";
        } else {
            verb = "travel";
        }
        System.out.println("The sound took " + soundSpeed.timeInSeconds + " seconds to " 
        + verb + " and thus the distance is " + soundSpeed.getResult() + " feet away.");
    }
}


Example output (using above parameters in main):


  The sound took 35.079 seconds to echo and thus the distance is 19293.45 feet away.


Example output if I set soundSpeed.isEcho to false:


  The sound took 35.079 seconds to travel and thus the distance is 38586.9 feet away.
What you want to do is simply

public static double distanceBySounding(double seconds, boolean isEcho) {
    return (isEcho ? 0.5 : 1.0) * seconds * 1100 /* ft. per second */;
}


The question is, how should this function be expressed in an object-oriented way?  It's pretty safe to say that this…

SoundSpeed soundSpeed = new SoundSpeed();
soundSpeed.timeInSeconds = 35.079;
soundSpeed.isEcho = true;
System.out.println(soundSpeed.getResult());


… is an unpalatable interface.  Manipulating public instance variables like that is not only cumbersome, it is an unsound practice (pardon the pun).  Given a choice between the two options above, I would much prefer the simpler function to the contorted object.  And that's fine.  Java allows you to write code that doesn't fully conform to OOP.



What if you wanted an object-oriented design?  The key to object-oriented design is that you want to model objects.  That means that you have to find plausible objects to model — the more realistic, the better.  What's a SoundSpeedCalc?  I don't know; you can't buy one.  But you can sure buy a sonar!  It makes sense to instantiate a Sonar with its configuration parameters (the sound medium and its active/passive mode), then you can start "aiming" it at stuff by calling .distance().

public class Sonar {
    public enum Medium {
        AIR(1100);

        private final double ftPerSec;

        Medium(double ftPerSec) {
            this.ftPerSec = ftPerSec;
        }
    }

    private final Medium medium;
    private final boolean isEcho;

    public Sonar(Medium medium, boolean isEcho) {
        this.medium = medium;
        this.isEcho = isEcho;
    }

    public double distance(double seconds) {
        return medium.ftPerSec * seconds / (isEcho ? 2 : 1);
    }
}


Here's how you can use it effectively:

public static void main(String[] args) {
    double seconds = 35.079;
    boolean isEcho = true;

    Sonar sonar = new Sonar(Sonar.Medium.AIR, isEcho);
    String verb = isEcho ? "echo" : "travel";
    System.out.printf("The sound took %s seconds to %s and thus the distance is %s feet.\n",
                      seconds, verb, sonar.distance(seconds));
}


Note the use of System.out.printf() as a nicer alternative to string concatenation.  Also, I've used conditional expressions — it's a perfect way to assign one of two values depending on a boolean switch.