Где золотая середина между классом и объектом в 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
, и два Router
s. Один маршрутизатор (LAN) имеет ссылки на ActiveDirectory
и Firewall
; другой (глобальный) имеет ссылки на Firewall
и некоторое количество публичных серверов (мы будем игнорировать их на этой диаграмме). Вполне возможно, что оба Router
s одного и того же производителя, модели и т. д. У них будут разные серийные номера (объекты разные), но они определенно оба Router
s. Тем не менее, чтобы поставить оба на диаграмме классов, я должен подкласс 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
в вашей диаграмме, потому что вы на самом деле тот, кто создает класс.
Вы правы насчет диаграммы объекта: в этом нет необходимости. То же самое с синтаксисом шаблонов, который вы предложили.