CS247 Lecture 16
Last Time: Observer Pattern, Casting
This Time: Casting, Coupling vs. Cohesion, SOLID
const_cast
- the only type of cast that can “remove” constness. Example: using some library that gives us:
Let’s say we know g
doesn’t modify the int
pointed to by p
in any way. Also, I have a const int*
I’d like to call g
with.
Compiler will prevent us from calling g
, because it might modify p
. (that is it might modify our const int*
, which is p
, since we pass it in as an argument).
I can use const_cast
to call g
in the following way:
Generally, const_cast
should be avoided.
Another example, working on legacy codebase that doesn’t use const anywhere. We want to add consts
, make our program const-correct.
Issue: const- poisoning 🍎👎. Adding const
in one location often means we must add it to other locations to allow it to continue to compile.
We can use const-cast to bridge between const-correct and non-const-correct parts of our program. Make small independent parts of the program const-correct, use const-cast to allow the program to compile as the work is done.
- Dynamic_cast: Used for safely casting between pointers/references in an inheritance hierarchy.
Only safe if pb
actually points to a Text
.
Instead,
If the cast succeeds (i.e dynamic type is Text
), then pt
points at the Text.
Otherwise, pt
is set to nullptr
.
Also can be used on references. (Not just on pointers)
What if br
is actually referencing a comic?
Cannot set it to null, no such thing as a null reference.
If dynamic_cast
fails with a reference: throw a std::bad_cast
exception.
There exist smart pointer versions of each of these casts:
Cast shared_ptrs
to other shared_ptrs
. ??????? Don’t really understand ?????
Those allow us to make decisions based on RTTI (run-time type information).
Note*: This function is poor OOD. Don’t do this.
Note
Also Note:
dynamic_cast
only works if you have at least one virtual method.
Recall: polymorphic assignment problem. We considered making operator=
virtual.
- operator= non-virtual: partial assignment
- operator= virtual: mixed assignment
Let’s make operator=
virtual in Book. ???????? WTF
Measures of Design Quality
Question: How can we evaluate the quality of our code - what is good, what is bad, when we’re not just following a particular design pattern?
We’ll discuss a number of ways:
- code smells - refactoring
- coupling/cohesion - how do my classes interact?
- SOLID design principles
First: Coupling + cohesion
Coupling: Description of how strongly different modules depend on one another.
Low:
- Function calls with primitive args/results
- Function calls with structs/arrays as results
- Modules affect each other’s control flow
- Modules sharing global data
High:
- Modules have access to each other’s implementation (friend)
Ideally, we desire low coupling:
- easier to reason about our program
- easier to make changes
We can cheat: If I put everything in one class, super low coupling!
Counterbalancing force: Cohesion: How closely do the parts of my module relate to one another?
Low:
- parts of module are completely unrelated, e.g.
<utility>
- module has a unifying common theme, e.g.
<algorithm>
- Elements manipulate state over lifetime of an object, e.g.
<fstream>
didn’t understand this at first, but Ross went over this again in the Final Review CS247
High:
- Elements are cooperating to perform exactly on a task
Cheat at cohesion:
- put every function in its own class, then they each do only one thing! ?????? What does that mean????
The Goal
We strive for: low coupling, high cohesion
Ex: whatIsIt
function - exhibits high coupling with the Book
hierarchy.
Any new classes in Book
hierarchy necessitate changes to this function.
Which is generally not good right?
SOLID Design Principles
Acronym of 5 principles: purpose is to reduce software rot (the property that long lasting codebase become more difficult to maintain)
- Single Responsibility Principle
- Open-Closed Principle
- Liskov Substitution Principle
- Interface Segregation Principle
- Dependency Inversion Principle
Single Responsibility Principles (SRP): “A class should only have one reason to change”.
i.e. a class should do one thing, not several.
A change to a program spec requires a change to the source.
If changes to different parts of the spec require changes in the same class, then SRP is violated.
Example: Consider ChessBoard
Class
This is actually somewhat bad design. ChessBoard should not print to the screen.
What if I instead want to print to a file? Cannot use the same class without replacing cout
s with printing to a file.
Little more flexible, but still has issues.
What if we want a graphic display? Or to change the language, or to communicate over the internet?
We have low cohesion, violating SRP. ChessBoard is doing multiple things: manipulate state, implement logic, control rendering to the screen.
Instead: ChessBoard should communicate via results, parameters, exceptions. Allow other classes to handle communication with the user.
Next: CS247 Lecture 17