我正在尝试创建自己的一个包含(除其他外)一个名为Point的class的库。顾名思义,它旨在封装2D空间中表示的点。这是我想出的:

package geom;

import static java.lang.Math.atan2;
import static java.lang.Math.cos;
import static java.lang.Math.pow;
import static java.lang.Math.sin;
import static java.lang.Math.sqrt;
import static java.lang.Math.toDegrees;
import static java.lang.Math.toRadians;

/**
 * A class that provides factory methods for creation of {@code Points}, utility
 * functions based on operations regarding points.
 * <p>
 * The constructor has been made {@code private} because of the following
 * reason: A {@code Point} can be initialized in <i>two</i> different ways:
 * <ul>
 * <li>Using the <b>Cartesian System</b> of using two variables to represent the
 * coordinates of the {@code Point}.</li>
 * <li>Using the <b>Polar System</b> of using the distance from the original and
 * the angle between the line joining the point and the origin and the X-axis to
 * represent the location of the {@code Point}.</li>
 * </ul>
 * Since there are <i>two</i> parameters in both cases, and each is of the type
 * {@code double}, an ambiguity arises.
 * <p>
 * A solution to the above problem is to use <em>Factory Methods</em>.<br>
 * Factory methods are {@code public static} methods which can accessed through
 * class reference, like:
 * <p>
 * <code>Point point = Point.getCartesian(10, 20);</code><p>
 * The advantages of using Factory Methods are many; a few of them are:
 * <ol><li>Through the use of Factory Methods with descriptive names, the
 * ambiguity is removed.</li>
 * <li>These methods return a {@code new Point} if it has not been created
 * before.<br>
 * If a pre-existing point exists, the {@code Point} is returned from the
 * {@code PointTracker}.</li>
 * </ol>
 *
 *
 * @author ambigram_maker
 * @version 1.0
 * @version 2014-04-02
 */
public class Point {

    /*
     * This variable represents the state of the output.
     * It is static because any change to this variable is reflected in all the 
     * instances.
     */
    private static State state;
    /**
     * The Point object representing the reference point at the intersection of
     * the coordinate axes. Essentially, it is a point whose coordinates are
     * <pre>(0,0)</pre>.
     */
    public static final Point ORIGIN = getCartesianPoint(0, 0);

    /**
     * This factory method returns a {@code Point} object with the (Cartesian)
     * coordinates passed as parameters.
     *
     * @param x The X-coordinate of the {@code Point} object.
     * @param y The Y-coordinate of the {@code Point} object.
     * @return The required {@code Point} object.
     */
    public static Point getCartesianPoint(double x, double y) {
        Point p = new Point();
        p.x = x;
        p.y = y;
        p.radius = sqrt(x * x + y * y);
        p.angleR = atan2(x, y);
        p.angleD = toDegrees(p.getAngleRadians());
        return p;
    }

    /**
     * This factory method returns a {@code Point} object with the (Polar)
     * coordinates passed as the parameters.
     *
     * @param radius The distance of the required {@code Point} from the origin
     * (0,0)
     * @param degrees The angle between the line joining the required
     * {@code Point} and the origin and the X-axis (in degrees i.e. from 0 to
     * 360).
     * @return The required {@code Point} object.
     */
    public static Point getPolarDegreesPoint(double radius, double degrees) {
        Point p = new Point();
        p.radius = radius;
        p.angleD = degrees;
        p.angleR = toRadians(degrees);
        initPolar(p);
        return p;
    }

    /**
     * This factory method returns a {@code Point} object with the (Polar)
     * coordinates passed as the parameters.
     *
     * @param radius The distance of the required {@code Point} from the origin
     * (0,0)
     * @param radians The angle between the line joining the required
     * {@code Point} and the origin and the X-axis (in radians i.e. from 0 to
     * 360).
     * @return The required {@code Point} object.
     */
    public static Point getPolarRadiansPoint(double radius, double radians) {
        Point p = new Point();
        p.radius = radius;
        p.angleR = radians;
        p.angleD = toDegrees(radians);
        initPolar(p);
        return p;
    }

