7.5 Object-Oriented Design
Contents
7.5 Object-Oriented Design#
Estimated time for this notebook: 15 minutes
In this session, we will finally discuss the thing most people think of when they refer to “Software Engineering”: the deliberate design of software. We will discuss processes and methodologies for planned development of large-scale software projects: Software Architecture.
The software engineering community has, in large part, focused on an object-oriented approach to the design and development of large scale software systems. The basic concepts of object orientation are necessary to follow much of the software engineering conversation.
Design processes#
In addition to object-oriented architecture, software engineers have focused on the development of processes for robust, reliable software development. These codified ways of working hope to enable organisations to repeatably and reliably complete complex software projects in a way that minimises both development and maintainance costs, and meets user requirements.
Design and research#
Software engineering theory has largely been developed in the context of commercial software companies.
The extent to which the practices and processes developed for commercial software are applicable in a research context is itself an active area of research.
Recap of Object-Orientation#
Classes: User defined types#
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def grow_up(self):
self.age += 1
terry = Person("Terry", 76)
⚠️ Note that in Python, you can add properties to an object once it’s been defined. Just because you can doesn’t mean you should!
terry.home = "Colwyn Bay" # don't add new properties like this!
Declaring a class#
Class: A user-defined type
class MyClass:
pass
Object instances#
Instance: A particular object instantiated from a class.
my_object = MyClass()
Method#
Method: A function which is “built in” to a class
class MyClass:
def someMethod(self, argument):
pass
my_object = MyClass()
my_object.someMethod(value)
Constructor#
Constructor: A special method called when instantiating a new object
class MyClass:
def __init__(self, argument):
pass
my_object = MyClass(value)
Member Variable#
Member variable: a value stored inside an instance of a class. Define (and initialise) them in the class constructor.
class MyClass:
def __init__(self):
self.member = "Value"
my_object = MyClass()
print(my_object.member)
Value
Each object has its own set of member variables.
my_object.member = "Changed"
print(my_object.member)
my_second_object = MyClass()
print(my_second_object.member)
Changed
Value
Object refactorings#
Replace add-hoc structure with user defined classes#
💩Smell: A data structure made of nested arrays and dictionaries becomes unwieldy.
before:
from random import random
birds = [
{"position": random(), "velocity": random(), "type": kind} for kind in bird_types
]
average_position = average([bird["position"] for bird in birds])
after:
from random import random
class Bird:
def __init__(self, kind):
self.type = kind
self.position = random()
self.velocity = random()
birds = [Bird(kind) for kind in bird_types]
average_position = average([bird.position for bird in birds])
Replace function with a method#
💩Smell: A function is always called with the same kind of thing
before:
def can_see(source, target):
return (source.facing - target.facing) < source.viewport
if can_see(hawk, starling):
hawk.hunt()
after:
class Bird:
def can_see(self, target):
return (self.facing - target.facing) < self.viewport
if hawk.can_see(starling):
hawk.hunt()
Replace method arguments with member variables#
💩Smell: A variable is nearly always used in arguments to a class.
before:
class Person:
def __init__(self, genes):
self.genes = genes
def reproduce_probability(self, age):
pass
def death_probability(self, age):
pass
def emigrate_probability(self, age):
pass
after:
class Person:
def __init__(self, genes, age):
self.age = age
self.genes = genes
def reproduce_probability(self):
pass
def death_probability(self):
pass
def emigrate_probability(self):
pass
Replace global variable with class and member variable#
💩Smell: A global variable is referenced by a few functions
before:
name = "Terry Jones"
birthday = [1, 2, 1942]
today = [22, 11]
if today == birthday[0:2]:
print(f"Happy Birthday, {name}")
else:
print("No birthday for you today.")
after:
class Person:
def __init__(self, birthday, name):
self.birth_day = birthday[0]
self.birth_month = birthday[1]
self.birth_year = birthday[2]
self.name = name
def check_birthday(self, today_day, today_month):
if not self.birth_day == today_day:
return False
if not self.birth_month == today_month:
return False
return True
def greet_appropriately(self, today):
if self.check_birthday(*today):
print(f"Happy Birthday, {self.name}")
else:
print("No birthday for you.")
john = Person([5, 5, 1943], "Michael Palin")
john.greet_appropriately(today)
Object Oriented Refactoring Summary#
Replace ad-hoc structure with a class
Replace function with a method
Replace argument to method with member variable
Replace global variable with member variable