Software Engineering CSC 331/631 - Spring 2018 Object-Oriented Design Principles Paul Pauca April 10
Design Principles DP1. Identify aspects of the application that vary and separate them from what stays the same Encapsulate behaviors that change based on object type DP2. Program to an interface, not an implementation Function overloading is an example of programming to an implementation DP3. Favor composition over inheritance Better to achieve polymorphic behavior and code reuse through composition than inheritance
DP1. Identify aspects of the application that vary and separate them from what stays the same A typical scenario Subclasses overload inherited functions (behaviors) Issues Added behaviors are statically inherited by all subclasses Must override behavior in subclass Significant maintenance overhead
DP1. Identify aspects of the application that vary and separate them from what stays the same Refactored using the design principle Behaviors dynamically associated when objects of subclasses are created Adding behaviors does not require modification of existing code
DP2. Programming to an interface, not an implementation Programming to an implementation When your code uses knowledge of the underlying implementation When your code relies directly on concrete classes When your code is concerned with how something is implemented rather than on what it does Result: high coupling between codes Programming to an interface When your code relies on abstract classes, not their implementations When your code dynamically creates and assigns behavior (polymorphism) Result: low coupling
DP3. Favor composition over inheritance Inheritance Benefits Implementation is straightforward Code can be easy to change New implementations can be easy to make and extend Disadvantages If superclass changes then subclasses may have to change also (tight coupling) Exposes a subclass to the implementation details of the superclass (breaks encapsulation) Inheritance determined at compile time (base code added to every child class), not at run-time White-box reuse, since internal details of superclass are often visible to the subclasses
DP3. Favor composition over inheritance Composition Benefits Contained objects accessed by the containing class solely through their interfaces Black-box reuse, internal details of contained objects are not visible Good encapsulation Fewer implementation dependencies (loose coupling) Composition can be defined dynamically at run-time Disadvantages Design tends to have more objects and tends to be more complex Interfaces identifying composition blocks must be carefully designed (see DP1) Extreme composition can lead to overly simple superclass that only delegates to others
DP3. Favor composition over inheritance When to use inheritance or composition Coad s rule - use inheritance only when all of the following criteria are satisfied: A subclass expresses is a special kind of relationship and not is a role played by a An instance of a subclass never needs to be come an object of another class A subclass extends, rather than overrides or nullifies, the responsibilities of its superclass A subclass does not extend the capabilities of what is merely a utility class For a class in the actual problem domain, the subclass specializes a role, transaction, or device.
SOLID https://en.wikipedia.org/wiki/solid_(object-oriented_design) (SRP) Single responsibility principle A class should have a single responsibility All services should be aligned with this responsibility (high cohesion) (OCP) Open/closed principle Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification Achieved through inheritance from abstract classes or interfaces (LSP) Liskov substitution principle If S is a subtype of T, then objects of type T in a program may be replaced with objects of type S without altering any of the desirable properties of that program
SOLID (ISP) Interface segregation principle Many client-specific interfaces are better than one general-purpose interface No client should be forced to depend on methods it does not use (DIP) Dependency inversion principle High-level modules should not depend on low-level modules. Both should depend on abstractions Abstractions should not depend on details. Details should depend on abstractions