今年在找工作,回顾之前在网易做的赢彩票项目时,发现到现在业内在网上也没有出现像我那样基于 Swift 关联协议
去实现的一个高内聚的数据类型系统的 Case,所以在此分享一下我在该项目基于 Swift 关联协议
的高内聚的数据类型系统:彩票数据类型系统。
这里先简单介绍一下业务背景吧:该项目需要接入大量的彩票,然后我如下图基于生物分类法对彩票进行了分类,然后以彩票属
为业务单元构建命名空间(NameSpace)
,命名空间(NameSpace)
内部收敛存放该彩票属
下的彩票种
的具体类型、玩法类型、以及通用的业务逻辑模块(此处的「通用」是指同一个彩票属
的彩票种
的业务逻辑是一样的,如解析逻辑、选号逻辑、计费逻辑等)。

存在多个彩票属
的情况下,这时候必然需要构建一个协议保证每个具体的彩票属
类型的内部实现是一致的,比如内部必须有具体的彩票种
类型,具体的玩法类型等。这时候则是通过 Swift
的关联协议实现的。下面是一个简略版的彩票数据类型系统实现代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
|
import Foundation
public enum LotteryGenusType: String { case unknown case ssq case k3 case sfc case plw }
public protocol LotterySpeciesKeyProtocol: Hashable, Equatable, RawRepresentable {
static var allCases: [Self] { get } }
public protocol LotterySpeciesTypeProtocol: CustomStringConvertible { init()
init?(lotteryId: Int)
var supportedIds: Set<Int> { get }
var lotteryId: Int { get }
var lotteryName: String { get }
var defaultGamePlay: GamePlayType { get }
var lotteryGenusType: LotteryGenusType { get }
}
extension LotterySpeciesTypeProtocol { public var description: String { return "LotterySpeciesName:\(self.lotteryName)\n{lotteryId:\(self.lotteryId),supportedLotteryIds:\(self.supportedIds)}" } }
public protocol GamePlayType {
init?(gamePlayId: Int)
var gamePlayId: Int { get } var gamePlayName: String { get } }
protocol LotteryGenusContentProtocol { associatedtype LotterySpeciesKey: LotterySpeciesKeyProtocol associatedtype LotterySpecies: LotterySpeciesTypeProtocol associatedtype GamePlay: GamePlayType
}
public struct LotteryCategory { }
extension LotteryCategory {
public struct Ssq: LotteryGenusContentProtocol {
public enum LotterySpeciesKey: String, LotterySpeciesKeyProtocol { case ssq
public static var allCases: [LotterySpeciesKey] { return [.ssq] } }
internal static func localSupportedIdsOfGenus() -> Set<Int> { return [1,2,3] }
internal static func localSupportedIdsOfSpecies(_ speciesKey: LotterySpeciesKey) -> Set<Int> { switch speciesKey { case .ssq: return [1,2,3] } }
public struct LotterySpecies: LotterySpeciesTypeProtocol { internal var instLotteryId: Int internal var instSpeciesKey: LotterySpeciesKey
internal init(lotteryId: Int, speciesKey: LotterySpeciesKey) { self.instLotteryId = lotteryId self.instSpeciesKey = speciesKey }
public init() { let speciesKey = LotterySpeciesKey.ssq let lotteryId = Ssq.localSupportedIdsOfSpecies(speciesKey).sorted()[0] self.init(lotteryId: lotteryId, speciesKey: speciesKey) }
public init(speciesKey: LotterySpeciesKey) { let lotteryId = Ssq.localSupportedIdsOfSpecies(speciesKey).sorted()[0] self.init(lotteryId: lotteryId, speciesKey: speciesKey) }
public init?(lotteryId: Int) { let allSpeciesKey = LotterySpeciesKey.allCases for key in allSpeciesKey { if Ssq.localSupportedIdsOfSpecies(key).contains(lotteryId) { self.init(lotteryId: lotteryId, speciesKey: key) return } }
return nil }
public var supportedIds: Set<Int> { return Ssq.localSupportedIdsOfSpecies(self.instSpeciesKey) }
public var lotteryId: Int { return self.instLotteryId }
public var speciesKey: LotterySpeciesKey { return self.instSpeciesKey }
public var lotteryName: String { switch self.instSpeciesKey { case .ssq: return "双色球" } }
public var defaultGamePlay: GamePlayType { return Ssq.GamePlay.general }
public var lotteryGenusType: LotteryGenusType { return LotteryGenusType.ssq } }
public enum GamePlay: Int, GamePlayType { case general = 1
public init?(gamePlayId: Int) { self.init(rawValue: gamePlayId) }
public var gamePlayId: Int { return self.rawValue }
public var gamePlayName: String { switch self { case .general: return "普通" } }
} } }
struct Demo { func test() { let lotterySpecies = LotteryCategory.Ssq.LotterySpecies.init() print("cur lotterySpecies details:\n\(lotterySpecies)") } }
|
以上的 Demo 地址:https://github.com/YK-Unit/ProgrammableNamespaceDemo
结合以上代码看,你或许能看到,高内聚的数据类型系统的实现是在 Swift
的关联协议的基础上结合以下特别的工程手段达成的:
- 在一级协议内(如代码中的
彩票属
内容协议LotteryGenusContentProtocol
)通过关联类型定义内聚的数据类型的协议(如代码中的associatedtype LotterySpeciesKey
)
- 实现一级协议的具体数据类型(如代码中的
Ssq
彩票属)里实现具体的内聚的数据类型(如代码中的Ssq
彩票属里的public enum LotterySpeciesKey
)
如果你也需要实现一个高内聚的数据类型系统,不妨参考上述的工程思路。