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

あなたはどう書き なぜそう書くのか?

あなたはどう書き なぜそう書くのか?

コードを書き方は何かしらの判断基準や理由を持って決定されます。今回は1つの題材を元にSwiftという多様な表現が可能な言語で、どういう状況で、どういう書き方ができ、なぜそう書くのかについて考えてみました。

[email protected]

March 19, 2019
Tweet

Other Decks in Programming

Transcript

  1. ʁ

  2. struct AnalyticsEvent { var name: String var metadata: [String: Any]

    = [:] init(name: String, metadata: [String: Any] = [:]) { self.name = name self.metadata = metadata } } class AnalyticsManager { private let engine: AnalyticsEngine init(engine: AnalyticsEngine) { self.engine = engine } func log(_ event: AnalyticsEvent) { engine.send(name: event.name, metadata: event.metadata) } } protocol AnalyticsEngine: class { func send(name: String, metadata: [String: Any]) } struct Book { let name: String } final class Library { private var library: [Book] = [] func add(_ book: Book) -> Book { library.append(book) return book } }
  3. final class BookDetailViewController: UIViewController { func addBook(book: Book) { library.add(book)

    let event = AnalyticsEvent(name: "bookOdded") analytics.log(event) } } wλΠϓϛεΛ͢ΔՄೳੑ͕͋Δ wमਖ਼͕ෳ਺ՕॴʹೖΔ final class BookListViewController: UIViewController { func addBook(book: Book) { library.add(book) let event = AnalyticsEvent(name: "bookAdded") analytics.log(event) } }
  4. enum AnalyticsEvent { case bookAdded(Book) var name: String { switch

    self { case .bookAdded: return "bookAdded" } } var metadata: [String: Any] { switch self { case .bookAdded(let book): return ["book": book.name] } } } final class BookListViewController: UIViewController { func addBook(book: Book) { library.add(book) analytics.log(.bookAdded(book)) } } final class BookDetailViewController: UIViewController { func addBook(book: Book) { library.add(book) analytics.log(.bookAdded(book)) } } ɾλΠϓϛε͕ͳ͘ͳΔ ɾίϯύΠϧ࣌ʹνΣοΫ͕Ͱ͖Δ
  5. enum AnalyticsEvent { case loginScreenViewed case loginAttempted case loginFailed(reason: LoginFailureReason)

    case loginSucceeded case bookListViewed case bookAdded(Book) case bookSelected(Book) case bookDeleted(Book) var name: String { switch self { case .loginScreenViewed, .loginAttempted, .loginSucceeded, .bookListViewed: return String(describing: self) case .loginFailed: return "loginFailed" case .bookAdded: return "bookAdded" case .bookSelected: return "bookSelected" case .bookDeleted: return "bookDeleted" } } var metadata: [String: Any] { switch self { case .loginScreenViewed, .loginAttempted, .loginSucceeded, .bookListViewed: return [:] case .loginFailed(let reason): return ["reason" : String(describing: reason)] case .bookAdded(let book): return ["book" : book.name] case .bookSelected(let index): return ["book" : book.name] case .bookDeleted(let book): return ["book" : book.name] } } } w4XJUDIจ͕େྔʹͳΔ wએݴͱม਺ͷఆ͕ٛ෼͔ΕΔ ͲΜͲΜ૿͍͑ͯ͘ɻɻɻ
  6. struct AnalyticsEvent { let name: String let metadata: [String :

    Any] private init(name: String, metadata: [String: Any] = [:]) { self.name = name self.metadata = metadata } } • ҰՕॴͰએݴͰ͖Δ • ݺͼग़͠ํ͸enumͷ࣌ͱมΘΒͳ͍ final class BookListViewController: UIViewController { func addBook(book: Book) { library.add(book) analytics.log(.bookList_BookAdded(book)) } } extension AnalyticsEvent { static func bookList_BookAdded(_ book: Book) -> AnalyticsEvent { return AnalyticsEvent( name: "bookList_BookAdded", metadata: ["book" : book.name] ) } }
  7. w໊લͷিಥ final class BookListViewController: UIViewController { func addBook(book: Book) {

    library.add(book) analytics.log(.bookList_BookAdded(book)) } } extension AnalyticsEvent { static func bookList_BookAdded(_ book: Book) -> AnalyticsEvent { return AnalyticsEvent( name: "bookList_BookAdded", metadata: ["book" : book.name] ) } static func bookDetail_BookAdded(_ book: Book) -> AnalyticsEvent { return AnalyticsEvent( name: “bookDetail_BookAdded", metadata: ["book" : book.name] ) } } final class BookDetailViewController: UIViewController { func addBook(book: Book) { library.add(book) analytics.log(.bookDetail_BookAdded(book)) } }
  8. ݺͼग़͠ଆͰॳظԽͷํ๏Λ TUSVDUͷ౎߹ʹ߹ΘͤΔඞཁ͕͋Δ extension AnalyticsEvent { static func bookDetail_BookRead(_ book: Book,

    count: Int) -> AnalyticsEvent { return AnalyticsEvent( name: “bookDetail_BookAdded", metadata: ["book": book.name, “read_count”: count] ) } }
  9. init(book: Book, count: Int) { self.init( name: "bookRead", metadata: ["book"

    : book.name, "read_count": count] ) } init(a: String) { self.init(name: a) } init(b: String) { self.init(name: b) } init(c: String) { self.init(name: c) } init(d: String) { self.init(name: d) } … DVTUPNJOJUΛఆٛͰ͖Δ͕ʜ ͲΜͲΜ૿͍͑ͯ͘ɻɻɻ
  10. • ৽͍͠Πϕϯτͷ࡞੒͕༰қ(case΍extensionͷ௥Ճඞཁͳ͠) • ಠࣗͷΠχγϟϥΠβΛఆٛͰ͖ݺͼग़͠ଆͷෛ୲Λ૿΍͞ͳ͍ • enum΋struct΋class΋࢖͑Δ • Xcodeͷࣗಈิ׬΋໰୊ͳ͠ protocol AnalyticsEvent

    { var name: String { get } var metadata: [String: Any] { get } } struct BookReadErrorAnalyticsEvent: AnalyticsEvent { var name: String { return "BookReadError" } var metadata: [String: Any] { return ["code": reason.code, "description": reason.description] struct Reason { let code: Int let description: String } private let reason: Reason init(reason: Reason) { self.reason = reason } }
  11. final class ProductionAnalyticsEngine: AnalyticsEngine { func log(_ event: AnalyticsEvent) {

    let name = "Production-\(event.name)" AnalyticsAPI.send(name: name, metadata: event.metadata) } } final class DevAnalyticsEngine: AnalyticsEngine { func log(_ event: AnalyticsEvent) { let name = "Dev-\(event.name)" AnalyticsAPI.send(name: name, metadata: event.metadata) } } final class StagingAnalyticsEngine: AnalyticsEngine { func log(_ event: AnalyticsEvent) { let name = "Staging-\(event.name)" AnalyticsAPI.send(name: name, metadata: event.metadata) } } final class TestAnalyticsEngine: AnalyticsEngine { func log(_ event: AnalyticsEvent) { let name = "Test-\(event.name)" AnalyticsAPI.send(name: name, metadata: event.metadata) } } ಉ͡Α͏ͳܕͷఆ͕ٛ૿͍͑ͯ͘
  12. • ܕΛఆٛ͢Δඞཁ͕ͳ͘ͳΔ • ΧελϚΠζ͕ࣗ༝ʹͳΔ struct AnalyticsEngine<Event: AnalyticsEvent> { let log:

    (Event) -> Void init(log: @escaping (Event) -> Void) { self.log = log } } final class BookListViewController: UIViewController { private let engine: AnalyticsEngine<BookListAnalyticsEvent> } let prefix = "Production-" let bookListEngine = AnalyticsEngine<BookListAnalyticsEvent> { event in let name = "\(prefix)\(event.name)" AnalyticsAPI.send(name: name, metadata: event.metadata) } let collection = Library() let vc = BookListViewController(library: collection, engine: bookListEngine) let bookDetailEngine = AnalyticsEngine<BookDetailAnalyticsEvent> { event in let name = "\(prefix)\(event.name)" AnalyticsAPI.send(name: name, metadata: event.metadata) }
  13. struct MultipleAnalyticsEngine<Event: AnalyticsEvent> { let log: ([Event]) -> Void init(log:

    @escaping ([Event]) -> Void) { self.log = log } } let multiEngine = MultipleAnalyticsEngine<BookListAnalyticsEvent> { events in events.forEach { AnalyticsAPI.send(name: $0.name, metadata: $0.metadata) } } let book = Book(name: "ΧϥϚʔκϑͷܑఋ") multiEngine.log([BookListAnalyticsEvent.bookAdded(book), BookDetailAnalyticsEvent.bookAdded(book)]) struct MultipleAnalyticsEngine { let log: ([AnalyticsEvent]) -> Void init(log: @escaping ([AnalyticsEvent]) -> Void) { self.log = log } } let book = Book(name: "ΧϥϚʔκϑͷܑఋ") multiEngine.log([BookListAnalyticsEvent.bookAdded(book), BookListAnalyticsEvent.bookAdded(book)]) ຊ౰͸BookDetailAnalyticsEvent.bookAdded(book)])ΛૹΓ͔ͨͬͨɻɻɻ
  14. protocol AnalyticsEvent { var name: String { get } var

    metadata: [String: Any] { get } } "OBMZUJDTʹΑͬͯ͸ ࠶ม׵ͷඞཁ͕ग़ͯ͘Δ
  15. ʁ