Classes are the foundation of Object Oriented Programming.
Object-oriented programming (OOP) refers to a type of computer programming (software design) in which programmers define not only the data type of a data structure, but also the types of operations (functions) that can be applied to the data structure.
We may find that we need a lot of objects that behave the same but have different values for their data elements. A template for creating objects is called a class. An object is said to be an instance of its class.
Rather than copy and paste code to get the same behaviour from many identical or similar entities (game), we create a class as a blueprint, and internally assign data (variables) & methods (functions that ONLY operate on an instance of the class)
One aim of OOP is to make it similar to building a hardware system- it doesn’t matter if the HDD has 2 disks or 6, is magnetic or SSD. Nor if the RAM is made in Korea or Japan. Nor if the motherboard has 4 layers or six.
We can build a system using blocks or modules, and we don’t care what is inside each box as long as they work, and can be re-used in many systems easily.
Class: A class is a definition of objects of the same kind. In other words, a class is a blueprint, template, or prototype that defines and describes the static attributes and dynamic behaviours common to all objects of the same kind.
Instance: An instance is a realisation of a particular item of a class. In other words, an instance is an instantiation of a class. All the instances of a class have similar properties, as described in the class definition. For example, you can define a class called "Student" and create three instances of the class "Student" for "Peter", "Paul" and "Pauline".
The class diagram is a way to visually represent the object we are trying to model in Software. When we looked at Users of the system in Use Case Diagrams, we said that a user can perform a use-case (verb-noun). If we were to design a class called User, the actions become methods. The user would also have a bunch of attributes... in the Website example:
Class User has:
Attributes:
Name
Password
Actions (aka Operations or methods)
login
editProfile
enrolCourse
createComment
flagPost
etc.
A class diagram has only THREE sections to it as shown here. It is important to note that:
the '-' sign indicates that this is private, meaning it cannot be accessed or modified directly
the '+' sign indicates that this is public, meaning it can be accessed from outside the class
Note that it is most common in OOP design that:
attributes are private (usually)
methods are public (usually)
when the designer may want to access private variables (attributes) the user must do this using:
a getter (accessor) method (operation)
a setter (modifier) method (operation)
Signed (with signature) will indicate
for the attribute, the data type of the attribute (int, float, bool or whatever)
for the methods, the data type of the arguments and the return type.
Note that void is the standard reference- in Python this is the same as None, or in JS it is the same as Null
A very simple class diagram for a rectangle with all public attributes and methods. Note that this concept will be used again later.
class Rectangle:
"""Emulates a rectangle"""
def __init__(self, length, width):
"""
Constructor
"""
self.length = length
self.width = width
def area(self):
return self.length * self.width
def perimeter(self):
perim = 2 * (self.width + self.length)
return perim
def __str__(self):
return f"Rectangle {self.length} long and {self.width} wide"
Here is some very basic code to implement the rectangle class above. Note that the methods are missing docstrings.
Typically class diagrams and their implementation will have documentation and a greater range of methods.
All of these concepts are part of the OOP "way of things". The remaining concepts shown here are all tightly related, and overlap with one another. This can make it difficult to "get" but here's my best attempt to demo each.
These two terms are closely related and concepts overlap quite a lot in their definitions.
Abstraction is the method of hiding the unwanted information. Whereas encapsulation is a method to hide the data in a single entity or unit along with a method to protect information from outside. It's almost the same thing, right?
The wrapping up of the data and methods into the single unit is known as encapsulation. The (private) data is accessible only to those (getter and setter) methods, which are wrapped in the class, and not to the outside world. This insulation of data from the direct access of the program is called data hiding. Encapsulation of the object makes it possible for the objects to be treated like ‘black boxes’ that perform a specific task without any concern for internal implementation.
An example of this could be something like a get_volume() method for a Sphere. Now, the question is:
When the method is called on the Sphere object, does it:
Calculate the volume at the time it is called? or
Calculate the volume at the time of creation and store this internally?
The answer is that- it doesn't matter! Not a bit. Either could be used but the implementation is hidden away. There are some practical considerations- for example, if you are instantiating many instances of the object, and each object is "carrying" more data, maybe is takes more memory? It would take longer to instantiate but the value would be retrieved more quickly. On the flip side the time to calculate is small, so a "lighter" object carrying less data internally may be easier to work with and calculate the values "on the fly".
Objects in an OOP language provide an abstraction that hides the internal implementation details. ...in any object-oriented programming language, the syntax might be a little bit different, but the general concept is the same. How does the .lower() method of a str work? Who cares- that's abstraction! The underlying implementation is hidden, and the user does not care how it works, as long as it does. How does the print function get information to the screen? I don't know. I don't care.
Imagine we had to write the code to do even "simple" things - all of these methods and functions make it easier for us to do the higher-level work and not have to worry about what is happening under the hood... a car analogy but you don't need to know about engines in order to drive a car or get on a bus- but you can use that functionality.
Another simple example of this “hiding” is the keys to move an object- you could have 4 methods for movement (U/D/L/R) but how is it handles internally? 4 variables? A list? A dict? You might have a single move method for all movements and another for an action like shoot or shield? Or these could all be mutated by a method called action… how this is represented internally may still be abstracted/hidden or not explicit.
OOP languages permit higher level of abstraction for solving real-life problems!
A sample class diagram for a sphere. This has private (-) attributes (as an example) and so each attribute needs a getter and setter. Public (+) attributes do not require getter and setter methods.
In addition, this class has two methods- one for calculating the volume, and one for calculating the area.
In the implementation (development) stage, you can see that the code has- for the most part - one line of actual code for each method.
You will see that the type-contract; what each method takes as an argument and returns, matches the class diagram.
Note that:
Methods ALWAYS take a reference to self, which is the instance of the object that the method is being called for.
When a method does not take any other arguement, the method often is being called to return some value
this value may be calculated "on the spot" or it could be an attribute of the object
Note also that each method has a docstring which gives the user some basic information on the method itself. Docstrings come in various formats, and companies often standardise a docstring for use throughout the organisation. The one used in the example is different from the format used by Google, for example. Ericsson may use a different format in their business. Whatever format is specified, you should use that spec and not deviate from it.
Often, when writing a new class, you should perform a series of tests to ensure proper functionality. There are a few simple tests shown here, but often you will have a test-case table outlining a range of tests that your code must pass.
import math # to get value for pi
class Sphere:
"""Simulates a sphere
"""
def __init__(self, radius):
"""
(num) -> obj
Constructor
"""
self.__radius = radius # private
self.__color = (255, 0, 0) # rgb, private
def set_radius(self, rad):
"""
(num) -> None
Sets the radius of the sphere
"""
self.__radius = rad
def get_radius(self):
"""
(None) -> num
Gets the radius of the sphere
"""
return self.__radius
def set_color(self, col):
"""
(tuple of int) -> None
Sets the color of the sphere
"""
self.__color = col
def get_color(self):
"""
(None) -> tuple of int
Gets the color of the sphere
"""
return self.__color
def area(self):
"""Calculates surface area of sphere
Returns:
float: surface area of sphere object
"""
return 4 * math.pi * self.get_radius() ** 2
def volume(self):
"""Calculates volume of the sphere
Returns:
float: volume of sphere object
"""
return 4 / 3 * math.pi * self.get_radius() ** 3
# short test code
s1 = Sphere(5)
print(s1.get_color())
s1.set_radius(7.5)
print(s1.area())
print(s1.volume())
Using the OOP approach, we would break a program or application down into a series of (related) classes. Each of these classes would have their own attributes and methods.
The example here shows a Soccer Game. This concept overlaps with the concept of modularisation.
While not a huge part of this module, you can learn more about abstract classes here. Abstract classes overlap with another principle of polymorphism.
Ever heard from relatives “you look exactly like yer father/mother”? The reason for this is called ‘inheritance’. From the Programming aspect, it means inheriting or completing a transfer of characteristics from parent and passing them on to the child”. The new class is called the derived/child class and the one from which it is derived is called a parent/base class.
Let us understand each of the subtopics in detail. There is a longer explanation with code examples here.
Single level inheritance enables a derived class to inherit characteristics from a single parent class.
Example:
Output:
Rectangle Area: 21
Rect Perim: 20
Square Area: 25
Square Perim: 20
Explanation:
I am taking the parent class Rectangle and created a constructor (__init__) and associated methods. Note that to create a separate class for Square would require copying most of the code but we can manage this "special case" using inheritance.
Created a child class ‘Square’ which is inheriting the properties and methods from a parent class
Finally instantiated objects ’rect_1′ and ’sqr_1′ against the parameters.
Finally, I have printed the volume and area of both.... Note I did not have to re-write any more methods for Square.
The word polymorphism means having many forms. In programming, polymorphism means same function name (but different signatures) being uses for different types. Polymorphism refers to the ability of the function with the same name to carry different functionality altogether. It creates a structure that can use many forms of objects.
In the example above, the Parent class may have an abstract draw() method, but we know it is a different method to draw each of the children; i.e. a compass for drawing a circle, a t-square to draw the rectangle and so on. You've seen many examples of polymorphism at work already without even realising it.
Polymorphism with builtin functions
# Python program to demonstrate in-built polymorphic functions
# len() being used for a string
print(len("snake case"))
# len() being used for a list
print(len([10, 20, 30]))
Len can be used for many object types. If you code a class, you can design it to support the __len__ function if you write the related method for that class. Same function, used on many types!
Polymorphism with user functions
# A simple Python function to demonstrate Polymorphism
def add(x, y, z = 0):
return x + y + z
# main code
print(add(2, 3))
print(add(2, 3, 4))
There are two ways this function can be called- many forms. We can do this another way and show another form of this as well.
# Another simple Python function to demonstrate Polymorphism
def add(x, y):
return x + y
# main code
print(add("Hello", "There"))
print(add(2, 3))
In the first example, the operator used will be the "+" for strings, and in the second, the "+" for integers. The "+" has different forms.
Polymorphism with operators
Python supports operator overloading. This is another type of polymorphism where an operator behaves differently based on the type of the operands.
In the previous example, you likely recognised the use of the "+" operator when used with strings. Similarly we could use the "*" operator to "multiply" a str by an int. You may note that we cannot similarly use the "/" or the "-" operators with strs or mixes of str and int. Still, the use of the "+" and "*" show that we can use the same operator (which underneath is a function or method) and use them on different types.
operator adds two numbers and concatenate two strings
operator multiplies two numbers and when used with a string and int, repeats the string given int times and concatenate them.
Polymorphism with class methods
Below code shows how python can use two different class types, in the same way.
>>> x="some long word"
>>> x.index("w")
10
>>> y=[1, 4, 7, "K", "word", True]
>>> y.index(4)
1
There are more examples that you may come across. If you can understand the meaning from the examples here, then you'll know polymorphism when you see it.
The previous diagram shows the distinction- here is an example from class showing both.
In the code implementation for these classes, the "Real" classes have two additions- pos and a draw() method. For the demos, we are using the turtle module, so we are instantiating an instance of turtle.Turtle in order to draw the shapes. When showing inheritance, it is important to use the open arrow head pointing to the parent (super) class.