tgsn 2 oop

Upload: mas728405

Post on 09-Apr-2018

228 views

Category:

Documents


0 download

TRANSCRIPT

  • 8/7/2019 tgsn 2 oop

    1/64

    Hiding Data within Object-Oriented Programmingy July 29, 2004

    y By Matt Weisfeld

    y Bio

    y Send Email

    y More Articles

    Introduction

    This is the seventh installment in a series of articles about fundamental object-oriented (OO)

    concepts. The material presented in these articles is based on material from the second edition of my

    book, The Object-Oriented Thought Process, 2nd

    edition. The Object-Oriented Thought Process is

    intended for anyone who needs to understand the basic object-oriented concepts before jumping into

    the code. Click here to start at the beginning of the series.

    Now that we have covered the conceptual basics of classes and objects, we can start to explore

    specific concepts in more detail. Remember that there are three criteria that are applied to object-

    oriented languages: They have to implement encapsulation, inheritance, and polymorphism. Ofcourse, these are not the only important terms, but they are a great place to start a discussion.

    In the previous article, this article, and several of the ones that follow, we will focus on a single

    concept and explore how it fits in to the object-oriented model. We will also begin to get much more

    involved with code. In keeping with the code examples used in the previous articles, Java will be the

    language used to implement the concepts in code. One of the reasons that I like to use Java is

    because you can download the Java compiler for personal use at the Sun Microsystems Web

    site http://java.sun.com/. You can download the J2SE 1.4.2 SDK (software development kit) to

    compile and execute these applications and will provide the code listings for all examples in this

    article. I have the SDK 1.4.0 loaded on my machine. I will provide figures and the output for these

    examples. See the previous article in this series for detailed descriptions for compiling and running all

    the code examples in this series.

    Checking Account Example

    Recall that in the previous article, we created a class diagram that is represented in the following UML

    diagram; see Figure 1.

    y Post a comment

    y Email Article

    y Print Article

    y Share Articles

    Figure 1: UML Diagram for Checking Account Class

  • 8/7/2019 tgsn 2 oop

    2/64

    This class is designed to illustrate the concept of encapsulation. This class encapsulates both the

    data (attributes) and methods (behaviors). Encapsulation is a fundamental concept of object-oriented

    designall objects contain both attributes and behavior. The UML class diagram is composed of only

    three parts: the class name, the attributes, and the behaviors. This class diagram maps directly to the

    code in Listing 1.

    class CheckingAccount {

    private double balance = 0;

    public void setBalance(double bal) {

    balance = bal;

    };

    public double getBalance(){

    return balance;

    };

    }

    Listing 1: CheckingAccount.java

    This direct mapping from class diagram to code (and back) is another important issue when designing

    classes. This mapping not only helps with the design of the class, but also with the maintenance of

    the class. Multiple class diagrams are used to design an object-oriented system and make up what is

    termed an object model. Object models will be discussed at length in later articles.

    Once a system is complete, the documentation pertaining to the design is obviously very important.

    The deployment of a new application marks the beginning of the maintenance phase, which will

    continue for the lifetime of the application and contain many, many bug fixes, updates, andenhancements to the system. While the object-model is a great design tool, it is also a great

    maintenance tool.

    However, as with all documentation, an object model that is not updated as the application changes is

    at best useless, and at worst misleading. Anyone who has used flowcharts knows exactly what the

    problem is. For example, consider an application that is deployed with flow charts included in the final

    documentation. If a bug is identified and fixed in the code, unless the change in logic is retrofitted to

    the flow chart, the flow chart is out of date and does not represent the current application. In the

    future, when another programmer, or perhaps even the original programmer, revisits the

    documentation, it can be misleading. This may lead to a major problem. The same problem can occur

    when classes change and the class diagram is not updated properly to reflect the changes.

    Why have we taken this interlude about updating documentation at this point? Because there now are

    tools to manage much of the documentation process and I believe that anyone wanting to learn

    object-oriented design must integrate the knowledge of these tools into their thought process as early

    as possible. In short, the process of creating class diagrams and mapping them directly to code (and

    back) is tracked, and somewhat controlled, by software tools. In future articles, we will get into much

    more detail about the tools themselves. Right now, just be aware that the class diagrams and the

    code are tightly coupled.

  • 8/7/2019 tgsn 2 oop

    3/64

    Note: Some of the products that can help with the process are Rational Rose, owned by IBM and

    Visio, owned by Microsoft.

    Data Hiding

    Returning to the actual CheckingAccount example, we can see that while the class contains both

    attributes and behavior, not all of the class is accessible to other class. For example, consider again

    the balance attribute. Note that balance is defined as private.

    private double balance = 0;

    We proved in the last article that attempting to access this attribute directly from an application would

    produce an error. The application that produces this error is shown in Listing 2.

    class Encapsulation {

    public static void main(String args[]) {

    System.out.println("Starting myEncapsulation...");

    CheckingAccount myAccount = new CheckingAccount();

    myAccount.balance = 40.00;

    System.out.println("Balance = " + myAccount.getBalance());

    }

    }

    Listing 2: Encapsulation.java

    The offending line is the where this main application attempts to set balance directly.

    myAccount.balance = 40.00;

    This line violates the rule of data hiding. As we saw in last month's article, the compiler does not allow

    this; however, it fails to set balance to 40 only because the access was declared asprivate. It is

    interesting to note that the Java language, just as C++, C#, and other languages, allows for the

    attribute to be declared aspublic. In this case, the main application would indeed be allowed to

    directly set the value ofbalance. This then would break the object-oriented concept of data hiding and

    would not be considered a proper object-oriented design.

    This is one area where the importance of the design comes in. If you abide by the rule that all

    attributes areprivate, all attributes of an object are hidden, thus the term data hiding. This is so

    important because the compiler now can enforce the data hiding rule. If you declare all of a class's

    attributes as private, a rogue developer cannot directly access the attributes from an application.

    Basically, you get this protection checking for free.

    Whereas the class's attributes are hidden, the methods in this example are designated aspublic.

  • 8/7/2019 tgsn 2 oop

    4/64

    public void setBalance(double bal) {

    balance = bal;

    };

    public double getBalance(){

    return balance;

    };

    Object Oriented Programming Concepts

    3.1 Introduction

    The use of Object Oriented (OO) design and Object Oriented Programming (OOP) are becoming

    increasingly popular. Thus, it is useful to have an introductory understanding of OOP and some of

    the

    programming features of OO languages. You can develop OO software in any high level language,

    like

    C or Pascal. However, newer languages such as Ada, C++, and F90 have enhanced features that make

    OOP much more natural, practical, and maintainable. C++ appeared before F90 and currently, is

    probably the most popular OOP language, yet F90 was clearly designed to have almost all of the

    abilities of

    C++ . However, rather than study the new standards many authors simply refer to the two decades

    old

    F77 standard and declare that Fortran can not be used for OOP. Here we will overcome that

    misinformed

    point of view.

    Modern OO languages provide the programmer with three capabilities that improve and simplify

    the design of such programs: encapsulation, inheritance, and polymorphism (or generic

    functionality).

    Related topics involve objects, classes, and data hiding. An object combines various classical data

    types

    into a set that denes a new variable type, or structure. A class unies the new entity types and

    supporting

  • 8/7/2019 tgsn 2 oop

    5/64

    data that represents its state with routines (functions and subroutines) that access and/or modify

    those

    data. Every object created from a class, by providing the necessary data, is called an instance of the

    class. In older languages like C and F77, the data and functions are separate entities. An OO language

    provides a way to couple or encapsulate the data and its functions into a unied entity. This is a

    more

    natural way to model real-world entities which have both data and functionality. The encapsulation

    is

    done with a module block in F90, and with a class block in C++. This encapsulation also includes

    a mechanism whereby some or all of the data and supporting routines can be hidden from the user.

    The

    accessibility of the specications and routines of a class is usually controlled by optional public and

    private qualiers. Data hiding allows one the means to protect information in one part of a

    program

    from access, and especially from being changed in other parts of the program. In C++ the default is

    that data and functions are private unless declared public, while F90 makes the opposite choice

    for

    its default protection mode. In a F90 module it is the contains statement that, among other

    things,

    couples the data, specications, and operators before it to the functions and subroutines that follow

    it.

    Class hierarchies can be visualized when we realize that we can employ one or more previously

    dened classes (of data and functionality) to organize additional classes. Functionality programmed

    into

    the earlier classes may not need to be re-coded to be usable in the later classes. This mechanism is

    called

    inheritance. For example, if we have dened an Employee class, thena Manager class would

    inherit all of the data and functionality of an employee. We would then only be required to add only

    the totally new data and functions needed for a manager. We may also need a mechanism to re-

    dene

    specic Employee class functions that differ for a Manager class. By using the concept of a class

  • 8/7/2019 tgsn 2 oop

    6/64

    hierarchy, less programming effort is required to create the nal enhanced program. In F90 the

    earlier

    class is brought into the later class hierarchy by the use statement followed by the name of the

    module

    statement block that dened the class.

    Polymorphism allows different classes of objects that share some common functionality to be used

    in

    code that requires only that common functionality. In other words, routines having the same generic

    name

    c 2001 J.E. Akin 33are interpreted differently depending on the class of the objects presented as

    arguments to the routines.

    This is useful in class hierarchies where a small number of meaningful function names can be used to

    manipulate different, but related object classes. The above concepts are those essential to objectoriented

    design and OOP. In the later sections we will demonstrate by example additional F90

    implementations

    of these concepts.

    3.2 Encapsulation, Inheritance, and Polymorphism

    We often need to use existing classes to dene new classes. The two ways to do this are called

    composition

    and inheritance. We will use both methods in a series of examples. Consider a geometry program

    that uses two different classes: class Circle and class Rectangle, as represented graphically in

    Figs. 3.1 and 3.2. and as partially implemented in F90 as shown in Fig. 3.3. Each class shown has the

    data types and specications to dene the object and the functionality to compute their respective

    areas

    (lines 322). The operator % is employed to select specic components of a dened type. Within the

    geometry (main) program a single routine, compute area, is invoked (lines 38 and 44) to return the

    area for any of the dened geometry classes. That is, a generic function name is used for all classes

    of its arguments and it, in turn, branches to the corresponding functionality supplied with the

    argument

    class. To accomplish this branching the geometry program rst brings in the functionality of the

    desired

  • 8/7/2019 tgsn 2 oop

    7/64

    classes via a use statement for each class module (lines 25 and 26). Those modules are coupled

    to

    the generic function by an interface block which has the generic function name compute area

    (lines

    28, 29). There is included a module procedure list which gives one class routine name for each ofthe

    classes of argument(s) that the generic function is designed to accept. The ability of a function to

    respond

    differently when supplied with arguments that are objects of different types is called polymorphism.

    In this example we have employed different names, rectangular area and circle area, in their

    respective class modules, but that is not necessary. The use statement allows one to rename the

    class

    routines and/or to bring in only selected members of the functionality.

    Circle Class

    radius

    make_Circle

    real

    real pi

    Circle

    real

    Circle

    Circle_Area

    Circle

    Figure 3.1: Representation of a Circle Class

    Another terminology used in OOP is that of constructors and destructors for objects. An intrinsic

    constructor is a system function that is automatically invoked when an object is declared with all of

    its

    possible components in the dened order (see lines 37 and 43). In C++, and F90 the intrinsic

    constructor

    has the same name as the type of the object. One is illustrated in the statement

  • 8/7/2019 tgsn 2 oop

    8/64

    four sides = Rectangle (2.1,4.3)

    where previously we declared

    type (Rectangle) :: four sides

    which, in turn, was coupled to the class Rectangle which had two components, base and height,

    dened in that order, respectively. The intrinsic constructor in the example statement sets

    component

    c 2001 J.E. Akin 34Figure 3.2: Representation of a Rectangle Class

    [ 1] ! Areas of shapes of different classes, using different

    [ 2] ! function names in each class

    [ 3] module class Rectangle ! define the first object class

    [ 4] implicit none

    [ 5] type Rectangle

    [ 6] real :: base, height ; end type Rectangle

    [ 7] contains ! Computation of area for rectangles.

    [ 8] function rectangle area ( r ) result ( area )

    [ 9] type ( Rectangle ), intent(in) :: r

    [10] real :: area

    [11] area = r%base * r%height ; end function rectangle area

    [12] end module class Rectangle

    [13]

    [14] module class Circle ! define the second object class

    [15] real :: pi = 3.1415926535897931d0 ! a circle constant

    [16] type Circle

    [17] real :: radius ; end type Circle

    [18] contains ! Computation of area for circles.

    [19] function circle area ( c ) result ( area )

    [20] type ( Circle ), intent(in) :: c

    [21] real :: area

  • 8/7/2019 tgsn 2 oop

    9/64

    [22] area = pi * c%radius**2 ; end function circle area

    [23] end module class Circle

    [24]

    [25] program geometry ! for both types in a single function

    [26] use class Circle

    [27] implicit none

    [28] use class Rectangle

    [29] ! Interface to generic routine to compute area for any type

    [30] interface compute area

    [31] module procedure rectangle area, circle area ; end interface

    [32]

    [33] ! Declare a set geometric objects.

    [34] type ( Rectangle ) :: four sides

    [35] type ( Circle ) :: two sides ! inside, outside

    [36] real :: area = 0.0 ! the result

    [37]

    [38] ! Initialize a rectangle and compute its area.

    [39] four sides = Rectangle ( 2.1, 4.3 ) ! implicit constructor

    [40] area = compute area ( four sides ) ! generic function

    [41] write ( 6,100 ) four sides, area ! implicit components list

    [42] 100 format ("Area of ",f3.1," by ",f3.1," rectangle is ",f5.2)

    [43]

    [44] ! Initialize a circle and compute its area.

    [45] two sides = Circle ( 5.4 ) ! implicit constructor

    [46] area = compute area ( two sides ) ! generic function

    [47] write ( 6,200 ) two sides, area

    [48] 200 format ("Area of circle with ",f3.1," radius is ",f9.5 )

  • 8/7/2019 tgsn 2 oop

    10/64

    [49] end program geometry ! Running gives:

    [50] ! Area of 2.1 by 4.3 rectangle is 9.03

    [51] ! Area of circle with 5.4 radius is 91.60885

    Figure 3.3: Multiple Geometric Shape Classes

    base = 2.1 and component height = 4.3 for that instance, four sides, of the type Rectangle.

    This intrinsic construction is possible because all the expected components of the type were

    supplied. If

    all the components are not supplied, then the object cannot be constructed unless the functionality

    of the

    c 2001 J.E. Akin 35[ 1] function make Rectangle (bottom, side) result (name)

    [ 2] ! Constructor for a Rectangle type

    [ 3] implicit none

    [ 4] real, optional, intent(in) :: bottom, side

    [ 5] type (Rectangle) :: name

    [ 6] name = Rectangle (1.,1.) ! default to unit square

    [ 7] if ( present(bottom) ) then ! default to square

    [ 8] name = Rectangle (bottom, bottom) ; end if

    [ 9] if ( present(side) ) name = Rectangle (bottom, side) ! intrinsic

    [10] end function make Rectangle

    [11] . . .

    [12] type ( Rectangle ) :: four sides, square, unit sq

    [13] ! Test manual constructors

    [14] four sides = make Rectangle (2.1,4.3) ! manual constructor, 1

    [15] area = compute area ( four sides) ! generic function

    [16] write ( 6,100 ) four sides, area

    [17] ! Make a square

    [18] square = make Rectangle (2.1) ! manual constructor, 2

    [19] area = compute area ( square) ! generic function

    [20] write ( 6,100 ) square, area

  • 8/7/2019 tgsn 2 oop

    11/64

    [21] ! "Default constructor", here a unit square

    [22] unit sq = make Rectangle () ! manual constructor, 3

    [23] area = compute area (unit sq) ! generic function

    [24] write ( 6,100 ) unit sq, area

    [25] . . .

    [26] ! Running gives:

    [27] ! Area of 2.1 by 4.3 rectangle is 9.03

    [28] ! Area of 2.1 by 2.1 rectangle is 4.41

    [29] ! Area of 1.0 by 1.0 rectangle is 1.00

    Figure 3.4: A Manual Constructor for Rectangles

    class is expanded by the programmer to accept a different number of arguments.

    Assume that we want a special member of the Rectangle class, a square, to be constructed if the

    height is omitted. That is, we would use height = base in that case. Or, we may want to construct a

    unit square if both are omitted so that the constructor defaults to base = height = 1. Such a manual

    constructor, named make Rectangle, is illustrated in Fig. 3.4 (see lines 5, 6). It illustrates some

    additional features of F90. Note that the last two arguments were declared to have the additional

    type

    attributes of optional (line 3), and that an associated logical function present is utilized (lines 6

    and 8)

    to determine if the calling program supplied the argument in question. That gure also shows the

    results

    of the area computations for the corresponding variables square and unit sq dened if the

    manual

    constructor is called with one or no optional arguments (line 5), respectively.

    In the next section we will illustrate the concept of data hiding by using the private attribute. The

    reader is warned that the intrinsic constructor can not be employed if any of its arguments have

    been

    hidden. In that case a manual constructor must be provided to deal with any hidden components.

    Since

    data hiding is so common it is probably best to plan on prividing a manual constructor.

  • 8/7/2019 tgsn 2 oop

    12/64

    3.2.1 Example Date, Person, and Student Classes

    Beforemoving to some mathematical exampleswe will introduce the concept of data hiding and

    combine

    a series of classes to illustrate composition and inheritancey

    First, consider a simple class to dene dates .

    and to print them in a pretty fashion, as shown in Figs. 3.5 and 3.6. While other modules will have

    access to the Date class they will not be given access to the number of components it contains (3),

    nor their names (month, day, year), nor their types (integers) because they are declared private in

    the

    dening module (lines 5 and 6). The compiler will not allow external access to data and/or routines

    declared as private. The module, class Date, is presented as a source include le in Fig. 3.6, and

    in the future will be reference by the le name class Date.f90. Since we have chosen to hide all

    the user dened components we must decide what functionality we will provide to the users, who

    may

    have only executable access. The supporting documentation would have to name the public routines

    and

    describe their arguments and return results. The default intrinsic constructor would be available only

    to y

    those that know full details about the components of the data type, and if those components arepublic.

    These examples mimic those given in Chapter 11 and 8 of the J.R. Hubbard book Programming with

    C++, McGraw-Hill,

    1994, and usually use the same data for verication.

    c 2001 J.E. Akin 36Date Class

    month

    Date_

    integer

    integer day

    Date

    Date

    Date

  • 8/7/2019 tgsn 2 oop

    13/64

    Print_Date

    Date

    integer year

    Date Read_Date

    Date Set_Date

    Figure 3.5: Graphical Representation of a Date Class

    The intrinsic constructor, Date (lines 14 and 34), requires all the components be supplied, but it does

    no error or consistency checks. My practice is to also dene a public constructor whose name is

    the

    same as the intrinsic constructor except for an appended underscore, that is, Date . Its sole purpose

    is to

    do data checking and invoke the intrinsic constructor, Date. If the function Date (line 10) is declared

    public it can be used outside the module class Date to invoke the intrinsic constructor, even if the

    components of the data type being constructed are all private. In this examplewe have provided

    another

    manual constructor to set a date, set Date (line 31),with a variable number of optional arguments.

    Also

    supplied are two subroutines to read and print dates, read Date (line 27) and print Date (line 16),

    respectively.

    A sample main program that employs this class is given in Fig. 3.7, which contains sample outputs

    as comments. This program uses the default constructor as well as all three programs in the public

    class

    functionality. Note that the denition of the class was copied in via an include (line 1) statement

    and

    activated with the use statement (line 4).

    Now we will employ the class Date within a class Person which will use it to set the date of

    birth (DOB) and date of death (DOD) in addition to the other Person components of name,

    nationality, and sex. As shown in Fig. 3.8, we have made all the type components private, but

    make all the

    supporting functionality public, as represented graphically in Fig. 3.8. The functionality shown

    provides

  • 8/7/2019 tgsn 2 oop

    14/64

    a manual constructor, make Person, routines to set the DOB or DOD, and those for the printing of

    most components. The source code for the new Person class is given in Fig. 3.9. Note that the

    manual

    constructor (line 12) utilizes optional arguments and initializes all components in case they are not

    supplied to the constructor. The Date public function from the class Date is inherited to initialize

    the DOB and DOD (lines 18, 57, and 62). That function member from the previous module was

    activated with the combination of the include and use statements. Of course, the include could

    have

    been omitted if the compile statement included the path name to that source. A sample main

    program

    for testing the class Person is in Fig. 3.10 along with comments containing its output. It utilizes the

    constructors Date (line 7), Person (line10), and make Person (line 24).

    Next, we want to use the previous two classes to dene a class Student which adds something

    else special to the general class Person. The student person will have additional private

    components for an identication number, the expected date ofmatriculation (DOM), the total

    course credit hours

    earned (credits), and the overall grade point average (GPA), as represented in Fig. 3.11. The source

    lines

    for the type denition and selected public functionality are given in Fig. 3.12. There the constructors

    are make Student (line 19) and Student (line 47). A testing main program with sample output is

    illustrated in Fig. 3.13. Since there are various ways to utilize the various constructors three alternate

    methods have been included as comments to indicate some of the programmers options. The rst

    two

    include statements (lines 1, 2) are actually redundant because the third include automatically brings

    in those rst two classes.

    c 2001 J.E. Akin 37[ 1] module class Date ! filename: class Date.f90

    [ 2] implicit none

    [ 3] public :: Date ! and everything not "private"

    [ 4]

    [ 5] type Date

    [ 6] private

  • 8/7/2019 tgsn 2 oop

    15/64

    [ 7] integer :: month, day, year ; end type Date

    [ 8]

    [ 9] contains ! encapsulated functionality

    [10]

    [11] function Date (m, d, y) result (x) ! public constructor

    [12] integer, intent(in) :: m, d, y ! month, day, year

    [13] type (Date) :: x ! from intrinsic constructor

    [14] if ( m < 1 .or. d < 1 ) stop Invalid components, Date

    [15] x = Date (m, d, y) ; end function Date

    [16]

    [17] subroutine print Date (x) ! check and pretty print a date

    [18] type (Date), intent(in) :: x

    [19] character (len=*),parameter :: month Name(12) = &

    [20] (/ "January ", "February ", "March ", "April ",&

    [21] "May ", "June ", "July ", "August ",&

    [22] "September", "October ", "November ", "December "/)

    [23] if ( x%month < 1 .or. x%month > 12 ) print *, "Invalid month"

    [24] if ( x%day < 1 .or. x%day > 31 ) print *, "Invalid day "

    [25] print *, trim(month Name(x%month)), , x%day, ", ", x%year;

    [26] end subroutine print Date

    [27]

    [28] subroutine read Date (x) ! read month, day, and year

    [29] type (Date), intent(out) :: x ! into intrinsic constructor

    [30] read *, x ; end subroutine read Date

    [31]

    [32] function set Date (m, d, y) result (x) ! manual constructor

    [33] integer, optional, intent(in) :: m, d, y ! month, day, year

  • 8/7/2019 tgsn 2 oop

    16/64

    [34] type (Date) :: x

    [35] x = Date (1,1,1997) ! default, (or use current date)

    [36] if ( present(m) ) x%month = m ; if ( present(d) ) x%day = d

    [37] if ( present(y) ) x%year = y ; end function set Date

    [38]

    [39] end module class Date

    Figure 3.6: Dening a Date Class

    [ 1] include class Date.f90 ! see previous figure

    [ 2] program main

    [ 3] use class Date

    [ 4] implicit none

    [ 5] type (Date) :: today, peace

    [ 6]

    [ 7] ! peace = Date (11,11,1918) ! NOT allowed for private components

    [ 8] peace = Date (11,11,1918) ! public constructor

    [ 9] print *, "World War I ended on " ; call print Date (peace)

    [10] peace = set Date (8, 14, 1945) ! optional constructor

    [11] print *, "World War II ended on " ; call print Date (peace)

    [12] print *, "Enter today as integer month, day, and year: "

    [13] call read Date(today) ! create todays date

    [14]

    [15] print *, "The date is "; call print Date (today)

    [16] end program main ! Running produces:

    [17] ! World War I ended on November 11, 1918

    [18] ! World War II ended on August 14, 1945

    [19] ! Enter today as integer month, day, and year: 7 10 1997

    [20] ! The date is July 10, 1997

  • 8/7/2019 tgsn 2 oop

    17/64

    Figure 3.7: Testing a Date Class

    3.3 Object Oriented Numerical Calculations

    OOP is often used for numerical computation, especially when the standard storage mode for arrays

    is

    not practical or efcient. Often one will nd specialized storage modes like linked lists, or tree

    structures

    used for dynamic data structures. Here we should note that many matrix operators are intrinsic to

    F90,

    so one is more likely to dene a class sparse matrix than a class matrix. However, either

    class would allow us to encapsulate several matrix functions and subroutines into a module that

    could be

    reused easily in other software. Here, we will illustrate OOP applied to rational numbers andintroduce

    c 2001 J.E. Akin 38Person Class

    name

    Person_

    character

    character nationality

    Person

    Person

    Person

    make_Person

    Person

    integer sex

    Person print_DOB

    Person print_DOD

    Date Date_Of_Birth

    Date Date_Of_Death

    Person print_Name

    Person print_Nationality

  • 8/7/2019 tgsn 2 oop

    18/64

    Person print_Sex

    Person set_DOB

    Person set_DOD

    Figure 3.8: Graphical Representation of a Person Class

    the important topic of operator overloading. Additional numerical applications of OOP will be

    illustrated

    in later chapters.

    3.3.1 A Rational Number Class and Operator Overloading

    To illustrate an OOP approach to simple numerical operations we will introduce a fairly complete

    rational

    number class, called class Rational which is represented graphically in Fig. 3.14. The dening F90

    module is given in Fig. 3.15. The type components have been made private (line 5), but not the type

    itself, so we can illustrate the intrinsic constructor (lines 38 and 102), but extra functionality has

    been

    provided to allow users to get either of the two components (lines 52 and 57). The provided routines

    shown in that gure are:

    add Rational convert copy Rational delete Rational

    equal integer gcd get Denominator get Numerator

    invert is equal to list make Rational

    mult Rational Rational reduce

    Procedures with only one return argument are usually implemented as functions instead of

    subroutines.

    Note that we would form a new rational number,z , as the product of two other rational numbers,x

    andy , by invoking the mult Rational function (line 90),

    z = mult Rational (x, y)

    which returnsz as its result. A natural tendency at this point would be to simply write this asz =

    x y . However, before we could do that we would have to have to

    tell the operator, *, how to act

    when provided with this new data type. This is known as overloading an intrinsic operator. We had

    the

  • 8/7/2019 tgsn 2 oop

    19/64

    foresight to do this when we set up the module by declaring which of the module procedures were

    equivalent to this operator symbol. Thus, from the interface operator (*) statement block (line 14)

    the system now knows that the left and right operands of the * symbol correspond to the rst and

    second arguments in the function mult Rational. Here it is not necessary to overload the assignment

    operator, =, when both of its operands are of the same intrinsic or dened type. However, to

    convert

    c 2001 J.E. Akin 39[ 1] module class Person ! filename: class Person.f90

    [ 2] use class Date

    [ 3] implicit none

    [ 4] public :: Person

    [ 5] type Person

    [ 6] private

    [ 7] character (len=20) :: name

    [ 8] character (len=20) :: nationality

    [ 9] integer :: sex

    [10] type (Date) :: dob, dod ! birth, death

    [11] end type Person

    [12] contains

    [13] function make Person (nam, nation, s, b, d) result (who)

    [14] ! Optional Constructor for a Person type

    [15] character (len=*), optional, intent(in) :: nam, nation

    [16] integer, optional, intent(in) :: s ! sex

    [17] type (Date), optional, intent(in) :: b, d ! birth, death

    [18] type (Person) :: who

    [19] who = Person (" ","USA",1,Date (1,1,0),Date (1,1,0)) ! defaults

    [20] if ( present(nam) ) who % name = nam

    [21] if ( present(nation) ) who % nationality = nation

    [22] if ( present(s) ) who % sex = s

  • 8/7/2019 tgsn 2 oop

    20/64

    [23] if ( present(b) ) who % dob = b

    [24] if ( present(d) ) who % dod = d ; end function

    [25]

    [26] function Person (nam, nation, s, b, d) result (who)

    [27] ! Public Constructor for a Person type

    [28] character (len=*), intent(in) :: nam, nation

    [29] integer, intent(in) :: s ! sex

    [30] type (Date), intent(in) :: b, d ! birth, death

    [31] type (Person) :: who

    [32] who = Person (nam, nation, s, b, d) ; end function Person

    [33]

    [34] subroutine print DOB (who)

    [35] type (Person), intent(in) :: who

    [36] call print Date (who % dob) ; end subroutine print DOB

    [37]

    [38] subroutine print DOD (who)

    [39] type (Person), intent(in) :: who

    [40] call print Date (who % dod) ; end subroutine print DOD

    [41]

    [42] subroutine print Name (who)

    [43] type (Person), intent(in) :: who

    [44] print *, who % name ; end subroutine print Name

    [45]

    [46] subroutine print Nationality (who)

    [47] type (Person), intent(in) :: who

    [48] print *, who % nationality ; end subroutine print Nationality

    [49]

  • 8/7/2019 tgsn 2 oop

    21/64

    [50] subroutine print Sex (who)

    [51] type (Person), intent(in) :: who

    [52] if ( who % sex == 1 ) then ; print *, "male"

    [53] else ; print *, "female" ; end if ; end subroutine print Sex

    [54]

    [55] subroutine set DOB (who, m, d, y)

    [56] type (Person), intent(inout) :: who

    [57] integer, intent(in) :: m, d, y ! month, day, year

    [58] who % dob = Date (m, d, y) ; end subroutine set DOB

    [59]

    [60] subroutine set DOD(who, m, d, y)

    [61] type (Person), intent(inout) :: who

    [62] integer, intent(in) :: m, d, y ! month, day, year

    [63] who % dod = Date (m, d, y) ; end subroutine set DOD

    [64] end module class Person

    Figure 3.9: Denition of a Typical Person Class

    an integer to a rational we could, and have, dened an overloaded assignment operator procedure(line

    10). Here we have provided the procedure, equal Integer, which is automatically invoked when

    we write : type(Rational)y; y = 4. That would be simpler than invoking the constructor called

    make rational. Before moving on note that the system does not yet know how to multiply an integer

    times a rational number, or visa versa. To do that one would have to add more functionality, such as

    a

    function, say int mult rn, and add it to the module procedure list associated with the * operator.

    A typical main program which exercises most of the rational number functionality is given in Fig.

    3.16,

    along with typical numerical output. It tests the constructors Rational (line 8), make Rational

    c 2001 J.E. Akin 40[ 1] include class Date.f90

    [ 2] include class Person.f90 ! see previous figure

  • 8/7/2019 tgsn 2 oop

    22/64

    [ 3] program main

    [ 4] use class Date ; use class Person ! inherit class members

    [ 5] implicit none

    [ 6] type (Person) :: author, creator

    [ 7] type (Date) :: b, d ! birth, death

    [ 8] b = Date (4,13,1743) ; d = Date (7, 4,1826) ! OPTIONAL

    [ 9] ! Method 1

    [10] ! author = Person ("Thomas Jefferson", "USA", 1, b, d) ! NOT if private

    [11] author = Person ("Thomas Jefferson", "USA", 1, b, d) ! constructor

    [12] print *, "The author of the Declaration of Independence was ";

    [13] call print Name (author);

    [14] print *, ". He was born on "; call print DOB (author);

    [15] print *, " and died on "; call print DOD (author); print *, ".";

    [16] ! Method 2

    [17] author = make Person ("Thomas Jefferson", "USA") ! alternate

    [18] call set DOB (author, 4, 13, 1743) ! add DOB

    [19] call set DOD (author, 7, 4, 1826) ! add DOD

    [20] print *, "The author of the Declaration of Independence was ";

    [21] call print Name (author)

    [22] print *, ". He was born on "; call print DOB (author);

    [23] print *, " and died on "; call print DOD (author); print *, ".";

    [24] ! Another Person

    [25] creator = make Person ("John Backus", "USA") ! alternate

    [26] print *, "The creator of Fortran was "; call print Name (creator);

    [27] print *, " who was born in "; call print Nationality (creator);

    [28] print *, ".";

    [29] end program main ! Running gives:

  • 8/7/2019 tgsn 2 oop

    23/64

    [30] ! The author of the Declaration of Independence was Thomas Jefferson.

    [31] ! He was born on April 13, 1743 and died on July 4, 1826.

    [32] ! The author of the Declaration of Independence was Thomas Jefferson.

    [33] ! He was born on April 13, 1743 and died on July 4, 1826.

    [34] ! The creator of Fortran was John Backus who was born in the USA.

    Figure 3.10: Testing the Date and Person Classes

    Student Class

    who

    Student_

    Person

    character id [SSN]

    Student

    Student

    Student

    make_Student

    Student

    Date matriculation

    Student get_Person

    Student print_DOM

    integer credits

    real gpa

    Student print_GPA

    Student set_DOM

    Figure 3.11: Graphical Representation of a Student Class

    (lines 14, 18, 25), and a simple destructor delete Rational (line 38). The intrinsic constructor (line

    6) could have been used only if all the attributes were public, and that is considered an undesirable

    practice in OOP. The simple destructor actually just sets the deleted number to have a set of

    default

  • 8/7/2019 tgsn 2 oop

    24/64

    components. Later we will see that constructors and destructors often must dynamically allocate

    and

    deallocate, respectively, memory associated with a specic instance of some object.

    c 2001 J.E. Akin 41[ 1] module class Student ! filename class Student.f90

    [ 2] use class Person ! inherits class Date

    [ 3] implicit none

    [ 4] public :: Student, set DOM, print DOM

    [ 5] type Student

    [ 6] private

    [ 7] type (Person) :: who ! name and sex

    [ 8] character (len=9) :: id ! ssn digits

    [ 9] type (Date) :: dom ! matriculation

    [10] integer :: credits

    [11] real :: gpa ! grade point average

    [12] end type Student

    [13] contains ! coupled functionality

    [14]

    [15] function get person (s) result (p)

    [16] type (Student), intent(in) :: s

    [17] type (Person) :: p ! name and sex

    [18] p = s % who ; end function get person

    [19]

    [20] function make Student (w, n, d, c, g) result (x) ! constructor

    [21] ! Optional Constructor for a Student type

    [22] type (Person), intent(in) :: w ! who

    [23] character (len=*), optional, intent(in) :: n ! ssn

    [24] type (Date), optional, intent(in) :: d ! matriculation

    [25] integer, optional, intent(in) :: c ! credits

  • 8/7/2019 tgsn 2 oop

    25/64

    [26] real, optional, intent(in) :: g ! grade point ave

    [27] type (Student) :: x ! new student

    [28] x = Student (w, " ", Date (1,1,1), 0, 0.) ! defaults

    [29] if ( present(n) ) x % id = n ! optional values

    [30] if ( present(d) ) x % dom = d

    [31] if ( present(c) ) x % credits = c

    [32] if ( present(g) ) x % gpa = g ; end function make Student

    [33]

    [34] subroutine print DOM (who)

    [35] type (Student), intent(in) :: who

    [36] call print Date(who%dom) ; end subroutine print DOM

    [37]

    [38] subroutine print GPA (x)

    [39] type (Student), intent(in) :: x

    [40] print *, "My name is "; call print Name (x % who)

    [41] print *, ", and my G.P.A. is ", x % gpa, "." ; end subroutine

    [42]

    [43] subroutine set DOM (who, m, d, y)

    [44] type (Student), intent(inout) :: who

    [45] integer, intent(in) :: m, d, y

    [46] who % dom = Date ( m, d, y) ; end subroutine set DOM

    [47]

    [48] function Student (w, n, d, c, g) result (x)

    [49] ! Public Constructor for a Student type

    [50] type (Person), intent(in) :: w ! who

    [51] character (len=*), intent(in) :: n ! ssn

    [52] type (Date), intent(in) :: d ! matriculation

  • 8/7/2019 tgsn 2 oop

    26/64

    [53] integer, intent(in) :: c ! credits

    [54] real, intent(in) :: g ! grade point ave

    [55] type (Student) :: x ! new student

    [56] x = Student (w, n, d, c, g) ; end function Student

    [57] end module class Student

    Figure 3.12: Dening a Typical Student Class

    When considering which operators to overload for a newly dened object one should consider those

    that are used in sorting operations, such as the greater-than,>, and less-than,

  • 8/7/2019 tgsn 2 oop

    27/64

    [ 8] ! Method 1

    [ 9] p = make Person ("Ann Jones","",0) ! optional person constructor

    [10] call set DOB (p, 5, 13, 1977) ! add birth to person data

    [11] x = Student (p, "219360061", Date (8,29,1955), 9, 3.1) ! public

    [12] call print Name (p) ! list name

    [13] print *, "Born :"; call print DOB (p) ! list dob

    [14] print *, "Sex :"; call print Sex (p) ! list sex

    [15] print *, "Matriculated:"; call print DOM (x) ! list dom

    [16] call print GPA (x) ! list gpa

    [17] ! Method 2

    [18] x = make Student (p, "219360061") ! optional student constructor

    [19] call set DOM (x, 8, 29, 1995) ! correct matriculation

    [20] call print Name (p) ! list name

    [21] print *, "was born on :"; call print DOB (p) ! list dob

    [22] print *, "Matriculated:"; call print DOM (x) ! list dom

    [23] ! Method 3

    [24] x = make Student (make Person("Ann Jones"), "219360061") ! optional

    [25] p = get Person (x) ! get defaulted person data

    [26] call set DOM (x, 8, 29, 1995) ! add matriculation

    [27] call set DOB (p, 5, 13, 1977) ! add birth

    [28] call print Name (p) ! list name

    [29] print *, "Matriculated:"; call print DOM (x) ! list dom

    [30] print *, "was born on :"; call print DOB (p) ! list dob

    [31] end program main ! Running gives:

    [32] ! Ann Jones

    [33] ! Born : May 13, 1977

    [34] ! Sex : female

  • 8/7/2019 tgsn 2 oop

    28/64

    [35] ! Matriculated: August 29, 1955

    [36] ! My name is Ann Jones, and my G.P.A. is 3.0999999.

    [37] ! Ann Jones was born on: May 13, 1977 , Matriculated: August 29, 1995

    [38] ! Ann Jones Matriculated: August 29, 1995 , was born on: May 13, 1977

    Figure 3.13: Testing the Student, Person, and Date Classes

    machines. It includes most of the High Performance Fortran features that are in wide use. Thus,

    efcient

    use of OOP on parallel machines is available through F90 and F95.

    None of the OOP languages have all the features one might desire. For example, the useful concept

    of a template which is standard in C++ is not in the F90 standard. Yet the author has found that a

    few dozen lines of F90 code will dene a preprocessor that allows templates to be dened in F90and

    expanded in line at compile time. The real challenge in OOP is the actual OOA and OOD that must be

    completed before programming can begin, regardless of the language employed. For example,

    several

    authors have described widely different approaches for dening classes to be used in constructing

    OO

    nite element systems. Additional example applications of OOP in F90 will be given in the following

    chapters.

    c 2001 J.E. Akin 43Rational Class

    numerator

    Rational_

    integer

    Rational

    Rational

    Rational

    make_Rational

    reduce

    Rational add_Rational

  • 8/7/2019 tgsn 2 oop

    29/64

    Rational convert

    integer denominator

    Rational copy_Rational

    Rational is_equal_to

    integer gcd

    Rational Rational

    Rational list

    Rational mult_Rational

    Rational invert

    Rational delete_Rational

    Rational equal_Rational

    Rational get_Denominator

    Rational get_Numerator

    Figure 3.14: Representation of a Rational Number Class

    c 2001 J.E. Akin 44[ 1] module class Rational ! filename: class Rational.f90

    [ 2] implicit none

    [ 3] ! public, everything but following private routines

    [ 4] private :: gcd, reduce

    [ 5] type Rational

    [ 6] private ! numerator and denominator

    [ 7] integer :: num, den ; end type Rational

    [ 8]

    [ 9] ! overloaded operators interfaces

    [ 10] interface assignment (=)

    [ 11] module procedure equal Integer ; end interface

    [ 12] interface operator (+) ! add unary versions & (-) later

    [ 13] module procedure add Rational ; end interface

    [ 14] interface operator (*) ! add integer mult Rational, etc

  • 8/7/2019 tgsn 2 oop

    30/64

    [ 15] module procedure mult Rational ; end interface

    [ 16] interface operator (==)

    [ 17] module procedure is equal to ; end interface

    [ 18] contains ! inherited operational functionality

    [ 19] function add Rational (a, b) result (c) ! to overload +

    [ 20] type (Rational), intent(in) :: a, b ! left + right

    [ 21] type (Rational) :: c

    [ 22] c % num = a % num*b % den + a % den*b % num

    [ 23] c % den = a % den*b % den

    [ 24] call reduce (c) ; end function add Rational

    [ 25]

    [ 26] function convert (name) result (value) ! rational to real

    [ 27] type (Rational), intent(in) :: name

    [ 28] real :: value ! decimal form

    [ 29] value = float(name % num)/name % den ; end function convert

    [ 30]

    [ 31] function copy Rational (name) result (new)

    [ 32] type (Rational), intent(in) :: name

    [ 33] type (Rational) :: new

    [ 34] new % num = name % num

    [ 35] new % den = name % den ; end function copy Rational

    [ 36]

    [ 37] subroutine delete Rational (name) ! deallocate allocated items

    [ 38] type (Rational), intent(inout) :: name ! simply zero it here

    [ 39] name = Rational (0, 1) ; end subroutine delete Rational

    [ 40]

    [ 41] subroutine equal Integer (new, I) ! overload =, with integer

  • 8/7/2019 tgsn 2 oop

    31/64

    [ 42] type (Rational), intent(out) :: new ! left side of operator

    [ 43] integer, intent(in) :: I ! right side of operator

    [ 44] new % num = I ; new % den = 1 ; end subroutine equal Integer

    [ 45]

    [ 46] recursive function gcd (j, k) result (g) ! Greatest Common Divisor

    [ 47] integer, intent(in) :: j, k ! numerator, denominator

    [ 48] integer :: g

    [ 49] if ( k == 0 ) then ; g = j

    [ 50] else ; g = gcd ( k, modulo(j,k) ) ! recursive call

    [ 51] end if ; end function gcd

    [ 52]

    [ 53] function get Denominator (name) result (n) ! an access function

    [ 54] type (Rational), intent(in) :: name

    [ 55] integer :: n ! denominator

    [ 56] n = name % den ; end function get Denominator

    (Fig. 3.15, A Fairly Complete Rational Number Class (continued))

    c 2001 J.E. Akin 45[ 57] function get Numerator (name) result (n) ! an access function

    [ 58] type (Rational), intent(in) :: name

    [ 59] integer :: n ! numerator

    [ 60] n = name % num ; end function get Numerator

    [ 61]

    [ 62] subroutine invert (name) ! rational to rational inversion

    [ 63] type (Rational), intent(inout) :: name

    [ 64] integer :: temp

    [ 65] temp = name % num

    [ 66] name % num = name % den

    [ 67] name % den = temp ; end subroutine invert

    [ 68]

  • 8/7/2019 tgsn 2 oop

    32/64

    [ 69] function is equal to (a given, b given) result (t f)

    [ 70] type (Rational), intent(in) :: a given, b given ! left == right

    [ 71] type (Rational) :: a, b ! reduced copies

    [ 72] logical :: t f

    [ 73] a = copy Rational (a given) ; b = copy Rational (b given)

    [ 74] call reduce(a) ; call reduce(b) ! reduced to lowest terms

    [ 75] t f = (a%num == b%num) .and. (a%den == b%den) ; end function

    [ 76]

    [ 77] subroutine list(name) ! as a pretty print fraction

    [ 78] type (Rational), intent(in) :: name

    [ 79] print *, name % num, "/", name % den ; end subroutine list

    [ 80]

    [ 81] function make Rational (numerator, denominator) result (name)

    [ 82] ! Optional Constructor for a rational type

    [ 83] integer, optional, intent(in) :: numerator, denominator

    [ 84] type (Rational) :: name

    [ 85] name = Rational(0, 1) ! set defaults

    [ 86] if ( present(numerator) ) name % num = numerator

    [ 87] if ( present(denominator)) name % den = denominator

    [ 88] if ( name % den == 0 ) name % den = 1 ! now simplify

    [ 89] call reduce (name) ; end function make Rational

    [ 90]

    [ 91] function mult Rational (a, b) result (c) ! to overload *

    [ 92] type (Rational), intent(in) :: a, b

    [ 93] type (Rational) :: c

    [ 94] c % num = a % num * b % num

    [ 95] c % den = a % den * b % den

  • 8/7/2019 tgsn 2 oop

    33/64

    [ 96] call reduce (c) ; end function mult Rational

    [ 97]

    [ 98] function Rational (numerator, denominator) result (name)

    [ 99] ! Public Constructor for a rational type

    [100] integer, optional, intent(in) :: numerator, denominator

    [101] type (Rational) :: name

    [102] if ( denominator == 0 ) then ; name = Rational (numerator, 1)

    [103] else ; name = Rational (numerator, denominator) ; end if

    [104] end function Rational

    [105]

    [106] subroutine reduce (name) ! to simplest rational form

    [107] type (Rational), intent(inout) :: name

    [108] integer :: g ! greatest common divisor

    [109] g = gcd (name % num, name % den)

    [110] name % num = name % num/g

    [111] name % den = name % den/g ; end subroutine reduce

    [112] end module class Rational

    Figure 3.15: A Fairly Complete Rational Number Class

    c 2001 J.E. Akin 46[ 1] include class Rational.f90

    [ 2] program main

    [ 3] use class Rational

    [ 4] implicit none

    [ 5] type (Rational) :: x, y, z

    [ 6] ! ------- only if Rational is NOT private ----------

    [ 7] ! x = Rational(22,7) ! intrinsic constructor if public components

    [ 8]

    [ 9] x = Rational (22,7) ! public constructor if private components

    [10] write (*,("public x = "),advance=no); call list(x)

  • 8/7/2019 tgsn 2 oop

    34/64

    [11] write (*,("converted x = ", g9.4)) convert(x)

    [12] call invert(x)

    [13] write (*,("inverted 1/x = "),advance=no); call list(x)

    [14]

    [15] x = make Rational () ! default constructor

    [16] write (*,("made null x = "),advance=no); call list(x)

    [17] y = 4 ! rational = integer overload

    [18] write (*,("integer y = "),advance=no); call list(y)

    [19] z = make Rational (22,7) ! manual constructor

    [20] write (*,("made full z = "),advance=no); call list(z)

    [21] ! Test Accessors

    [22] write (*,("top of z = ", g4.0)) get numerator(z)

    [23] write (*,("bottom of z = ", g4.0)) get denominator(z)

    [24] ! Misc. Function Tests

    [25] write (*,("making x = 100/360, "),advance=no)

    [26] x = make Rational (100,360)

    [27] write (*,("reduced x = "),advance=no); call list(x)

    [28] write (*,("copying x to y gives "),advance=no)

    [29] y = copy Rational (x)

    [30] write (*,("a new y = "),advance=no); call list(y)

    [31] ! Test Overloaded Operators

    [32] write (*,("z * x gives "),advance=no); call list(z*x) ! times

    [33] write (*,("z + x gives "),advance=no); call list(z+x) ! add

    [34] y = z ! overloaded assignment

    [35] write (*,("y = z gives y as "),advance=no); call list(y)

    [36] write (*,("logic y == x gives "),advance=no); print *, y==x

    [37] write (*,("logic y == z gives "),advance=no); print *, y==z

  • 8/7/2019 tgsn 2 oop

    35/64

    [38] ! Destruct

    [39] call delete Rational (y) ! actually only null it here

    [40] write (*,("deleting y gives y = "),advance=no); call list(y)

    [41] end program main ! Running gives:

    [42] ! public x = 22 / 7 ! converted x = 3.143

    [43] ! inverted 1/x = 7 / 22 ! made null x = 0 / 1

    [44] ! integer y = 4 / 1 ! made full z = 22 / 7

    [45] ! top of z = 22 ! bottom of z = 7

    [46] ! making x = 100/360, reduced x = 5 / 18

    [47] ! copying x to y gives a new y = 5 / 18

    [48] ! z * x gives 55 / 63 ! z + x gives 431 / 126

    [49] ! y = z gives y as 22 / 7 ! logic y == x gives F

    [50] ! logic y == z gives T ! deleting y gives y = 0 / 1

    Figure 3.16: Testing the Rational Number Class

    c 2001 J.E. Akin 473.5 Exercises

    1. Use the class Circle to create a class Sphere that computes the volume of a sphere. Have

    a method that accepts an argument of a Circle. Use the radius of the Circle via a new member

    get Circle radius to be added to the class Circle.

    2. Use the class Circle and class Rectangle to create a class Cylinder that computes

    the volume of a right circular cylinder. Have a method that accepts arguments of a Circle and a

    height,

    and a second method that accepts arguments of a Rectangle and a radius. In the latter member

    use the height of the Rectangle via a new member get Rectangle height to be added to the

    class Rectangle.

    3. Create a vector class to treat vectors with an arbitrary number of real coefcients. Assume that

    the

    class Vector is dened as follows:

    Vector Class

    size

  • 8/7/2019 tgsn 2 oop

    36/64

    assign

    integer

    Vector

    Vector make_Vector

    Vector add_Real_to_Vector

    Vector add_Vector

    real, pointer data (:)

    Vector copy_Vector

    logical is_equal_to

    Vector Vector

    real values

    Vector normalize_Vector

    Vector list

    Vector delete_Vector

    real dot_Vector

    Vector equal_Real

    real length

    integer size_Vector

    Vector subtract_Real

    Vector real_mult_Vector

    Vector read_Vector

    Vector subtract_Vector

    Vector Vector_mult_real

    Vector Vector_

    real Vector_max_value

    real Vector_min_value

    Overload the common operators of (+) with add Vector and add Real to Vector, () with

  • 8/7/2019 tgsn 2 oop

    37/64

    subtract Vector and subtract Real, (*) with dot Vector, real mult Vector and Vector mult real, (=) with

    equal Real to set all coefcients to a single real number, and (==) with

    routine is equal to.

    Include two constructors assign and make Vector. Let assign convert a real array into an instance

    of a Vector. Provide a destructor, means to read and write a Vector, normalize a Vector, and

    determine its

    extreme values.

    c 2001 J.E. Akin 484. Modify the above Vector class to extend it to a Sparse Vector Class where the

    vast majority

    of the coefcients are zero. Store and operate only on the non-zero entries.

    Sparse_Vector Class

    integer non_zeros

    Sparse_Vector make_Sparse_Vector

    Sparse_Vector add_Real_to_Sparse_Vector

    Sparse_Vector add_Sparse_Vector

    real, pointer values (:)

    el_by_el_Mult

    Sparse_Vector

    logical is_equal_to

    Sparse_Vector Sparse_Vector

    Sparse_Vector show_r_v

    normalize_Vector

    integer largest_index

    Sparse_Vector

    delete_Sparse_Vector

    real dot_Vector

    Sparse_Vector equal_Vector

    real length

    integer rows_of

  • 8/7/2019 tgsn 2 oop

    38/64

    Sparse_Vector set_element

    Sparse_Vector real_mult_Sparse

    read_Vector

    Sparse_Vector

    Sparse_Vector show

    Sparse_Vector Sparse_mult_real

    integer size_of

    Sparse_vector sub_Sparse_Vector

    Sparse_Vector sum_Sparse_Vector

    integer, pointer rows (:)

    real get_element

    real norm

    normalize_Vector

    Sparse_Vector pretty

    Sparse_Vector

    real Vector_max_value

    real Vector_min_value

    Sparse_Vector Vector_to_Sparse

  • 8/7/2019 tgsn 2 oop

    39/64

    What is Object Oriented Programming?

    Now we move onto what might have been termed an advanced topic up untilabout 10 years ago. Nowadays 'Object Oriented Programminghas become the

    norm. Languages like Java and Python embody the concept so much that you

    can do very little without coming across objects somewhere. So what's it allabout?

    The best introductions are, in my opinion:

    y Object Oriented Analysis by Peter Coad & Ed Yourdon.

    y Object Oriented Analysis and Design with Applications by Grady Booch (the

    1st edition if you can find it)

    y Object Oriented Software Construction by Bertrand Meyer (definitely the

    2nd edition of this one)

    These increase in depth, size and academic exactitude as you go down the list.For most non professional programmers' purposes the first is adequate. For amore programming focused intro try Object Oriented ProgrammingbyTimothy Budd(2nd edition). This uses several languages to illustrate object

    oriented programming techniques. It is much more strongly oriented towards

    writing programs than any of the other books which cover the whole gamut oftheory and principle behind object orientation, at the design level as well as at

    the code level. Finally for a whole heap of info on all topics OO try the Web

    link site at: http://www.cetus-links.org

    Assuming you don't have the time nor inclination to researc h all these booksand links right now, I'll give you a brief overview of the concept. ( Note:Some

    people find OO hard to grasp others 'get it' right away. Don't worry if you

    come under the former category, you can still use objects even without really

    'seeing the light'.)

    One final point: it is possible to implement an Object Oriented design in a non

    OO language through coding conventions, but it's usually an option of last

    resort rather than a recommended strategy. If your problem fits well with OO

    techniques then it's best to use an OO language. Most modern languages,

    including Python, VBScript and JavaScript support OOP quite well. That

    having been said I will be using Python throughout all the examples and onlyshowing the basic concepts in VBScript and JavaScript with little additional

    explanation.

    Data and Function - together

    Objects are collections of data and functions that operate on that data. Theseare bound together so that you can pass an object from one part of your

  • 8/7/2019 tgsn 2 oop

    40/64

    program and they automatically get access to not only the dataattributes but

    the operations that are available too. This combining of data and function isthe very essence of Object Oriented Programming and is known

    as encapsulation. (Some programming languages make the data invisible to

    users of the object and thus require that the data be accessed via the object's

    methods. This technique is properly known as data hiding, however in sometexts data hiding and encapsulation are used interchangeably.)

    As an example of encapsulation, a string object would store the character

    string but also provide methods to operate on that string - search, change case,

    calculate length etc.

    Objects use a message passingmetaphor whereby one object passes a message

    to another object and the receiving object responds by executing one of its

    operations, a method. So a method is invokedon receipt of the corresponding

    message by the owning object. There are various notations used to represent

    this but the most common mimics the access to items in modules - a dot. Thus,for a fictitious widget class:

    w = Widget() # create new instance, w, of widget w.paint() # send the message 'paint' to it

    This would cause the paint method of the widget object to be invoked.

    Defining Classes

    Just as data has various types so objects can have different types. These

    collections of objects with identical characteristics are collectively known asa class. We can define classes and create instances of them, which are theactual objects. We can store references to these objects in variables in our

    programs.

    Let's look at a concrete example to see if we can explain it better. We will

    create a message class that contains a string - the message text - and a methodto print the message.

    class Message:def __init__(self, aString):

    self.text = aString

    def printIt(self):print self.text

    Note 1:One of the methods of this class is called __init__ and it is a special

    method called a constructor. The reason for the name is that it is called when anew object instance is created or constructed. Any variables assigned (andhence created in Python) inside this method will be unique to the newinstance. There are a number of special methods like this in Python, nearly all

  • 8/7/2019 tgsn 2 oop

    41/64

    distinguished by the __xxx__ naming format. The exact timing of when a

    constructor is called varies between languages, in Python init gets called afterthe instance has actually been created in memory, in other languages the

    constructor actually returms the instance itself. The difference is sufficiently

    subtle that you don't usually need to worry about it.

    Note 2:Both the methods defined have a first parameter self. The name is aconvention but it indicates the object instance. As we will soon see this

    parameter is filled in by the interpreter at run -time, not by the programmer.

    Thus printIt is called, on an instance of the class (see below), with noarguments: m.printIt().

    Note 3:We called the class Message with a capital 'M'. This is purelyconvention, but it is fairly widely used, not just in Python but i n other OO

    languages too. A related convention says that method names should begin with

    a lowercase letter and subsequent words in the name begin with uppercase

    letters. Thus a method called "calculate current balance" would bewritten: calculateCurrentBal ance.

    You may want to briefly revisit the 'Raw Materials' section and look again at

    'user defined types'. The Python address example should be a little clearernow. Essentially the only kind of user-defined type in Python is a class. A

    class with attributes but no methods (except __init__ ) is effectively

    equivalent to a construct called a record orstruct in some programming

    languages..

    U

    sing ClassesHaving defined a class we can now create instances of our Message class and

    manipulate them:

    m1 = Message("Hello world")m2 = Message("So long, it was short but sweet")

    note = [m1, m2] # put the objects in a list for msg in note:

    msg.printIt() # print each message in turn

    So in essence you just treat the class as if it was a standard Python data type,

    which was after all the purpose of the excercise!

    What is "self"?

    No, it's not a philosophical debate, it's one of the questions most often askedby new Python OOP programmers. Every method definition in a class in

    Python starts with a parameter called self. The actual name selfis just aconvention, but like many programming conventions consistency is good so

  • 8/7/2019 tgsn 2 oop

    42/64

    let's stick with it! (As you'll see later JavaScript has a similar concept but uses

    the name this instead.)

    So what is self all about? Why do we need it?

    Basically self is just a reference to the current instance. When you create aninstance of the class the instance contains its own data (as created by theconstructor) but not of the methods. Thus when we send a message to aninstance and it calls the corresponding method, it does so via an internalreference to the class. It passes a reference to itself (self!) to the method so that

    the class code knows which instance to use.

    Let's look at a relatively familiar example. Consider a GUI application which

    has lots of Button objects. When a user presses a button the method associatedwith a button press is activated - but how does the Button method know which

    of the buttons has been pressed? The answer is by referring to the self value

    which will be a reference to the actual button instance that was pressed. We'llsee this in practice when we get to the GUI topic a little later.

    So what happens when a message is sent to an object? It works like this:

    y the client code calls the instance (sending the message in OOP speak).

    y The instance calls the class method, passing a reference to itself (self).

    y The class method then uses the passed reference to pick up the instance

    data for the receiving object.

    You can see this in action in this code sequence, notice that we can explicitly

    call the class method, as we do in the last line:

    >>> class C:... def __init__(self, val): self.val = val... def f(self): print "hello, my value is:", self.val...>>># create two instances >>> a = C(27)>>> b = C(42)>>># first try sending messages to the instances >>> a.f()hello, my value is 27>>> b.f()

    hello, my value is 42>>># now call the method explicitly via the class >>> C.f(a)hello, my value is 27

    So you see we can call the methods via the instance, in which case Python fillsin the self parameter for us, or explicitly via the class, in which case we need

    to pass the self value explicitly.

  • 8/7/2019 tgsn 2 oop

    43/64

    Now you might be wondering why, if Python can provide the invisible

    reference between the instance and its class can't Python also magically fill inthe self by itself? The answer is that Guido van Rossum designed it this way!

    Many OOP languages do indeed hide the self parameter, but one of

    the guiding principles of Python is that "explicit is better than implicit". You

    soon get used to it and after a while not doing it seems strange.

    Same thing, Different thing

    What we have so far is the ability to define our own types (classes) and createinstances of these and assign them to variables. We can then pass messages tothese objects which trigger the methods we have defined. But there's one last

    element to this OO stuff, and in many ways it's the most important aspect of

    all.

    If we have two objects of different classes but which support the same set of

    messages but with their own corresponding methods then we can collect theseobjects together and treat them identically in our program but the objects will

    behave differently. This ability to behave differently to the same input

    messages is known aspolymorphism.

    Typically this could be used to get a number of different graphics objects todraw themselves on receipt of a 'paint' message. A circle draws a very

    different shape from a triangle but provided they both have a paint method we,

    as programmers, can ignore the difference and just think of them as 'shapes'.

    Let's look at an example, where instead of drawing shapes we calculate their

    areas:

    First we create Square and Circle classes:

    class Square:def __init__(self, side):

    self.side = sidedef calculateArea(self):

    return self.side**2

    class Circle:def __init__(self, radius):

    self.radius = radius

    def calculateArea(self):import mathreturn math.pi*(self.radius**2)

    Now we can create a list of shapes (either circles or squares) and then print out

    their areas:

    list = [Circle(5),Circle(7),Square(9),Circle(3),Square(12)]

  • 8/7/2019 tgsn 2 oop

    44/64

    for shape in list:print "The area is: ", shape.calculateArea()

    Now if we combine these ideas with modules we get a very powerfulmechanism for reusing code. Put the class definitions in a module - say

    'shapes.py' and then simply import that module when we want to manipulate

    shapes. This is exactly what has been done with many of the standard Pythonmodules, which is why accessing methods of an object looks a lot like using

    functions in a module.

    Inheritance

    Inheritance is often used as a mechanism to implement polymorphism. Indeedin many OO languages it is the only way to implement polymorphism. It

    works as follows:

    A class can inheritboth attributes and operations from a parentorsuperclass.This means that a new class which is identical to another class in most respects

    does not need to re-implement all the methods of the existing class, rather itcan inherit those capabilities and then override those that it wants to do

    differently (like the calculateArea method in the case above)

    Again an example might illustrate this best. We will use a class hierarchy ofbank accounts where we can deposit cash, obtain the balance and make a

    withdrawal. Some of the accounts provide interest (which, for our purposes,we'll assume is calculated on every deposit - an interesting innovation to the

    banking world!) and others charge fees for withdrawals.

    The BankAccount class

    Let's see how that might look. First let's consider the attributes and operationsof a bank account at the most general (orabstract) level.

    It's usually best to consider the operations first then provide attributes as

    needed to support these operations. So for a bank account we can:

    y Depositcash,

    y Withdrawcash,

    y Check current balance and

    y Transferfunds to another account.

    To support these operations we will need a bank account ID (for the transfer

    operation) and the current balance.

  • 8/7/2019 tgsn 2 oop

    45/64

    We can create a class to support that:

    class BalanceError(Exception):value = "Sorry you only have $%6.2f in your account"

    class BankAccount:def __init__(self, i nitialAmount):

    self.balance = initialAmountprint "Account created with balance %5.2f" % self.balance

    def deposit(self, amount):self.balance = self.balance + amount

    def withdraw(self, amount):if self.balance > = amount:

    self.balance = self.balance - amountelse:

    raise BalanceError, BalanceError.value % self.balance

    def checkBalance(self):return self.balance

    def transfer(self, amount, account):try:

    self.withdraw(amount)account.deposit(amount)

    except BalanceError:print BalanceError.value

    Note 1: We check the balance before withdrawing and also use an exception

    to handle errors. Of course there is no Python error type BalanceError so weneeded to create one of our own - it's simply an subclass of the

    standard Exception class with a string value. The string value is defined as an

    attribute of the exception class purely as a convenience, it ensures that we cangenerate standard error messages every time we raise an error. When we raise

    BalanceError we pass the internal format string value filled in with the current

    value of the object's balance. Notice that we didn't use self when defining the

    value inBalanceError, that's because value is a shared attribute across allinstances, it is defined at the class level and known as a class variable. We

    access it by using the class name followed by a dot: BalanceError.value asseen above. Now, when the error generates its traceback it concludes by

    printing out the formatted error string showing the current balance.

    Note 2: The transfer method uses

    the BankAccount's withdraw/depositmember functions or methods to do thetransfer. This is very common in OO and is known as self messaging. It means

    that derived classes can implement their own versions of deposit/withdraw but

    the transfer method can remain the same for all account types.

  • 8/7/2019 tgsn 2 oop

    46/64

    The InterestAccount class

    Now we use inheritance to provide an account that adds interest (we'll assume3%) on every deposit. It will be identical to the standard BankAccount class

    except for the deposit method. So we simply override that:

    class InterestAccount(BankAccount):def deposit(self, amount):

    BankAccount.deposit(self,amount)self.balance = self.balance * 1.03

    And that's it. We begin to see the power of OOP, all the other methods have

    been inherited from BankAccount (by putting BankAccount inside theparentheses after the new class name). Notice also that deposit calledthesuperclass's deposit method rather than copying the code. Now if wemodify the BankAccount deposit to include some kind of error checking

    thesub-class will gain those changes automatically.

    The ChargingAccount class

    This account is again identical to a standard BankAccount class except thatthis time it charges $3 for every withdrawal. As for the InterestAccount we

    can create a class inheriting from BankAccount and modifying the withdraw

    method.

    class ChargingAccount(BankAccount):def __init__(self, initialAmount):

    BankAccount.__init__(self, initialAmount)

    self.fee = 3

    def withdraw(self, amount):BankAccount.withdraw(self, amount+self.f ee)

    Note 1: We store the fee as an instance variable so that we can change it later

    if necessary. Notice that we can call the inherited __init__ just like any other

    method.

    Note 2: We simply add the fee to the requested withdrawal and call the

    BankAccount withdraw method to do the real work.

    Note 3: We introduce a side effect here in that a charge is automatically leviedon transfers too, but that's probably what we want, so is OK.

    Testing our system

    To check that it all works try executing the following pi ece of code (either at

    the Python prompt or by creating a separate test file).

  • 8/7/2019 tgsn 2 oop

    47/64

    from bankaccount import *

    # First a standard BankAccount a = BankAccount(500)b = BankAccount(200)a.withdraw(100)# a.withdraw(1000)

    a.transfer(100,b)print "A = ", a.checkBalance()print "B = ", b.checkBalance()

    # Now an InterestAccount c = InterestAccount(1000)c.deposit(100)print "C = ", c.checkBalance()

    # Then a ChargingAccount d = ChargingAccount(300)d.deposit(200)print "D = ", d.checkBalance()

    d.withdraw(50)print "D = ", d.checkBalance()d.transfer(100,a)print "A = ", a.checkBalance()print "D = ", d.checkBalance()

    # Finally transfer from charging account to the interest one# The charging one should charge and the interest one add# interestprint "C = ", c.checkBalance()print "D = ", d.checkBalance()d.transfer(20,c)print "C = ", c.checkBalance()

    print "D = ", d.checkBalance()

    Now uncomment the line a.withdraw(1000) to see the exception at work.

    That's it. A reasonably straightforward example but it shows how inheritance

    can be used to quickly extend a basic framework with powerful new features.

    We've seen how we can build up the example in stages and how we can puttogether a test program to check it works. Our tests were not complete in that

    we didn't cover every case and there are more checks we could have included -

    like what to do if an account is created with a negative amount...

    Collections of Objects

    One problem that might have occurred to you is how we deal with lots ofobjects. Or how to manage objects which we create at runtime. It's all very

    well creating Bank Accounts statically as we did above:

    acc1 = BankAccount(...)acc2 = BankAccount(...)

  • 8/7/2019 tgsn 2 oop

    48/64

    acc3 = BankAccount(...)etc...

    But in the real world we don't know in advance how many accounts we need

    to create. How do we deal with this? Lets consider the problem in more detail:

    We need some kind of 'database' that allows us to find a given bank accountby its owners name (or more likely their bank account number - since one

    person can have many accounts and several persons can have the same

    name...)

    Finding something in a collection given a unique key....hmmm, sounds like a

    dictionary! Lets see how we'd use a Python dictionary to hold dynamically

    created objects:

    from bankaccount import *import time

    # Create new function to generate unique id numbers def getNextID():

    ok = raw_input("Create account[y/n]? ")if ok[0] in 'yY': # check valid input

    id = time.time() # use current time as basis of ID id = int(id) % 10000 # convert to int and shorten to 4 digits

    else: id = -1 # which will stop the loop return id

    # Let's create some accounts and store them in a dictionary accountData = {} # new dictionary while 1: # loop forever

    id = getNextID()if id == -1:

    break # break forces an exit from the while loop bal = float(raw_input("Opening Balance? ")) # convert string to

    floataccountData[id] = BankAccount(bal) # use id to create new dictionary

    entryprint "New account crea ted, Number: %04d, Balance %0.2f" % (id,bal)

    # Now let's access the accounts for id in accountData.keys():

    print "%04d\t%0.2f" % (id,accountData[id].checkBalance())

    # and find a particular one # Enter non-number to force exception and end program while 1:

    id = int(raw_input("Which account number? "))if id in accountData.keys():

    print "Balance = %0.2d" % accountData[id].checkBalance()else: print "Invalid ID"

    Of course the key you use for the dictionary can be anything that uniquely

    identifies the object, it could be one of its attributes, like balance say (exceptthat balance would not be a very good unique key!). Anything at all that is

  • 8/7/2019 tgsn 2 oop

    49/64

    unique. You might find it worthwhile going back to the raw materials chapter

    and reading the dictionary section again, they really are very useful containers.

    Saving Your Objects

    One snag with all of this is that you lose your data when the program ends.You need some way of saving objects too. As you get more advanced you will

    learn how to use databases to do that but we will look at using a simple textfile to save and retrieve objects. (If you are using Python there are a couple of

    modules called Pickle and Shelve) that do this much more effectively but as

    usual I'll try to show you the generic way to do it that will work in anylanguage. Incidentally the technical term for the ability to save and restore

    objects isPersistence.

    The generic way to do this is to create save and restore methods at the highestlevel object and override in each class, such that they call the inherited version

    and then add their locally defined attributes:

    class A:def __init__(self,x,y):self.x = xself.y = y

    def save(self,fn):f = open(fn,"w")f.write(str(self.x)+ ' \n') # convert to a string and add newline f.write(str(self.y)+' \n')return f # for child objects to use

    def restore(self, fn):

    f = open(fn)self.x = int(f.readline()) # convert back to original type self.y = int(f.readline())return f

    class B(A):def __init__(self,x,y,z):A.__init__(self,x,y)self.z = z

    def save(self,fn):f = A.save(self,fn) # call parent save f.write(str(self.z)+'\n')return f # in case further children exist

    def restore(self, fn):f = A.restore(self,fn)self.z = int(f.readline())return f

    # create instances a = A(1,2)b = B(3,4,5)

    # save the instances

  • 8/7/2019 tgsn 2 oop

    50/64

    a.save('a.txt').close() # remember to close the file b.save('b.txt').close()

    # retrieve instances newA = A(5,6)newA.restore('a.txt').close() # remember to close the file newB = B(7,8,9)

    newB.restore('b.txt').close()print "A: ",newA.x,newA.yprint "B: ",newB.x,newB.y,newB.z

    Note: The values printed out are the restored values not the ones we used to

    create the instances.

    The key thing is to override the save/restore methods in each class and to call

    the parent method as the first step. Then in the child class only deal with childclass attributes. Obviously how you turn an attribute into a string and save it is

    up to you the programmer but it must be output on a single line. When

    restoring you simply reverse the storing process.

    Mixing Classes and Modules

    Modules and classes both provide mechanisms for controlling the complexityof a program. It seems reasonable that as programs get bigger we would wantto combine these features by putting classes into modules. Some authorities

    recommend putting each class into a separate module but I find this simplycreates an explosion of modules and increases rather than decreases

    complexity. Instead I group classes together and put the group into a module.

    Thus in our example above I might put all the bank account class definitions in

    one module, bankaccount, say, and then create a separate module for theapplication code that uses the module. A simplified representation of that

    would be:

    # File: bankaccount.py## Implements a set of bank account classes###################

    class BankAccount: ....

    class InterestAccount: ...

    class ChargingAccount: ...

    And then to use it:

    import bankaccount

    newAccount = bankaccount.BankAccount(50)newChrgAcct = bankaccount.ChargingAccount(200)

  • 8/7/2019 tgsn 2 oop

    51/64

    # now do stuff

    But what happens when we have two classes in different modules that need to

    access each others details? The simplest way is to import both modules, create

    local instances of the classes we need and pass the instances of one class to theother instances methods. Passing whole objects around is what m akes itobject orientedprogramming. You don't need to extract the attributes out ofone object and pass them into another, just pass the entire object. Now if thereceiving object uses a polymorphic message to get at the information it needs

    then the method will work with any kind of object that supports the message.

    Let's make that more concrete by looking at an example. Let's create a shortmodule called logger that contains two classes. The first logs activity in a file.

    This logger will have a single method log() which has a "loggable object" as a

    parameter. The other class in our module is a Loggable class that can be

    inherited by other classes to work with the logger. It looks like this:

    # File: logger.py## Create Loggable and Logger classes for logging activities# of objects############

    class Loggable:def activity(self):

    return "This needs to be overridden locally"

    class Logger:def __init__(self, logfilename = "logger.dat"):

    self._log = open(logf ilename,"a")

    def log(self, loggedObj):self._log.write(loggedObj.activity() + ' \n')

    def __del__(self):self._log.close()

    Note that we have provided a destructormethod (__del__) to close the filewhen the logger object is deleted or garbage collected. This is another "magic

    method" in Python (as shown by the double '_' characters) similar in many

    ways to __init__()

    Also notice that we've called the log attribute _log with a '_' character in frontof the name. This is another common naming convention in Python, like usingcapitalized words for class names. A single underscore indicates that the

    attribute is not intended to be accessed directly, but only via the methods of

    the class.

  • 8/7/2019 tgsn 2 oop

    52/64

    Now before we can use our module we will create a new module which

    defines loggable versions of our bank account classes:

    # File: loggablebankaccount.py#

    # Extend Bank account classes to work with logger module.###############################

    import bankaccount, logger

    class LoggableBankAccount( bankaccount.BankAccount, logger.Loggable):def activity(self):

    return "Account balance = %d" % self.checkBalance()

    class LoggableInterestAccount(bankaccount.InterestAccount,logger.Loggable):

    def activity(self):return "Account balance = %d" % self.checkBalance()

    class LoggableChargingAccount(bankaccount.ChargingAccount,

    logger.Loggable):def activity(self):

    return "Account balance = %d" % self.checkBalance()

    Notice we are using a feature called multiple inheritance, where we inherit notone but two parent classes. This isn't strictly needed in Python since we could

    just have added an activity() method to our original classes and achieved the

    same effect but in statically typed OOP languages such as Java or C++ thistechnique would be necessary so I will show you the technique here for future

    reference.

    The sharp eyed amongst you may have noticed that the activity() method inall three classes is identical. That means we could save ourselves some typing

    by creating an intermediate type of loggable account class that inheritsLoggable and only has an activity method. We can then create our three

    different loggable account types by inheriting from that new class as wel l as

    from the vanilla Loggable class. Like this:

    class LoggableAccount(logger.Loggable):def activity(self):

    return "Account balance = %d" % self.checkBalance()

    class LoggableBankAccount(bankaccount.BankAccount, LoggableAccount):

    pass

    class LoggableInterestAccount(bankaccount.InterestAccount,LoggableAccount):

    pass

    class LoggableChargingAccount(bankaccount.ChargingAccount,LoggableAccount):

    pass

  • 8/7/2019 tgsn 2 oop

    53/64

    It doesn't save a lot of code but it does mean we only have one method

    definition to test and maintain instead of three identical methods. This type ofprogramming where we introduce a superclass with shared functionality is

    sometimes called mixin programmingand the minimal class is called a mixin

    class. It is a common outcome of this style that the final class definitions have

    little or no body but a long list of inherited classes, just as we see here. It's alsoquite common that mixin classes do not themselves inherit from anything,

    although in this case we did. In essence it's just a way of adding a commonmethod (or set of methods) to a class or set of classes via the power of

    inheritance. (The term mixin originates in the world of ice cream parlours

    where different flavours of ice cream are added (or mixed i