2. Orthogonality
• Why this talk?
• I saw some frameworks with unmanageable
complexity. “We do complex stuff, so complexity
is OK”
• Most of the time, it’s not!
• Hoping to share some techniques that will help.
3. Orthogonality
• What is it?
• Make <features> that don’t(*) depend on each
other.
• (*) minimally depend
• Where <feature> is:
• Code - Method - Class - Library - Project - App - etc.
• Self-discipline
4. Orthogonality
• Why?
• Easier maintenance. Changes to some part of
the code shouldn’t affect some other part.
• Easier reading. Because less (told or untold)
dependencies.
• Easier reuse.
• Future-proof, adapts more easily.
5. Orthogonality
• How?
• Some prefer to “think before”. Does not really
work for me. (could work for you)
• I start from real-world needs & use-cases, not
trying to predict them *all* beforehand.
• Then I continuously challenge the design, and try
to improve it with new real-world usage, until it
settles/converges.
7. App
Library
Class
Method
• One purpose (avoid Swiss knives)
• Expose what’s strictly required
• Less state (fewer properties) & immutability
• One purpose
• Clear naming
• Option dictionaries instead of signature variants
• One purpose, keep them thematic
• Don’t expose internals
• Limit external dependencies (vertical slicing)
• One purpose
• Factor them out, make reusable libs
(horizontal slicing)
Code • One purpose code blocks, think of those as
a “flow”, avoid local hacks
• Use local variables
Orthogonality
8. Techniques
• Think Lego® Bricks (standard, reusable blocks), avoid custom
solutions.
• Keep as much internal things as possible. (Swift)
• Make abstractions, handle complexity internally (~ class clusters)
• Take a step back when designing APIs
• Use of static libs / frameworks / resource bundles
• Use categories
• Minimize state, favor the immutable.
9. API
• A meeting point.
• Insulates inside from outside.
• Keep complexity inside, there’s really no point in
exposing it.
• An API separates a feature from its implementation
choices.
10. Anatomy of a Method
• Method signature, required args, optional ones, return
values, completion handlers.
• Be aware of the flow & dependencies
• No internal returns. Single entry, single exit.
11. Global Variables
• Constants that absolutely need to be known
outside: OK
• Avoid otherwise, they break orthogonality
12. Local variables…
… insulate your code from the outside (think threads)
• Interactions with self have a meaning. Don’t abuse those.
Consider them as input / output for your code.
• Code with lots of reference to self or ivar is suspect.
13. Less State
• Each property has to be maintained over the lifetime of
the object.
• It’s like planting a tree. It’s cool & nice, until you’re
mowing the lawn around it.
• Very often, it’s easy and convenient to use a property.
But you’ll pay for it over time.
• Avoid properties if you can. Even private / internal
ones. Compute values from other state or from inputs
instead. [Exception: caching]
14. Immutability
• Has been there from day one in Cocoa.
• Once you get an object, it won’t change behind
your back. It stops all form of change propagation.
• Very interesting in a parallel world (GCD).
• Makes code robust.
• Trendy: Swift’s let & struct vs. class, reactive/
functional programming, etc.
15. Property Check List
• Do you need a property? Why?
• Internal or public?
• Read-only or read-write?
• Public read-only & internal read-write?
• Read-only & mutable or read-write & immutable?
• Public & read-only or full read-write? read-only + set
through specific method?
16. Method Check List
• Private or public? Internal?
• Inputs as arguments or options dictionary?
• Flow: do you really need those internal return’s?
• Are you insulating against self ?
• Isn’t it too long, woud factoring it out make more
sense?
17. Class/API Check List
• Do you need that class?
• Internal or public? Class cluster?
• Do you need each of those methods, public/
private?
• Do you need to include that additional framework in
the .h? Isn’t a .m inclusion enough?
• Do you compartmentalize deps with categories?
20. Keep cool, man!
• Good framework, method, API design is clear for everyone. There’s
not that much room for ego or personal preferences.
• Junior could come up with a better design. That’s OK, you should
welcome it & even encourage it! Be thankful for learning new stuff.
• Search for design examples. It helps. For API design, Apple
*typically* makes effort about this. (OK, they screw up sometimes)
• When you reach good design, you typically feel you’ve achieved
something. It fits nicely together, hacks are not needed, it is easily
reusable… But don’t stop there!
• Simplicity of code often converges to the same design, even from
people with different backgrounds.
23. Case Study 1: CeedOCR
• Reusable OCR library
• Possibly different OCR engines
• Mac / iOS
DEMO: Prizmo
24. Case Study 1: CeedOCR
• Simple API: 1 class, 1 method. OCR engines are
strings.
• Similar to class-cluster design (variety of internal
classes)
• No internal API is exposed (engine types, classes,
etc.)
• Dynamically looked up (not linked -> Class is Nil)
• Extensible through delegation (binarization)
25. Case Study 2: CeedAnalytics
• Reusable Analytics library. Goal: track which app
features are used, which are not.
• Possibly different backends (Google, Countly…)
• Similar solution as CeedOCR
DEMO: Prizmo
26. Case Study 3: CeedVision
• Has a class to perform Accelerate-based image
processing
• Variety of image source options (CoreVideo,
CoreImage, OpenCV, CoreGraphics, vImage…)
DEMO: Prizmo
27. Case Study 3: CeedVision
• Core features part of the class
• Input options as categories (Swift: extensions)
28. Case Study 4: CeedImage
• GPU Image Processing lib, similar to Core Image
(tiled rendering) but with more control (color vs.
gray, memory cache…).
• Optional features (external JPEG lib)
• We don’t want app have JPEG part if app don’t
need it (external lib dep, maintenance…)
DEMO: Hydra
30. Case Study 4: CeedImage
• Use extension/category for the optional feature
(JPEG)
• Put it in its own library: CeedImage.JPEGExt.a
• App link against that lib if it needs the feature.
31. Case Study 5: CeedGL
• Obj-C Wrapper for OpenGL
• Optional features: handling of Core Video buffers
• We don’t want app have Core Video dependency if
they don’t need it
• We use a separate library: CeedGL.CoreVideoExt.a
• It’s open source -> have a look!
DEMO: Hydra
34. Frameworks or static libs?
• Frameworks are available on iOS 8+ (any OS X)
• We use frameworks to share code between app
and extensions, static libs otherwise.
• Libraries can have their resource bundle
• Frameworks can be built from libraries
36. Tips & Tricks
• OK, but libs that depend on libs are a mess in
Xcode.
• How do you solve that? Use scheme to force lib
build order? No, cumbersome.
• There’s a better way.
40. Workspace
Project A
Lib A
Project B
Lib B (depends on Lib A)
Project C
App (depends on Lib A, Lib B)
Stub A
• Using (empty) stub libraries, clean schemes!
implicit dependency explicit dependency