    /*
     * This method is common to both the 'getPolar_______Point()' methods.
     */
    private static void initPolar(Point point) {
        double angle = point.getAngleRadians();
        point.x = point.getRadius() * cos(angle);
        point.y = point.getRadius() * sin(angle);
    }

    /**
     * This method is used to change the form of representation of <em>ALL</em>
     * {@code Point} objects.
     *
     * @see State
     * @param state The {@code State} constant to set.
     */
    public static void setState(State state) {
        Point.state = state;
    }

    /*
     * The coordinates of this Point in the Cartesian system.
     */
    private double x;       // The perpendicular distance from the Y-axis.
    private double y;       // The perpendicular distance from the X-axis.

    /*
     * The coordinates of this Point in the Polar System.
     */
    private double radius;  // The distance from the Origin (0,0).
    private double angleR;  // The angle in Radians.
    private double angleD;  // The angle in Degrees.

    private Quadrant location;
    /*
     * Instances of Point are not meant to be created through the default
     * contructor. Use the Factory Methods instead.
     */

    private Point() {
        // Provision to add itself to the PointTracker.
    }

    /**
     * Returns the <i>distance</i> between {@code this Point} and the one passed
     * in the parameter.
     *
     * @param other The <i>other</i> {@code Point} which acts as the reference
     * for calculating the distance.
     * @return The distance {@code this Point} and the <i>other</i> one.
     */
    public double distanceFrom(Point other) {
        return other.equals(Point.ORIGIN) ? radius
                : sqrt(pow(this.getX() - other.getX(), 2)
                        + pow(this.getY() - other.getY(), 2));
    }

    /**
     * This method returns the {@code Point} which is a reflection of
     * {@code this Point} in the X-axis.
     *
     * @return Returns the {@code Point} which is a reflection of
     * {@code this Point} in the X-axis.
     */
    public Point reflectionXAxis() {
        return getCartesianPoint(getX(), -getY());
    }

    /**
     * This method returns the {@code Point} which is a reflection of
     * {@code this Point} in the Y-axis.
     *
     * @return Returns the {@code Point} which is a reflection of
     * {@code this Point} in the Y-axis.
     */
    public Point reflectionYAxis() {
        return getCartesianPoint(-getX(), getY());
    }

    /**
     * This method returns the {@code Point} which is a reflection of
     * {@code this Point} in the Origin.
     *
     * @return Returns the {@code Point} which is a reflection of
     * {@code this Point} in the Origin.
     */
    public Point reflectionOrigin() {
        return getCartesianPoint(-getX(), -getY());
    }

    /**
     * This method returns the {@code Point} which is a reflection of
     * {@code this Point} in the {@code Point} passed as a parameter.
     *
     * @param other The reference for calculating the reflection of
     * {@code this Point}
     * @return Returns the {@code Point} which is a reflection of
     * {@code this Point} in the X-axis.
     */
    public Point reflectionFrom(Point other) {
        if (other.equals(Point.ORIGIN)) {
            return reflectionOrigin();
        }
        return getCartesianPoint(
                2 * other.getX() - this.getX(),
                2 * other.getY() - this.getY());

    }

    /**
     * Returns the X-coordinate of {@code this Point}.
     * <p>
     * The magnitude of the X-coordinate is the perpendicular distance between
     * {@code this Point} and the Y-axis. If {@code this Point} is above the
     * X-axis, the X-coordinate is positive. If {@code this Point} is below the
     * X-axis, the X-coordinate is negative.
     *
     * @return the X coordinate of {@code this Point}.
     */
    public double getX() {
        return x;
    }

    /**
     * Returns the Y-coordinate of {@code this Point}.
     * <p>
     * The magnitude of the Y-coordinate is the perpendicular distance between
     * {@code this Point} and the X-axis. If {@code this Point} is above the
     * Y-axis, the Y-coordinate is positive. If {@code this Point} is below the
     * Y-axis, the Y-coordinate is negative.
     *
     * @return the Y coordinate of {@code this Point}.
     */
    public double getY() {
        return y;
    }

