Есть ли способ добиться упорядоченной безопасности условного типа в массивах?
Например, если мне нужен DSL-подобный способ создания бургера, я мог бы сказать что-то вроде:
let burgerComponents = .burger(
.buns(2),
.beefPatty(1),
.lettuce(),
.mustard(),
.mushrooms()
)
Где, если вы добавите тип булочки, вы не сможете добавить его снова к этомумассив.Если вы добавите beefPatty, вы не сможете добавить его снова и т. Д.
Наивным подходом будет создание Struct
:
struct Burger {
let buns: Int
let patties: Int
let lettuce: Bool
let mustard: Bool
let mushrooms: Bool
init(
buns: Int = 2,
patties: Int = 1,
lettuce: Bool = false,
mustard: Bool = false,
mushrooms: Bool = false) {
self.buns = buns
self.patties = patties
self.lettuce = lettuce
self.mustard = mustard
self.mushrooms = mushrooms
}
}
extension Burger {
static func burger(
buns: Int = 2,
patties: Int = 1,
lettuce: Bool = false,
mustard: Bool = false,
mushrooms: Bool = false) -> Burger {
return Burger(buns: buns,
patties: patties,
lettuce: lettuce,
mustard: mustard,
mushrooms: mushrooms
)
}
}
let burger: Burger = .burger(buns: 2,
mushrooms: true)
Однако вы не можете расширять и добавлять дополнительные свойства кструктура бургера (майонез, острая горчица и т. д.).Решение Typed Tagless решает эту проблему, но, к сожалению, не дает упорядоченного соответствия.
Решение Struct
работает, если вам нужно нерасширяемое решение, однако, поскольку я создаю расширяемый API, к которому другие участники смогут добавить дополнительные функции, решение struct, к сожалению, не отвечает этим требованиям.!
Изученные мною пути - это «Призрачные типы» и «Типизированные решения без тегов».
Типизированные без тегов
struct Burger {
let buns: Int
let beefPatty: Int
let lettuce: Bool
let mustard: Bool
let mushrooms: Bool
}
enum BurgerAttributes {
case buns(Int)
case beefPatty(Int)
case lettuce
case mustard
case mushrooms
}
protocol BurgerComponents {
static func buns(_ numberOfBuns: Int) -> Self
static func beefPatty(_ numberOfPatties: Int) -> Self
static func lettuce() -> Self
static func mustard() -> Self
static func mushrooms() -> Self
}
struct BurgerProcess {
let attributes: BurgerAttributes
init(_ attributes: BurgerAttributes) {
self.attributes = attributes
}
}
extension BurgerProcess: BurgerComponents {
static func buns(_ numberOfBuns: Int) -> BurgerProcess {
return BurgerProcess(.buns(numberOfBuns))
}
static func beefPatty(_ numberOfPatties: Int) -> BurgerProcess {
return BurgerProcess(.beefPatty(numberOfPatties))
}
static func lettuce() -> BurgerProcess {
return BurgerProcess(.lettuce)
}
static func mustard() -> BurgerProcess {
return BurgerProcess(.mustard)
}
static func mushrooms() -> BurgerProcess {
return BurgerProcess(.mushrooms)
}
}
protocol BurgerComposer {
static func burger(_ processes: BurgerProcess...) -> Self
}
struct BurgerBuilder {
typealias BurgerCompose = () -> Burger
let compose: BurgerCompose
init(compose: @escaping BurgerCompose) {
self.compose = compose
}
}
extension BurgerBuilder: BurgerComposer {
static func burger(_ processes: BurgerProcess...) -> BurgerBuilder {
return BurgerBuilder {
var _buns: Int = 0
var _patties: Int = 0
var _lettuce: Int = 0
var _mustard: Int = 0
var _mushrooms: Int = 0
for process in processes {
switch process.attributes {
case .buns(let buns):
_buns = buns
case .beefPatty(let patties):
_patties = patties
case .lettuce:
_lettuce = lettuce
case .mustard:
_mustard = true
case .mushrooms:
_mushrooms = true
}
}
return Burger(
buns: _buns,
beefPatty: _patties,
lettuce: _lettuce,
mustard: _mustard,
mushrooms: _mushrooms
)
}
}
}
Теперь вы сможете создавать бургер в DSL-стиле.fashion:
let builder: BurgerBuilder = .burger(
.buns(2),
.beefPatty(1),
.lettuce(),
.mustard(),
.mushrooms()
)
let burger: Burger = builder.compose()
// Output: Burger(buns: 2, beefPatty: 1, lettuce: true, mustard: true, mushrooms: true)
Таким образом, решение Typed Tagless удовлетворяет некоторым требованиям, однако, если вы хотите иметь условное упорядочение, это невозможно с решением Typed Tagless.
С Typed Tagless вы можете определить неограниченное количество атрибутов:
let builder = BurgerBuilder = .burger(
.buns(2),
.buns(0),
.buns(10),
.mustard()
)
Там нет безопасности для внешнего пользователя API.
Есть ли способ достичьупорядоченный тип соответствия в следующем формате?
let burgerComponents = .burger(
.buns(2),
.beefPatty(1),
.lettuce(),
.mustard(),
.mushrooms()
)