Hands-On Software Engineering with Python
上QQ阅读APP看书,第一时间看更新

Object-oriented programming

The distinctive feature of Object-Oriented Programming is (no great surprise) that it represents data and provides functionality through instances of objects. Objects are structures of data, or collections of attributes or properties, that have related functionality (methods) attached to them as well. Objects are constructed as needed from a class, through a definition of the properties and methods that, between them, define what an object is, or has, and what an object can do. An OO approach allows programming challenges to be handled in a significantly different, and usually more useful, manner than the equivalents in a procedural approach, because those object instances keep track of their own data.

The following is the same functionality as the simple procedural example shown previously, but written using an Object-Oriented approach:

#!/usr/bin/env python
"""
An example of a simple OOP-based program. Asks the user for a URL,
retrieves the content of that URL, writes it to a temp-file, and
repeats until the user tells it to stop.
"""

# Importing stuff we'll use
import os

import urllib.request

if os.name == 'posix':
tmp_dir = '/tmp/'
else:
tmp_dir = 'C:\\Temp\\'
if not os.path.exists(tmp_dir):
os.mkdirs(tmp_dir)

# Defining the class

class PageReader:
# Object-initialization method
def __init__(self, url):
self.url = url
self.local_file = ('%s%s.data' % (tmp_dir,
''.join(
[c for c in the_url if c not in ':/']
)
)).replace('https', '').replace('http', '')
self.page_data = self.get_page_data()
# Method to read the data from the URL
def get_page_data(self):
page = urllib.request.urlopen(self.url)
page_data = page.read()
page.close()
return page_data
# Method to save the page-data
def save_page_data(self):
with open(self.local_file, 'w') as out_file:
out_file.write(str(self.page_data))
print('Page-data written to %s' % (self.local_file))

if __name__ == '__main__':
# Almost the same loop...
the_url = ''
while the_url.lower() != 'x':
the_url = input(
'Please enter a URL to read, or "X" to cancel: '
)
if the_url and the_url.lower() != 'x':
page_reader = PageReader(the_url)
page_reader.save_page_data()
print('Exiting. Thanks!')

Although this performs the exact same task, and in the exact same fashion as far as the user is concerned, underneath it all is an instance of the PageReader class that does all the actual work. In the process, it stores various data, which could be accessed as a member of that instance. That is, the page_reader.url, page_reader.local_fileand page_reader.page_data properties all exist and could be retrieved and used if there were a need to retrieve that data, and the page_reader.get_page_data method could be called again to fetch a fresh copy of the data on the page. It's important to note that the properties are attached to the instance, so it'd be possible to have multiple instances of PageReader, each with it's own data, that can all do the same things with their own data. That is, if the following code were executed:

python_org = PageReader('http://python.org')
print('URL ................ %s' % python_org.url)
print('Page data length ... %d' % len(python_org.page_data))
google_com = PageReader('http://www.google.com')
print('URL ................ %s' % google_com.url)
print('Page data length ... %d' % len(google_com.page_data))

It would yield the following output:

Object-Oriented design and implementation make the development of a complex system, with the attendant complex interactions, considerably easier a fair portion of the time, though it may not be a panacea for all development challenges and efforts. If the basic principles of good OO designs are adhered to, however, they will usually make code easier to write, easier to maintain, and less prone to breakage. A full discussion of OO design principles is well beyond the scope of this book, but some of the more fundamental ones that can cause a lot of difficulty if they aren't adhered to are as follows:

  • Objects should have a Single Responsibility—each should do (or represent) one thing, and do so well
  • Objects should be open for extension but closed for modification—changes to what an instance actually does, unless it's a new functionality that flat-out doesn't exist, should not require modification to the actual code
  • Objects should encapsulate what varies—it shouldn't require the use of an object to know anything about how it does and what it does, just that it can do it
  • Use of objects should be exercises in programming to an interface, not to an implementation—this is a complex topic that's worth some detailed discussion, with some substance and context, so it'll be looked at in some detail in Chapter 9, Testing the Business-Objects, while working out the architecture of the hms_sys project