    /**
     * Returns the distance between the origin (0,0) and {@code this Point}.
     * Considering the origin to be at the center and {@code this Point} at the
     * circumference, this distance is the <i>radius</i>.
     *
     * @return The Distance between the origin and {@code this Point}.
     */
    public double getRadius() {
        return radius;
    }

    /**
     * Returns the angle between the line joining {@code this Point} and the
     * origin, and the X-axis in Radians.
     *
     * @return The angle between the line joining {@code this Point} and the
     * origin, and the X-axis in Radians.
     */
    public double getAngleRadians() {
        return angleR;
    }

    /**
     * Returns the angle between the line joining {@code this Point} and the
     * origin, and the X-axis in Degrees.
     *
     * @return The angle between the line joining {@code this Point} and the
     * origin, and the X-axis in Degrees.
     */
    public double getAngleDegrees() {
        return angleD;
    }

    /**
     * Returns the <i>location</i> of {@code this Point} in a broader
     *
     * @return
     */
    public Quadrant getLocation() {
        if (location == null) {
            if (this.equals(Point.ORIGIN)) {
                location = Quadrant.ON_ORIGIN;
            } else if (x == 0) {
                location = Quadrant.ON_Y_AXIS;
            } else if (y == 0) {
                location = Quadrant.ON_X_AXIS;
            } else if (x > 0 && y > 0) {
                location = Quadrant.FIRST_QUADRANT;
            } else if (x < 0 && y > 0) {
                location = Quadrant.SECOND_QUADRANT;
            } else if (x < 0 && y < 0) {
                location = Quadrant.THIRD_QUADRANT;
            } else if (x > 0 && y < 0) {
                location = Quadrant.FOURTH_QUADRANT;
            }
        }
        return location;
    }

    /**
     * This method is used to check if two instances of {@code Point} are equal.
     * This method checks the {@code Point}s using their hashcodes.
     *
     * @see Point#hashCode()
     * @param o The {@code Object} to compare this instance with.
     * @return {@code true} if the {@code  Object} passed as parameter is an
     * instance of type {@code Point} and the two {@code Point}s are
     * <i>equal</i>
     */
    @Override
    public boolean equals(Object o) {
        if (o instanceof Point) {
            Point p = (Point) o;
            return this.hashCode() == p.hashCode();
        }
        return false;
    }

    @Override
    public int hashCode() {
        int hash = 0;
        hash += (int) (Double.doubleToLongBits(this.getX())
                ^ (Double.doubleToLongBits(this.getX()) >>> 32));
        hash += (int) (Double.doubleToLongBits(this.getY())
                ^ (Double.doubleToLongBits(this.getY()) >>> 32));
        return hash;
    }

    @Override
    public String toString() {
        Thread t = new Thread();
        String summary = "\tCartesian:\t( x\t: " + x + ", y\t: " + y + " )";
        if (state == null) {
            setState(State.SHORT_SUMMARY);
        }
        if (!state.equals(State.NO_SUMMARY)) {
            summary += "\n\tPolar:\n\t\tDegrees\t( radius\t: " + radius + ", angle\t: "
                    + angleD + " )\n";
            summary += "\t\tRadians\t( radius\t: " + radius + ", angle\t: "
                    + angleR + " )\n";
        }
        if (state.equals(State.LONG_SUMMARY)) {
            summary += "\tQuadrant\t: " + getLocation();
            // summary += "\n\t" + Integer.toHexString(hashCode());
        }
        return summary;
    }

    /**
     *
     */
    @SuppressWarnings("PublicInnerClass")
    public static enum State {

        /**
         * If the {@code state} of a {@code Point} is set to this value, then
         * the {@code toString()} will display:
         * <ol>
         * <li>Cartesian Representation : (x,y)</li>
         * <li>Polar Representation (r,θ) in :
         * <ul><li>Degrees</li><li>Radians</li></ul></li>
         * <li>The quadrant in which the {@code Point} is located.</li>
         * </ol>
         */
        LONG_SUMMARY,
        /**
         * If the {@code state} of a {@code Point} is set to this value, then
         * the {@code toString()} will display:
         * <ol>
         * <li>Cartesian Representation : (x,y)</li>
         * <li>Polar Representation (r,θ) in :
         * <ul><li>Degrees</li><li>Radians</li></ul></li>
         * </ol>
         *
         */
        SHORT_SUMMARY,
        /**
         * If the {@code state} of a {@code Point} is set to this value, then
         * the {@code toString()} will display the Cartesian Representation :
         * (x,y) of this {@code Point}.
         */
        NO_SUMMARY;
    }

