CSci 555: Functional Programming
Object Oriented Software Development

H. Conrad Cunningham

14 February 2019

Copyright (C) 2017, 2018, 2019, H. Conrad Cunningham
Professor of Computer and Information Science
University of Mississippi
211 Weir Hall
P.O. Box 1848
University, MS 38677
(662) 915-5358

Advisory: The HTML version of this document may require use of a browser that supports the display of MathML. A good choice as of February 2019 is a recent version of Firefox from Mozilla.

Object-Oriented Software Development

Introduction

TODO: Add

Simplified Software Development Lifecycle

For the purposes of this discussion, assume that software development proceeds through the four lifecycle phases shown in the following diagram.

    .__________________.
    |                  |
    |     Analysis     |<--:
    |__________________|   |
             |             |
             |             ^
    .________V_________.   |
    |                  |-->:
    |      Design      |   |
    |__________________|<--.
             |             |
             |             ^
    .________V_________.   |
    |                  |-->:
    |  Implementation  |   |
    |__________________|<--:
             |             |
             |             |
    .________V_________.   ^
    |                  |   |
    |   Maintenance    |-->:
    |__________________|

Analysis

In the analysis phase we move from a vague description of the problem to be solved to a precise and unambiguous requirements specification.

The requirements specification might be a precise, but informal description written in careful natural language text and diagrams. Alternatively, the specification might be a formal description written in a mathematically precise language. Or the requirements specification might be something in between these.

The requirements specification should be:

Design

In the design phase, we move from a requirements specification to a design specification. The design specification gives the system structure. The design tasks are to:

Implementation

In the implementation phase, we move from a design specification to a tested executable system. The implementation tasks are to:

Maintenance

In the maintenance phase, we move from a complete “working” system to a modified system. The maintenance tasks are to:

We observe the following.

We conclude that we should:

“Programming in the Small” and “Programming in the Large”

The type of software development projects familiar to most students can be described as programming in the small. Such projects have the following attributes:

Programming in the large characterizes projects with the following attributes:

The techniques of object-oriented design and programming are useful in both programming in the small and programming in the large situations. However, some of the techniques are best appreciated when the difficulties of programming in the large are understood.

Object Orientation

In contemporary practice, most software engineers approach the design of programs from an object-oriented perspective.

Key idea (notion?) in object orientation:
The real world can be accurately described as a collection of objects that interact.

This approach is based on the following assumptions:

  1. Describing large, complex systems as interacting objects make them easier to understand than otherwise.

  2. The behaviors of real world objects tend to be stable over time.

  3. The different kinds of real world objects tend to be stable. (That is, new kinds appear slowly; old kinds disappear slowly.)

  4. Changes tend to be localized to a few objects.

Assumption 1 simplifies analysis, design, and implementation–makes them more reliable.

Assumptions 2 and 3 support reuse of code, prototyping, and incremental development.

Assumption 4 supports design for change.

The object-oriented approach:

An Object Model

See Chapter 3 of the 2018 textbook Exploring Languages with Interpreters and Functional Programming.

Requirements Analysis

The task of the analysis phase is to define the problem and write a clear, consistent, complete, and precise description of the system to be constructed. The analysts must elicit the requirements for the system from the clients and communicate the requirements to the system designers, who have the task of designing the new system.

The analysts must determine the scope of the system: What must the system accomplish? And, perhaps just as importantly, what behaviors are clearly outside the system in its environment?

In approaching the requirements analysis for a new system, the analysts should do the following.

Good analysts are good detectives! Among the mass of detail, the analysts must find the clues that allow them to solve the mystery of what system the client needs. It is important that analysts keep a complete record of the information they have gathered and their reasoning on any “conclusions” they reach about that information.

The result of the analysts’ work is a document called the requirements specification. Typically this will be a natural language (e.g., English) document. The writers of this document should use great care, using the language in a clear, consistent, and precise manner. The writers are establishing the vocabulary for communication with the clients and among the designers, implementers, and testers of the system.

Note: The accompanying slide set Using CRC Cards discusses the processes of object-oriented requirements (this subsection) and design (next subsection) using methods built around Class-Responsibility-Collaboration (CRC) cards.

Object-Oriented Design

The goals of the design phase are to:

The actual design process is iterative. Elaboration of a class may lead to identification of additional classes or changes to those already identified.

Classes should be crisply defined. It should be as easy as possible to determine what class an object belongs in. Coming up with a good categorization is often difficult in complex situations.

The responsibilities of a class are of two types:

  1. to carry out some action – that is, an operation.

  2. to hold some key information – that is, an attribute (part of the state).

Responsibilities should be defined precisely. Ambiguity and imprecision will likely cause problems later in the design and implementation.

The collaboration relationships among classes should not be excessively complex. A collaboration between two classes means that one class depends upon the other in some way. A change in one class may necessitate a change in the other.

The information gathered in the design phase is the basis for the implementation. A good design makes the implementation easier and faster and the resulting product more reliable.

The basic methods for identifying classes and responsibilities are as follows.

