Проверка на равенство и ухудшение запаха кода в C # - PullRequest
0 голосов
/ 27 октября 2018

У меня есть базовый абстрактный класс Shape с тремя производными классами: Arc, Circle, Rectangle.

У меня есть проверка на равенство в классах, и поскольку дуги иногда могут иметь формуполного круга, я хочу считать их также равными!Для этого я проверяю тип фигуры, уменьшаю ее, а затем выполняю другие проверки, чтобы увидеть, действительно ли две фигуры представляют одну и ту же геометрию.Моя проблема здесь с понижением.

Разве это не запах кода?Как я могу сделать это правильно.

Например, скажем, в будущем кто-то наследует от Shape и создает класс, который также может иметь форму, аналогичную дуге!Поскольку я не учел это в условиях проверки на равенство, у меня возникнут проблемы!

public abstract class Shape : IEquatable<Shape>
{
    public abstract bool Equals(Shape other);
}

public class Arc : Shape
{
    public override bool Equals(Shape other)
    {            
        if (other is Arc || other is Circle)
        {
            // Downcast and check for equality criteria!!
        }
        else
        {
            return false;
        }
    }
}

public class Circle : Shape
{
    public override bool Equals(Shape other)
    {
        if (other is Arc || other is Circle)
        {
            // Downcast and check for equality criteria!!
        }
        else
        {
            return false;
        }
    }
}

public class Rectangle : Shape
{
    public override bool Equals(Shape other)
    {
        if (other is Rectangle)
        {
            // Downcast and check for equality criteria!!
        }
        else
        {
            return false;
        }
    }
}

ОБНОВЛЕНИЕ:

После прочтения ответов и обдумывания чуть-чуть дальшеЯ считаю, что мне нужно изменить свой дизайн следующим образом:

Класс Shape должен иметь такие методы, как IsEqualToCircle (Circle circle) и IsEqualToCircle (Arc arc).

Тогда в равенстве класса Arc Iможно написать: if(shape.IsEqualToArc(this) return true;) и т. д.

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

Ответы [ 4 ]

0 голосов
/ 27 октября 2018

Решением для такого рода проблем в ООП является метод Visitor. Я не большой поклонник этого решения, так как оно несет много накладных расходов, но оно позволит вам иметь несколько отправок, то есть, по сути, один метод сравнения для каждой пары или типов фигур. Это, очевидно, приведет к большому количеству кода, где вы должны заявить, что линия никогда не равна кругу, просто возвращая false и т. Д., Но если для вас важна расширяемость с низким риском ошибок, это имеет преимущество проверки времени компиляции, что у любого недавно представленного подкласса Shape есть метод сравнения для всех других подклассов.

0 голосов
/ 27 октября 2018

Я бы сделал один шаг назад и подумал бы о ваших занятиях. Что у них общего? Очевидно, что дуга - это некоторая линейная особенность, тогда как окружность или прямоугольник являются площадными. Таким образом, на самом деле они имеют различные размеры , причем первый является одномерным, а второй - двухмерным. Так почему же сравнение линейного объекта с пространственным объектом должно возвращать равенство?

Так что лучше ИМХО иметь некоторый базовый интерфейс, например IGeometry, для всех типов геометрии, где вы определяете Equals -метод. Если две фигуры имеют одинаковое измерение, их можно сравнивать дальше. В противном случае они не равны. Сказав это, Arc никогда не равно Circle. Однако круги Boundary (если вы определили такое свойство), которые могут быть реализованы как экземпляр 360 ° Arc, могут быть равны Arc, так как оба являются одномерными.

0 голосов
/ 27 октября 2018

Вся эта установка - запах кода. Два объекта разных типов не должны быть равны, даже если они представляют одну и ту же реальность.

Вы можете пойти сюда двумя способами:

  1. Неявные операторы

    Например, (1.0).Equals(1) возвращает true; целое число аргумента 1 неявно преобразуется в число с плавающей точкой 1.0, и проверка на равенство проходит успешно. Дело не в том, что класс Double каким-то образом знает, как сравнивать целые и двойные числа, это не так, он знает только, как сравнивать точки.

    Обратите внимание, что 1.Equals(1.0) потерпит неудачу, поскольку аргумент double 1.0 неявно не преобразуется в целое число 1; разные типы ... странно, а? С одной стороны это правда, с другой не ...

  2. Явный механизм преобразования, который пользователь может безопасно использовать. Arch может иметь метод ToCircle, который может создать эквивалентный круг, если это возможно, и потерпеть неудачу в противном случае. Чтобы сделать его безопасным, вам также понадобится свойство IsCircle для отметки, если метод безопасен для вызова.

0 голосов
/ 27 октября 2018

Похоже, это связано с тем, что ваше представление избыточно.Вместо того, чтобы предоставлять конструкторы конкретной формы непосредственно вашим клиентским классам, пусть они используют фабричные методы.Вы можете либо иметь метод CreateCircle, который просто создает дугу 360 ° внутри, и полностью удалить класс круга, либо иметь метод CreateArc, который фактически возвращает экземпляр Circle, если дуга равна 360 °, в зависимости от того, что лучше.Тогда у вас никогда не будет экземпляров разных классов, которые должны быть равны.

...