- Building Better Programs with Patterns
- people develop solutions for common problems in all domains
- GOF popularized idea of prepackaged solutions and defined 23 patterns
- Separate things that change from things that don’t
- change is given
- ideally changes are local and don’t require updates everywhere
- Program to an interface not an implementation
- polymorphism
- decouples
- code is more flexible for types of class it can work for
- idea is more about programming to a more general type
- Composition over inheritance
- inheritance marries classes together
- subclasses must know inner workings of super class
- favor composition, build bottom up and use object references
- increases encapsulation
- delegate, delegate, delegate
- flexible, but at cost of extra method calls and more boiler plate
- You Ain’t Gonna Need It (YAGNI)
- don’t add flexibility you don’t need
- you are doing upfront work and betting you will need it
- also assuming you will do it right before encountering need in real life
- will always have more info later
- be wary of over-engineering for infinite flexibility
- Getting Started with Ruby
- overview of language basics
- Template Method
- When only some portion of code needs flexibility
- Build abstract base class with skeletal method
- Concrete subclasses overrides methods that require specific implementation
- Strategy
- Drawback of template method is inheritance
- Strategy pattern is about pulling out the algorithm into a separate object
- define family of objects (the strategies) that share an interface
- data can be passed via args or passing an object reference
- can pass procs instead of objects which might be better for simple strategies
- avoid coupling strategy so theres no room for new strategies
- Observer
- keep objects in a system in sync with some state
- keep an array of observers and notify them all when something changes
- can also pass in code blocks
- pull: sends object and observer must extract relevant info
- push: sends specific info
- some problems 1) too many updates 2) if multiple fields change may be caught in inconsistent state 3) what if observer causes an error?
- intent is about informing other objects of an intent
- Composite Pattern
- pattern where the sum acts like one of the parts
- instructions can be thought of as a tree
- requires 1) common interface for components 2) need leaf classes that implement Component 3) at least one composite high-level class
- need a strategy for distinguishing leaf nodes from composite nodes
- common error is assuming one-level deep
- Iterator
- access elements of an aggregate object without exposing underlying representation
- external iterators e.g. keeping track of index of an array
- internal iterators e.g. passing logic down into the aggregate data structure
- external iterators allow more control and portability at the cost of more overhead
- issues can arise if structure changes during iteration / make a copy
- Commands
- keep track of actions
- undo / redo: state tracking, execute, unexecute
- sometimes easier to just do what you want
- overhead with remembering how to do something
- need to think through circumstances of when the command was initialized vs executed
- good for keeping track of what you need to do or have already done
- Adapter
- helps handle interfaces that don’t match
- in ruby can modify the class
- modification might be ok if 1) they are simple and 2) you understand the class and the way it is used
- use adapter if 1) the interface mismatch is extensive or 2) you don’t know how the class works
- duck-typing allows for the adapter to implement only the methods that are used, but issues if client calls methods not there
- Proxy
- involves controlling access to an object
- proxies have a hidden reference to the object
- allows for better separation of concerns / don’t want unrelated code tangled together
- Protection: verification or authorization
- Remote: hide complexity of network communication
- Virtual: can delay creating expensive objects / can pass in creation block
- method_missing can be adapted, but need to keep in mind that all objects come with some methods
- Decorator
- adds enhancement to an object
- implements the same interface
- might require a lot of boilerplate
- forwardable module can simplify delegation
- in ruby can 1) wrap methods with alias or 2) extend module
- Singleton
- class with only one instance with global access
- can use class or module
- can tightly couple classes
- meant to model things that only occur once
- shouldn’t be used because of ease of use
- testing can be harder, but you can make a base class which the singleton inherits from and let the test use the base class
- Factory
- delegate the “which class” decision
- abstract factory can return a factory
- common issue is using when you don’t need it
- technique is useful when you have a choice of several different classes and you need to pick one
- Builder
- sometimes configuring an object is difficult
- make sure not to return references to the same object
- might be good if seeing object creation logic repeated or producing invalid objects
- default to using new until compelling reason
- Interpreter
- some programming problems best solved by creating a specialized langauge
- good if crisp boundaries or lots of discrete code chunks you find yourself combining in various ways
- work on abstract syntax tree
- can be complex / slow
- can be good for queries or configs
- Domain-Specific Languages
- focuses on the language not the interpreter
- sometimes problem can be solved by providing a convenient syntax
- external DSL: requires parser
- internal DSL: start with a language and bend to become DSL i.e. using Ruby
- building in ruby allows you to leverage language capabilities
- limited to language
- error messages can be hard to interpret
- Meta-programming
- in dynamic language can modify singleton object and customize
- can use Object or some base class with some base functionality
- can also define modules and extend the object with appropriate modules
- can create custom methods with class_eval
- reflection features let you learn about the object and available methods etc
- the history of the object may matter more than the class
- meta-programming makes the running code look different from the source
- can be much harder to debug
- tests are vital
- attr_accessor/attr_reader/attr_writer and the Forwardable module are examples
- Convention Over Configuration
- concerned with pulling together whole applications
- how to structure to make extensible
- code can become dependent on configuration
- some good guidelines
- attempt to anticipate the user’s needs
- don’t make the user repeat himself or herself
- provide a starter template
- make what the user wants to do a lot default/easy
- if we follow a convention e.g. name class <protocol>Adapter, the system can pick up by name
- if we have a directory convention, things can be auto-loaded / required
- too many conventions can make it seem like magic
- needs good documentation and thorough unit tests
- can be very confusing if inconsistent or broken conventions
Like this:
Like Loading...