using abstraction to manage complexity

27
Using Abstraction to Manage Complexity Abstraction: procedural abstraction & data abstraction. Procedural abstraction=> function development should separate the concern of what is to be achieved by a function from the details of how it is to be achieved. Example (standard library function): Calculate square root of x sqrt(x); In this example of procedural abstraction, we see that the function sqrt(x) will calculate the square root of x. sqrt(x) is a standard library function.

Upload: taran

Post on 21-Jan-2016

91 views

Category:

Documents


0 download

DESCRIPTION

Using Abstraction to Manage Complexity. Abstraction: procedural abstraction & data abstraction. Procedural abstraction => function development should separate the concern of what is to be achieved by a function from the details of how it is to be achieved. - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: Using Abstraction to Manage Complexity

Using Abstraction to Manage Complexity• Abstraction: procedural abstraction & data abstraction.• Procedural abstraction=> function development should

separate the concern of what is to be achieved by a function from the details of how it is to be achieved.

Example (standard library function): Calculate square root of x sqrt(x);• In this example of procedural abstraction, we see that the

function sqrt(x) will calculate the square root of x. sqrt(x) is a standard library function.

Page 2: Using Abstraction to Manage Complexity

Procedural Abstraction

• Reuse of these existing functions mean that we never have to concern ourselves with the detail of how this task is accomplished.

• Powerful libraries of functions is very important in reducing the complexity of large systems.

Page 3: Using Abstraction to Manage Complexity

Procedural Abstraction• Examples (user-defined functions): complex_t multiply_complex (complex_t num1,

complex_t num2); complex_t add_complex(complex_t num1, complex_t

num2);• In this example, the two functions are identified in this

first level of procedural abstraction.• We can assign these two functions to separate

members of a program development team.• Once the purpose and parameter lists of each function

are spelled out, neither developer will have any need to be concerned about the details of how the other member carries out the assignment task.

Page 4: Using Abstraction to Manage Complexity

Data Abstraction• Data abstraction => separation of the logical view of a

data object (what is stored) from the physical view (how the information is stored).

• Data abstraction is very important for breaking down a large problem into manageable chunks.

• When we apply data abstraction to a complex problem, we initially specify the data objects involved and the operations to be performed on these data objects.

• We do not need to know much about how the data objects will be represented and stored in memory.

• We can describe what information is stored in the data object (logical view) without knowing its actual representation in memory (physical view).

Page 5: Using Abstraction to Manage Complexity

Example of Data Abstraction

• One example of data abstraction is the use of the data type double, which is an abstraction of real numbers.

• The computer hardware limits the range of real numbers that can be represented.

• Different computers use variety of representation schemes for type double.

• We can use the data type double and its associated operators (+,-, *, /, =, ==, <, and so on) without being concerned with the details of its implementation.

Page 6: Using Abstraction to Manage Complexity

Information Hiding• At the top level of the design, designer focuses on how

to use a data object and its operators.• At the lower levels of design, the designer works out the

implementation details.• In this way, the designer can hierarchically break down

a large problem, controlling and reducing its overall complexity.

• The higher level module can access the data object only through its operators.

• This limitation allows the designer to change his mind at a later time.

Page 7: Using Abstraction to Manage Complexity

Information Hiding

• If the higher-level modules reference a data object only through its operators, a change in the data object’s representation will require no change in a higher level module.

• The process of protecting the implementation details of a lower-level module from direct access by a higher-level module is called information hiding.

Page 8: Using Abstraction to Manage Complexity

Reusable Code• Reusable code => code that can be reused in many

different applications, preferably without having to be modified or recompiled.

• One way to achieve this in C++ is to encapsulate a data object together with its operators in a personal library.

• Then we can use the #include preprocessor directive to give functions (in a file) access to this library.

• Example: One encapsulated object that we are all familiar with is an aspirin.

• We all know what aspirin does (relieves pain and reduces fever) when activated through the standard interface (swallowing).

• Only producers and prescribers care how aspirin does it.

Page 9: Using Abstraction to Manage Complexity

Encapsulation

• Encapsulate => packaging as a unit data object and its operators.

• By applying the principles of procedural and data abstraction, we can package the “bitter” details of a complex problem’s solution in equally neat, easy-to-use capsules.

Page 10: Using Abstraction to Manage Complexity

Abstract data type (ADT)

• Data abstraction comes into play when we need a data type that is not built into the programming language.

• We can define the new data type as an abstract data type (ADT), concentrating only on its logical properties and deferring the details of its implementation.

• ADT => A data type whose properties (domain and operations) are independent of any particular implementation.

• The user of ADT needs to know only the specifications, not the implementations.

Page 11: Using Abstraction to Manage Complexity

Personal Libraries• The standard libraries are not extensive enough to handle

every programming need.• Often we find that programs are more useful in a context

than one for which it was originally written.• We can copy the code of this function into other programs.

This allows reuse of code. However it is not that simple.• The Easiest solution is to use the C++ preprocessor

directive #include to make available personal libraries like the standard libraries.

• Since C++ permits source code files to be compiled separately and then linked prior to loading and execution, we can provide our personal libraries as object files.

• Programs using our personal libraries need not first compile the functions in them.

Page 12: Using Abstraction to Manage Complexity

Header Files• To create a personal library, we first have to make a header

file.• Header file => a text file containing all the information

about a library needed by the compiler when compiling a program that uses the facilities defined in the library.

• It defines the interface between a library and any program that uses the library.

• Typically contents of a header file include: - a block comment summarizing the library’s purpose - #define directives naming constant macros. - Type definitions. - Block comments stating the purpose of each library

function and declarations of the form extern prototype

Page 13: Using Abstraction to Manage Complexity

