Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Dobry zwyczaj: nie odziedziczaj

Dobry zwyczaj: nie odziedziczaj

Slides for my conference talk from Mobilization 2015, 2015/10/17.

Polish only.

English title would be "Solely on merit: do not inherit"

Krzysztof Siejkowski

October 17, 2015
Tweet

More Decks by Krzysztof Siejkowski

Other Decks in Programming

Transcript

  1. Dobry zwyczaj
    nie odziedziczaj
    @_siejkowski
    siejkowski.net
    !

    View Slide

  2. View Slide

  3. class A : B

    View Slide

  4. BaseViewController
    BaseDataFetchingViewController
    :BaseViewController
    BaseFormPresentingViewController
    :BaseDataFetchingViewController

    View Slide

  5. Przepraszam, czy mogę
    przeciążyć?

    View Slide

  6. super
    ?

    View Slide

  7. View Slide

  8. NSManagedObject
    https://github.com/rentzsch/mogenerator

    View Slide

  9. BaseViewController : UIViewController
    ThirdPartyBehaviorEnablingViewController
    : UIViewController
    UserAccountDetailsViewController : !

    View Slide

  10. struct SomeStruct {}
    enum SomeEnum {}
    typealias SomeFunction = (Int -> String)
    class C : SomeFunction
    ❗ Inheritance from non-protocol, non-class type
    'SomeFunction'

    View Slide

  11. agregacja
    protokoły
    funkcje

    View Slide

  12. ThirdPartyBehaviorEnablingService
    ThirdPartyBehaviorEnablingDelegate

    View Slide

  13. init(accountService: AccountService,
    transactionService: TransactionService,
    sessionService: SessionService,
    favouritesService: FavouritesService,
    locationService: LocationService,
    ...)

    View Slide

  14. class *-Utils
    class *-Helper

    View Slide

  15. UITableViewDataSource
    UITableViewDelegate

    View Slide

  16. code review
    TDD
    style guide
    clean code

    View Slide

  17. agregacja
    protokoły
    funkcje

    View Slide

  18. protocol SessionAware
    protocol LocationAware
    protocol FavouritesFetchable
    class MyViewController : UIViewController,
    SessionAware, LocationAware,
    FavouritesFetchable

    View Slide

  19. extension
    UIViewController
    : SessionAware

    View Slide

  20. protocol SessionAware {
    func login(username: String,
    _ password: String,
    _ callback: (UserDetails) -> ())
    }
    extension MyViewController : SessionAware {}
    extension SessionAware {
    func login(username: String,
    _ password: String,
    _ callback: (UserDetails) -> ())) {
    // domyślna implementacja
    }
    }
    ❗ http://nomothetis.svbtle.com/the-ghost-of-swift-bugs-future

    View Slide

  21. protocol FavouritesDataSource : UITableViewDataSource {
    var state: [FavouriteDetails] { get } // "obietnica" stanu
    }
    extension FavouritesDataSource {
    func tableView(_ tableView: UITableView,
    numberOfRowsInSection section: Int) -> Int {
    return state.count
    // ...
    protocol FavouritesTableDelegate : UITableViewDelegate {
    var state: [FavouriteDetails] { get } // "obietnica" stanu
    }
    extension FavouritesTableDelegate {
    func tableView(_ tableView: UITableView,
    heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
    // ...
    class MyViewController : NSObject, FavouritesDataSource, FavouritesTableDelegate {
    var state: [FavouriteDetails] = Array()
    init(newState: [FavouriteDetails]) {
    state = newState
    }

    View Slide

  22. ⬆ dziedziczenie ⬇
    ⬅ protokoły ➡

    View Slide

  23. agregacja
    protokoły
    funkcje

    View Slide

  24. func login(username: String,
    _ password: String,
    _ callback: (UserDetails) -> ())
    func update(view: HeaderView)
    (_ user: UserDetails) -> UserDetails
    func fetchFavourites(user: UserDetails)
    (_ callback: (FavouritesData) -> ())
    func update(source: FavoritesDataSource)
    (_ tableView: UITableView)
    (_ favourites: FavouritesData)

    View Slide

  25. class SomeViewController {
    let updateHeader = update(headerView)
    let updateFavourites = update(favouritesDataSource)(tableView)
    override func viewDidLoad() {
    // ..., m.in super.viewDidLoad()
    login("siejkowski","mobilization") { details in
    fetchFavourites(updateHeader(details))(updateFavourites)
    }
    }

    View Slide

  26. infix operator |> { associativity left }
    infix operator <| { associativity right }
    public func |> (a : A, f : A -> B) -> B {
    return f(a)
    }
    public func <| (f : A -> B, a : A) -> B {
    return f(a)
    }

    View Slide

  27. infix operator useFor { associativity left }
    details useFor updateHeader

    !

    View Slide

  28. login("siejkowski","mobilization") { details in
    fetchFavourites(updateHeader(details))(updateFavourites)
    }
    ⬇ ⬇ ⬇
    login("siejkowski","mobilization") { details in
    (details |> updateHeader |> fetchFavourites) <| updateFavourites
    }

    View Slide

  29. agregacja
    protokoły
    funkcje

    View Slide

  30. class SomeViewController {
    let updateHeader = update(headerView)
    let updateFavourites = update(favouritesDataSource)(tableView)
    override func viewDidLoad() {
    super.viewDidLoad()
    login("siejkowski","mobilization") { details in
    (details |> updateHeader |> fetchFavourites) <| updateFavourites
    }
    }
    !

    View Slide

  31. pure functions
    Swift functions

    View Slide

  32. struct FunctionUsingStruct {
    let func: String -> Bool
    init(_ injectedFunc: String -> Bool) {
    // funkcje można wstrzykiwać
    func = injectedFunc
    ...
    }
    }
    !

    View Slide

  33. class DIFactory {
    func realFunc(string: String) -> Bool { ... }
    // w produkcyjnym kodzie wstrzykujemy prawdziwą funkcję
    func functionUsingStruct() -> FunctionUsingStruct {
    return FunctionUsingStruct(realFunc)
    }
    class TestCase {
    func testFunc(string: String) -> Bool { return true }
    // w testowym kodzie używamy mockowej funkcji
    let testStruct = FunctionUsingStruct(testFunc)
    !

    View Slide

  34. func login(username: String,
    _ password: String,
    _ callback: (UserDetails) -> ())
    func fullLogin(loginManager: LoginManager)
    (_ username: String,
    _ password: String,
    _ callback(UserDetails) -> ()) -> () {
    loginManager.login(username, password, callback)
    }
    let login = fullLogin(actualLoginManager)
    let mockLogin = fullLogin(mockLoginManager)

    View Slide

  35. funkcje ❤ protokoły

    View Slide

  36. extension MyViewController : SessionAware {
    func login(username: String,
    _ password: String,
    _ callback: (UserDetails) -> ()) {
    // tutaj przekazany jest domyślnie self
    }
    }
    func login(username: String,
    _ password: String,
    _ callback: (UserDetails) -> ()) {
    // tutaj przekazane jest to co w sygnaturze
    }

    View Slide

  37. func root(double: Double) -> Double? { ... }
    func root(float: Float) -> Float? { ... }
    func root(int: Int) -> Int? { ... }
    protocol Rootable { func root() -> Self? }
    extension Double : Rootable { ... }
    extension Float : Rootable { ... }
    extension Int : Rootable { ... }
    func useRootable(rootable: Rootable) {
    rootable.root()
    }

    View Slide

  38. *-able
    protocol JSONDecodable {
    static func decode(json: JSON) -> Self
    }
    protocol Validatable {
    func validate(validator: Validator) -> Bool
    }
    protocol Hashable : Equatable {
    public var hashValue: Int { get }
    }

    View Slide

  39. Co jest problemem?
    nil
    ErrorType
    asynchroniczność
    logowanie
    sekwencja
    callback
    cokolwiek chcesz !

    View Slide

  40. func canCauseProblem(input: Int) -> ProblemHiding
    func possibleProblem(input: String) -> ProblemHiding
    func yetAnotherProblem(input: Double) -> ProblemHiding
    let input = 42
    ProblemHiding(input) // udajemy że wszystko gra
    .execute(canCauseProblem) // jakby nic się nie stało
    .execute(possibleProblem) // bez nerwów, bez ifów
    .execute(yetAnotherProblem) // jak kwiat lotosu
    .obtainValue() // sprawdzam!

    View Slide

  41. protocol Monad {
    typealias A
    typealias B
    typealias FB
    func bind(f: A -> FB) -> FB
    }
    !

    View Slide

  42. extension Optional : Monad {
    typealias A = Wrapped
    typealias B = Any // nil jest problemem
    typealias FB = B?
    func bind(f: A -> B?) -> B? {
    return self.flatMap(f)
    }
    }
    extension Array : Monad {
    typealias A = Element
    typealias B = Any // wiele wartości to problem
    typealias FB = Array
    func bind(f: A -> [B]) -> [B] {
    return self.flatMap(f)
    }
    }

    View Slide

  43. enum Result { // możliwy ErrorType to problem
    case Success(Value)
    case Failure(ErrorType)
    }
    extension Result : Monad {
    typealias A = Value
    typealias B = Any
    typealias FB = Result
    func bind(f: A -> Result) -> Result {
    switch self {
    case .Success(let value):
    return f(value)
    case .Failure(let error):
    return .Failure(error)
    }
    }
    }

    View Slide

  44. https://github.com/typelift
    Swiftx
    Swiftz
    https://github.com/thoughtbot
    Runes
    Argo
    https://github.com/robrix
    Prelude

    View Slide

  45. agregacja

    protokoły

    funkcje

    View Slide

  46. agregacja

    protokoły

    funkcje

    dziedziczenie

    View Slide

  47. Dziękuję!
    @_siejkowski
    !

    View Slide