Finding Classes and Responsibilities

As an example, consider the following telephone book example:

You are to build an automated telephone book system for a university. The telephone book should contain entries for each person in the university community–student, professor, and staff member. Users of the directory can look up entries. In addition, the administrator of the telephone book can, after supplying a password, insert new entries, delete existing entries, modify existing entries, print the telephone book to a printer, and print a listing of all students or of all faculty. The entries in a listing are to be arranged in alphabetical order by family name.

To develop an object-oriented design model for this application, as designers we can carry out the following steps:

  1. Identify the candidate classes.

    Begin by listing the nouns, noun phrases, and pronoun antecedents from the requirements specification, changing all the plurals to singular.

    In the telephone book example, these include:

    you (the designer), automated telephone book system, university, telephone book, entry, person, university community, student, professor, staff member, employee, user, directory, administrator, password, printer, listing, faculty, alphabetical order, family name

    Other classes may be be implicit in the specification or may emerge as the design of individual classes proceed and the designer’s knowledge of the application domain increases. For example, the design may need classes corresponding to:

    Several, but not necessarily all, of these will become classes in our design.

  2. Identify the candidate responsibilities.

    Begin by listing the verbs from the specification. Listing the objects of transitive verbs may also be helpful.

    In the telephone book example, these include:

    build, contain (entry), look (up entry), supply (password), insert (new entry), delete (existing entry), modify (existing entry, print (telephone book), print (all students), print (all employees), set (telephone number field), get (telephone number field), compare (entries), to be arranged

    Transitive verbs become operations that respond to inputs to the object. All of the above verbs except “to be arranged” are transitive and, hence, will likely give rise to operations.

    Intransitive verbs typically specify attributes of classes. For example, “to be arranged in alphabetical order” in the above example denotes an ordering property of the entries in a listing. It is not an operation. Of course, in some cases, this kind of attribute might require a “sort” operation.

  3. Eliminate classes and responsibilities that are outside of the system scope.

    At this point, we may want to narrow the list of candidate classes by quickly dividing them into three categories based on their relevance to the system scope:

    1. critical classes (i.e., the “winners”), which we will definitely continue to consider. These are items that directly relate to the main entities of the application.

      In the telephone book example, these might include telephone book, entry, student, faculty, staff member, and so forth.

    2. irrelevant candidates (i.e., the “losers”), which we will definitely eliminate at this point. These are items that are clearly outside the system scope.

      In the telephone book example, these might include university which we do not need to explicitly model in this application) and printer (which is handled by other software/hardware outside of this application).

    3. undecided candidates (i.e., the “maybes”), which we will review further for categorization. These are items that we may not be able to categorize without first clarifying the system boundaries and definition

      In the telephone book example, these might include the automated telephone book system, an item that definitely will be built (it is the whole system) but for which we may not need an explicit class.

  4. Combine synonym classes and synonym operations into single abstractions.

    For example, automated telephone book, telephone book, and directory can be combined into a single PhoneBook class.

    Similarly, entry and person can be combined into a single Person class.

    In addition, all the print operations probably could be combined into a single print operation with the differing functionalities specified by the parameter.

  5. Distinguish attributes from classes.

    Some candidate classes may turn out to represent information held by other classes instead of being classes themselves.

    A candidate class may be an attribute (i.e., a responsibility) of another class rather than itself a class if:

    For example, we might choose to make the entity name an immutable string and make it an attribute of a class Person rather than have it a separate class.

  6. Be wary of adjectives.

    Adjectives modify nouns. In our technique, nouns give rise to classes or objects.

    Prepositional phrases may modify nouns in the requirements specification. Such phrases may lead to subclasses, objects, or instances as described above for adjectives.

    Adverbs may modify adjectives in the requirements specification. They provide other information about the adjective that should be considered in the analysis. For example, a reference to a “brilliantly red truck” might signal the need for another attribute, subclass, or instance that is different from a plain “red truck” or from a “dull red truck”.

  7. Consider architectural design issues.

  8. Associate the operations with the appropriate classes.

    For example, in the telephone book example associate lookup, insert, delete, and modify entry operations with the PhoneBook class, associate compare, setPhoneNumber and getPhoneNumber with the Person class, etc.

    Sometimes there might be a choice on where to associate an operation. For example, the “insert person into telephone book” operation could be a operation of Person (that is, insert this person into the argument telephone book) or a operation of PhoneBook (that is, insert the argument person in this telephone book).

    Which is better? Associating the operation with Person would require the Person class to have access to the internal representation details of the PhoneBook. However, associating the operation with PhoneBook would not require the PhoneBook to know about the internal details of the Person class–except to be able to compare two entries. Thus making insert an operation of PhoneBook and compare an operation of Person would best maintain the encapsulation of data.

Principle:
An object cannot manipulate the internal data of another object directly; it must use an operation of that object.

During design we should not be concerned with the minute details of the implementation. However, it is appropriate to consider whether there is a “reasonable” implementation. In fact, it is better to make sure there are two different possible implementations to ensure flexibility and adaptability.

