Posted Saturday, April 18, 2015 in Software Engineering
When giving instructions there are several ways to go about such a task. For example, a potential home-buyer may wish to determine the best method of getting the output of a house by taking different approaches. Should the homeowner give explicit instructions to a contractor to build the house or request a house based on a set of criteria? Each method gives the homeowner several benefits according to what the homeowner desires to dedicate to effort and cost. In a similar fashion, programmers have various methods to give instructions such that a program may be compiled. These methods are described as programming paradigms and define the structure and practice in writing code much in the same way that there are several methods of giving instruction to output a house. Where effort and cost matter to the homeowner, maintainability and performance matter to the developer. These programming paradigms will be discussed in detail to assess the benefit of each.
On the example of a homeowner attempting to output a house, the paradigms of imperative and declarative programming come into play. Imperative programming can be thought of the method of giving step by step instructions to a contractor to build a house of the homeowner's choosing. The same step-by-step instruction can be found in imperative programming where calls to methods and variable assignments modify the program state to produce the output (Sebesta, 2012). For example, the homeowner may instruct a foundation builder to take a variable amount of concrete to build a base of an appropriate length and width. Similarly, the programmer may create methods that accept parameters and output results and call these methods in a step-by-step fashion to create a program using the iterative paradigm. Iterative programming gives the developer minute control over the behavior and process of the program's execution. Writing programs for common processor architectures are often executed by operating systems in machine code which is an imperative programming language. The developer may achieve greater program performance by using a programming language that is closer to what is being executed against the processor.
Not all homeowners want to be involved in the smallest of details even with the potential for a higher quality product. Instead, the homeowner may wish to take the declarative approach to outputting a house. In the declarative paradigm the output is described and given to an interpreter to execute the steps to produce the result. A homeowner may achieve a similar approach by describing their criteria to a real estate agent in which they'll perform the steps necessary to find the house. Programming languages such as SQL use declarative programming for concise database queries while abstracting the details of how such queries are executed. A developer may wish to mix the two programming languages for tasks that benefit from each approach. For example, an imperative language may be used for calculations while a declarative language is used to retrieve data from a database.
Using declarative programming may require that the developer has a deep understanding of how code is interpreted. For example, a homeowner may ask a real estate agent for a listing of all blue houses in Phoenix, AZ. The real estate agent, being a bit literal in the request, may look for every blue house in the United States then reduce that listing to houses in Phoenix. The same inefficiencies may also occur with the imperative approach when layers of abstraction hide how code is interpreted. For example, a homeowner may instruct a contractor to build a foundation by giving minimal instructions for the contractor to interpret on their own. This layer of abstraction may lead to the contractor performing better than the homeowner could instruct themselves or possibly lead to a highly inefficient process. For both imperative and declarative programming, a developer's practical knowledge can largely influence the quality and efficiency of a program.
Let's go back to the homeowner and assume they have chosen the iterative approach to build their own home. Astronomer Carl Sagan (1980) once said, “if you wish to make an apple pie from scratch, you must first invent the universe” (p. 218). The point is that there is an incredible potential level of detail to describing and creating things in the real world. The homeowner may run into an extremely literal contractor in which things such as nails and procedures such as hammering require the most detailed of explanations. The homeowner may wish to take the object-oriented approach to the iterative paradigm. In object-oriented programming, everything is defined as an object which may have methods and properties. For the homeowner, this can mean defining the nail and hammer objects and giving the hammer object a hammer nail method. The homeowner may not give complex instructions faster by first defining the objects required and then defining the execution steps by creating objects (e.g. 5 hammers, 200 nails, 300 boards, etc…) then calling methods on those objects (e.g. create wall, install door, etc…).
To start with writing understandable object-oriented code a developer may look to the encapsulation programming pattern. With encapsulation, all the methods and properties that belong to an object are defined in that object (Sebesta, 2012). An object-oriented programming language may sometimes still be used without encapsulation and may induce a considerable use of duplicate code. For example, a hammer object may be defined but instead of giving it the hammer nail method the method is implemented over and over in the main execution flow of the program. To prevent such inefficiencies it's up to the developer to properly develop object-oriented programs with encapsulation in mind.
Another potential benefit to object-oriented programming is the use of inheritance. With the homeowner example, several types of hammers may need to be defined such as sledgehammers, claw hammers and so forth. The homeowner may use inheritance to define a generic hammer object that contains the properties and methods that all other hammers may inherit such as having a handle and hammer head property. The alternate to inheritance would be defining possible duplicate methods and properties in similar hammers which would lead to unnecessary duplicate code.
The last object-oriented pattern to be touched on is polymorphism which concerns the declaration of methods. Polymorphism allows the creation of methods with the same name but with different parameters. There can be several benefits to the use of polymorphism, one of which is backward compatible changes to a method. For example, the homeowner may find that the hammer nail method needs to now accept a new type of nail. Instead of creating a differently named hammer nail method that could confuse the behavior of the hammer object a new hammer method can be created with the same name but with a new parameter.
The homeowner may find themselves in a predicament after using an object-oriented imperative approach. During the construction of the house, the homeowner may find that several complex calculations are needed to determine dimensions for the house, calculate interest rates on loaned equipment, and create estimates on amperage for parts of the house. Using imperative programming such calculations require algorithms to be translated to step-by-step instructions that can be quite lengthy. There may also be difficulties in managing and creating types for the variables in each algorithm. Instead of the imperative approach, the homeowner may wish to use the functional approach. The functional programming paradigm allows declarative programming to be used to define functions similar to mathematical functions (Sebesta, 2012).
There are several benefits to using functional programming compared to imperative such as being able to display more actions in fewer lines of code (Barendregt, Monzonetto, & Plasmeijer, 2013). Additionally, as imperative programming can benefit from object-oriented programming so can functional and declarative programming.
Several programming languages such as certain versions Java, C#, and C++ support imperative, functional, and object-oriented programming (Franklin, Gyori, Lahoda, & Dig, 2013). For example, Java 8 allows for the use of lamda expressions which can be used to define declarative and functional expressions which can be useful for data queries and calculations. A developer may find that such languages do not easily fit into one or two programming paradigms, but are a hybrid of several. Such multi-purposed languages may support a variety of programming paradigms to extend their utility into a multitude of scenarios.
Complex programs can have many changes in state that require the triggering of events to notify when things happen. Events allow decoupled parts of a program or separate threads to register for an event and to be notified when the event is triggered. For example, a graphical user interface (GUI) may have a field for a user to type into. This input field may have an event that fires every time a key is pressed while the field has focus. The program or operating system will run an event listener that listens for the key press event and contacts an event dispatcher when triggered which then calls event handlers that are registered to the event (Sebasta, 2012). The event handler for the pressed key may perform some action such as incrementing a counter for number of keys pressed in the GUI.
Reactive programming takes event handling a bit further by allowing for properties and objects to change state when their definition is changed. That is, in reactive programming an observable property (a) may be assigned the sum of two other properties (b and c). When the b property changes the a property will be automatically updated typically through the automatic registration and triggering of events by the reactive language (Bainomugisha, Carreton, Cutsem, Mostinckx, & Meuter, 2013).
The basics of exception handling is to interrupt the flow of a program and pass the flow to an exception handler. For a programming language like C that doesn't have builtin exception handling this means defining what constitutes an exception and jumping to a different part of the stack which can be quite complex. Therefore, handling an exception can be seen as rewinding the execution of a program when a given condition is met then jumping to different part of the code where the exception is handled (Sebesta, 2012). In several programming languages, this behavior is defined syntactically as with the try and catch keywords. Code defined after try may jump to the code defined after the catch keyword if an exception condition is met. Managed applications that have a just-in-time compiler may also globally handle errors and gracefully exit the application while providing the exception details to the operating system.
There are several paradigms and patterns useful to conceptualizing programming. In practice, programming paradigms help developers understand the complexity of a program and help developers choose the best approach to developing a program. Many modern multi-purposed languages take a hybrid approach and implement several programming paradigms. The developer is allowed to take several approaches to developing a program within a single programming language. Some languages and implementations take the core concepts a step further such as with reactive programming and advanced exception handling. Just as the homeowner may benefit from knowing the approaches to purchasing a house, the developer can benefit greatly from knowledge of programming paradigms.