# Design Patterns in Swift: Strategy

I'm solving the following problem using the Strategy design pattern in Swift:

We order our parts from three different vendors. Each vendor has its own system for verifying and fulfilling parts orders.

1. ACME Parts Co which provides parts for domestic cars requires authorization from our parts supervisor before it can finalize any orders.

2. PartsNStuff which provides parts for asians cars needs to check each mechanic's id against its own databse before finalizing orders.

3. AutoPart Co which provides parts for European cars, as part of their state of the art secure ordering, sends us a number that we have to return true if even and false if odd before they can fulfill our orders.

We need a system that can fulfil mechanic's order from all three vendors.

I would appreciate feedback on how this can be improved, if it remains true to the strategy design pattern and any input on design decisions I've made for solving this problem.

Here is the full code, the full repo can be found here: Design Patterns in Swift: Strategy

class Order: Hashable, Equatable {
let orderId: Int
var parts: [Part]
var carType: CarType
var fulfilled: Bool = false

init(orderId: Int, parts: [Part], carType: CarType) {
self.orderId = orderId
self.parts = parts
self.carType = carType
}

func orderFulfilled() {
self.fulfilled = true
print("Order: \(self.orderId) is fulfilled")
}

var hashValue: Int {
return orderId
}
}

func == (lhs: Order, rhs: Order) -> Bool {
return lhs.orderId == rhs.orderId
}

struct Part {
let name: String
let price: Double

init(name: String, price: Double) {
self.name = name
self.price = price
}
}

enum CarType: Int {
case Domestic = 0, Asian, European
}

struct Mechanic: Hashable, Equatable {
let mechanicId: Int
let name: String

init(mechanicId: Int, name: String) {
self.mechanicId = mechanicId
self.name = name
}

var hashValue: Int {
return mechanicId
}
}

func == (lhs: Mechanic, rhs: Mechanic) -> Bool {
return lhs.mechanicId == rhs.mechanicId
}

protocol OrderStrategy {
func fulfillOrder(order: Order) -> Bool
}

class ACMEStrategy: OrderStrategy {

func fulfillOrder(order: Order) -> Bool {
let signature = PartsSupervisor.instance.getSupervisorSignatureOnOrder(order)
if ACME.instance.fulfillOrder(order) {
print("ACME strategy worked correctly, order fulfilled")
order.orderFulfilled()
return true
} else {
print("ACME strategy error: order id: \(order.orderId) could not be fulfilled")
}
} else {
print("ACME strategy error: order id: \(order.orderId) could not be approved by vendor, " +
"error with supervisor signature")
}
return false
}
}

class PartsNStuffStrategy: OrderStrategy {

func fulfillOrder(order: Order) -> Bool {
let mechanicId = MechanicOrderDataProvider.instace.getMechanicIdFromOrderId(order.orderId)
if let mechanicId = mechanicId {
if PartsNStuff.instance.fulfillOrder(order, mechanicId: mechanicId) {
print("PartsNStuff strategy worked correctly, order fulfilled")
order.orderFulfilled()
return true
} else {
print("PartsNStuff strategy error: mechanic id: \(mechanicId) " +
"is not approved to order from PartsNStuff")
}
} else {
print("PartsNStuff strategy error: order id: \(order.orderId) did not match any mechanics")
}
return false
}
}

class AutoPartsStrategy: OrderStrategy {

func fulfillOrder(order: Order) -> Bool {
let toBeVerified = AutoPart.instace.getVerifyingNumber()
if AutoPart.instace.authenticateOrder(order, response: toBeVerified % 2 == 0) {
if AutoPart.instace.fulfillOrder(order) {
print("Auto part strategy worked correctly, order fulfilled")
order.orderFulfilled()
return true
} else {
print("AutoPart strategy error: order id: \(order.orderId) could not be fulfilled")
}
} else {
print("AutoPart strategy error: order id: \(order.orderId) could not be verified by vendor")
}
return false
}
}

class Orderer {

let strategy: OrderStrategy

init(strategy: OrderStrategy) {
self.strategy = strategy
}

func fulfillOrder(order: Order) -> Bool {
return self.strategy.fulfillOrder(order)
}
}

