Как спроектировать класс формы - PullRequest
0 голосов
/ 10 апреля 2010

Я хочу разработать класс формы. Мне нужно различать несколько разных форм:

-Point  
-Line  
-Triangle  
-Circle  
-Polygons  

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

float Distance(Shape a, Shape b)

Самый простой способ сделать это - поместить много операторов if, а затем вызвать соответствующий метод, но это определенно не ООП.

Как спроектировать такой класс в стиле ООП?

Ответы [ 6 ]

3 голосов
/ 11 апреля 2010

Это сложная проблема, потому что если вы реализуете метод для вычисления расстояния между двумя объектами, используя ближайшую точку, вам действительно нужно знать, какие типы оба объектов. Если бы вы сравнивали его, используя, например, центральную точку, то это было бы легко - вы просто добавили бы метод GetCenter, но в этом случае это просто не сработало.

Проблема в том, что иерархии классов полезны, если вы можете создать их как расширяемые, то есть разрешить добавление других типов без изменения существующих. Здесь дело обстоит не так, потому что когда вы добавляете Ellipse, вам нужно реализовать DistancePointEllipse, DistanceTriangleEllipse и т. Д. Было бы намного проще представить это, используя алгебраический тип данных известный из функциональных языков. Например в F #:

type Shape = 
  | Circle of float * float * float // center & radius
  | Point of float * float          // center

Тогда вы можете использовать сопоставление с образцом для обработки всех возможных случаев:

match shape1, shape2 with
| Circle(x1, y1, r1), Circle(x2, y2, r2) -> // two circles
| Point(x1, y1), Point(x2, y2) -> // two points
| Circle(cx, cy, r), Point(px, py) 
| Point(px, py), Circle(cx, cy, r) ->
    // point and a circle (both combinations

Функциональное программирование просто лучше подходит для этой проблемы: -).

В любом случае, один из возможных (но все еще не расширенных) объектно-ориентированных проектов будет иметь базовый класс Shape с методами DistanceToPoint, DistanceToTriangle и т. Д., Которые вычисляют расстояние от текущего типа до другого. тип фигуры (так как вам действительно нужны все комбинации).

Другим подходом было бы просто написать перегруженный метод на C #:

float Distance(Triangle t, Point p);
float Distance(Triangle t, Circle c);
// ... etc

Приятной особенностью этой опции является то, что вы можете легко сократить количество методов, которые вам нужно написать. Например, если у вас есть регистр для Ellipse и Point, вы можете наследовать Circle от Ellipse, а затем использовать существующий регистр при сравнении Circle с Point.

1 голос
/ 11 апреля 2010

Один из проектов, который мог бы сделать это расширяемым, заключался бы в инкапсуляции определения расстояния в качестве Стратегии, работающей с определенными формами. Вы должны зарегистрировать делегатов для новых пар типов Shape, которые система затем может использовать для разрешения соответствующей функции для любой пары типов фигур. Это позволит расширить возможности, но также увеличит сложность и снизит производительность. Если ваша иерархия так же проста, как вы описали, простые условные выражения будут и быстрее, и проще в обслуживании.

Реальная проблема здесь заключается в том, что рассматриваемая функция не является простым поведением определенного класса, а скорее включает в себя знания обо всех формах в системе. Лучшее, что вы можете сделать для «ООП» - это инкапсулировать все беспорядочные условия в классе менеджера, который знает обо всех ваших формах. Вы не можете избежать необходимой связи, которая появляется в результате предоставления этой функции, но вы можете, по крайней мере, скрыть ее за более простым интерфейсом, как вы описали.

1 голос
/ 10 апреля 2010

Это зависит от того, что вы определяете как «расстояние». Вы можете дать базовому классу абстрактное свойство CenterPoint, переопределенное каждым производным классом Теперь это просто.

0 голосов
/ 14 июля 2010

Создание абстрактного базового класса Shape с помощью метода flatten, который преобразует фигуру в многоугольники:

abstract class Shape
{
    virtual Polygons Flatten();
}

Код для вычисления расстояния между двумя полигонами должен быть достаточно простым. Тогда ваша функция расстояния выглядит так:

float Distance(Shape a, Shape b)
{
    Polygons polygonsA = a.Flatten();
    Polygons polygonsB = b.Flatten();
    return polygonsA.Distance(polygonsB);
}
0 голосов
/ 10 апреля 2010

Вы можете создать абстрактный базовый класс Shape, от которого вы наследуете классы для каждой из этих фигур. В базовом классе вы объявляете абстрактный метод для нужной вам функциональности (например, CalculateDistance), но без какого-либо кода (т. Е. Нет body ). В каждом из унаследованных классов вам нужно будет предоставить реализацию для этого метода.

В связанной заметке: выберите свойства и методы для вашего Shape класса таким образом, что вам потребуется скопировать значительные объемы кода из одного производного от формы класса в другой. Если это так, вы должны поместить этот код в свойство или метод абстрактного класса Shape. В конце концов, каждый подкласс содержит только код для тех частей, которые отличаются для каждой фигуры. Это зависит от вашей реализации, от того, как вы хотите представить форму внутри.

0 голосов
/ 10 апреля 2010

Если все фигуры симметричны как по осям X, так и по оси Y, а расстояние следует рассчитывать по центру, то вы хотите иметь метод Point GetCenter() в абстрактном базовом классе. Оттуда используйте формулу расстояния , чтобы вычислить расстояние между ними.

...