我一直在学习Java,这是关于数据类型的一章的一部分,这是一个挑战: br />发出声音。声音每秒以大约1100英尺的速度通过空气传播。例如,如果拍拍手和时间
您需要多长时间才能听到回声,那么您就知道了往返的总时间。

Java:A初学者指南,第六版-赫伯特·希尔德(Herbert Schildt)·麦格劳-希尔教育(McGraw-Hill Education)(第2章) -说话。我已将逻辑尽可能地分为一类(以我目前的技能水平)。

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.");
    }
}


示例输出(使用main中的上述参数):


声音花费了35.079秒回声,因此距离为19293.45英尺。


示例输出如果我将soundSpeed.isEcho设置为false


声音耗时35.079秒传播,因此距离为38586.9英尺。

#1 楼

您只想简单地

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


问题是,该函数应如何以面向对象的方式表达?可以肯定地说,这个……

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


…是一个令人讨厌的界面。像这样处理公共实例变量不仅麻烦,而且是不明智的做法(对不起)。给定以上两个选项之间的选择,相对于扭曲的对象,我更喜欢简单的函数。很好。 Java允许您编写不完全符合OOP的代码。


如果要面向对象的设计该怎么办?面向对象设计的关键是要对对象建模。这意味着您必须找到合理的对象进行建模-越真实越好。什么是SoundSpeedCalc?我不知道;你买不到。但是您可以确定购买声纳!用配置参数(声音介质及其主动/被动模式)实例化Sonar是有意义的,然后您可以通过调用.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);
    }
}


这里是如何有效使用它的方法:另外,我使用了条件表达式-这是根据布尔开关分配两个值之一的理想方法。

评论


\ $ \ begingroup \ $
OP的“面向对象”方法使我想起了我的AP计算机科学课。不正确或不实际,但足以教授对象的基础知识
\ $ \ endgroup \ $
–科尔·约翰逊(Cole Johnson)
2015年2月23日在14:18

\ $ \ begingroup \ $
@ColeJohnson:的确,这些示例的问题通常是,通过删除任何可能掩盖所涉及的OOP技术的实质性内容,您最终会遇到一个琐碎的问题,很难证明抛出任何类型的错误是合理的从根本上讲,这是技术上的问题,并且所得到的代码是荒谬的;-)更现实的是,可以在测试工具中看到这些抽象的价值,因为这表明了其所有主要用途。如果您的目标只是将输入乘以1100的函数,那么您已经迷失了方向,我们需要有趣的要求来编写有趣的代码!
\ $ \ endgroup \ $
–史蒂夫·杰索普(Steve Jessop)
2015年2月23日在16:30



\ $ \ begingroup \ $
+1喜欢为真实物体建模的要点。我们常常忘记这一点。为了可扩展性,与枚举相比,我更喜欢MEDIUM的接口和最终实现。
\ $ \ endgroup \ $
–psaxton
2015年2月25日于4:00

#2 楼

其他名称的结果是否闻起来有甜味?
timeInSeconds的常规名称是seconds

SOUND_TRAVEL_SPEED可能会受益于说明单位和其他假设的注释: SPEED_OF_SOUND可以使用更具描述性的名称。

要实例化还是不实例化,这就是问题。

SOUND_TRAVEL_SPEED可能不需要实例化。可以将getResult保持不变,并使用feet函数进行计算,这可能很好:

/* approximate, feet/second at sea level */


评论


\ $ \ begingroup \ $
我不同意你的第一行。切勿以其单位调用变量或参数。时间是可以接受的,elapsedTime,travelTime或该概念的其他变化也可以接受。例外:如果要进行单位转换,则单位将是可接受的参数名称。
\ $ \ endgroup \ $
–克里斯·库德莫(Chris Cudmore)
2015年2月23日在17:42

\ $ \ begingroup \ $
@ChrisCudmore我以为我们正在进行单位转换。
\ $ \ endgroup \ $
–韦恩·康拉德
15年2月23日在18:56

\ $ \ begingroup \ $
@ChrisCudmore,为什么不呢?我有多个引用,在变量名称中包括度量单位是一个好习惯。
\ $ \ endgroup \ $
– Vorac
2015年2月24日12:41



\ $ \ begingroup \ $
让我们看一下函数调用:feet(double seconds,boolean isEcho)它返回什么?脚?不,它返回一个距离。它需要什么参数?需要一些时间。您可以使它们更明确,例如distanceTravelled和sonarPingTime。单位应该是API文档的一部分,或者更可靠地作为参数(例如Units.SECONDS)
\ $ \ endgroup \ $
–克里斯·库德莫(Chris Cudmore)
15年2月24日在13:30

\ $ \ begingroup \ $
@ChrisCudmore如果变量的含义在上下文中很清楚,并且更改它们将需要添加注释以传达相同的意图,则该规则将损害代码,而无济于事。但是,我确实同意,通过使用单位库或使用单位参数来明确指定单位通常会更好。
\ $ \ endgroup \ $
–韦恩·康拉德
15年2月24日在16:26

#3 楼



SOUND_TRAVEL_SPEED也应该是static

private static final double SOUND_TRAVEL_SPEED = 1100;


这是因为它不属于特定的类实例。 >
其他数据成员仍然应该是private

private double timeInSeconds;
private boolean isEcho;


这通常适用于所有类。


>可以通过三元语句简化getResult()中的计算: />

评论


\ $ \ begingroup \ $
我尝试将这两个设置为私有,但是在运行SoundSpeedCalc时出现编译器错误... SoundSpeed.timeInSeconds字段不可见,等等。是否还需要更改?
\ $ \ endgroup \ $
– ran
2015年2月23日在1:30



\ $ \ begingroup \ $
@Phrancis:这是因为您在声明实例后设置其值,因此不允许修改该私有字段。您应该改为使用该值构造soundSpeed。
\ $ \ endgroup \ $
– Jamal♦
2015年2月23日在1:32



\ $ \ begingroup \ $
“ SOUND_TRAVEL_SPEED也应该是静态的:”这将排除在水下将此类用于SONAR。
\ $ \ endgroup \ $
–阿伦
2015年2月23日下午6:52

#4 楼

只是一件事:

没有以is开头的布尔字段。就像这样:

public boolean isEcho;


(200_success和其他正确的说法,它不应该是public)吸气剂是它们应该以is开头,后跟它们是吸气剂的变量的名称。假设这个变量有一个吸气剂:

public boolean isIsEcho() {
    return this.isEcho;
}


现在听起来很奇怪!

变量和变量的正确命名约定其吸气方法是:

private boolean echo;

public boolean isEcho() {
    return this.echo;
}