class OrderManager {
static var instance = OrderManager()
private var acmeStrategy: Orderer
private var partsnstuffStrategy: Orderer
private var autopartsStrategy: Orderer
private var currentOrderId: Int

private init() {
self.acmeStrategy = Orderer(strategy: ACMEStrategy())
self.partsnstuffStrategy = Orderer(strategy: PartsNStuffStrategy())
self.autopartsStrategy = Orderer(strategy: AutoPartsStrategy())
self.currentOrderId = 1558
}

func generateOrderForMechanic(mechanic: Mechanic, parts: [Part], carType: CarType) -> Order {
let orderId = currentOrderId + 1
let order = Order(orderId: orderId, parts: parts, carType: carType)
currentOrderId = orderId
return order
}

func fulfillOrder(order: Order) -> Bool {
switch order.carType {
case .Domestic:
return acmeStrategy.fulfillOrder(order)
case .Asian:
return partsnstuffStrategy.fulfillOrder(order)
case .European:
return autopartsStrategy.fulfillOrder(order)

}
}
}

class PartsSupervisor {
static let instance = PartsSupervisor()
private let privateKey = 5

private init() {}

func getSupervisorSignatureOnOrder(order: Order) -> Int {
return order.orderId ^ privateKey
}
}

class MechanicOrderDataProvider {
static let instace = MechanicOrderDataProvider()
private var data: [Int:Int]

private init() {
data = [Int:Int]()
}

func getMechanicIdFromOrderId(orderId: Int) -> Int? {
return data[orderId]
}

func addMechanicOrder(order: Order, mechanic: Mechanic) {
data[order.orderId] = mechanic.mechanicId
}
}

class ACME {

static let instance = ACME()
var approvedOrders: Set<Order>
private let privateKey = 5

private init() {
approvedOrders = Set<Order>()
}

func addToApprovedOrder(order: Order, partsSupervisorSignature: Int) -> Bool {
if partsSupervisorSignature ^ privateKey == order.orderId {
approvedOrders.insert(order)
return true
}
return false
}

func fulfillOrder(order: Order) -> Bool {
if approvedOrders.contains(order) {
//Code by ACME to fulfill Order
approvedOrders.remove(order)
return true
}
return false
}
}

class PartsNStuff {

static var instance = PartsNStuff()
private var approveMechanics = Set<Int>()
private init() {}

approveMechanics.insert(mechanicId)
}

func fulfillOrder(order: Order, mechanicId: Int) -> Bool {
if approveMechanics.contains(mechanicId) {
//Code by PartsNStuff to fulfill order
return true
}
return false
}
}

class AutoPart {

static var instace = AutoPart()
private var expectedResponse: Bool {
return self.selectedNumber % 2 == 0
}
private var selectedNumber: Int
private var approvedOrders: Set<Order>

private init() {
approvedOrders = Set<Order> ()
self.selectedNumber = Int(arc4random_uniform(1000) + 1)
}

private func setVerificationNumbers() {
self.selectedNumber = Int(arc4random_uniform(1000) + 1)
}

func getVerifyingNumber() -> Int {
return selectedNumber
}

func authenticateOrder(order: Order, response: Bool) -> Bool {
if response == expectedResponse {
approvedOrders.insert(order)
return true
}
return false
}

func fulfillOrder(order: Order) -> Bool {
if approvedOrders.contains(order) {
//Code by AutoPart Co to fulfill order
return true
}
return false
}
}


Here is the main setup and some test cases:

// Mechanic setup
var joe = Mechanic(mechanicId: 6653, name: "Joe Stevenson")
var mike = Mechanic(mechanicId: 7785, name: "Mike Rove")
var sam = Mechanic(mechanicId: 5421, name: "Sam Warren")
var tom = Mechanic(mechanicId: 99, name: "Tom Tanner")

// Approving one of our mechanics to order from PartsNStuff

// Create orders
var order1 = OrderManager.instance.generateOrderForMechanic(
joe,
parts: [Part(name: "Brake pads", price: 15.22),
Part(name: "Brake Fluid", price: 18.99)],
carType: .Asian)

var order2 = OrderManager.instance.generateOrderForMechanic(
mike,
parts: [Part(name: "5 qt Synthetic Oil", price: 15.99),
Part(name: "Standard Filters", price: 8.49)],
carType: .European)

var order3 =  OrderManager.instance.generateOrderForMechanic(
sam,
parts: [Part(name: "Engine Coolant", price: 18.99)],
carType: .Domestic)

// Fulfill orders
OrderManager.instance.fulfillOrder(order1)
OrderManager.instance.fulfillOrder(order2)
OrderManager.instance.fulfillOrder(order3)


Here is the output we get with the current setup and test cases

PartsNStuff strategy worked correctly, order fulfilled
Order: 1559 is fulfilled
Auto part strategy worked correctly, order fulfilled
Order: 1560 is fulfilled
ACME strategy worked correctly, order fulfilled
Order: 1561 is fulfilled
Program ended with exit code: 0