    @SuppressWarnings("PublicInnerClass")
    public static enum Quadrant {

        FIRST_QUADRANT,
        SECOND_QUADRANT,
        THIRD_QUADRANT,
        FOURTH_QUADRANT,
        ON_X_AXIS,
        ON_Y_AXIS,
        ON_ORIGIN;
    }
}



我使用NetBeans 8.0创建了上述类,因此安排了警告抑制该软件建议。我需要人们批评和讨论:


代码的有效性。
代码的组织。
可能的方法来提高性能,可读性,
哈希是否足够好?
代码文档中的任何错误(技术,偶然)。
其他任何方面都致力于提高我的编程技能。

通过下注(和上注1),评论和答案(当然!)。请注意,这是一个图书馆类,任何人都可以使用或修改,但请先通知我。


编辑:

[遇到]很多答案之后,我决定更改这些方面:


正如RoToRa在此答案中所建议的,我将完全摆脱State的想法,因为它实际上是暂时的。
从相同的答案中,我将更改工厂方法的名称以create...开头。
修复与角度有关的错误(从相同的答案)。 (我已经在这里完成了。)
正如coredump在此答案中建议的那样,我将更改hashcode()(或者甚至摆脱它)。
如Eike Schulte在此答案中建议的那样,我会解决equals()的错误。
如ChrisW所建议,我将始终使用initPolar()
请使用5参数构造函数(供工厂方法使用)。

(这些更改尚未应用到上面的代码中。)


除了这些,我正在考虑添加一个atan2(y,x)变量(pow(..)),该变量用于确定准确性的程度(并克服浮点问题)。

scale的解释:
过一会儿,我将在后面添加关于double类的帖子。该类(顾名思义)应该跟踪(全部?)在运行时创建的PointTracker。它将添加对另一个库类的支持:PointTracker。 (因此,您可能会期望有很多与此主题相关的帖子。)

最后但并非最不重要的一点是,我邀请了与上述主题不同的其他主题的更多答案,因此,此Points是理想的选择类。我还邀请您演示角度间转换或其他相关操作的即时实现。

,请记住,这是给您的;因此,请提出一些建议使其更具个性化。


1:帮助CodeReview毕业生!

评论

散列可能更有效,但实际上没问题。取决于hashCode的equals非常糟糕。我们不希望(1,0)和(0,1)被认为相等,而现在它们是相等的。

为什么要在ReflectionFrom中打扰特殊的原点?

为什么可变?

@ user2357112正如我在ChrisW的回答下面评论的那样:“使用this.equals(ORIGIN)可以节省一些时间,因为此Point and Point.ORIGIN的距离黑白已存储->半径”。但是话又说回来,我不知道这是否可行。

@Alex我不希望这样,但是我不得不使其可变以实现工厂方法。现在,感谢CodeReview的所有慷慨帮助,我可以使x,y,radius等最终定型。 :-)

#1 楼

使用“状态”来区分不同的输出格式是非常错误的。如果需要不同的格式,请使用多种输出方法(只需toString用于调试输出),或者甚至更好,编写一个单独的类将Point转换为字符串表示形式。

我认为您已经过头了与JavaDoc。恕我直言,这既不是有关坐标系如何工作的理论研究的地方,也不是讨论实现决策的地方(“因为在两种情况下都有两个参数,并且每个参数都是{@code double}类型,所以会产生歧义。”) 。

工厂方法的名称不应以get...开头,因为它们不是吸气剂。请改用create...

应将度数模数设为360(弧度为2π),否则会得到:

等号内的哈希码不好。 equals的功能之一是区分具有相同哈希码的两个对象-考虑到您将两个64位double值“压缩”为一个32位整数,这将发生。

评论


\ $ \ begingroup \ $
感谢您关于角度的漏洞...(请问?)可以演示一种更好的方法来散列点数吗?
\ $ \ endgroup \ $
–饥饿的蓝色开发者
2014年4月3日14:45

\ $ \ begingroup \ $
哈希没问题。只是不要在equals()方法中使用它。
\ $ \ endgroup \ $
–RoToRa
2014年4月3日在14:46

\ $ \ begingroup \ $
我已经编辑了问题,所以请看一看。
\ $ \ endgroup \ $
–饥饿的蓝色开发者
2014年4月4日在8:07

#2 楼

1.为什么使用static?我在以下方面有一个小问题:


private static void initPolar(Point point){


为什么当参数是Point的实例并因此可能是方法时,为什么要定义静态函数?

private void initPolar() {
    double angle = getAngleRadians();
    x = getRadius() * cos(angle);
    y = getRadius() * sin(angle);
}


2。是否需要缓存?


如果存在预先存在的点,则{@code PointTracker}返回{@code Point}。


对我来说,这似乎是过早的优化。在缓存中存储和查找点(这会花费时间和内存)而不是创建多个点实例有什么好处?在完全相同的位置创建点的可能性有多大?

您将如何实现跟踪器?使用哈希图?您是否会创建一个临时点来检查它是否已经属于该集合并丢弃它以返回存储的实例?

此外,如果您真的需要缓存,这不是责任Point类可以做到这一点。该类公开了hash函数,以便其他类可以使用它。

3。类层次结构

已经有人提出过建议,但是您可以定义两种类型的点:“极点”和“笛卡尔”点,它们都具有定义明确的公共构造函数:
 public CartesionPoint (double x, y) ...
 public CartesionPoint (PolarPoint pp) ...

 public PolarPoint (double radius, radians) ...
 public PolarPoint (CartesionPoint cp) ...


如果您真的想用弧度/度来构建PolarPoint,也许您的构造函数可以采用可选的单位参数(例如RADIANS或DEGREES,以枚举类型定义)。

我希望我不要听起来太刺耳。祝你好运:-)

评论


\ $ \ begingroup \ $
对于#2,您绝对正确:我将为它创建一个PointTracker类。
\ $ \ endgroup \ $
–饥饿的蓝色开发者
2014年4月3日14:44

\ $ \ begingroup \ $
@ambigram_maker很好,但是老实说我不明白为什么你需要一个PointTracker。真的有必要吗?
\ $ \ endgroup \ $
– coredump
2014年4月3日在15:24

\ $ \ begingroup \ $
我已经编辑了问题,所以请看一看。
\ $ \ endgroup \ $
–饥饿的蓝色开发者
2014年4月4日在8:08

\ $ \ begingroup \ $
@ambigram_maker让我们尝试另一种方法:在标准库中,我看不到FileTracker,DoubleTracker或InputStreamTracker(尽管有MediaTracker类)。如果有人想跟踪其应用程序中的点,他们就会这样做。但是您不能以一般方式执行此操作:您将使用空间哈希吗?还是只是列表?你不知道什么对每个人都有好处;恐怕您过度设计了图书馆的这一部分。我建议编写应用程序来测试其API:尝试使用自底向上的方法,而不是自顶向下的方法。
\ $ \ endgroup \ $
– coredump
2014年4月4日在8:37

\ $ \ begingroup \ $
非常好的答案!欢迎随时加入我们的聊天室!
\ $ \ endgroup \ $
–syb0rg
2014年4月10日在22:27

#3 楼

Math.atan2函数的定义非常奇怪(不仅在Java中,而且在我知道的每种编程语言中),正确的用法是atan2(y, x)

评论


\ $ \ begingroup \ $
我不熟悉atan2或任何数学知识,但是为什么会正确使用呢?是否应使用atan2(y,x)调用Java版本?
\ $ \ endgroup \ $
–马克·安德烈(Marc-Andre)
2014年4月3日在18:32

\ $ \ begingroup \ $
@ Marc-Andre Java的atan2想要y,x:但是OP编写了x,y,因此这似乎是一个错误。
\ $ \ endgroup \ $
– ChristW
2014年4月3日18:43



