Skip to content

Instantly share code, notes, and snippets.

@d-ronnqvist
Created December 10, 2014 20:04
Show Gist options
  • Save d-ronnqvist/7ea8b1dc7d10f23917c2 to your computer and use it in GitHub Desktop.
Save d-ronnqvist/7ea8b1dc7d10f23917c2 to your computer and use it in GitHub Desktop.
I'm designing something where there are multiple different objects that can hold exactly _one_ out of a couple of different other types, and I don't know what style I prefer.
// I'm designing something where there are multiple different objects that can hold
// exactly _one_ out of a couple of different other types, and I don't know what style I prefer.
//
// For example, an Input always has a name and it can hold a Symbol, a Target, or a Texture.
// It should always hold exactly one of them. In just the same way, a Symbol has a name and can
// hold different data of exactly exactly _one_ type of many.
//
// Which one of the following alternatives do you prefer?
// assume an instance of Symbol (doens't matter what it looks like)
let positionSymbol = Symbol(/* ... */)
// ----- V1 ----- //
// My first though was to have a separate enum that defines the kind of value being stored (with associated values)
enum InputKind {
case SymbolKind(Symbol)
case TargetKind(Target)
case TextureKind(Texture)
}
struct InputV1 {
let name: String
let kind: InputKind
}
// A new input is then created like this
let positionInputV1 = InputV1(name: "position", kind: .SymbolKind(positionSymbol))
// This makes it resonably easy to switch on the different kinds of data,
// but the init gets a bit wordy and I feel like it's unnecessary to make the enum so "public"
// ----- V2 ----- //
// Next I thought that I could have optional variables for the different types of data to store
struct InputV2 {
let name: String
let symbol: Symbol?
let target: Target?
let texture: Texture?
}
// A new input is then created like this
let positionInputV2 = InputV2(name: "position", symbol: positionSymbol, target: nil, texture: nil)
// I can get to the different kinds of data using optional binding (if let),
// but it doesn't protect me from having multiple types of data or no data at all
// ----- V3 ----- //
// Then I wend back to the first alternative an put the enum inside the stuct.
struct InputV3 {
enum Kind {
case SymbolKind(Symbol)
case TargetKind(Target)
case TextureKind(Texture)
}
let name: String
let kind: Kind
}
// A new input is then created like this
let positionInputV3 = InputV3(name: "position", kind: .SymbolKind(positionSymbol))
// This meant that I could give the enum a shorter, more contextual name
// but still had the problem with a lengthy init
// ----- V4 ----- //
// The same problem can be solved using inheritance, but it didn't feel very Swift to me
class InputV4 {
let name: String
init(_ aName: String) {
name = aName
}
}
class SymbolInputV4 : InputV4 {
let symbol: Symbol
init(_ aName: String, symbol aSymbol:Symbol) {
symbol = aSymbol
super.init(aName)
}
}
class TargetInputV4 : InputV4 {
let target: Target
init(_ aName: String, symbol aTarget:Target) {
target = aTarget
super.init(aName)
}
}
class TextureInputV4 : InputV4 {
let texture: Texture
init(_ aName: String, symbol aTexture:Texture) {
texture = aTexture
super.init(aName)
}
}
// A new input is then created like this
let positionInputV4 = SymbolInputV4("position", symbol: positionSymbol)
// Which version do you prefer? Do you have any other versions that I haven't thought of?
@sofacoder
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment