In the first case, there is an exact goal for the software capabilities,
and we work on the software until that target is met.
In the second case, we have a set of goals and qualities, but a fixed time
frame.
We attain as many of the goals and as high quality as possible in the
allotted time.
A rough breakdown of the time spent on small projects is likely to
look something like this:
Throughout the course we will be studying ways to effectively identify
and record product requirements, carry out design and implementation,
conduct appropriate verification and validation, and adequately plan and
document software.
Many of the techniques discussed will be "overkill" for small projects.
In this opening section, we will discuss some of the common design ideas
that should be followed even on the smallest of programs.
Hopefully the reader can extract other appropriate ideas from the
balance of the notes. One of the key requirements in software engineering
is the ability to judge what is appropriate and effective for the task at
hand,
and this is particularly true for small individual projects - where what is
"appropriate and effective" depends very much upon the skills, abilities, and
traits of the individual.
It is probably also worth keeping multiple versions of the source code:
often you will introduce bugs or other problems into the code, and having a
copy
of a previous version to back up to can save a great deal of time and effort
"rediscovering" the right way to do things.
What are the overhead costs? (including development and support staff
salary, pension plans, health insurance,
heating, lighting, office space costs, etc etc)
In this section there are three main topics we'll consider:
- Measuring software projects
- Preparing work schedules
- Cost estimation
Measuring software projects
When describing progress to upper management or to clients,
or when trying to decide how effective your chosen software development
process is, it is usually desirable to have some facts on which
to base your analysis.
In this section we look at some of the information you can gather
about a project, specifically
-
- maintainability metrics: how much effort is being expended
on maintenance, and how quickly are problems being addressed?
- productivity metrics: how much work is being successfully done
by each team member, or by the team as a whole?
- complexity and completion metrics: how large is the project, how
complex is it, and how much of the project has been completed to date?
Each of these topics is considered in more detail below.
WHATEVER YOU ARE MEASURING, BE SURE YOU HAVE A SOUND REASON
FOR GATHERING THE DATA AND FOR ANALYZING THE DATA ONCE YOU HAVE IT
Don't force your team to spend hours every week supplying you
with all sorts of detailed numbers that you have no real use for.
A good guideline is that your team members shouldn't need to spend
more than 15 minutes a week supplying you with the data you need
to monitor the project.
- Estimate project elements (cost, schedules, etc) early in a project
- Track the actual levels as the project progresses
- Refine your estimates and your estimation techniques based
on the results you observe
When you do decide to gather data, make sure it is clearly specified
who is to gather/supply the data, when and to whom the data is to be
provided, and precisely what data is to be gathered (possibly providing
a very clear form to be filled in?)
Some of the useful metrics include:
- Cost metrics: obviously every project has a bottom line,
and information should be tracked throughout the project. Each staff member
may be asked to record the hours they spend (weekly, biweekly, or monthly)
on the following activities:
- Development activities:
- By activity (specifications, design, implementation, testing)
- Maintenance activities:
- By maintenance type (corrective, enhancement, adaptive)
- By activity (isolation, design, implementation, testing)
- Error/reliability metrics: these metrics should indicate
how frequently errors are being found, what their nature is, and how quickly
they are being addressed
The data gathered could include
- The date the error was found and the date corrected
- A classification of the error (interface, initialization,
data, control, etc) and its source (specifications, design, implementation,
previous maintenance, etc)
- The total effort required to isolate the error
- The total effort required to correct the error
- Project dynamics metrics: these try to capture
the changes which occur over the software life cycle, in terms of
- changes to requirements
- modifications of baseline code
- additions to baseline code
- changes in predicted project characteristics: e.g. at each stage
record what the predictions were for the final project size, cost,
and completion dates.
- Productivity metrics: individual/team
productivity is most commonly measured as either
lines of code per month, or the number of function features produced per
month,
with the assumption
that all related documentation and other tasks are appropriately produced
in addition to the code.
The function points are usually counts based on external inputs and outputs,
user interactions, etc
Productivity varies widely between programmers depending on skill, experience,
the programming language used, and the nature of the project,
and anywhere from 100 to 1000 lines per month might be perfectly normal
Plotting the total size of source code (or documentation) on a weekly basis
can give a greater understanding of how your process is functioning,
as can plotting the rate at which existing source code is modified
- Product quality metrics: it is possible to try to
measure product quality in a number of different ways, here are
a couple of suggestions
- Readability may be measured by the average length of
identifiers, or the ratio of comments and whitespace to code
- Complexity for each routine, R, let FI be the number of
functions which call R, let FO be the number of routines
that R calls, let P be the number of parameters for R, and let L
be the length of R, then define complexity(R) = L * (FI + FO + P)
Alternatively, measure the complexity as the total number of possible
execution paths through the source code, the average nesting depth
of instructions, or simply the total length of the code
- Maintainability maintainability may be seen as a function
of all of the factors listed above
When looking at your development organization as a whole,
across multiple projects,
here are some typical questions and the data that might help
answer them:
- What are the cost characteristics of software in my organization?
Look at the number of hours and/or dollars spent in each area:
design, coding, test, maintenance, documentation, and management
Look at the same hours/dollars, but compare the numbers per line of code.
- What are the reliability characteristics of software in
my organization?
Look at the number of errors found during development, and the
number found during maintenance. Possibly subdivide the errors into classes
by their origin (e.g. data, computation, initialization, control, interfaces)
Look at the number of errors found in the specifications (again possibly
subdividing them)
Look at the pass/fail rates for your projects at the testing milestones
- How does the size of the software project relate to the time and
cost?
Look at the total number of lines of code, pages of documentation,
total dollar cost, total staff hours, and calendar duration of the project.
You might find some interesting results: for instance,
it might well be the case
that coding takes a significantly higher percentage of the project's
total measured effort (by hours)
than it takes in calendar time, simply because other stages require a
higher degree of interaction with other groups (clients etc), and hence
encounter delays and conflicts.
Preparing work schedules
In preparing work schedules, the manager must choose a
life cycle model, populate it with an appropriate set of
specific activities, add the training and other support activities
necessary for the project, and then consider which
activities depend upon which other activities.
Once the dependencies between activities have been considered,
the manager can begin to plan the order in which activities will be
carried out, and which activities can be carried out in parallel.
Estimates are then needed for the resources required by each activity,
and the time required for the activity. Once all this information has
been formulated, the estimated schedule can be laid out and tentative
milestone dates can be established.
Cost estimation
There are several common methods for up-front estimation of the
cost of a project:
- Algorithmic estimations (see COCOMO below) - base the estimate on some
software metric and historical cost information
- Expert judgement: a panel of experts is consulted, the panel is
responsible for
deriving an estimate based on their experience
- Analogy: the cost is based on analogies with comperable projects in
the same application domain
- Available resources: the cost is based on the time and personel available
(assumes all objectives can be met, and any extra time is dedicated to
enhancements)
- Pricing to win: the cost is based on the funds the customer has
available,
not on the software functionality
We'll consider one of the algorithmic estimation techniques in
detail: COCOMO:
- First, projects are divided into one of three categories:
- Simple projects: well understood applications developed by small teams
- Intermediate projects: more complex projects where team members may have
limited experience of related systems
- Complex projects where the software is part of a strongly coupled
collection of
hardware, software, regulations, and operational procedures
- Then the difficulty of different aspects of the project is judged,
each aspect is assigned a rating between 0.7 and 1.66, where 1 is "average".
The aspects chosen can cover things like
- speed and size constraints
- reliability constraints
- associated data base size
- platform stability
- team experience and ability level
- level of tool support
- development constraints (oo, formal ver. etc)
- Then the size of the program is estimated in terms of thousands of
lines of code (KDSI)
- Finally the person-months of project effort required is estimated as
follows:
- Simple projects: 2.4 * (KDSI)1.05 * Aspect multipliers
- Intermediate projects: 3.0 * (KDSI)1.12 * Aspect multipliers
- Embedded projects: 3.6 * (KDSI)1.20 * Aspect multipliers
- Example: suppose we have an intermediate project which we expect to
require about 35000 lines of code, and we decide that the
platform stability is better than normal (multiplier 0.8), the team
experience is slightly better
than normal (0.9), but the reliability constraints are strict (multiplier 1.4)
Then the person months of effort required are estimated as
3.0 * (35)1.12 * 0.8 * 0.9 * 1.4 = 162.16 months
- After a series of projects, an organization or manager might tailor
the assorted
constants to refine the estimation accuracy
Software design processes
Typical stages of design:
- Problem understanding: look at the problem (as stated in the
requirements/specifications)
from different angles to discover the design requirements.
- Identify one or more solutions: evaluate the possible solutions and
choose the most appropriate (appropriateness depends on many factors,
including the designer's experience and available resources)
- Describe solution abstractions: use graphical, formal or other
descriptive notations to describe the components of the design.
- Typically this involves
- Common design phases:
- Architectural design: identify the subsystems and their
interrelationships
- Abstract specification: specify the subsystems - the services they
provide,
and the constraints they must operate under
- Interface design: describe subsystem interfaces
- Component design: decompose subsystems into components,
and fix the interfaces for each component
- Data structure design: specify where and how to store data
- Algorithm design: determine the algorithms needed to provide each service
- These phases naturally result in a hierarchical design with more
abstract ideas towards the top and more concrete ones towards the
bottom.
- Top-down design
- In principle: start at the uppermost components in the hierarchy
and work down level by level.
- In practice large systems design is almost never purely top-down. Some
branches are fully designed before others are begun. The order is
usually determined by designers reusing experience or components
during the design process.
- Often a structured design approach will require
modelling the system using each of:
- A data flow model (to describe the data transformations)
- An entity relation model (to describe the logical data structures)
- A structural model (to show the relationship between system components:
e.g. a structure chart)
- An inheritance model (for OO designs)
- Design description
- Graphical notations: used to display component relationships.
- Program design languages: based on programming languages but with
more flexibility to represent abstract concepts. A more formal
version of pseudo code.
- Informal text: natural language description.
- All of these are usually used in the design for a single system.
- Design strategies
- Function-oriented design: system is designed from a functional
viewpoint. System state is centralised and shared between the
functions operating on that state. That is, we focus on the set of
tasks that must be performed and how they interrelate.
Possible function oriented view of a compiler
source program
|
SCAN SOURCE
|
tokens
|
BUILD SYMBOL TABLE
/ \
symbol tokens
table /
\ /
ANALYZE
/ \
OUTPUT GENERATE
ERRORS CODE
| |
error object
messages code
- Object-oriented design: system is viewed as a collection of
interacting objects. System state is decentralised and each object
manages its own state. Objects may be instances of an object class
and communicate by sending messages to each other.
Possible object-oriented view of a compiler
SOURCE
PROGRAM
|
| scan
|
TOKEN
STREAM
/ |
add/ |
/ |
SYMBOL |check
TABLE |
\ |
get\ |
\|
GRAMMAR
/ \
build/ \print
/ \
SYNTAX ERROR
TREE MESSAGES
|
|generate
|
ABSTRACT____________OBJECT
CODE generate CODE
- Mixed-strategy design
- Although it is sometimes suggested that one approach to design is
superior, in practice, an object-oriented approach and a
function-oriented approach to design are complementary.
- Good software engineers select the most appropriate approach for
whatever subsystem is being designed.
Design quality
- An elusive concept; there is no objective way of measuring the
"goodness" of a design.
- May be most efficient, most maintainable, most reliable etc.
- Usually maintainability is very important so we talk about the
attributes that affect that.
- Applies equally to function-oriented and object-oriented design
strategies.
Cohesion
- A measure of how well a component fits together.
- A component should implement a single logical entity or
function.
- Cohesion is a desirable design component attribute because when a
change has to be made it is likely to be localised in a single
cohesive component
- There are different aspects or levels of cohesion one might consider:
- Coincidental cohesion - parts of a component are not truly related,
but have simply been bundled into a single component
- Logical association - components that perform similar functions
are bundled into a single component
- Temporal cohesion - bundling all components which are activated
at a single time (e.g. startup)
- Procedural cohesion - bundling components which make up a single
control sequence
- Communication cohesion - bundling components which operate on the
same input data or produce common output data
- Sequential cohesion - bundling components where the output from one
component serves as input to the other
- Functional cohesion - each part of the component is necessary for the
execution of a single function
- Object cohesion - each operation provides functionality which allows
the attributes
of the object to be modified, inspected, or used as the basis for service
provision
Coupling
- Related to cohesion; a measure of the strength of
inter-connections between program units.
- Loose coupling is desirable since it means changes to one component are
unlikely to affect other components.
- Shared variables or control information exchange leads to tight
coupling.
- Loose coupling can be achieved by state decentralisation (as in
objects) and component communication via parameters or message
passing.
Understandability
- cohesion: can a component be understood on its own?
- naming: are meaningful names used?
- documentation: is the design well-documented?
- complexity: are complex algorithms used?
Adaptability
- its components are loosely related
- it is well documented and documentation is up to date
- there is an obvious correspondence between design levels
- each component is a self-contained entity (tightly cohesive)
Architectural design
- Architectural design frequently comes (in whole or in part)
during the specifications stage as an aid in structuring and organizing
the specifications
- There is no one generally-accepted method for carrying out
architectural design - it is largely based on skill, experience,
and intuitive appraisals of the nature of the project
- The key objectives in an architectural design are to
- identify the decomposition of the overal system into principal
subsystems (independant software units),
- identify the communication necessary between subsystems,
- identify the control relationships between subsystems,
- and possibly to decompose the subsystems into modules
(The distinction between modules and subsystems in this context
being that subsystems are largely independent of one another,
while modules within a subsystem might rely heavily on one another)
- Even independent subsystems are likely to require shared
access to certain data, there are two main ways of achieving this:
- A repository model: having one shared central database
- A message passing system: each subsystem has its own database,
and data is shared between subsystems by message passing
- In addition to data, we must also model how control is handled between
subsystems,
and again there are two main categories:
- centralized control: one subsystem has responsibility for controlling
the activation/deactivation of the other subsystems
- event-based control: in which each subsystem responds independently
to externally-generated events
- If the subsystems are divided into modules, there are again multiple ways
of modelling the decomposition:
- Object-oriented models - decomposition into a set of communicating
objects
- Data-flow models - decomposition into functional modules, each performing
certain transformations on data within the subsystem
- After a first attempt at deriving the program structure,
it may be worth reviewing and revizing the structure:
- try to limit both fan-out and fan-in of modules throughout
the structure (fan-out being the number of other routines called by
a given routine, and fan-in being the number of other routines that
can call a given routine)
- review the module interfaces, trying to reduce reduncancy,
improve consistency, and reduce complexity
Architectural styles, and attribute-based styles
An architectural "style" for a software design reflects
- the types of components in the design
- how the components interact,
- the interactions of control and data in the design
The key assumption: there are small number (say 15-20) of general
styles that, in practical experience, describe the majority
of successful software designs.
Here we discuss work at the
Software Engineering Institute of CMU - attempts to
categorize
- a set of core architectural design styles, and
- methodologies for choosing an appropriate style for a project
based on the desired attributes of that project
- methodoligies for carrying out the analysis
and design process, tailored for the chosen design style
and targetted attributes
The goal is to have an experience-based (tried and true) set of
guidelines that suggest the class of design to use. E.g. (oversimplified)
"Use the pipe and filter style when reuse is desired and performance
is not a top priority".
See the SEI website on
Attribute-based architecture styles.
Even ignoring the SEI work, however, the notion of
architectural design styles is important
for software engineers:
- a vast amount of software development is carried out on
product lines, rather than completely seperate products:
e.g. control software for printers: there may be only minor differences
in the control software for models q3091 and q3092, slightly more substantial
differences between the q3xxx series and the q2xxx series, etc etc
Given a good architectural design, changes should only be required
at the detailed design level - hence allowing substantial reuse of
previous design efforts and expertise
- many companies become specialized in developing certain types
of systems, even if not in the same product families
- many software engineers become specialized in developing certain
types of systems
In any of these cases, the most appropriate
software architectural styles are likely to be the same across many of
the different specific systems implemented
Architectural design decisions are generally finalized very early
in the design (or even analysis) process, so we need as much help as
possible in making those decisions safely.
Key goal:
Allow safe selection of an architecture appropriate for the type of system
to be designed, based on the desired attributes of that system
(e.g. in terms of availability, performance, modifiability).
Developing a "handbook" of architectural styles is necessarily
an anecdotal exercise: the quality of styles is based on the
experience and judgement of the design community
SEI's attempts are to produce a generic set, for the software
community as a whole, similar efforts might be more effective
on a domain-specific (or company-specific) basis
As an example, an SEI ABAS is defined to include the following (see
"Attribute-based Architecture Styles", by Klein et al)
- A description of the design problem the style is meant to address,
including the key constraints
and attributes of interest (availability, performance, modifiability, etc)
- The key attribute/constraint measures
- A description of the architectural style in terms of major
components, connections, properties, and data/control interactions
- A description of the parameters relevant to the architectural style
(see discussion below)
- An analysis of how the quality attributes as modeled are
formally related to the elements of the architectural style
The SEI effort is further attempting to allow prediction and analysis
of design effectiveness based on the relevant attributes/parameters
Quality attribute models and parameters
As an example we'll consider the information relevant to a performance
attribute:
Performance
- the two common metrics for performance are
- throughput: the number of transactions per time unit
- latency: the response time to an event
- the attribute parameters for performance might include
- resource characteristics: which will depend on whether the resource
whose performance being measured is a CPU (processor speed) or a network
(bandwidth), etc
- scheduling policy: again depending on the resource (cpu scheduling,
bus arbitration, queuing policy, etc)
- resource usage (priority mechanism, preemptable? etc)
- In order to predict our design behavior, we will need to consider
how the system responds to stimuli, which can in turn be characterized, e.g.:
- periodic - events occur at fixed intervals
- stochastic - events follow a known probability distribution
- sporadic - events occur randomly, but possibly with a known lower
bound on the time interval between events
Using the parameters and characteristics specified, and models appropriate
for the policies given, we should be able to predict behavior, and possibly
use this to enhance our knowledge of system requirements and specifications
e.g. knowing the rate of occurence of events, the resource characteristics
and policies, can we calculate the required response times for specific
components?
The ABAS approach is very much a work-in-progress, but may well represent
the future of early-stage software design
Design methodology
The two main forms of design are function-oriented (structural) design
and object-oriented design.
Many large organizations (e.g. see the NASA website)
are finding that, in general, systems designed under
function-oriented approaches are less reusable and less maintainable
than those designed under object-oriented approaches.
However, function-oriented methods may be more applicable where
the product is one-of-a-kind (hence reuse is not a major factor) and
is expected to have a relatively short life span (hence long term
maintenance is not a major factor)
Historically the major drawback with object-oriented approaches has been
a lack of OO experience on the part of many designers, and a lack
of OO support tools.
Function-oriented design
- Goals: to explain how a software design can be represented as a set of
interacting functions
with shared information, and to introduce suitable notation for the
representation
- Essentially "what comes naturally" given most programmers' backgrounds.
- The main drawback is that some functions can change the values of
shared data
in a manner not expected by other functions - either through undocumented
side effects
or unclear interfaces/documentation
- To a degree, the confusion can be minimized by minimizing the sharing
of data
(global variables) - restricting the impact of a function to the data
passed to it
(and hence controlled) by the calling function
- Vs. Object-oriented design
- Not an "either-or" type of decision,
each method has advantages for different systems or even
different parts of the same system.
- many transaction-based systems or data processing systems are
naturally function-oriented, in that current actions depend only on the
processing of independent records (e.g. the processing of one user's
transactions
at an ATM is largely independent of the processing of the next user's)
- object oriented approaches are useful in contexts where there is
a clear delineation between objects - we can change the state of one object
without needing to reference other objects
- distributed objects may execute sequentially or in parallel,
and decisions on parallelism need not be taken at the early design stages
- A general functional design process:
- identify the data transformations and high level functions
- decompose the high level functions into hierarchy of subfunctions
- describe the operation and interface of each system entity
- document the flow of control in the system
- System modelling: as with the requirements/specifications, accurate
representation of the system is a key feature of the design.
There are a number of common modelling tools which can be invoked to represent
different aspects of the system:
- Data flow diagrams model the movement of and operations on data,
but give no representation of heirarchical structure of functions
or control flow
- Structure charts give a heirarchical view of the functions in the
program,
and also of the data flow between functions,
but still give no strong control information
- Program description languages (PDLs) give control documentation
(again like specialised pseudo-code) but are a less useful visual aid for
demonstrating the relationships between functions
- Data flow diagrams
- describe a series of operations (functions) with the data they
specifically use or generate
- as discussed earlier in the context of requirements, but here they
portray
the actual flow of data items in the program rather than the conceptual flow.
- DFDs show how the input data is functionally transformed into output
data.
Example: a DFD for a report generator:
functions are in capitol letters,
data is in lowercase,
data stores are enclosed in square brackets,
user I/O is enclosed in round brackets
(name)--GET WAREHOUSE NAME
|
|name
|
[database]--GET PRODUCTS LIST
|
|list
|
SORT LIST PRODUCE ORDERING LIST
| / \
|sorted list /inlist \order report
| / \
[inventory]--GET STOCK-------------SORT BY VOLUME IN/OUT PRODUCE FINAL
REPORT
stocklist \ / |
\outlist /billing |
\ / |
PRODUCE BILLING LIST PRINT
REPORT
|
(report)
- Structure charts
- an alternative representation, showing some of the information from
data flow diagrams
(missing some sequencing information)
plus giving the top-down design heirarchy of the functions
- Data flow diagrams are flat structures.
- Structure charts show the hierarchical structure of a system.
- Possible representation:
- Show each functional component as a rectangle,
with a layered approach (e.g. main controlling routine at top,
the routines it uses at the next level, the routines they use
at the next level, etc.)
- Show the presence of function calls using an arrow from
the calling routine to the called routine, annotate the arrow
with parameters passed/returned.
- Show I/O (e.g. databases, generated reports, user input, etc) in circles.
- Structure chart derivation
- Usually it is fairly straightforward to come up with data flow
diagram.
- Then one must derive a structure chart from the data flow diagram.
- Aim: to derive design units which are highly cohesive and loosely
coupled.
- General rule that has been observed to work well in practice: try
to restrict your components so that they are responsible for one of
four types of data flow:
- Input: component accepts input and passes it to a higher level
component, perhaps in a modified form.
- Output: component accepts data from a higher-level component,
transforms it and passes it to a lower-level component as output.
- Transform: component accepts data from a higher-level component,
transforms it and passes it back the first component.
- Coordinate: component is responsible for coordinating the actions
of other components.
- As usual a data dictionary can be used to augment the data flow
diagrams and structure charts.
- Detailed design
- Each structure chart entity should have an associated detailed
design.
- For data entities this may simply be the data dictionary entry
- For functions, this may be a form containing things such as:
- The function name
- The parameter list and types
- The sources of the parameters
- The names of any global data used
- Any assumptions or requirements on the data
and current state of the program
- The names of any calling functions
- The names of any called functions
- A plain text black box description
- Pseudo-code for the function's algorithm
For each case where a reference is made to another entity from the
structure chart,
some identifier should be present allowing the reader to look up the detailed
design entry on that entity
Object-oriented design
- Goal: Explain how software designs can be represented as a set of
interacting objects, and introduce models with describe the OO design process.
- Designing systems as sets of interacting objects, each a
self-contained entity.
- Each object maintains its own state, and offers a set of services to
other objects.
- Shared data areas are eliminated; objects communicate by message
passing (e.g. parameters).
- Objects encapsulate state and representation information.
- Advantages?
- Easier maintenance since objects may be understood as standalone
entities.
- Objects are appropriate reusable components.
- For some systems, there may be an obvious mapping from real world
entities to system objects. E.g., for simulation applications.
- Objects can execute sequentially or in parallel (given appropriate
system and language support), so one need not make early decisions on
parallelism.
- Object-oriented design (OOD) should not be confused with
object-oriented programming (OOP).
- OOD is a design strategy independent of implementation language.
- OOP relies on an object-oriented language like C++ which has
features specially designed for OOP.
- Object
- An entity which has a state (whose representation is hidden from
other objects) and a defined set of operations which operate on that
state.
- The state is represented as a set of object attributes (also
called slots, fields, data members).
- The operations associated with the object provide services to
other objects.
- Example: stacks as objects
- the stack would have a set of publicly-usable services, e.g.:
create, destroy, pop, push, top, isempty, isfull
- the stack data structures would be private, i.e. accessible only
to the stack, not outside objects, e.g.: an integer constant to
represent the maximum number of elements the stack can hold,
an integer variable to indicate how many elements are currently in
the stack, and an array to hold the data elements in the stack
- the stack may also have some private services - routines the
stack can use but other objects cannot, e.g.: reset-stack, peek-at-element,
count-items
- Object communication
- Conceptually, objects communicate by message passing.
- Messages consist of the identity of the target object, the name of
the requested operation and any other information needed to perform
the operation.
- An example of communication with the stack might be to specify
the stack you want to talk to (i.e. give its variable name),
specify the routine you want to use (e.g. push), and give any other
data needed (i.e. the data to push on the stack):
mystack.push(17)
- Messages are often implemented as procedure calls (name ==
procedure name, information = parameter list).
- Examples of messages (using C++ syntax)
- Tell a list
L1
to print itself.
L1.print ();
- this specifies L1 is the name of the specific list we want to work
with,
and print() is a public service routine associated with lists (in this case
we'll apply the print routine specifically to list L1)
- Insert an item
i
into L1
.
L1.insert (i);
- Ask an array
A
to return its maximum element.
result = A.max ();
- Ask a calendar object
C
to return the data a week
after a given date.
aDay = C.nextWeek (anotherDay);
- Object-oriented design involves a number of typical phases:
- identify objects, their attributes, and their operations
- identify which objects are sub-parts of other objects
(see inheritance below)
- show (e.g. graphically) which objects actually use which services
provided by which other objects
- specify the object interfaces
- design the individual objects
- Classes
- A class describes a type of object, generally giving
- a type name for the object (e.g. list, stack, etc)
- interfaces for the public services offered
- the private, internal data structures used
- the private services available in the object
(i.e. routines the object can call but other outside objects cannot)
- the type(s) of object(s) this class inherits attributes from
(e.g. this class may be a fancy list, inheriting some attributes
of simple lists)
- Usually each object is an instance of a class.
- All the objects of a given class have the same operations (i.e.,
understand the same messages) and the same attributes, but each
object has its own set of attributes.
- A class is a bit like a type in languages like Pascal but more
powerful because you can define the operations.
- E.g., from previous examples could have classes List, Array and
Calendar of which L1, A and C, respectively, are instances.
- Example of a stack class definition from C++
class Stack {
public:
// public services
stack(); // construct
~stack(); // destroy
void push(data d);
void pop();
data top();
Boolean isempty();
Boolean isfull();
private:
// internal data structures
int num_items;
data mystack[20];
// private routines
void print_error(int errtype);
}
// example of routine implementation
void Stack::push(data d)
{
if (isfull()) print_error(1);
else mystack[num_items++] = d;
}
void main()
{
Stack S1;
S1.push(13);
S1.pop();
etc
- Inheritance
- Classes may be arranged in an inheritance hierarchy where classes
can be derived from other classes.
- The inheriting class is called a subclass of the class from it is
inheriting (its superclass).
- A subclass inherits the attributes and operations of its
superclass and may add new operations or attributes of its own or
override operations of the superclass.
- Example of inheritance (recall algebraic specification).
- Assume a class List implementing lists of data. An OrderedList
class might be a subclass of List where the elements are forced to be
stored in order.
- Similarly, a Stack class might force additions and deletions to
take place only at one end of the list. Similarly for Queue.
- Each of these subclasses can use the basic implementation of
Lists but might need to redefine some operations (for OrderedList) or
add some new ones (e.g., Push and Pop for Stack).
- Example: Employee class and subclasses
Employee: name, address, salary, manager
Manager: includes employee attributes
plus dept, staff, grade
Programmer: includes employee attributes
plusproject, languages
Project Manager: includes manager attributes
plus project, appointment date
Software project manager: includes programmer AND
project manager attributes, but has to
rename "project" in either the programmer
or project manager superclass since there
is a conflict
- Change in an object-oriented system
- Encapsulating data within objects means that representations can
change without affecting other objects.
- Improves maintenance and system evolution.
- Inheritance means that new capabilities can be built on old ones
and probably do not require any change in old ones. E.g., consider
adding a dual-directional queue.
- Object identification
Design documentation example
Just for the sake of example, let's say the sections
of the complete design document are as follows: