Shape.java
/* nhgrif says:
* Phrancis ready for inheritance/polymorphism?
* Given the following abstract class:
*
* public abstract class Shape {
* public abstract double area();
* public abstract double perimeter();
* }
*
* Implement a Circle, Triangle, and Rectangle class which extend the class Shape.
* Ex: public class Circle extends Shape ... etc
*/
public abstract class Shape {
public abstract double area();
public abstract double perimeter();
}
Rectangle.java
public class Rectangle extends Shape {
private final double width, length; //sides
public Rectangle() {
this(1,1);
}
public Rectangle(double width, double length) {
this.width = width;
this.length = length;
}
@Override
public double area() {
// A = w * l
return width * length;
}
@Override
public double perimeter() {
// P = 2(w + l)
return 2 * (width + length);
}
}
Circle.java
public class Circle extends Shape {
private final double radius;
final double pi = Math.PI;
public Circle() {
this(1);
}
public Circle(double radius) {
this.radius = radius;
}
@Override
public double area() {
// A = π r^2
return pi * Math.pow(radius, 2);
}
public double perimeter() {
// P = 2πr
return 2 * pi * radius;
}
}
Triangle.java
public class Triangle extends Shape {
private final double a, b, c; // sides
public Triangle() {
this(1,1,1);
}
public Triangle(double a, double b, double c) {
this.a = a;
this.b = b;
this.c = c;
}
@Override
public double area() {
// Heron's formula:
// A = SquareRoot(s * (s - a) * (s - b) * (s - c))
// where s = (a + b + c) / 2, or 1/2 of the perimeter of the triangle
double s = (a + b + c) / 2;
return Math.sqrt(s * (s - a) * (s - b) * (s - c));
}
@Override
public double perimeter() {
// P = a + b + c
return a + b + c;
}
}
这就是我用来测试所有内容的东西,所有这些都按预期工作:
TestShape.java
public class TestShape {
public static void main(String[] args) {
// Rectangle test
double width = 5, length = 7;
Shape rectangle = new Rectangle(width, length);
System.out.println("Rectangle width: " + width + " and length: " + length
+ "\nResulting area: " + rectangle.area()
+ "\nResulting perimeter: " + rectangle.perimeter() + "\n");
// Circle test
double radius = 5;
Shape circle = new Circle(radius);
System.out.println("Circle radius: " + radius
+ "\nResulting Area: " + circle.area()
+ "\nResulting Perimeter: " + circle.perimeter() + "\n");
// Triangle test
double a = 5, b = 3, c = 4;
Shape triangle = new Triangle(a,b,c);
System.out.println("Triangle sides lengths: " + a + ", " + b + ", " + c
+ "\nResulting Area: " + triangle.area()
+ "\nResulting Perimeter: " + triangle.perimeter() + "\n");
}
}
#1 楼
通常,我对实现的一致性,简洁性等印象深刻。我对基本前提有一个评论。
Shape
不应是抽象类,而应该是接口。它没有任何方法的具体实现,并且使其成为抽象类也使得很难从其他地方继承。相反,请考虑以下问题:public interface Shape {
public double area();
public double perimeter();
}
除了这种担忧之外,您还应考虑以下几点:您的
pi
类别上的字段应为私有字段。无需以其他方式重新公开已经公开的常量。我会避免使用“ unit”默认构造函数。他们没有任何帮助。什么时候有人要调用
Circle
而不想要尺寸?您的类缺少
new Triangle()
方法。由于许多原因,这些功能很有用,尤其是在调试时。负,NaN或无限输入将如何处理?评论
\ $ \ begingroup \ $
我什至建议不要使用pi字段,而直接使用Math.PI。或者做一个导入静态java.lang.Math.PI;并直接使用PI
\ $ \ endgroup \ $
–伊沃·贝克斯(Ivo Beckers)
15年3月11日在11:25
\ $ \ begingroup \ $
接口方法默认情况下是公共的。 (我的宠儿之一是多余的修饰符-Java已经足够冗长了)。
\ $ \ endgroup \ $
–蜘蛛鲍里斯(Boris)
15年3月11日在12:19
\ $ \ begingroup \ $
@BoristheSpider-是的,默认情况下它们是公开的,我理解您的关注,您是对的,应该指出。碰巧的是,我在从事代码风格准则要求的工作时学习了Java,这是我一直保持的习惯,现在我更喜欢将其明确。我并不是说我是对的,但是我声称如果我将它编辑出来,那不是我的答案;-)
\ $ \ endgroup \ $
–rolfl
15年3月11日在12:24
#2 楼
名称上的小问题:矩形的尺寸应为width
和height
而不是width
和length
。 Triangle的构造函数应使用描述性参数名称,例如side1Length
。您有更多借口将a
和b
用于私有字段(尤其是在此简短的纯数学代码中),但是通常不赞成使用单字符名称。在您的抽象类/接口中。 Shape
类的注释几乎是一个Javadoc,但缺少一个*
。为了获得额外的荣誉,用3个单元测试(1个测试类和3个方法)替换
main()
方法很容易从IDE运行。评论
\ $ \ begingroup \ $
我建议为每个实现使用一个单元测试类-这是公认的标准方法。除此之外,所有的优点都很好。
\ $ \ endgroup \ $
–蜘蛛鲍里斯(Boris)
2015年3月11日在12:21
\ $ \ begingroup \ $
@BoristheSpider没错,但是我个人不会在这种琐碎的情况下坚定地坚持这一规则,而是希望减少测试类的数量并使测试代码重用只是私有方法testShape(Shape ShapeShape,类ShapeTest中的shape,double ExpectedArea,Double ExpectedPerimiter)而不是为其构建继承层次结构。
\ $ \ endgroup \ $
–亚历山大·杜宾斯基(Aleksandr Dubinsky)
2015年3月11日15:31
#3 楼
我很高兴您这样做,这使我有机会进一步执行/更正某些错误信息。 br />静态,因为pi的值不依赖于Circle的任何实例
最终,因为您当然不希望对其进行更改
大写,因为这两个东西都被认为是常量,因此是命名约定。
但是,当您可以直接使用
final double pi = Math.PI;
时,不需要占用名称空间或内存。例如,您的PI
方法的实现可能是:Math.PI
出于相同的原因,而不是使用其他地方没有使用的占位符变量:
double a = 5, b = 3, c = 4;
Shape triangle = new Triangle(a,b,c);
简单地使用它们
Shape triangle = new Triangle(5, 3, 4);
如果您选择,a,b和c开头的名称就不太合适要使用它们,在这种情况下,请尝试使用更具描述性的名称,例如base,side1和side2。未来,只要确保您了解,当您依赖仅存在于子类中的特定方法时,就必须使用该子类进行声明。
评论
\ $ \ begingroup \ $
占位符变量提高了可读性。
\ $ \ endgroup \ $
–亚历山大·杜宾斯基(Aleksandr Dubinsky)
2015年3月10日23:21
\ $ \ begingroup \ $
我不同意a,b和c不是好名字。虽然对字段使用多个连续的单字母名称通常会是一种代码味道,但在这种情况下,它们是适当的。它们是三角形三个边的标准名称,并且在三角形几何图形中无处不在。称它们为base,side1和side2错误地表明这三个具有不同的含义。
\ $ \ endgroup \ $
– jwg
15年3月11日在11:41
#4 楼
实际上,一个较小的nitpick:// where s = (a + b + c) / 2, or 1/2 of the perimeter of the triangle
double s = (a + b + c) / 2;
在注释中,您指的是三角形的周长,但是在您的代码中,您并未使用实际的周长。这是非常小的代码重复。如果您愿意的话,您的代码会更加自我记录:
double s = perimeter() / 2;
我个人对a,b,c名称一无所知案件。在数学中经常会给双方起名字,我认为在这里也给它们起名字并不可怕。
评论
\ $ \ begingroup \ $
这是一个小问题,但非常好。
\ $ \ endgroup \ $
– jwg
15年3月11日在11:42
#5 楼
我个人认为,如果这些是不可变的类,则默认的无参数构造函数是非常不必要的。默认情况下,宽度为1且高度为1的矩形有意义吗?在适当的方法中。我还认为方法的命名可能会更好。代替
area()
,我将使用getArea()
。修改后的Rectangle如下所示(注意Shape类也需要编辑):
public class Rectangle extends Shape {
private final double width, height, area, perimeter;
public Rectangle(double width, double height) {
this.width = width;
this.height= height;
this.area = width * height;
this.perimeter = 2 * (width + height);
}
@Override
public double getArea() {
return this.area;
}
@Override
public double getPerimeter() {
return this.perimeter;
}
}
评论
\ $ \ begingroup \ $
getX通常是该方法返回预定义值的标志。此处是这种情况,但不适合其实施。我确实没有看到名称的问题,但是如果他必须更改名称,我可能会选择computeX
\ $ \ endgroup \ $
–莱加托
15年3月11日在12:43
\ $ \ begingroup \ $
对..或者也许是calculateArea()?
\ $ \ endgroup \ $
– juunas
15年3月11日在13:20
\ $ \ begingroup \ $
同样有效。
\ $ \ endgroup \ $
–莱加托
15年3月11日在13:21
\ $ \ begingroup \ $
从Effective Java:例如,返回调用对象的非布尔函数或属性的方法通常以名词,名词短语或以动词get开头的动词短语来命名。 ,大小,hashCode或getTime。有一个声音特遣队声称只有第三种形式(以get开头)是可以接受的,但是这种主张没有任何依据。前两种形式通常会导致代码更具可读性。很少使用非getter方法的值类可以使用诸如area()之类的名词。 computeArea建议该函数具有副作用或明显较慢。
\ $ \ endgroup \ $
–亚历山大·杜宾斯基(Aleksandr Dubinsky)
2015年3月11日15:47
评论
现在执行棘手的操作:椭圆和正方形。好的正方形可以扩展矩形。椭圆肯定会很有趣。
是的,并且矩形会一直延伸到矩形。
正方形绝对不能延伸矩形,反之亦然。这直接违反了Liskov替代原理,并可能导致意想不到的副作用。
仅当@PeteBecker Square不可变时才允许使用它们。禁止使用其他方法。