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

Boundaries In Practice // 実践的 “Boundaries”

Boundaries In Practice // 実践的 “Boundaries”

Talk given 3 Mar 2016 at try! Swift Tokyo

// Boundaries In Practice
One of the talks that I’ve enjoyed watching is Boundaries by Gary Bernhardt (https://www.destroyallsoftware.com/talks/boundaries). If you’ve seen a talk about functional programming in Swift, you’ve probably heard it being referenced. When I first watched the talk a few years ago, I understood the theory but wasn’t sure where exactly to apply the concepts. As I’ve been writing more and more Swift, I started to see that the concept of “Functional Core, Imperative Shell” applies not just to functional programming, but also to good engineering in general. In this talk, we’ll go over a couple of specific examples how these “boundaries” can help us write better, safer, and more future-proof Swift code. You don’t need to watch the Boundaries talk to understand this talk, but if you have time, definitely watch it because it’s a great talk.

// 実践的 “Boundaries”
Gary BernhardtさんのBoundariesの講演 (https://www.destroyallsoftware.com/talks/boundaries) 、ご存知ですか。ファンクショナルプログラミングの講演で良く参照される講演です。私は数年前に初めてこの講演を観た時、「Functional Core, Imperative Shell」の理論は理解できたのですが、実際どの様にこのコンセプトを実用化できるのか良く分かりませんでした。しかし、毎日少しずつSwiftを書いているうちに、このコンセプトはファンクショナルプログラミングだけではなく、良いエンジニアリングに一般的に適用できるのが明らかになってきました。今回はどの様に「Boundaries」を利用して、もっと良い、もっと安全なそして将来性のあるSwiftを書くことが出来るのかを幾つかの事例を紹介しながらお話しさせて頂きます。「Boundaries」をご覧になった事がなくても私の話をご理解頂けると思いますが、もしお時間があれば是非一度ご覧下さい。素晴らしい話です。

Ayaka Nonaka

March 03, 2016
Tweet

More Decks by Ayaka Nonaka

Other Decks in Programming

Transcript

  1. ࣮ફత
    BOUNDARIES
    IN PRACTICE
    TRY! SWIFT 2016

    View Slide

  2. HI, I’M AYAKA.
    A.K.A. ࠼Ֆ
    @AYANONAGON

    View Slide

  3. View Slide

  4. FUNCTIONAL CORE
    IMPERATIVE SHELL

    View Slide

  5. FUNCTIONAL CORE

    View Slide

  6. APP = MANY FUNCTIONAL CORES

    View Slide

  7. WITH IMPERATIVE SHELLS

    View Slide

  8. “ALL YOU REALLY KNOW MAYBE IS THAT
    YOUR TASTE IS IMPROVING A LOT
    FASTER THAN YOUR ABILITY.”
    ANDY MATUSCHAK, ADVANCED IOS APPLICATION ARCHITECTURE AND PATTERNS (WWDC 2014)

    View Slide

  9. ࣮ફత
    BOUNDARIES
    IN PRACTICE
    TRY! SWIFT 2016

    View Slide

  10. ୈҰ
    IMMUTABLE CORE
    NETWORK-Y SHELL
    ෆมͳਊɺωοτϫʔΫͬΆ͍ද໘

    View Slide

  11. View Slide

  12. View Slide

  13. struct Story {
    let ID: String
    let title: String
    let message: String
    let sender: User
    let recipient: User
    let date: NSDate
    // ...
    }

    View Slide

  14. class StoriesViewController: UIViewController {
    let stories: [Story]
    // ...
    }

    View Slide

  15. class StoryDetailViewController: UIViewController {
    init(story: Story)
    }

    View Slide

  16. class StoryDetailViewController: UIViewController {
    private let titleView: StoryTitleView
    private let senderView: AvatarView
    private let recipientView: AvatarView
    private let dateLabel: DateLabel
    init(story: Story) {
    titleView = StoryTitleView(story: story)
    senderView = AvatarView(user: story.sender)
    recipientView = AvatarView(user: story.recipient)
    dateLabel = DateLabel(date: story.date)
    }
    // ...
    }

    View Slide

  17. url_scheme://stories/12345

    View Slide

  18. class StoryDetailViewController: UIViewController {
    init(story: Story)
    }

    View Slide

  19. class StoryDetailViewController: UIViewController {
    init(story: Story)
    init(storyID: String)
    }

    View Slide

  20. class StoryDetailViewController: UIViewController {
    private let titleView: StoryTitleView
    private let senderView: AvatarView
    private let recipientView: AvatarView
    private let dateLabel: DateLabel
    init(story: Story) { /* Same as before લͱಉ͡ */ }
    init(storyID: String) {
    // Hmmmmmmm.ɹ͜ΕԿͩΖ͏ʁ
    }
    }

    View Slide

  21. class StoryDetailViewController: UIViewController {
    let storyID: String
    private var titleView: StoryTitleView?
    private var senderView: AvatarView?
    private var recipientView: AvatarView?
    private var dateLabel: DateLabel?
    init(story: Story) { /* Same as before લͱಉ͡ */ }
    init(storyID: String) {
    self.storyID = storyID
    titleView = nil
    senderView = nil
    recipientView = nil
    dataLabel = nil
    }
    // Load everything from API in viewDidLoad?
    // viewDidLoadͰAPI͔Βϩʔυʁ!ɹ
    }

    View Slide

  22. class StoryContainerViewController: UIViewController {
    let storyID: String
    init(storyID: String) {
    self.storyID = storyID
    }
    override func viewDidLoad() {
    client.showStory(ID: storyID) { result in
    switch result {
    case .Success(let story):
    let viewController = StoryDetailViewController(story: story)
    self.addChildViewController(viewController)
    self.view.addSubview(viewController.view)
    viewController.view.frame = view.bounds
    viewController.didMoveToParentViewController(self)
    case .Error(let error): // Show errorɹΤϥʔදࣔ
    }
    }
    }
    }

    View Slide

  23. url_scheme://stories/12345
    StoryContainerViewController(storyID: "12345")

    View Slide

  24. url_scheme://stories/12345

    View Slide

  25. url_scheme://stories/12345
    url_scheme://users/007

    View Slide

  26. url_scheme://stories/12345
    url_scheme://users/007
    url_scheme://messages/9876

    View Slide

  27. protocol RemoteContentProviding {
    typealias Content
    func fetchContent(completion: Result -> Void)
    func viewControllerForContent(content: Result) -> UIViewController
    }

    View Slide

  28. class RemoteContentContainerViewController: UIViewController {
    let provider: T
    init(provider: T) {
    self.remoteContentProvider = remoteContentProvider
    super.init(nibName: nil, bundle: nil)
    }
    override func viewDidLoad() {
    super.viewDidLoad()
    provider.fetchContent { content in
    let viewController = self.provider.viewControllerForContent(content)
    self.addChildViewController(viewController)
    self.view.addSubview(viewController.view)
    viewController.view.frame = view.bounds
    viewController.didMoveToParentViewController(self)
    }
    }
    }

    View Slide

  29. struct StoryProvider: RemoteContentProviding {
    let ID: String
    func fetchContent(completion: Result -> Void) {
    client.showStory(ID: ID, completion: completion)
    }
    func viewControllerForContent(content: Result) -> UIViewController {
    switch content {
    case .Success(let story): return StoryDetailViewController(story: story)
    case .Error(_): return ErrorViewController(title: "Could not find story.")
    }
    }
    }

    View Slide

  30. url_scheme://stories/12345
    let provider = StoryProvider(ID: "12345")
    RemoteContentContainerViewController(provider: provider)

    View Slide

  31. url_scheme://users/007
    let provider = UserProvider(ID: "007")
    RemoteContentContainerViewController(provider: provider)

    View Slide

  32. url_scheme://messages/9876
    let provider = MessageProvider(ID: "9876")
    RemoteContentContainerViewController(provider: provider)

    View Slide

  33. class StoryDetailViewController: UIViewController {
    let storyID: String
    private var titleView: StoryTitleView?
    private var senderView: AvatarView?
    private var recipientView: AvatarView?
    private var dateLabel: DateLabel?
    init(story: Story) { /* Same as before */ }
    init(storyID: String) {
    self.storyID = storyID
    titleView = nil
    senderView = nil
    recipientView = nil
    dataLabel = nil
    }
    // Load everything from API in viewDidLoad?
    // viewDidLoadͰAPI͔Βϩʔυʁ!
    }

    View Slide

  34. View Slide

  35. class StoryDetailViewController: UIViewController {
    private let titleView: StoryTitleView
    private let senderView: AvatarView
    private let recipientView: AvatarView
    private let dateLabel: DateLabel
    init(story: Story) {
    titleView = StoryTitleView(story: story)
    senderView = AvatarView(user: story.sender)
    recipientView = AvatarView(user: story.recipient)
    dateLabel = DateLabel(date: story.date)
    }
    // ...
    }

    View Slide

  36. View Slide

  37. ୈೋ
    INDEPENDENT CORES
    CONNECTIVE SHELL
    ಠཱͨ͠ਊͱܨ͛Δද໘

    View Slide

  38. View Slide

  39. COORDINATORS
    SOROUSH KHANLOU @ NSSPAIN

    View Slide

  40. VIEW CONTROLLERS DON’T KNOW
    ABOUT OTHER VIEW CONTROLLERS

    View Slide

  41. @UIApplicationMain
    class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?
    private lazy var applicationCoordinator: ApplicationCoordinator = {
    return ApplicationCoordinator(window: self.window!)
    }()
    func application(application: UIApplication, didFinishLaunchingWithOptions
    launchOptions: [NSObject: AnyObject]?) -> Bool {
    window = UIWindow(frame: UIScreen.mainScreen().bounds)
    applicationCoordinator.start()
    return true
    }
    }

    View Slide

  42. protocol Coordinator {
    func start()
    }

    View Slide

  43. class ApplicationCoordinator: Coordinator {
    let window: UIWindow
    let rootViewController = UITabBarController()
    let wordsNavigationController = UINavigationController()
    let phrasesNavigationController = UINavigationController()
    let wordsCoordinator: WordsCoordinator
    let phrasesCoordinator: PhrasesCoordinator
    init(window: UIWindow) {
    self.window = window
    let viewControllers = [wordsNavigationController, phrasesNavigationController]
    self.rootViewController.setViewControllers(viewControllers, animated: false)
    self.wordsCoordinator = WordsCoordinator(presenter: wordsNavigationController)
    self.phrasesCoordinator = PhrasesCoordinator(presenter: phrasesNavigationController)
    }
    func start() {
    window.rootViewController = rootViewController
    wordsCoordinator.start()
    phrasesCoordinator.start()
    window.makeKeyAndVisible()
    }
    }

    View Slide

  44. class WordsCoordinator: Coordinator {
    let presenter: UINavigationController
    private let listViewController: ListViewController
    private let dataSource: WordsDataSource
    init(presenter: UINavigationController) {
    self.presenter = presenter
    self.dataSource = WordsDataSource()
    self.listViewController = ListViewController()
    self.listViewController.title = "Words"
    self.listViewController.items = dataSource.words
    self.listViewController.configureCell = { cell, item in
    cell.item = item
    }
    self.listViewController.didSelectItem = { item in
    presenter.pushViewController(WordViewController(word: item), animated: true)
    }
    }
    func start() {
    presenter.pushViewController(listViewController, animated: false)
    }
    }

    View Slide

  45. class PhrasesCoordinator: Coordinator {
    let presenter: UINavigationController
    private let listViewController: ListViewController
    private let dataSource: PhrasesDataSource
    init(presenter: UINavigationController) {
    self.presenter = presenter
    self.dataSource = PhrasesDataSource()
    self.listViewController = ListViewController()
    self.listViewController.title = "Phrases"
    self.listViewController.items = dataSource.phrases
    self.listViewController.configureCell = { cell, item in
    cell.item = item
    }
    self.listViewController.didSelectItem = { item in
    presenter.pushViewController(PhraseViewController(phrase: item), animated: true)
    }
    }
    func start() {
    presenter.pushViewController(listViewController, animated: false)
    }
    }

    View Slide

  46. ApplicationCoordinator
    / \
    WordsCoordinator PhrasesCoordinator

    View Slide

  47. ApplicationCoordinator
    / \
    WordsCoordinator PhrasesCoordinator
    /
    SearchCoordinator

    View Slide

  48. ApplicationCoordinator
    / \
    DictionaryCoordinator LoginCoordinator
    / \
    WordsCoordinator PhrasesCoordinator
    /
    SearchCoordinator

    View Slide

  49. ApplicationCoordinator
    / \ \
    DictionaryCoordinator LoginCoordinator SignUpCoordinator
    / \
    WordsCoordinator PhrasesCoordinator
    /
    SearchCoordinator

    View Slide

  50. View Slide

  51. View Slide

  52. ந৅Խ
    ABSTRACTION

    View Slide

  53. ݎ࣮
    SOLID

    View Slide

  54. ྲྀಈత
    FLUID

    View Slide

  55. ྲྀಈత 㲗 ݎ࣮
    FLUID 㲗 SOLID

    View Slide

  56. কདྷੑ
    FUTURE PROOF

    View Slide

  57. THANK YOU
    ͋Γ͕ͱ͏ʂ

    View Slide

  58. View Slide