Data Structures and Algorithms using Python
Copyright © 2023 by Rance Necaise
Table Of Contents
Data Structures and Algorithms using Python
Copyright © 2023
by Rance Necaise

1.10 Multiple Implementations

As you saw with the Time ADT, there are usually multiple ways to implement an ADT. By working with abstractions, a programmer can focus on the use of an ADT in the design of a program or the development of an algorithm instead of getting bogged down in the implementation details. Eventually, we must implement an ADT, but the decision as to which approach to use can be delayed without interfering with the use of the ADT during the design phase.

To further illustrate the advantages of working with abstractions, consider a second implementation of the Line Segment ADT. Instead of storing the individual coordinates for the two endpoints in four instance variables, we can define and use a storage class to group the coordinates for a single point:

class Point :
  def __init__(self, x, y) :
    self.xCoord = x
    self.yCoord = y

A storage class is a like any other class, but it only contains a onstructor that defines and initializes public instance variables or data fields. Storage classes are typically used to create objects for storing structured or related data. Tuples or lists could be used for this purpose, but the named fields provided by a class helps to reduce or prevent logic errors in programs.

Special Topic
Storage Classes

To use the Point storage class for implementing the Line Segment ADT will require a new implementation of the LineSegment class. In the new constructor:

class LineSegment :
  def __init__(self, x0, y0, x1, y1) :
    self._pointA = Point(x0, y0)
    self._pointB = Point(x1, y1)

we create and store two Point objects, one for each endpoint. The remaining methods must now refer to the x- and y-coordinates in the two storage objects. For example, here is the new implementation of the isVertical method:

def isVertical(self) :
  return self._pointA.xCoord == self._pointB.yCoord

When using a different approach to store the endpoints for the physical representation of the line segment, we can not change the definition of the ADT. For example, the endPointA method, as defined by the Line Segment ADT, returns a 2-tuple containing the x- and y-coordinates of the first endpoint. Even though we store those values in a storage object, the new implementation of the endPointA method must still return a 2-tuple:

def endPointA(self):
  return (self._pointA.xCoord, self._pointA.yCoord)

Given the abstract view, the user of the ADT does not know nor do they need to know how we are storing the endpoint coordinates. No matter how we store the data, however, we must ensure that each operation of the ADT is implemented as defined.

The new implementation of the Line Segment ADT using storage objects for the endpoints is provided in the listing below:

Program Listing
Program: line2.py
  1. # line2.py
  2. # Implementation of the Line Segment ADT in which the coordinates for
  3. # end points are stored in storage objects.
  4. from math import sqrt
  5.  
  6. class LineSegment :
  7.    # Creates a new LineSegment instance from the given end point coords.
  8.   def __init__(self, x0, y0, x1, y1) :
  9.     self._pointA = Point(x0, y0)
  10.     self._pointB = Point(x1, y1)
  11.  
  12.    # Returns the first end point of the line as a 2-tuple (x, y).
  13.   def endPointA(self) :
  14.     return (self._pointA.xCoord, self._pointA.yCoord)
  15.  
  16.    # Returns the second end point of the line as a 2-tuple (x, y).
  17.   def endPointB(self) :
  18.     return (self_pointB.xCoord, self._pointB.yCoord)
  19.  
  20.    # Returns a Boolean value indicating if this is a vertical line segment.
  21.   def isVertical(self) :
  22.     return self._pointA.xCoord == self._pointB.xCoord
  23.    
  24.    # Returns the Euclidean distance between the two endpoints.    
  25.   def length(self) :
  26.     xDiff = self._pointA.xCoord - self._pointB.xCoord
  27.     yDiff = self._pointA.yCoord - self._pointB.yCoord
  28.     dist = sqrt(xDiff ** 2 + yDiff ** 2)
  29.     return dist
  30.    
  31.    # Returns a new LineSegment instance that is the result of shifting
  32.    # this line segment in both the x- and y-direction.
  33.   def shift(self, xInc, yInc) :
  34.     newX0 = self._pointA.xCoord + xInc
  35.     newY0 = self._pointA.yCoord + yInc
  36.     newX1 = self._pointB.xCoord + xInc
  37.     newY1 = self._pointB.yCoord + yInc
  38.     return LineSegment(newX0, newY0, newX1, newY1)
  39.    
  40.    # Returns the slope of the line segment given as the rise over the run.
  41.   def slope(self) :
  42.     assert not self.isVertical(), "Line segement can not be vertical."
  43.     rise = self._pointA.yCoord - self._pointB.yCoord
  44.     run = self._pointA.xCoord - self._pointB.xCoord
  45.     return rise / run    
  46.    
  47.    # --- The remaining methods go here ---
  48.    
  49. # Private storage class for representing a 2-D point.
  50. class Point :
  51.   def __init__(self, x, y) :
  52.     self.xCoord = x
  53.     self.yCoord = y      

If we want to use the new implementation of the Line Segment ADT, what changes are needed in the distance.py program in Section 1.7? Both versions implement the same abstract data type. In the second version, there were no changes made to the method names or parameters, nor did we redefine the ADT itself. Thus, we can easily swap out the first version of the LineSegment class (defined in the line module) with the second version (defined in the line2 module) without making any changes to the executable code in the distance.py program. To select the second version, however, we have to change the import statement at the top of the program to indicate we want to import the class from the line2 module:

from line2 import LineSegment
Page last modified on July 30, 2023, at 08:16 PM