\ $ \ begingroup \ $
我已经编辑了问题,所以请看一看。
\ $ \ endgroup \ $
–饥饿的蓝色开发者
2014年4月4日在8:09

#4 楼

这将是一个不完整的评论,因为我不太了解Java。

可以用StateSummaryType代替FormatType。对distanceFrom有特殊情况;考虑到您这样做,我不明白为什么您对other.equals(Point.ORIGIN)也没有特殊情况。

您对this.equals(Point.ORIGIN)的实现可能有问题:如果点相等,则它们的哈希码相等;但是不同的点可能具有相同的哈希码。

在每个实例中您都携带着冗余/重复的信息:例如以度和弧度为单位的角度。如果需要,您可以只存储一个并计算其他时间。同样,您正在预先计算并存储其笛卡尔坐标和极坐标表示形式:最好存储一个并及时计算另一个坐标;或对于两种不同的表示形式有两种不同的类(equalsCartesianPoint?)有时使用PolarPoint有时使用pow(..., 2):是故意的吗?沿轴)x * x由于其行为是渐近的而不能很好地工作?如果它们“足够接近”,通常用浮点运算来确定它们是否“相等”。但是,这可能会干扰散列(如果要求相等的值必须具有相等的散列)。因此,您可能想定义另一个函数“ nearlyEqual”,如果atan2小于某些小epsilon,则返回true。使用极坐标初始化的点不会返回正确的distanceFrom值。如果您有两个类别,则极地类别的getLocation()方法可以使用角度而不是使用getLocationx值进行测试。

您要为ydouble类型使用x吗?如果合适,例如y1是整数像素值的度量,则使用1.0000000000000001可能会更好(例如,这样您就可以轻松比较intx)。

我想查看单元测试的列表,以便知道您对其进行了测试的值。


如果存在预先存在的点,则{@code PointTracker}返回{@code Point}。


如果数据成员为y会更好。即使有多个工厂方法,也可以通过以下方法实现:


具有构造函数,该构造函数通过参数获取所有值
在静态工厂方法中计算所有这些参数值,然后调用构造函数。


评论


\ $ \ begingroup \ $
我的包裹不完整,就像您的评论一样。 (->开玩笑:没有违法!):-)我还没有实现PointTracker(请参阅构造函数的正文(但这是另一篇文章))。
\ $ \ endgroup \ $
–饥饿的蓝色开发者
2014年4月3日,11:48

\ $ \ begingroup \ $
由于模棱两可的问题,我更喜欢工厂方法而不是构造函数:阅读(或尝试:-))在主类上方的文档以进行论证。
\ $ \ endgroup \ $
–饥饿的蓝色开发者
2014年4月3日在11:49

\ $ \ begingroup \ $
我故意在x和y上使用double而不是int(这就是为什么我需要一些有关哈希和相等性的提示)。
\ $ \ endgroup \ $
–饥饿的蓝色开发者
2014年4月3日在11:52

\ $ \ begingroup \ $
使用this.equals(ORIGIN)可以节省一些时间,即距离。 b / w此Point and Point.ORIGIN已存储->半径。
\ $ \ endgroup \ $
–饥饿的蓝色开发者
14年4月3日,11:54

\ $ \ begingroup \ $
@ambigram_maker我理解为什么要使用工厂方法(尽管最好使用不同的类,一个用于笛卡尔坐标系,另一个用于Polar)。如果您的成员是最终成员(也许可以/应该是最终成员),那么我认为您可以在一处初始化它们,例如在构造函数中。具有非平凡的构造函数并不与具有工厂方法不兼容:您可以有一个采用全部5个参数并在工厂方法中计算这5个参数值的构造函数。
\ $ \ endgroup \ $
– ChristW
2014年4月3日在12:13

#5 楼

一行评论

我看不到在Thread t = new Thread()方法中使用toString

评论


\ $ \ begingroup \ $
我已经编辑了问题,所以请看一看。
\ $ \ endgroup \ $
–饥饿的蓝色开发者
2014年4月4日在8:10

