Content
- Basics of UML Class in Design
- Class Notations
- Responsibility-Driven Object-Oriented Design
- General Principles of Design
- Maintainability Evaluation
- Traditional Properties of a Good Design
- Realization Examples
- Information Expert
- Creating Objects
- Handling System Operations
- General Principles of Design
- SOLID: Principles of Class Design
- Single Responsibility Principle (SRP)
- Open Closed Principle (OCP)
- Liskov Substitution Principle (LSP)
- Interface Segregation Principle (ISP)
- Dependency Inversion Principle (DIP)
Basics of UML Class in Design
Class Notations
Description | |
---|---|
Association | Direction of navigability. An object knows the other and can access its visible attrs & opers. |
Composition | Strong ownership. Whole-part association. The whole part responsible for creating its parts. |
Aggregation | Weak ownership. Whole-part association. The part may be in >= 1 aggregate. |
Interfaces | A class implements/realizes an interface. Another class uses the interface. |
Responsibility-Driven Object-Oriented Design
We develop a design methodically by assigning responsibilities to a class. Major goal is maintainability.
- Responsibility of an object
- Knowledge
- Behavior
Responsibilities are fulfilled by implementations of operations (methods) and collaboration with other objects.
Operation Contract
Detailed post-conditions. Describes changes of states in the Domain Model. An analysis technique (contrary to design, which describes how it is carried out).
Domain Model v.s. Design Class Diagram
General Principles of Design
Maintainability Evaluation
- Modularity: change to one component has minimal effect to others
- Reusability: an asset can be used in other components or systems
- Analysability: easy to find defects or cause of failure the the system fails; easy to know where to modify when we want to change or add functionalities
- Modifiability: easy to make modifications without introducing defects
- Testability: easy to establish test criteria and perform tests to decide whether the criteria have been met
Traditional Properties of a Good Design
- Good abstraction and separation of concerns
- Form modules by grouping similar concerns together and separate them from other groups
- Each module captures single feature and exports services through a simple interface
- Good encapsulation/information hiding
- Export minimum amount of details through their interface
- Clear separation between interface and implementation -> good modifiability
- Strong cohesion within modules & methods
- How closely the operations within a module are related to one another
- Group strongly-related functionalities together and keep everything else out
- No unnecessary coupling between modules
- Reduce the number and strength of dependencies - connections to, knowledge of, or reliance on other modules
- Defeats almost all characteristics of maintainability
- Once and once only (no redundancy)
- Design for testability
Realization Examples
Information Expert
Who has the necessary information for this responsibility?
- Lowered coupling
- Better cohesion
- Not to be used when:
- Compromises seek good abstraction and separation of concerns e.g. presentation should not be a concern of domain objects
Creating Objects
A class that contains or has composition relationship with, closely uses, records, has data needed to initialize that type of object should have responsibility create that object.
Doesn't increase coupling since they already have visibilities.
- Not to be used when:
- Compromises seek good abstraction and separation of concerns e.g. creator has to decide which type of object to create; creation process too complex
- Separate concerns by passing an object already knowing the rules of creation or can do it behind the interface (factory)
- Compromises seek good abstraction and separation of concerns e.g. creator has to decide which type of object to create; creation process too complex
Handling System Operations
- Controller: receives messages from UI and coordinates the work (to delegate)
- Façade controller or coordinator: a class representing the overall (sub)system
- Used when there are only a small number of events
- Use case controller or session controller: classes created to handle all events for a particular scenario
- Maintain high cohesion by delegating tasks to other modules
- Façade controller or coordinator: a class representing the overall (sub)system
SOLID: Principles of Class Design
Responsibility-driven design may still lead to rigid or classes that still need to be improved.
- Apply when cannot summarize the responsibility of a class without using "and"
- Over-applied if tiny classes don't abstract a complete concept or service -> not cohesive
Single Responsibility Principle (SRP)
A class should have only 1 reason to change.
- To achieve high cohesion
- Responsibility: a collection of functions in a class that serves one particular actor
- e.g. shouldn't create collaborators in constructors
Open Closed Principle (OCP)
A class is open for extension but closed for modification. Key to object-oriented approach; an application of polymorphism. Implement new requirements by adding new code instead of changing them.
- Classes should depend on abstractions rather than concrete classes
- Not a problem in dynamic languages
Liskov Substitution Principle (LSP)
Subtypes must be substitutable for their base types.
- Substitution property
- If for each object
o1
of typeS
there is an objecto2
of typeT
s.t. for all programsP
defined in terms ofT
, the behaviour ofP
is unchanged wheno1
is substituted foro2
, thenS
is a subtype ofT
- If for each object
A client should be able to make reasonable assumptions about the behavior of a type, and to depend on these assumptions.
- Contract
- A set of method pre-conditions & post-conditions
- Reasonable assumptions
- A subtype object can weaken the pre-conditions
- A subtype object can strengthen the post-conditions
Interface Segregation Principle (ISP)
Clients should not be forced to depend on methods that they do not use.
- Single class plays multiple roles
- Split the class that different clients depend on and have unnecessary methods they would call into cohesive roles, and create interface for them
Dependency Inversion Principle (DIP)
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.
- Encourages reusability of higher layers
- Adapter is used when the lower-level layer is closed, or the application requires the reuse of existing services