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

API Design as an Art

Zac Sweers
November 05, 2017

API Design as an Art

As developers, we are constantly interacting with APIs. Good ones can make feature iterations a breeze, and bad ones can waste hours of developer productivity. But we rarely stop to consider how they fit into our own work or how to be cognizant of potential design pitfalls. Using AutoDispose, an open source reactive tool for automatic stream disposal, as a case study, this talk will evaluate strategies and best practices for iteratively fine tuning real world APIs for better testing and the overall developer experience of your consumers.

Zac Sweers

November 05, 2017
Tweet

More Decks by Zac Sweers

Other Decks in Programming

Transcript

  1. AutoDispose • RxJava 2 utility for automatic stream disposal •

    Originally built in a side project ~10/2016
  2. AutoDispose • RxJava 2 utility for automatic stream disposal •

    Originally built in a side project ~10/2016 • Mainlined to Uber ~12/2016
  3. AutoDispose • RxJava 2 utility for automatic stream disposal •

    Originally built in a side project ~10/2016 • Mainlined to Uber ~12/2016 • Open sourced 3/2017
  4. apiRequest() .subscribeOn(io()) .observeOn(mainThread()) .subscribe({ r -> // Handle response })

    { error: Throwable -> // Stuff // Stuff } .disposeOn(lifecycle)
  5. fun around(o: Observer<T>) fun around(c: Consumer<T>) fun around(c: Consumer<T>, e:

    Consumer<Throwable>) fun around(c: Consumer<T>, e: Consumer<Throwable>, a: Action) // Etc
  6. fun around(o: Observer<T>) fun around(c: Consumer<T>) fun around(c: Consumer<T>, e:

    Consumer<Throwable>) fun around(c: Consumer<T>, e: Consumer<Throwable>, a: Action) fun empty() // Etc
  7. interface AroundClause<T> { fun around(o: Observer<T>) fun around(c: Consumer<T>) fun

    around(c: Consumer<T>, e: Consumer<Throwable>) fun around(c: Consumer<T>, e: Consumer<Throwable>, a: Action) fun empty() // Etc }
  8. interface AroundClause<T> { fun around(o: Observer<T>) fun around(c: Consumer<T>) fun

    around(c: Consumer<T>, e: Consumer<Throwable>) fun around(c: Consumer<T>, e: Consumer<Throwable>, a: Action) fun empty() // Etc }
  9. interface AroundClause { fun <T> around(o: Observer<T>) fun <T> around(c:

    Consumer<T>) fun <T> around(c: Consumer<T>, e: Consumer<Throwable>) fun <T> around(c: Consumer<T>, e: Consumer<Throwable>, a: Action) fun empty() // Etc }
  10. class ObservableScoper<T> implements Function<Observable<T>, AroundClause> { ObservableScoper(Maybe<?> scope) { //

    ... }a @Override public AroundClause apply(Observable<T> o) { return new AroundClause() { // ... }c }b }v
  11. class ObservableScoper<T> implements Function<Observable<T>, AroundClause<T>> { ObservableScoper(Maybe<?> scope) { //

    ... }a @Override public AroundClause<T> apply(Observable<T> o) { return new AroundClause<T>() { // ... }c }b }v
  12. interface AroundClause<T> { fun around(o: Observer<T>) fun around(c: Consumer<T>) fun

    around(c: Consumer<T>, e: Consumer<Throwable>) fun around(c: Consumer<T>, e: Consumer<Throwable>, a: Action) fun empty() // Etc }
  13. interface SubscribeProxy<T> { fun around(o: Observer<T>) fun around(c: Consumer<T>) fun

    around(c: Consumer<T>, e: Consumer<Throwable>) fun around(c: Consumer<T>, e: Consumer<Throwable>, a: Action) fun empty() // Etc }
  14. interface SubscribeProxy<T> { fun subscribe(o: Observer<T>) fun subscribe(c: Consumer<T>) fun

    subscribe(c: Consumer<T>, e: Consumer<Throwable>) fun subscribe(c: Consumer<T>, e: Consumer<Throwable>, a: Action) fun empty() // Etc }
  15. interface SubscribeProxy<T> { fun subscribe(o: Observer<T>) fun subscribe(c: Consumer<T>) fun

    subscribe(c: Consumer<T>, e: Consumer<Throwable>) fun subscribe(c: Consumer<T>, e: Consumer<Throwable>, a: Action) fun subscribe() // Etc }
  16. “Prefer exposing interfaces” interface SubscribeProxy<T> { fun subscribe(o: Observer<T>) fun

    subscribe(c: Consumer<T>) fun subscribe(c: Consumer<T>, e: Consumer<Throwable>) fun subscribe(c: Consumer<T>, e: Consumer<Throwable>, a: Action) fun subscribe() // Etc }
  17. “Prefer composition in accepting external implementations. Inheritance can be inconvenient.”

    class ResponseObserver : BoundObserver<Response>() { // :( } PublishSubject<Response> sub = PublishSubject.create()