2.11k likes | 2.13k Views
Busy iOS Developer's Guide to Design Patterns in Swift. Ted Neward Neward & Associates http://www.tedneward.com | ted@tedneward.com. Credentials. Who is this guy? Principal, Neward & Associates ask me how I can help your project, your team or your firm
E N D
Busy iOS Developer's Guide to Design Patterns in Swift Ted Neward Neward & Associates http://www.tedneward.com | ted@tedneward.com
Credentials Who is this guy? • Principal, Neward & Associates ask me how I can help your project, your team or your firm • Microsoft MVP (F#, C#, Architect); Java Expert (JSRs 175, 277) • Author Professional F# 2.0 (w/Erickson, et al; Wrox, 2010) Effective Enterprise Java (Addison-Wesley, 2004) SSCLI Essentials (w/Stutz, et al; OReilly, 2003) Server-Based Java Programming (Manning, 2000) • Blog: http://blogs.tedneward.com • Writing: http://www.newardassociates.com/#/writing • Twitter: @tedneward • For more, see http://www.newardassociates.com/#/about
Objectives Before we leave, we want to have seen... • ... some discussion about patterns • ... some classic GOF patterns in Swift implemented several different ways • ... how patterns evolve • ... how patterns can still be useful
Patterns What are they, exactly?
Patterns Overview Patterns • originally "discovered" by Erich Gamma's PhD thesis • later turned into a book by "the Gang-of-Four" book • "Design Patterns" (Addison-Wesley) • Gamma • Richard Helm • Ralph Johnson • John Vlissides • then turned into a movement in the mid-2000s • ... which didn't really pay off well, unfortunately too much hype and mania
Patterns Overview Patterns vs Idioms • patterns are language-agnostic; idioms are language-specific • patterns are often tweaked; idioms are usually used as-is • patterns can sometimes turn into idioms
Pattern Goals Why would we do this?
Patterns Goals Opening your eyes • "you won't ever think about object-oriented design in the same way" • "you'll have insights that can make your own designs more flexible, modular, reusable, and understandable"
Patterns Goals Concepts, not code • "don't worry if you don’t understand this book completely on the first reading" • "this isn't a book to read once and put on a shelf"
Patterns Goals This means trying to "reuse" patterns as code don't work out well • this meant that patterns were a failure to many people However, they are still useful • specifically, they help identifiable reusable ideas/concepts • so long as we don't expect too much out of them... most notably, not reusable code • ... there's much good stuff that can come from them
Patterns Goals Common lexicon • we can use patterns to describe designs at a higher level • "use a Builder to create a Chain of Responsibility" • "you were thinking Visitor? I was thinking more Strategy, myself" • ... and so on
Pattern Forms How shall I describe thee? Let me count the ways....
Patterns Forms "Patterns Form" • I prefer the simplest distillation: "a probem, within a certain context, that was solved using a solution that yielded certain consequences" • Problem • Context • Solution • Consequences
Patterns Forms "GOF (Long) Form": • Name (and Classification) • Intent • Also Known As • Motivation • Applicability • Structure • Participants • Collaborations • Consequences • Implementation • Sample Code • Known Uses • Related Patterns
Creational Patterns Instantiating object instances
Creational Creational patterns • Creational class patterns defer some part of object creation to subclasses • Creational object patterns defer object creation to another object
Creational Creational Class Patterns • Factory Method (121) Creational Object Patterns • Abstract Factory (99) • Builder (110) • Prototype (133) • Singleton (144)
Abstract Factory Creational Object
Abstract Factory Intent "Provide an interface for creating families of related or dependent objects without specifying their concrete classes"
Abstract Factory Applicability • a system should be independent of how its products are created, composed, and represented • a system should be configured with one of multiple families of products • a family of related product objects is designed to be used together, and you need to enforce this constraint • you want to provide a class library of products, and you want to reveal just their interfaces, not their implementations
Abstract Factory Consequences • It isolates concrete classes • It makes exchanging product families easy • It promotes consistency among products • Supporting new kinds of products is difficult
Abstract Factory Abstract Factory (in Swift): AbstractProduct class Shape { let id = total++; func Draw() { preconditionFailure("Should never be invoked!") } static var total = 0; }
Abstract Factory Abstract Factory (in Swift): ConcreteProduct class Circle : Shape { override func Draw() { print("circle \(id): draw") } } class Square : Shape { override func Draw() { print("square \(id): draw") } } class Ellipse : Shape { override func Draw() { print("ellipse \(id): draw") } } class Rectangle : Shape { override func Draw() { print("rectangle \(id): draw") } }
Abstract Factory Abstract Factory (in Swift): AbstractFactory class Factory { func CreateCurvedInstance() -> Shape { preconditionFailure("Should never be invoked!") } func CreateStraightInstance() -> Shape { preconditionFailure("Should never be invoked!") } }
Abstract Factory Abstract Factory (in Swift): ConcreteProduct class SimpleShapeFactory : Factory { override func CreateCurvedInstance() -> Shape { return Circle() } override func CreateStraightInstance() -> Shape { return Square() } } class RobustShapeFactory : Factory { override func CreateCurvedInstance() -> Shape { return Ellipse() } override func CreateStraightInstance() -> Shape { return Rectangle() } }
Abstract Factory Abstract Factory (in Swift): Usage let factory = SimpleShapeFactory() // or RobustShapeFactory() let shapes = [ factory.CreateCurvedInstance(), factory.CreateStraightInstance(), factory.CreateCurvedInstance() ] for s in shapes { s.Draw() }
Abstract Factory But... don't forget about "class" modifiers instead of "static" • these can be overridden (!) in derived classes
Abstract Factory Abstract Factory (in Swift): Factory (revisited) class ClassFactory { class func CreateCurvedInstance() -> Shape { preconditionFailure("Should never be invoked") } } class SimpleClassShapeFactory : ClassFactory { override class func CreateCurvedInstance() -> Shape { return Circle() } }
Abstract Factory Abstract Factory (in Swift): Usage let factory2 = SimpleClassShapeFactory.self // now we are using the metatype of SimpleClassShapeFactory let shapes2 = [ factory2.CreateCurvedInstance(), factory2.CreateCurvedInstance() ] for s in shapes2 { s.Draw() }
Builder Creational Object
Builder Intent "Separate the construction of a complex object from its representation so that the same construction process can create different representations"
Builder Applicabilty • the algorithm for creating a complex object should be independent of the parts that make up the object and how they're assembled • the construction process must allow different representations for the object that's constructed
Builder Consequences • lets you vary a product's internal representation • isolates code for construction and representation • gives you finer control over the construction process
Builder Additional Notes: FluentBuilder perhaps this is actually a different pattern? • Applicability: allows for a more client-driven construction process • Applicability: the construction process should be more client-readable
Builder Builder (in Swift): Product class Product { var parts : String = "" }
Builder Builder (in Swift): AbstractCreator class Builder { func BuildPart() { preconditionFailure("Should never invoke this") } func GetResult() -> Product { preconditionFailure("Should never invoke this") } }
Builder Builder (in Swift): ConcreteCreator class ConcreteBuilder : Builder { override func BuildPart() { product.parts = product.parts + "Adding part #\(part++)\n" } override func GetResult() -> Product { return product } var part = 0 var product = Product() }
Builder Builder (in Swift): Director and Usage class Director { func Construct() -> Product { for _ in 1 ... 5 { builder.BuildPart() } return builder.GetResult() } let builder = ConcreteBuilder() } let director = Director() let product = director.Construct() print(product.parts)
Builder Builder (in Swift): FluentBuilder class FluentBuilder { var product = Product() func Begin() -> FluentBuilder { product = Product() return self } func Engine() -> FluentBuilder { product.parts = product.parts + "Engine\n" return self } func SteeringWheel() -> FluentBuilder { product.parts = product.parts + "SteeringWheel\n" return self } func Tire() -> FluentBuilder { product.parts = product.parts + "Tire\n" return self } func Construct() -> Product { return product } }
Builder Builder (in Swift): FluentBuilder Usage let vehicleBuilder = FluentBuilder() let motorcycle = vehicleBuilder.Begin() .Engine() .SteeringWheel() .Tire() .Tire() .Construct() print(motorcycle.parts)
Factory Method Creational Class
Factory Method Intent "Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses."
Factory Method Applicability • a class can't anticipate the class of objects it must create • a class wants its subclasses to specify the objects it creates • classes delegate responsibility to one of several helper subclasses, and you want to localize the knowledge of which helper subclass is the delegate
Factory Method Consequences • Eliminate the need to bind application-specific classes into your code • Clients might have to subclass the Creator class just to create a particular ConcreteProduct object • Provides hooks for subclasses • Connects parallel class hierarchies
Factory Method Additional Notes • Applicability: client may want/need to parameterize construction • Applicability: construction system may want to open itself up for extension from unknown sources
Factory Method Factory Method (in Swift): Product class Product { func DoSomething() { preconditionFailure("Should never be invoked") } }
Factory Method Factory Method (in Swift): ConcreteProduct class ConcreteProduct : Product { override func DoSomething() { print("ConcreteProduct did something\n") } }
Factory Method Factory Method (in Swift): Creator and ConcreteCreator class Creator { func FactoryMethod() -> Product { preconditionFailure("Should never be invoked") } } class ConcreteCreator : Creator { override func FactoryMethod() -> Product { return ConcreteProduct() } }
Factory Method Factory Method (in Swift): Usage let creator = ConcreteCreator() let product = creator.FactoryMethod() product.DoSomething()
Factory Method Sometimes we want some slightly different semantics • parameterized construction of Products • open-ended construction possibilities James Coplien called a variant of this his "Exemplar" idiom in the LSD book