Lecture 21
Measures of Design Quality
compiling and cohesion
Note: You want to balance between cohesion and coupling (if you have perfect coupling, that means all modules are in the same file, which isn't right)
Coupling
How much distinct modules depend on each other
Low coupling:
modules communicate via function calls basic params/results
modules pass arrays/struct back and forth
modules affect each other's control flow
High coupling:
modules have access to each other's implementation (friends)
implies changes to one module require changes to other modules
makes reuse hard
Cohesion
How closely elements of a module are related to each other
Low cohesion:
Arbitrary grouping of unrelated elements (eg.
<utility>
)Elements share a common theme, otherwise unrelated, maybe share some base code (eg.
<algorithm>
)Elements manipulate state over the lifetime of an object
eg. Open/read/close files
Elements pass data to each other
Poorly organized code
hard to understand, maintain, reuse
High cohesion
Elements cooperate to perform exactly one task
Goal: LOW COUPLING, HIGH COHESION
Decoupling the Interface (MVC)
Your primary program classes should not be printing things. Example:
Bad design - inhibits code reuse
What if you want to reuse the chessboard, but not have it communicate via stdout?
You could:
Give the class stream objects, where it can send its input/output
Better - but what if we don't want to use streams at all? Your chessboard should not be doing any communication at all
Single Responsibility Principle
A class should have only one reason to change.
game state and communication are two reasons
Better:
Communicate with the chessboard via params/results
Comtinue user communication to outside the game class
Question: Should main do the talking? Answer: No - hard to reuse code if it is in main Should have a class to manage interaction, separate from the game state class.
Architecture: Model-View-Controller (MVC)
Model-View-Controller (MVC)
separate the distinct notions of the data (or state - "model"), the presentation of the data ("view"), and the control or manipulation of the data ("controller")
Decouples presentation and control => promotes reuse
Model
can have multiple views (eg. text/graphics)
doesn't need to know their details
class Observer pattern (or could communicate through controller)
Controller
mediates control flow between model and view
might handle input (or could be the view)
Exception Safety
Consider the following:
No leaks, but what if g throws? What is guaranteed?
During stack-unwinding, all stack-allocated data is cleaned up
Destructors run, memory reclaimed
Heap-allocated objects NOT destroyed
Therefore, the line delete p; does not occur, and *p is leaked.
Duplication of code occurs here
How else can we guarantee that something (eg. delete p) will not happen no matter how we exit f? (normal or exception?)
In some languages, there are "finally" clauses that guarantee certain final actions
Only thing you can count on in C++: Destructors for stack-allocated data will run
Therefore use stack-allocated data with destructors as much as possible
use the guarantee to your advantage
C++ idiom: RAII (Resource Acquisition Is Initialization)
RAII (Resource Acquisition Is Initialization)
Every resource should be wrapped in a stack-allocated object whose job is to delete it Example:
This can be done with dynamic memory:
takes a T* in the constructor
the destructor will delete the pointer
in between - can dereference, just like a pointer
THIS IS THE FINAL SOLUTION:
No leaks guarant>eed - Constructor arguments
Difficulty: The following cannot happen because unique pointers cannot be copied
What happens when a unique_ptr is copied?
don't want to delete the same pointer twice
Instead - copying is disabled for unique_ptrs. They can only be moved.
Sample Implementation (repo):
If you need to be able to copy pointers:
first, answer the question of ownership
Who will own the resource? Who will have responsibility for freeing it?
That pointer should be a unique_ptr - all other pointers can be raw pointers (can fetch the underlying raw pointer with p.get())
If there is true shared ownership, i.e. any of several pointers might need to free the resource - use std::shared_ptr
Last updated