It is good to have more than one person involved in a design. A second designer can review the first’s work more objectively and ask difficult questions – and vice versa.

Coming Up with Names

The selection of names for classes and operations is an important task. Give it sufficient time and thought.

Finding Relationships Among Classes

Two classes in a design may be related in one of several ways. The three relationships that are common are:

Use Relationship

Class A uses B when:

If objects of A can carry out all operations without any awareness of B, then A does not use B.

From the telephone book example, PhoneBook uses Person objects (that is, it inserts, deletes, modifies, etc., the entries). Person objects do not use PhoneBook objects.

If class A uses class B, then a change to class B (particularly to its public interface) may necessitate changes to class A. The following principle will make modification of a design and implementation easier.

Principle:
Minimize the coupling between classes (that is, the number of classes used by a class.)
Aggregation Relationship

Class A uses class B for aggregation if objects of A contain objects of B.

Note: Object A contains (has an) object B if A has an instance variable that somehow designates object B–a Java reference to B, the index of B, the key of B, etc.

Aggregation is a one-to-N relationship; one object might contain several others.

Note: Aggregation is a special case of the use relationship. If asked to identify the aggregation and use relationships, identify an aggregation relationship as such rather than as a use relationship.

For example, objects of the Person class in the telephone book example contain (has) name, address, and phoneNumber objects.

The Pascal record and C struct are aggregations; they contain other data fields. They are not, however, objects.

Inheritance Relationship

Class D inherits from class B if all objects of D are also objects of B.

As noted previously,

In the telephone book example, we design Professor to inherit from Employee; similarly, we design Staff to inherit from Employee. We make Employee itself inherit from Person; similarly, we design Student to inherit from Person.

Inheritance can lead to powerful and extensible designs. However, use and aggregation are more common than inheritance.

Object-Oriented Implementation

The outputs of the object-oriented design phase include:

The purpose of the implementation phase is to code, test, and integrate the classes into the desired application system.

Object-oriented development involves an integration of testing with implementation:

Note: If we seek to evolve a prototype into a full program, we should never hesitate to reopen the analysis or design if new insight is discovered.

Conclusion

TODO: Add

Exercises

TODO: Add

Acknowledgements

In Fall 2016 and Spring 2017, I adapted these lecture notes from my previous notes on this topic. The material from my previous notes is based, in part, on the presentations in books [Bellin 1997], [Budd 2000], [Fishwick 1995], [Horstmann 1995], [Thomas 1995], and [Wirfs-Brock 1990]

I wrote the first version of these notes in Spring 1996 for my CSci 490 Special Topics course on object-oriented design and programming using C++. I expanded the notes for the first Java-based version of CSci 211 (then titled File Systems) during Fall 1996.

I revised the notes incrementally over the next decade for use in my Java-based courses on object-orientation, software architecture, and computer simulation.

I partially revised the notes for use in my Scala-based classes beginning in Fall 2008 and Lua-based classes beginning in Fall 2013.

In Fall 2016, I separated out the discussion of the Object Model and merged it into the discussion of programming paradigms from my Functional Programming notes. In the 2016-18 period, this evolved into Chapters 2 and 3 of the 2018 version of my textbook Exploring Languages with Interpreters and Functional Programming.

In 2017, 2018, and 2019, I reformatted these notes to use Pandoc Markdown, improved the presentation in places, and linked it into the other documents.

I maintain these notes as text in Pandoc’s dialect of Markdown using embedded LaTeX markup for the mathematical formulas and then translate the notes to HTML, PDF, and other forms as needed.

References

[Bellin 1997] : David Bellin and Susan Suchman Simone. The CRC Card BookUsUs, Addison Wesley, 1997.

[Budd 2000]
Timothy Budd. Understanding Object-Oriented Programming with Java, Updated Edition, Addison Wesley, 2000.
[Fishwick 1995]
Paul A. Fishwick. Simulation Model Design and Execution: Building Digital Worlds, Addison Wesley, 1995.
[Gamma 1995]
Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides. Design Patterns: Elements of Reusable Object-Oriented Software, Addison Wesley, 1995.
[Horstmann 1995]
Cay S. Horstmann. Mastering Object-Oriented Design in C++, Wiley, 1995. (Chapters 3-6 on “Implementing Classes”, “Interfaces”, “Object-Oriented Design”, and “Invariants” especially influenced my views on object-oriented design and programming.)
[Thomas 1995]
Pete Thomas and Ray Weedom. Object-Oriented Programming in Eiffel, Addison-Wesley, Workingham, UK, 1995.
[Wirfs-Brock 1990]
Rebecca Wirfs-Brock, Brian Wilkerson, and Lauren Wiener. Designing Object-Oriented Software, Prentice-Hall, 1990.
[Wirfs-Brock 2003]
Rebecca Wirfs-Brock and Alan McKean. Object Design: Roles, Responsibilities, and Collaborations, Addison-Wesley, 2003.

Terms and Concepts

TODO: Add