Где золотая середина между классом и объектом в UML?

Допустим, вы хотите построить диаграмму Map класс (математический, а не картографический). В Ruby у вас может быть что-то вроде:

class Map
  attr_accessor :nodes, :edges
  def initialize
    @nodes = Set.new
    @edges = Set.new
  end
  def minimum_spanning_tree(&mst_algorithm)
    mst_algorithm.call(@nodes, @edges)
  end
  ...
end

Когда я начинаю изображать это, мое мышление выглядит следующим образом:

Давайте попробуем диаграмму классов, так как мы говорим о классах. Я создам Map учебный класс. И Node учебный класс. И Edge учебный класс. И Set учебный класс. Хорошо. Теперь я нарисую составную линию (1:2) из Map в Set - по одному на каждого @nodes а также @edges, Тогда строка has-many(1:0..*) из Set в Node и еще один из Set в Edge, Но сейчас я говорю, что каждый набор может иметь любое сочетание Nodeс и Edgeс, что не соответствует действительности. И это не помогает отложить два Set элементы на диаграмме (с двумя соответствующими линиями Composed-Of(1:1)), поскольку они являются одним и тем же объектом.

Поэтому я подумал: ну, может быть, UML хочет, чтобы я был более C++/Java-EY. Шаблонный Set<Node> а также Set<Edge> не возможно в UML, но я мог бы создать подклассы: NodeSet а также EdgeSet,

Наконец, я рассмотрел диаграмму объектов, но это не так. Я говорю о Set класс, не индивидуальный Set экземпляров.

Есть ли лучший ответ? Или я уже нашел "наименее плохой"?

Потом

Ответы, которые Марк W и Пит Киркхэм являются фантастическими для вопроса, поскольку я сначала сформулировал это. Проблема в том, что я пытался использовать простую аналогию с моей реальной проблемой, потому что я не могу раскрыть проблему, как она существует. На самом деле я лишь немного рассказал о том, как иметь два одинаковых класса, которые имеют разные отношения, но действуют одинаково и имеют одинаковые атрибуты (хотя и не значения атрибутов).

Давайте попробуем еще раз с некоторыми различными моделями: ActiveDirectory, Firewall, и два Routers. Один маршрутизатор (LAN) имеет ссылки на ActiveDirectory и Firewall; другой (глобальный) имеет ссылки на Firewall и некоторое количество публичных серверов (мы будем игнорировать их на этой диаграмме). Вполне возможно, что оба Routers одного и того же производителя, модели и т. д. У них будут разные серийные номера (объекты разные), но они определенно оба Routers. Тем не менее, чтобы поставить оба на диаграмме классов, я должен подкласс Router в LANRouter а также WANRouter, Аналогия с решением Марка W заключается в подключении Firewall а также ActiveDirectory напрямую, оставляя реализацию (Router) в класс, чтобы определить. Но абстракция должна просочиться, если UML будет использоваться для фактического построения системы.

2 ответа

Решение

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

class Map
  +nodes : { unordered, unique } object[0..*]
  +edges : { unordered, unique } object[0..*]
  +minimum_spanning_tree(mst_algorithm:callable)

Хороший генератор кода / инструмент обратного инжиниринга будет отображать {неупорядоченные, уникальные} коллекции UML в / из Sets в исходном коде.

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

Если вы хотите задокументировать, что определенный класс реализован с использованием наборов, вы можете сделать это с помощью ассоциаций. Вы правы в том, что C++ или Java позволяют вам объявлять тип элемента в наборе, и это соответствует параметрическим типам в UML. AFAIK Ruby не имеет механизма для реализации таких ограничений типа, поэтому, чтобы документировать реализацию вашей карты в Ruby, документируйте только то, что находится в реализации - у Map есть две ассоциации для Set, и не накладывает никаких ограничений на то, что находится в Set. (возможно, вы захотите задокументировать это как инвариантное ограничение, которое затем будет проверено модулем, а не ограничением типа, но опять-таки немного трудно понять, почему вы будете работать в Ruby, если хотите ограничительную статическую проверку типов)

Не рисуйте линии от Map в Set, В этом случае, Set это просто структура данных, предоставленная вам Ruby, содержащая действительно важные объекты. Будь то Set, HashMap, Arrayи т. д. зависит от реализации и не имеет значения для UML. Просто сделайте составную строку (1:0..*) из Map в Node и из Map в Edge, В конце концов, на английском, ваш Map действительно состоит из узлов и ребер, правильно? Причина, по которой вы включаете Map в вашей диаграмме, потому что вы на самом деле тот, кто создает класс.

Вы правы насчет диаграммы объекта: в этом нет необходимости. То же самое с синтаксисом шаблонов, который вы предложили.

Другие вопросы по тегам