Functional versus object-oriented development

The functional approach is a relatively well-established method of software development. In essence, a functional approach amounts to decomposing a task into the steps that must be taken to complete it. For instance, when describing a compiler in a functional manner, a first decomposition can be made as follows: lexical scan -> parser -> code generation Each of these steps can be regarded as transforming its input to a representation suitable for the next step, finally resulting in an executable program. See slide 3-functional. Obviously, the method of functional decomposition lends itself to a structured approach, since each step (in the example above) may be decomposed into a number of smaller steps. Graphically, a functional decomposition may be pictured as a tree with the elementary steps as its leaves. A functional decomposition may be augmented by a data flow diagram that depicts the nature of the data transformations effected by each step. Basically, a data flow diagram is a graph with nodes labeled by the (intermediate) data representations and edges that are labeled by the functional components identified in the function decomposition tree.

Functional development methods

Disadvantages


slide: Functional development methods

In contrast, the components that result from an object-oriented approach do not represent actual steps toward a solution, but rather may be seen as (abstract) entities that contribute to the solution on the basis of their assigned responsibilities. Correspondingly, the flow of control is far less regular than in a functional decomposition. The equivalent of the combined data flow and functional decomposition diagram for an object-oriented system is a message-passing diagram depicting the actual interaction between objects. This diagram is a graph with objects as nodes, and edges labeled by requests to execute a method. In general, this graph will be too complex to be represented pictorially. It is possible, however, to divide up an object interaction graph into meaningful related pieces, which is the approach taken for Fusion, for example. See section Fusion. Moreover, the complexity of object-interaction is compensated for by the opportunities for a more tight definition of the semantics, that is the responsibilities and obligations, of each (object) component participating in the computation. The major drawback of a functional approach, as observed in  [Booch86], is the absence of data abstraction and information hiding. Typically, the functional approach is not concerned with issues of data representation, although it does allow additional procedural abstractions to access and modify the data. However, the functional approach does not in itself provide any support for a tight coupling between the functional components and data representations or the procedural abstractions that are used for the purpose of information hiding. Hence, changes in the data representation or the structure of the algorithm may ripple across the system in an unforeseen manner.

Object-oriented development -- design for change

Advantages

Support for concurrency {\em -- active objects}


slide: Support for change

In contrast, object-oriented development is neither data nor procedure oriented, but strives to encapsulate both data and procedures acting on these data in objects. Instead of a detailed description of the steps that need to be taken to solve a problem or complete a task, the required functionality is distributed among the objects that may later be assembled (in a rather straightforward way) to actually perform the task. Perhaps the most important advantage of encapsulating data and procedures, from the perspective of design, is that (in many cases) changes may be kept strictly local to the classes defining the relevant objects. See slide 3-OO. Ideally, object-oriented design is design for change, in other words the development of an architecture that is adaptable to changing requirements or changes in the underlying software and hardware support. However, to achieve this adaptability generally requires a non-trivial effort during design, in order to find stable abstractions with a well-defined semantics that determines their role in the system. Obvious potential spin-offs from the effort to design for change are improved maintainability of the system and a better understanding of its architectural structure. Another benefit of an object-oriented approach, mentioned in  [Booch86], is the possibility of introducing concurrency in a later stage. Starting from a functional decomposition, the introduction of concurrency would generally incur a total restructuring of the algorithm. With an object-oriented approach, however, concurrency may be introduced by employing active objects. In principle, clients of an object need not be aware of whether the object is passive, that is merely responding to messages, or active, which means that the object in addition to answering messages has (autonomous) behavior of its own. However, employing active objects imposes a number of additional constraints, on an implementation level but also on a design level. We will explore these issues in chapter \ref{Distribution and concurrency}.