This course is under active development.
Designing abstractions and interfaces well is key to both design for change and testability, but we can’t afford to go into the appropriate depth within those courses.
Theme: Think of yourself as an interface designer – it’s much more important than the day-to-day coding
Key Principles
Key principles:
- Information Hiding
- Separation of Concerns
- Single Responsibility Principle
- Avoid Hasty Abstractions
- Keep interfaces minimal
Key Challenges
- Abstractions are easiest to change when there is no code written for them
- Abstraction requires experience
- Optimize for change first
- Creating the wrong abstraction is harmful
Process for Defining Interfaces & Abstractions
Need to pull out the parnas paper on interface design for embedded
- Two lists method
Reviewing Interfaces
Reviewing interfaces (combine with the above?)
- Pull out example review types from Active Design Review Principles and Practices
Practical Advice
Keep the following guidelines in mind when designing abstract interfaces:
- Always use generic types in your abstract interfaces
- When creating abstractions, focus on the interaction points
- Clients should not be forced to take on dependencies they don’t need
- Design abstract interfaces that aren’t likely to change
- Abstractions don’t have to be perfect or comprehensive
- Abstract common behaviors and use custom APIs for non-common behaviors
- Embedded systems software benefits from the following types of abstract interfaces
- Hide code that might change in modules that conform to abstract interfaces
- Your abstractions don’t need to be perfect. They don’t need to be reusable on other systems, just yours. But if you want to move quickly, they need to exist.
“The interface must be general but the contents should not. Specialization is necessary for economy and flexibility.” – Dave Parnas, Designing Software for Ease of Extension and Contraction
Interface Design Styles
Interface design styles:
Development
What’s the process look like in day-to-day development?
-
- Why? Abstractions are easiest to change when there is no code written for them
- Start implementing by writing tests against the interface – test drive the interface before it’s hard to change
-
- Break dependencies with separation layers
-
- Interact with abstract interfaces, not implementations
- Even the presence or absence of a given module should be hidden from other modules
Demonstrations
Exercises
- Refactor a driver to use a minimal abstract interface to decouple it from hardware
- Have users come up with their own HAL for a board (simple style)
Models to Study
- A-7E Redesign documentation
- modm
- embvm
Conclusion
- Correct bad abstractions
- Tests written against abstract interfaces are portable and reusable
-
- Testing implementation details makes our tests brittle
- Always create an API to export data
Other ways to improve:
- Design by Contract – ideally with our interfaces, rather than the implementations (e.g., C++20)
References
- Active Design Review Principles and Practices by David Parnas
- Designing Software for Ease of Extension and Contraction by David Parnas
- Backward Compatibility: APIs
