Interfaces and abstraction are key concepts in modular code design. Abstraction allows you to hide complex implementation details and show only the necessary features. Interfaces define a contract that classes can implement, ensuring a consistent API.
Abstraction is a fundamental concept in programming that enables simplification by reducing the complexity of systems. It allows developers to focus on high-level design without needing to delve into intricate implementation details at every step. By concealing complex realities through simpler interfaces, abstraction promotes cleaner and more understandable code.
One of the ways abstraction aids in simplification is through de-duplication. By abstracting common patterns or functionalities into single units or interfaces, repeated code is minimized, which not only reduces errors but also enhances maintainability and readability of the code.
Abstraction also supports deferred execution and decision-making by providing mechanisms to define operations abstractly. This means certain processes can be implemented at a later phase or modified without impacting the overall system, allowing for flexibility and scalability in software development.
See this Training - Data Designer document that demonstrates how to use object-oriented programming and modularization to achieve separation of data and code.
Modularization is a design technique that involves splitting a large software system into separate, interchangeable modules with specific functionalities, thereby achieving higher levels of abstraction and reducing complexity.
In practice, modularization can significantly improve the maintainability and scalability of a system, allowing developers to update or enhance individual modules with minimal impact on others.
See this Training - Team Lead document that demonstrates how to use modularization to organize a team project for making a game.
See this Training - Game Programmer document that demonstrates how to use modularization to organize functions for game development.
Besides encapsulation, inheritance, and polymorphism, there are other sets of principles for guiding object-oriented programming. SOLID is another set of 5 principles, first described by Robert C. Martin.
An entity should have one, and only one, reason to change. This principle emphasizes that a class should only have a single responsibility pertaining to a specific functionality in a program.
Software entities like classes and methods should be open for extension but closed for modification. This means that you should be able to add new functionality to a class without altering its existing code.
Objects of a superclass should be replaceable with objects of a subclass without affecting the correctness of the program. This principle ensures that derived classes extend the base class without changing its behavior.
A client should not be forced to implement interfaces it does not use. This principle suggests that many specific interfaces are better than a large, general-purpose interface.
High-level modules should not depend on low-level modules. Both should depend on abstractions. Additionally, abstractions should not depend on details, but details should depend on abstractions. This principle underscores the importance of relying on interfaces or abstract classes rather than concrete implementations.
See this Training - Quality Assurance document that demonstrates how to apply some of the SOLID principles to unit testing.
While the above principles are common ways to achieve abstraction where it is necessary, developers should note that abstraction does not always bringa net benefit. Applying abstraction is a tradeoff: while it brings a reduction in duplicated code and generalization of code, it also splits up application logic into multiple places, and lengthens the chain of logic for debugging, which can make it harder to understand the flow of data.
Developers and teams should continually assess the marginal benefit of each additional abstraction to decide if it would be beneficial to the codebase.