Добавление другого ответа вместо редактирования старого.
Я буду говорить не обязательно с точки зрения системы типов Scala, поскольку идеи здесь применимы ко многим различным языкам.
Во-первых, прежде чем говорить о картах, позвольте мне показать, как создавать более глубокие параллельные иерархии. Это действительно легко, просто вставьте другой уровень:
FactoryBase
Factory[+F <: Factory[F,P], +P <: Product[F,P]] <: FactoryBase
SomeFactoryGroup[+F <: SomeFactoryGroup[F,P], +P <: SomeProductGroup[F,P]] <: Factory[SomeFactoryGroup, SomeProductGroup]
FooFactory <: SomeFactoryGroup[FooFactory, FooProduct]
ProductBase
Product[+F <: Factory[F,P], +P <: Product[F,P]] <: ProductBase
SomeFactoryGroup[+F <: SomeFactoryGroup[F,P], +P <: SomeProductGroup[F,P]] <: Product[SomeFactoryGroup, SomeProductGroup]
FooProduct <: SomeProductGroup[FooFactory, FooProduct]
Вы можете вставить столько уровней, сколько захотите. Нам нужны все из них, включая неуниверсальные FactoryBase
и ProductBase
. Хотя они не являются строго необходимыми, поскольку возможно избежать неприятностей с экзистенциальными типами, они могут стать громоздкими. Проще сказать FactoryBase
, чем (SomeFactory | exist F <: Factory[F,P], exist P <: Product[F,P], SomeFactory <: F)
(это намеренно не синтаксис Scala). Иногда экзистенциалы очень полезны, но не в этом примере (возможно, вы можете использовать их в других частях вашей системы). В любом случае, эти FactoryBase
объекты будут помещены в Iterable[FactoryBase]
для управления производственными прогонами.
Теперь к картам. Существуют производственные циклы, во время которых каждый завод может произвести один экземпляр продукта (или, возможно, ни одного). Нам нужно, учитывая завод и производственный цикл, найти продукт, произведенный этим заводом во время этого цикла.
Одним из решений является то, что вы пытались: представить производственные прогоны в виде (или содержать, или что-то еще) карт от заводов к продуктам. На мгновение игнорируем типы в псевдокоде:
productionrun.lookup(factory) = productionrun.mapFromFactoryToProduct.lookup(factory)
Это может даже сработать, если мы будем придерживаться FactoryBase
и ProductBase
. Но этот подход ограничивает. Карта отображает группу вещей типа A на группу вещей (возможно, разных) типа B. Все ключи идентичны по типу, а все значения идентичны по типу. Это явно не то, что мы имеем. Все фабрики разных типов, как и все продукты. Мы можем обойти эту проблему, забыв часть информации о типе, то есть, сохранив ключи и значения, приведенные к их типу наименьшего общего знаменателя. Но что, если нам понадобится восстановить эту информацию типа позже? Это невозможно сделать статически, это забыто, потеряно навсегда.
Другое решение внешне симметрично первому: фабрики представляют (или содержат, в данном случае) карты от производственных циклов до продуктов.
factory.lookup(productionrun) = factory.mapFromProductionRunToProduct.lookup(productionrun)
Производственные прогоны в этом случае представлены только их уникальными идентификаторами. Но на уровне типов это решение сильно отличается и намного лучше, чем первое. Все ключи на каждой карте идентичны по типу (фактически все они идентичны по типу на разных фабриках). Все значения в каждой карте идентичны типу (этот тип зависит от фабрики). На любом этапе информация о типе не теряется.
Итак, подведем итоги. Имейте класс PR
, который представляет производственный цикл. Создайте метод Factory[F,P].findProduct(pr:PR)->Product[F,P]
, реализованный с помощью Map[PR, Product[F,P]]
. Пусть метод makeProduct
примет значение PR
и добавит полученный продукт на карту, указав это значение PR
. Вы можете обернуть это в метод PR.lookUpProductByFactory[F,P](f:Factory[F,P])->Product[F,P]
или что-то подобное, если хотите. Кроме того, оберните это в FactoryBase.findProduct(pr:PR)->ProductBase
, и оберните , что в PR.lookUpProductBaseByFactoryBase(f:FactoryBase)->ProductBase
.
Вот и все. Я надеюсь, что эти два решения подойдут вам.