Stone City 的市長決定在過年前舉辦一場挖寶活動,帶動大家過年的氣氛,於是再次邀請了你和我來開發一套程式,讓市民可以通過程式來控制船隻進行挖寶。
民眾遙控船隻挖寶系統設計
TreasureMap
我們需要一個 TreasureMap 類,
- 寶物有三種類型:金(Gold)、銀(Silver)、銅(Bronze),
- 可以通過 findTreasure 獲得某種寶物的所在地
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 |
class TreasureMap { enum Tresures { case gold case silver case bronze } struct MapLocation { let gridLetter: Character let gridNumber: Int } func findTresure(type:Tresures) -> MapLocation { switch type { case .gold: return MapLocation(gridLetter: "A", gridNumber: 0) case .silver: return MapLocation(gridLetter: "F", gridNumber: 17) case .bronze: return MapLocation(gridLetter: "O", gridNumber: 40) } } } |
Ship
我們需要一個 Ship 類,
- 通過 Struct ShipLocation 來紀錄自身的座標。
- 提供一個移動船隻的方法 moveToLocation(),移動完成會通過 callback 反饋 location 給調用者。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
class Ship { struct ShipLocation { let NorthSouth:Int let EastWest:Int } var currentPosition:ShipLocation init() { currentPosition = ShipLocation(NorthSouth: 20, EastWest: 20) } func moveToLocation(location:ShipLocation, callback:(ShipLocation) -> Void) { currentPosition = location callback(currentPosition) } } |
Member
我們需要一個挖寶人行機器人,來幫忙挖寶物:
通過 enum Actions 來規定三種挖寶行為:digForGold, digForSilver, digForBronze
提供一個挖寶方法 performAction(),並且在成功挖到寶物時,通過 callback 反饋 prizeValue(寶物的價值)給調用者。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
class Member { enum Actions { case digForGold case digForSilver case digForBronze } func performAction(action:Actions, callback:(Int) -> Void) { var prizeValue = 0 switch action { case .digForGold: prizeValue = 5000 case .digForSilver: prizeValue = 2000 case .digForBronze: prizeValue = 1000 } callback(prizeValue) } } |
經過幾個小時的開發,我們兩個很快就把模型給做出來了,馬上來請Stone City市長試試看吧~!
- 市長先初始化了map, ship, member
- 接著通過 map 取得 gold 的藏寶地點,treasureLocation,
- 然而這個 treasureLocation 的內容,ship 是沒有辦法直接使用的,所以還需要進行一些翻譯,轉換成 shipTarget。
- 最後執行 ship.moveToLocation(),當移動到位置時,再請人型機器人開始挖寶,即調用 member.performAction(),挖到寶物時print出結果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
let map = TreasureMap() let ship = Ship() let member = Member() let treasureLocation = map.findTresure(type: TreasureMap.Tresures.gold) // convert from map to ship coordinates let sequence:[Character] = ["A", "B", "C", "D", "E", "F", "G"] let eastWestPos = sequence.index(of: treasureLocation.gridLetter) let shipTarget = Ship.ShipLocation(NorthSouth: Int(treasureLocation.gridNumber), EastWest: eastWestPos!) // relocate ship ship.moveToLocation(location: shipTarget, callback: {location in member.performAction(action: .digForGold, callback: { prize in print("挖到了 Gold 價值 \(prize) ") }) }) |
結果出來了,不過市長也滿身大汗了。
1 |
挖到了 Gold 價值 5000 |
Stone City的市長擦了擦汗,他感覺這樣的操作有點複雜,他覺得目的其實就是挖寶,能不能讓市民只需要告訴程式想要挖什麼寶物就開始挖寶,最後只要告訴市民挖的結果就好了呢?
外觀模式(Facade Pattern)
就像上面那台機器一樣,如果我們今天想要買飲料,除了投入硬幣選擇想要的飲料外,我們還需要像夾娃娃機一樣去遙控抓取飲料,似乎有點太不方便了(雖然多了點樂趣哈)。所以對我們來說,一台自動販賣機,我們希望他可以簡單地讓我們直接選擇需要的飲料,並把飲料吐出來給我們就好了(當然還需要付錢XD)。而這樣的想法,其實就是外觀模式,這種設計模式是用來簡化一個或者多個類提供的API,以讓常見任務可以更簡便的執行。
我們創建一個 StoneFacade,將挖寶過程中會操作到的 map, ship, member 整合在一起,並對外開放一個 getTresure() 方法。
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 |
enum TreasureTypes { case gold case silver case bronze } class StoneFacade { private let map = TreasureMap() private let ship = Ship() private let member = Member() func getTreasure(type: TreasureTypes) -> Int { var prizeAmount = 0 var treasureMapType:TreasureMap.Tresures var memberActionType:Member.Actions switch(type) { case .gold: treasureMapType = .gold memberActionType = .digForGold case .silver: treasureMapType = .silver memberActionType = .digForSilver case .bronze: treasureMapType = .bronze memberActionType = .digForBronze } let treasureLocation = map.findTresure(type: treasureMapType) // convert from map to ship coordinates let sequence:[Character] = ["A", "B", "C", "D", "E", "F", "G"] let eastWestPos = sequence.index(of: treasureLocation.gridLetter) let shipTarget = Ship.ShipLocation(NorthSouth: Int(treasureLocation.gridNumber), EastWest: eastWestPos!) // move ship ship.moveToLocation(location: shipTarget, callback: { location in self.member.performAction(action: memberActionType, callback: { prize in prizeAmount = prize }) }) return prizeAmount } } |
有了 StoneFacade,就可以讓 Stone 市民輕鬆去挖寶啦~!
1 2 3 |
let facade = StoneFacade() let prize = facade.getTreasure(type: .gold) print("挖到了 Gold 價值 \(prize)") |
1 |
挖到了 Gold 價值 5000 |
Reference
- 可以到 Github 上查看本片的例子「Swift Design Pattern – Facade」
感謝!