Header Files• Extern prototype The use of the keyword extern in a function declaration

notifies the compiler that the function’s definition will be provided to the linker.

• If we use angular brackets (<>), as in #include <stdio.h> it indicates to the preprocessor that

the header file is to be found in a system directory.• If we use quotes around the header file name, as in #include “planet.h” it indicates the preprocessor that

the header file belongs to the programmer.• Header files have extension .h

Page 14: Using Abstraction to Manage Complexity

Implementation Files

• The header (interface) file and the implementation files are two essential files in a personal library.

• Implementation file => file containing the C++ source code of all the library functions and any other information needed for compilation of these functions.

• Header file describes what the functions of the library do, the implementation file will show how the functions do it.

Page 15: Using Abstraction to Manage Complexity

Implementation Files• The elements of an implementation file are: - a block comment summarizing the library’s

purpose - #include directives for the library’s header

file and for other libraries used by the functions in this library.

- #define directives naming constant macros used only inside this library.

- type definitions used only inside this library. - function definitions including the usual

comments.

Page 16: Using Abstraction to Manage Complexity

Using a personal Library

• To use a personal library, you must complete these steps: Creation

C1: Create a header file containing the interface information for a program needing the library.

C2: Create an implementation file containing the code of the library functions and other details of the implementation that are hidden from the user program.

C3: Compile the implementation file. This step must be repeated any time either the header file or the implementation file is revised.

Page 17: Using Abstraction to Manage Complexity

Using a personal Library• To use a personal library, you must complete these steps:

Use• U1: Include the library’s header file in the user

program through an #include directive.• U2: After compiling the user program, include both its

object file and the object file created in C3 in the command that activates the linker.

Page 18: Using Abstraction to Manage Complexity

Storage Classes Storage Classes

• auto => Formal parameters and local variables of functions are variables that are automatically allocated on the stack when a function is called and automatically deallocated when the function returns.

• The scope (the program region in which the name is visible) of these names extends from the point of declaration to the end of the function in which the declaration appears.

auto extern static registerGlobal variable

Page 19: Using Abstraction to Manage Complexity

extern

• If the names of the functions are of storage class extern, they will be available to the linker.

Example: extern prototype - This statement does not create a function of

storage class extern. - It notifies the compiler that such a function exists

and that the linker will know where to find it.

Page 20: Using Abstraction to Manage Complexity

Global Variables• It is possible to declare variables at the top level.• The scope of this variable name extends from the point

of declaration to the end of the source file, except in functions where the same name is declared as a formal or local variable.

• Such a variable can be made accessible to all functions in a program and is therefore sometimes called a global variable.

int global_var_x;

void afun(int n) …..

Page 21: Using Abstraction to Manage Complexity

Global Variables

• The unrestricted access to a variable is generally regarded as detrimental to program’s readability and maintainability.

• However, in some applications, global variables are unavoidable.

• One context in which a global variable can be used without reducing program readability is when the global represents a constant.

• We can use const type qualifier to notify the compiler that the program can look at, but not modify, these locations.

Page 22: Using Abstraction to Manage Complexity

static Storage Class• If we place the static keyword at the beginning of a local

variable declaration, it will change the way the variable is allocated.

int fun_frag(int n) { static int once = 0; int many = 0; …….. }• many (storage class auto) is allocated space on the stack

each time the function fun_frag is called. For every call many is initialized to 0. Every time fun_frag returns, many is deallocated.

Page 23: Using Abstraction to Manage Complexity

static Storage Class

• Static variable is allocated and initialized one time, prior to program execution.

• It remains allocated until the entire program terminates.• One situation in which the use of a static local variable

does not degrade readability is in function main, since a return from this function terminates the program.

• On a system that allocates a relatively small run-time stack, one might wish to declare large arrays as static variables in function main.

• Then these arrays will not use up stack space.

Page 24: Using Abstraction to Manage Complexity

register Storage Class

• Storage class register is closely related to storage class auto and can be applied only to local variables and parameters.

• If we place the word register before a variable, it simply alerts the compiler to the fact that this memory cell will be referenced more often than most.

• The program would run faster if a register, a special high-speed memory location inside the CPU, is used for the variable.

static double matrix[50[40]; register int row, col;

Page 25: Using Abstraction to Manage Complexity

Modifying Functions for Inclusion in a Library• A library function should be as general as possible.• An unnecessary restriction can quickly negate the

function’s usefulness in another context.• All constants used should be examined to see whether

they could be replaced by input parameters.• Any restrictions on the library function’s parameters

should be carefully defined.• Our functions should deal with an error - either by returning an error code or - by displaying an error message and returning a

value that should permit continued execution.

Page 26: Using Abstraction to Manage Complexity

Modifying Functions for Inclusion in a Library• In some situations, it is better not to continue processing.• For example, manipulation of a large two-dimensional

array is very time-consuming, and is pointless to spend time on a matrix that contains erroneous data.

• Another example: If a factorial function is called with a negative number, there is no way it can return a valid answer. So, we should display an error message and terminate execution of the program.

• C++’s exit function from the standard library can be used in these situations to terminate the execution prematurely.

• Calling exit with the argument 1 indicates that some failure led to the exit. Argument 0 indicates successful function completion.

Page 27: Using Abstraction to Manage Complexity

Conditional Compilation• C++’s preprocessor recognizes commands that allow the

user to select parts of a program to be compiled and parts to be omitted. This can be helpful in some situations:

- One can build in debugging cout calls when writing a function and then include these statements in the compiled program only when they are needed.

- Inclusion of header files can be done conditionally. - Conditional compilation is also helpful in the design

of a system for use on a variety of computers. Conditional compilation allows one to compile only the code appropriate for the current computer.