Внедрение абстрактного класса Shape

Чтобы узнать больше о OOP, @nhgrif бросил вызов мне, чтобы реализовать абстрактный класс Shape (подробнее в комментариях к коду ниже) Вот как я это сделал. Все советы приветствуются!

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");
    }
}
29 голосов | спросил Phrancis 10 MarpmTue, 10 Mar 2015 20:55:26 +03002015-03-10T20:55:26+03:0008 2015, 20:55:26

5 ответов


21

Я в целом впечатлен согласованностью реализаций, аккуратности и т. д.

У меня есть комментарий об основной предпосылке. Shape не должен быть абстрактным классом, а интерфейсом. Он не имеет конкретных реализаций для каких-либо методов и делает его абстрактным классом, поэтому его трудно унаследовать и из других мест. Вместо этого рассмотрим это:

public interface Shape {
    public double area();
    public double perimeter();
}

В дополнение к этой проблеме вы также должны учитывать следующее:

  • Поле pi в классе Circle должно быть закрытым. Нет необходимости повторно раскрывать уже общедоступную константу по-другому.

  • Я бы избегал конструкторов по умолчанию «unit». Они ничего не помогают. Когда кто-то захочет вызвать new Triangle() и не захочет иметь размеры?

  • В ваших классах отсутствует метод toString(). Они полезны по многим причинам, особенно для отладки.

  • У вас нет плана для внеурочных измерений. Что вы будете делать с отрицательным, NaN или бесконечным входом?

ответил rolfl 10 MarpmTue, 10 Mar 2015 21:08:33 +03002015-03-10T21:08:33+03:0009 2015, 21:08:33
12

Нить на имена: размеры прямоугольника должны быть width и height not width и length , Конструктор Triangle должен принимать дескриптивные имена параметров, такие как side1Length. У вас есть больше оснований для использования a и b для частных полей (особенно в этом коротком, чистом математическом коде), но односимвольные имена, как правило, неодобрительно.

Добавьте комментарии javadoc, по крайней мере, к методам в вашем абстрактном классе /интерфейсе. Комментарий к вашему классу Shape является почти javadoc, но ему не хватает одного *.

Для дополнительного кредита замените метод main() тремя модульными тестами (1 тестовый класс с 3-мя методами), который можно легко запустить из среды IDE.

ответил Aleksandr Dubinsky 11 MaramWed, 11 Mar 2015 02:19:31 +03002015-03-11T02:19:31+03:0002 2015, 02:19:31
7

Я рад, что вы это сделали, дает мне возможность еще больше усилить /исправить некоторые дезинформации.

final double pi = Math.PI; Это обстоятельство, когда значение pi должно быть static и будет соответствующим образом записано как UPPERCASE PI.

  • Статический, поскольку значение pi не зависит от любого экземпляра Circle
  • Финал, так как, конечно, вы не хотите, чтобы что-то меняло его.
  • В верхнем регистре, поскольку это и то и другое, он считается константой , поэтому соглашение об именах.

Хотя вам не нужно использовать пространство имен или память, если вы можете напрямую использовать Math.PI. Например, реализация метода double area() может быть:

return Math.PI * Math.pow(radius, 2);

По той же причине вместо переменных-заполнителя, которые не используются в другом месте:

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.

Мне нравится, что вы используете абстрактный класс для объявления в своих тестах, делая это, давая вам некоторую гибкость в будущем, просто убедитесь, что вы понимаете, что, когда вы полагаетесь на определенные методы, которые существуют только в подклассе, объявить с этим подклассом.

ответил Legato 10 MarpmTue, 10 Mar 2015 23:15:16 +03002015-03-10T23:15:16+03:0011 2015, 23:15:16
6

Один незначительный 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 в этом случае. Сторон часто называют, что в математике я не думаю, что это ужасно неправильно назвать их здесь.

ответил Simon Forsberg 11 MaramWed, 11 Mar 2015 02:38:45 +03002015-03-11T02:38:45+03:0002 2015, 02:38:45
4

Лично я считаю, что конструктор no-arg по умолчанию очень не нужен, если это неизменные классы. Является ли прямоугольник по умолчанию шириной 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;
  }

}
ответил juunas 11 MaramWed, 11 Mar 2015 08:38:47 +03002015-03-11T08:38:47+03:0008 2015, 08:38:47

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132