1
\$\begingroup\$

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.addToApprovedOrder(order, partsSupervisorSignature: signature) {
      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)
    MechanicOrderDataProvider.instace.addMechanicOrder(order, mechanic: mechanic)
    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() {}

  func addApprovedMechanic(mechanicId: Int) {
    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
PartsNStuff.instance.addApprovedMechanic(joe.mechanicId)

// 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
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.