神刀安全网

[iOS] 基于 Bob 大叔 OOD 经典(S.O.L.I.D.) Swift 实现示例

import Swift import Foundation

The Principles of OOD in Swift 2.2

A short cheat-sheet with Xcode 7.3 Playground ( OOD-Principles-In-Swift.playground.zip ).

:construction_worker: Project maintained by: @nsmeme (Oktawian Chojnacki)

S.O.L.I.D.

  • The Single Responsibility Principle
  • The Open Closed Principle
  • The Liskov Substitution Principle
  • The Interface Segregation Principle
  • The Dependency Inversion Principle

:closed_lock_with_key: The Single Responsibility Principle

A class should have one, and only one, reason to change. ( read more )

Example:

protocol CanBeOpened {     func open() }  protocol CanBeClosed {     func close() }  // I'm the door. I have an encapsulated state and you can change it using methods. final class PodBayDoor: CanBeOpened, CanBeClosed {      private enum State {         case Open         case Closed     }      private var state: State = .Closed      func open() {         state = .Open     }      func close() {         state = .Closed     } }  // I'm only responsible for opening, no idea what's inside or how to close. class DoorOpener {     let door: CanBeOpened      init(door: CanBeOpened) {         self.door = door     }      func execute() {         door.open()     } }  // I'm only responsible for closing, no idea what's inside or how to open. class DoorCloser {     let door: CanBeClosed      init(door: CanBeClosed) {         self.door = door     }      func execute() {         door.close()     } }  let door = PodBayDoor()

⚠ Only the DoorOpener is responsible for opening the door.

let doorOpener = DoorOpener(door: door) doorOpener.execute()

⚠ If another operation should be made upon closing the door, like switching on the alarm, you don’t have to change the DoorOpener class.

let doorCloser = DoorCloser(door: door) doorCloser.execute()

:hand: The Open Closed Principle

You should be able to extend a classes behavior, without modifying it. ( read more )

Example:

protocol CanShoot {     func shoot() -> String }  // I'm a laser beam. I can shoot. final class LaserBeam: CanShoot {     func shoot() -> String {         return "Ziiiiiip!"     } }  // I have weapons and trust me I can fire them all at once. Boom! Boom! Boom! final class WeaponsComposite {      let weapons: [CanShoot]      init(weapons: [CanShoot]) {         self.weapons = weapons     }      func shoot() -> [String] {         return weapons.map { $0.shoot() }     } }  let laser = LaserBeam() var weapons = WeaponsComposite(weapons: [laser])  weapons.shoot()

I’m a rocket launcher. I can shoot a rocket.

:warning: To add rocket launcher support I don’t need to change anything in existing classes.

final class RocketLauncher: CanShoot {     func shoot() -> String {         return "Whoosh!"     } }  let rocket = RocketLauncher()  weapons = WeaponsComposite(weapons: [laser, rocket]) weapons.shoot()

:busts_in_silhouette: The Liskov Substitution Principle

Derived classes must be substitutable for their base classes. ( read more )

Example:

let requestKey: NSString = "NSURLRequestKey"  // I'm a NSError subclass. I provide additional functionality but don't mess with original ones. class RequestError: NSError {      var request: NSURLRequest? {         return self.userInfo[requestKey] as? NSURLRequest     } }  // I fail to fetch data and will return RequestError. func fetchData(request: NSURLRequest) -> (data: NSData?, error: RequestError?) {      let userInfo: [NSObject:AnyObject] = [ requestKey : request ]      return (nil, RequestError(domain:"DOMAIN", code:0, userInfo: userInfo)) }  // I don't know what RequestError is and will fail and return a NSError. func willReturnObjectOrError() -> (object: AnyObject?, error: NSError?) {      let request = NSURLRequest()     let result = fetchData(request)      return (result.data, result.error) }  let result = willReturnObjectOrError()  // Ok. This is a perfect NSError instance from my perspective. let error: Int? = result.error?.code  // But hey! What's that? It's also a RequestError! Nice! if let requestError = result.error as? RequestError {     requestError.request }

:fork_and_knife: The Interface Segregation Principle

Make fine grained interfaces that are client specific. ( read more )

Example:

// I have a landing site. protocol LandingSiteHaving {     var landingSite: String { get } }  // I can land on LandingSiteHaving objects. protocol Landing {     func landOn(on: LandingSiteHaving) -> String }  // I have payload. protocol PayloadHaving {     var payload: String { get } }  // I can fetch payload from vehicle (ex. via Canadarm). final class InternationalSpaceStation {

⚠ Space station has no idea about landing capabilities of SpaceXCRS8.

func fetchPayload(vehicle: PayloadHaving) -> String {         return "Deployed /(vehicle.payload) at April 10, 2016, 11:23 UTC"     } }  // I'm a barge - I have landing site (well, you get the idea). final class OfCourseIStillLoveYouBarge: LandingSiteHaving {     let landingSite = "a barge on the Atlantic Ocean" }  // I have payload and can land on things having landing site. // I'm a very limited Space Vehicle, I know. final class SpaceXCRS8: Landing, PayloadHaving {      let payload = "BEAM and some Cube Sats"

⚠ CRS8 knows only about the landing site information.

func landOn(on: LandingSiteHaving) -> String {         return "Landed on /(on.landingSite) at April 8, 2016 20:52 UTC"     } }  let crs8 = SpaceXCRS8() let barge = OfCourseIStillLoveYouBarge() let spaceStation = InternationalSpaceStation()  spaceStation.fetchPayload(crs8) crs8.landOn(barge)

:nut_and_bolt: The Dependency Inversion Principle

Depend on abstractions, not on concretions. ( read more )

Example:

protocol TimeTraveling {     func travelInTime(time: NSTimeInterval) -> String }  final class DeLorean: TimeTraveling {     func travelInTime(time: NSTimeInterval) -> String {         return "Used Flux Capacitor and travelled in time by: /(time)s"     } }  final class EmmettBrown {     private let timeMachine: TimeTraveling

⚠ Emmet Brown is given the DeLorean as a TimeTraveling device, not the concrete class DeLorean .

init(timeMachine: TimeTraveling) {         self.timeMachine = timeMachine     }      func travelInTime(time: NSTimeInterval) -> String {         return timeMachine.travelInTime(time)     } }  let timeMachine = DeLorean()  let mastermind = EmmettBrown(timeMachine: timeMachine) mastermind.travelInTime(-3600 * 8760)

Info

:book: Descriptions from: The Principles of OOD by Uncle Bob

转载本站任何文章请注明:转载至神刀安全网,谢谢神刀安全网 » [iOS] 基于 Bob 大叔 OOD 经典(S.O.L.I.D.) Swift 实现示例

分享到:更多 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址