\ $ \ begingroup \ $
@ambigram_maker关键是你不应该。
\ $ \ endgroup \ $
–user11153
2014年4月4日在11:49

\ $ \ begingroup \ $
@ambigram_maker嗯,你在说什么?你已经做到了。查看您的代码。
\ $ \ endgroup \ $
– Anirban Nag'tintinmj'
2014年4月4日在11:51

\ $ \ begingroup \ $
:-O。伙计...我疯了!
\ $ \ endgroup \ $
–饥饿的蓝色开发者
2014年4月4日在12:10

#6 楼

如果假定一个类充当数据持有者,则两个自我报告为相等的实例应具有相同的属性;此外,与构造函数/工厂参数相对应的值应与创建对象时使用的值匹配。可能有一个极坐标类型,其属性为rho和theta值,或者一个xy坐标类型,其属性为x和y,或者甚至可能是抽象坐标类型,其子类型具有度极坐标,弧度-极坐标和xy坐标(每个坐标将存储一组不同的属性)。但是,定义后者的工作方式可能很棘手。

考虑发生什么情况,例如,如果一个人用极坐标(角度= 45度,半径= 2.0)构造一个点,然后再构造另一个xy坐标与第一个匹配的点。由于xy构造的对象的半径为2.0000000000000004,因此该类的任何可能的行为都易于“令人惊讶”。这意味着:


xy构造的对象将不等于据说坐标相同的另一个对象,或者
xy构造的对象,尽管据推测等于极地构造的半径,将具有不同的半径,或者
极地构造的对象的半径与指定的值不匹配。

其中,也许第一个会如果说度极对象不等于任何xy对象,除非角度为90度的倍数,弧度极对象不等于任何其他对象,除非角度为零时,这是最合理的。即使最接近极坐标对象的x坐标的double是1.4142135623730951,实际x坐标也更接近1.4142135623730950488016887242097,因此它与x43坐标指定了xy坐标的任何点都不完全匹配。

除非有特殊的原因需要使用极坐标,否则我建议让double类型显式封装Pointx,两者均为y类型。将double字段用于这些字段,并将其设置在私有构造函数中。即使您有其他方法可以为给定的半径和角度构造一个点,或报告与该点关联的半径和角度,也要清楚地知道报告的值不是对象的固有属性,而只是根据对象的x进行计算和y。

评论


\ $ \ begingroup \ $
我已经编辑了问题,所以请看一看。
\ $ \ endgroup \ $
–饥饿的蓝色开发者
2014年4月4日在8:10

\ $ \ begingroup \ $
您的修改没有解决最大的问题,即构造一个(angle = 45deg; radius = 2.0)点的请求是否应该产生一个具有该角度和半径的点,或者是否应该产生x和y坐标可表示的最近点。至于PointTracker,我建议Point本身应该处理缓存(在一个或多个其他类的帮助下),但允许工厂方法的调用者指出何时缓存特别有用。关于计算缓存,我建议...
\ $ \ endgroup \ $
–超级猫
2014年4月4日在15:08

\ $ \ begingroup \ $
...使您的Point拥有X和Y以及对PointInfo对象的引用可能会很有用,该对象可能具有附加信息,可能包括序列号和另一个较低序的PointInfo的标识,已知是相等的。使用这种方法将意味着,如果将两个点进行比较并且发现相等,则将针对两者中的任何一个计算结果(无论是在比较之前还是之后执行)都进行缓存。此外,如果发现A等于B,并且B等于C,则将为C缓存对A的计算。
\ $ \ endgroup \ $
–超级猫
2014年4月4日在15:13

\ $ \ begingroup \ $
在这种设计中需要注意一些皱纹,以免在不同线程中访问点时避免麻烦,但这仍然是一种有趣的方法。
\ $ \ endgroup \ $
–超级猫
2014年4月4日在15:14

\ $ \ begingroup \ $
...关于准确性问题:正如我在编辑中所说的,“我正在考虑添加比例变量”。这应该注意一些小的调整(如四舍五入),但尚未实施。
\ $ \ endgroup \ $
–饥饿的蓝色开发者
2014年4月5日在4:32