⚠️ If you are using the GitHub Classroom version of this assessment, you must address the requirements inknowledge_and_evidence.md
. Otherwise, please use the workbook supplied on Blackboard.
Demonstrates key Python OOP concepts and provides the first project's starting code.
The project presents various smiley faces that appear on the SenseHAT LED matrix.
The project helps students explore the core pillars of OOP. Specifically:
- Abstraction: expose objects in terms of how they are used, not how they are implemented
- Polymorphism: handle different forms in the same way
- Inheritance: by subclassing a common Smiley class
- Encapsulation: protect the internal state of objects
Students are encouraged to play around with the files to get a feel for what's going on.
You must work against your forked version of this repository:
- From the top-right corner, select Fork and follow the prompts.
- Open the terminal (Command Prompt or Git Bash on Windows) and navigate to the desired parent folder for this project.
- Clone the forked repository:
git clone https://github.com/YOUR_USERNAME/civ-ipriot-smiley.git
Replace YOUR_USERNAME
with your GitHub username.
- Navigate to the cloned repository:
cd civ-ipriot-smiley
- Create a new branch:
git switch -c por2
- Modify the code based on the assessment requirements
- Stage the modifications you made:
git add .
- Commit the changes:
git commit -m "Addressed requirements of the porfolio"
- Push the changes:
git push origin at2-part1
Also, what is a fork anyway?
A fork creates a personal copy of a repository on your own github account.
What makes the fork a fork (rather than a plain old copy) is that the fork
still refers to the repository from whence it came as its upstream
.
This allows you to still pull from the upstream or push from it.
At a technical level, a clone of a forked repository has your
copy of the repository as its origin
and a reference to the forked
repository as upstream
.
If you cloned the original repository, and you want to keep the clone but change it to use your copy:
- Create a fork on GitHub and copy the name of your forked repository. Usually:
https://github.com/<YOUR_USERNAME>/civ-ipriot-smiley.git
- Go to your clone's local directory.
- Run the following command to verify that your current clone is pointing at the NM-TAFE repo:
git remote -v
- Change the origin to your personal upstream repository:
git remote set-url origin https://github.com/YOUR_USERNAME/civ-ipriot-smiley.git
Replace YOUR_USERNAME
with, you guessed it, your GitHub username.
- Add the original repository as the upstream:
git remote add upstream https://github.com/NM-TAFE/civ-ipriot-smiley.git
- Optionally, you can fetch any changes that have been made to the original repository by running:
git fetch upstream
You may want to merge the changes back to your main
branch.
The following are some activities that go beyond the project's basic requirements to teach us more modern software development principles. These activities should only be attempted after you have met the project requirements, and they will not be assessed.
While inheritance was one of the darlings of OOP, in modern software development, it is recognised that inheritance can introduce implicit dependencies (coupling) between various parts of the code. This is captured by the famous adage:
Favour composition over inheritance
Raf's corollary to the above statement is:
If there's some way to state a relationship in terms of
has a
, then use ahas a
relationship.
We know what inheritance looks like:
Parent:
"""Do common stuff"""
Child(Parent):
"""Do specialised stuff"""
But what does composition look like in Python?
Well, here's the crazy thing, you've already been doing composition, you just didn't know it. Let's go back to the very very first OOP example we did:
class Dog:
def __init__(self, name, breed):
self.name = name
self.breed = breed
Where's the composition?
That's right!
Remember, in Python, everything is an object!
So a dog has a name
string and a dog has a breed
string. Thus, a dog is composed of two strings: name and breed!
It is the same for any other object, including our own: when we create an instance of a class in another instance of the class, we are now composing one object from another.
Reimagine the relationship between Smiley and Happy. How can we maintain reusability while avoiding inheritance?
Clues
- "Happy
has a
Smiley?" doesn't sound right, does it? - "Smiley
has a
Happy?" sounds even worse! - What about a Smiley is composed of an Expression?
- What kind of class would Expression be?
When we do use inheritance, we tend to favour inheriting from abstractions, not concrete classes. For example, it is pretty helpful to standardise everything that an "Expression" can do so that we can (polymorphically) handle expressions, so it makes sense for Happy, Sad, Etc, to inherit from Expression(ABC) and for Smiley to be composed of an expression (which we can pass at instantiation time).