⚠️ This assessment may be distributed in one of the following forms:
- GitHub Classrooms: You will automatically receive a copy of this repo on NMTAFE's org (if you don't know what GitHub Classrooms is, then your group is not using it 😉)
- Blackboard: There will be an assessment handout that may refer to parts or all of this repo
Both versions are otherwise identical and require answering the same questions and performing the same tasks.
This assessment demonstrates your understanding of processes and techniques related to object-oriented programming, including the concepts and language. The assessment consists of a knowledge-based and practical component.
You begin with some starter code: A set of smiley faces that can be presented on a SenseHAT LED matrix (optional)
The project will help you demonstrate and cement your understanding of the four pillars of object-oriented programming:
- 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
If using GitHub Classrooms, use the repository uniquely allocated to you. Otherwise,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
- Create a
venv
for your project:
python -m venv .venv
-
Open the project in PyCharm; the root folder should be
civ-ipriot-smiley
-
Modify the code based on the assessment requirements
Recommended:
10. Stage the modifications you made:
git add .
- Commit the changes:
git commit -m "Complete portfolio activity"
- Push the changes:
git push -u origin por1
End of recommended section
- Submit your work as per the assessment instructions on Blackboard. You can use GitHub to create a
zip
of your project if you followed the recommended steps previously.
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 still allows you to pull or push from the upstream.
At a conceptual level, a clone of a forked repository has your
copy of the repository as its origin
with the origin's origin said to be '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 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 using a pull request.
The following activities go beyond the project's basic requirements to teach us more modern software development principles. These activities should only be tried 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. The famous adage captures this:
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 that's probably the right way
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).