why oop
superficial reason is straightforward
old code many oop but can be inefficient
applied well and thoughtful, can solve real problems
what is oop
classically:
- combination of data and code
- AEIP - Abstraction, Encapsulation, Inheritance, and Polymorphism
intuitively, Objects provide interchangeable services supporting higher level goals
OOP decouples implementation from use
classes review
- object - instances of classes
- fields - define state of object
- methods - define behaviour
- visibility modifier - decide what is published to outide
- nested construct - define scope for enum, classes
- virtual method and inheritance - derive classes to work
general classes guidelines
every guideline has reason and exception
- understand for cost-benefit analysis
common examples:
- SOLID
- Single Responsibility
- Open/Closed
- Liskov Substitutability
- Interface Segregation
- Dependency Inversion
- DRY
general guidelines:
- be careful about compiler provided methods
- minimize mutability
- min visibility
- refer objects by interfaces when possible
- dont give away internals
- prefer DI over hardwiring resources
- separating creation from wiring to make more flexible system
- use PIMPL (Pointer to IMPLementation) idiom
- consider roles of classes and uses
- values - pure data, safely copied (struct of primitives)
- identity - address/ reference uniquely identifies object
- indirection - refer/ borrow property of other objects
- managers - coordinate and clean up resources (unique shared ptr, vector)
thinking in services
modern thinking notes that OOP defines services
- inheritance and runtime polymorphism drives this
- base class as interface
- derived as implementations
also enables hetero aggregates
List<Person*> people = ...;
for (Person* person : people) {
person->sleep()
}
inheritance
why is it hard?
- LSP and has-a relationship unambiguously tell to apply inheritance
- every is-a could be a has-a
- better and finer grained relationships
- whenever is-a applies, still make design decision
is-a vs has-a
- if behaviour might change
- coarse inheritance to preclude
- composition simplifies
- make coarser gained composition
- if type used polymorphically
- composition doesn’t aid
- inheritance enables
- if set of possibly types known and bounded, better ways
shallow, fine-grained inheritance
- avoid reimplementation of common
- enable dynamic selection
- cat, stationary -> run -> stationary
- direct identifies and addresses risk of change
inheritance guidelines
- favor composition over inheritance
- do not inherit to reuse, inherit to be reused
- stack-is-a-vector bad
- more specialization with virtual methods
- use inheritance for semantic is-a relationship
- Liskov Substitutability - true for base, always true for derived
- prefer to inherit interfaces and push impl to leaves
- hierarchies delocalize code - yield yo-yo effect (down up down)
- ambigioues overrides break encapsulation
- alternative, only leaves should be instantiable
- design for inheritance
- choose customization pts for runtime polymorphism
- prevent it elsewhere