Chapter I.2. Different Methods for Writing Programs

The goal of computer science is to find the best ways to write a program. The reality of computer science is that nobody really knows what they're doing, so they're making up stuff as they go along and pretending that there's a scientific basis for everything they do. The fact that multimillion dollar programming projects routinely fall behind schedule and sometimes never work at all pretty much shows that computer programming is still less a science than an art.

Despite these problems, computer scientists are always searching for ways to make programming easier, faster, and more reliable by constantly developing

  • Better tools

  • Better programming languages

  • Better techniques for writing programs

Just as a carpenter doesn't build a house with rusty saws and a broken hammer, computer scientists are always developing better tools to help them write, fix, and create programs. One of the first improvements computer scientists made was in developing faster compilers. Instead of waiting overnight to see if a program worked, programmers could use a fast compiler that could show them the results in seconds. Other tool improvements included editors that would show programmers the specific line where an error occurred and special programs (known as debuggers) for making sure that every part of a program worked correctly.

Another way to improve programmer efficiency involves creating better programming languages. Assembly language was easier to write and modify than machine language, and high-level languages are easier to write and modify than assembly language.

Computer scientists are constantly inventing new programming languages or improving existing ones. These improvements or new languages typically offer some feature that existing languages don't offer or solve certain types of problems that existing languages do poorly. For example, the C++ language improves upon the C language, whereas the Java language improves upon the C++ language.

Perhaps two of the biggest problems with programming involve writing a program from scratch and modifying an existing program. When you write a program from scratch, you want to write a working program quickly with as few problems as possible.

That's why programming languages include so many built-in commands. The idea is that the more built-in commands available, the fewer commands you'll need to use to write a program and the shorter and easier your program will be to write in the first place.

In addition, many programming languages include built-in error-checking features to keep you from writing a program that doesn't work. With some languages, it's possible to write commands that work perfectly, but can also crash the computer if you give those commands the wrong type of data.

Warning

In Book I, Chapter 3, you find out more about the features of different programming languages.

Half the battle of programming is writing a program that works. The second half is modifying that program later. When you need to modify an existing program, you must first understand how that existing program works and then you need to modify it without messing up the existing program commands.

To help you understand how a program works, many programming languages let you divide a large program into separate parts. The theory is that if one part of a program isn't working or needs to be modified, you can yank out part of the program, rewrite it, and then plug it back into the existing program, much like snapping Lego building blocks together.

Finally, all the best tools and the latest programming languages aren't going to help you unless you know how to use them correctly. That's why computer scientists are constantly developing new programming techniques that work no matter what tools or language you use.

Warning

In Book I, Chapter 4, you find out more about the different programming tools computer scientists have created to make programming easier, faster, and more reliable.

The rest of this chapter discusses programming techniques based on problems encountered by programmers working in the real world. Basically, computer scientists keep developing and refining programming techniques after they see what really works and what doesn't.

Spaghetti Programming without a Plan

In the early days of programming, most programs were fairly short and simple. A typical program might just calculate a mathematical equation, which to a computer, is just a little more challenging than adding two numbers together.

To write such small, single-task programs, programmers would typically start typing commands in their favorite programming language with little planning, just to write a program quickly.

Unfortunately, many programs aren't just written once and then used forever. If a program isn't working exactly right, or if the program needs to do something new that the original programmer didn't include, you must take an existing program and modify it.

Modifying an existing program sounds simple, but it's not. First, you must understand how the program works so you'll know exactly how to modify that program. If you try modifying a program without understanding how it works, there's a good chance you could wreck the program and keep it from working, much like ripping out cables from your car engine without knowing what you're really doing.

After you understand how a program works, the second step involves writing new commands into the existing program. Now, here's where the problem occurs. Take an existing program and modify it once. Now take that same program and modify it again. Now take that same program and modify it 20 more times, and what do you get? Most likely, you'll have a mish-mash collection of code that works, but isn't organized logically, as shown in Figure 2-1.

Modifying a program several times by yourself might not be so bad because you probably remember what you changed and why. But what happens if seven other programmers modify the same program seven different times and then none of them are around to help you understand what changes they made? If you guessed you'll wind up with a bigger mess than before, you're right.

Constantly modifying a program eventually creates an unorganized mess.

Figure I.2-1. Constantly modifying a program eventually creates an unorganized mess.

With constant modifications, a small, simple program can grow into a convoluted monstrosity that may work, but nobody quite understands how or why. Because the program consists of so many changes scattered throughout the code, trying to figure out how the program even works can get harder with each new modification.

With a simple program, the computer follows each command from start to finish, so it's easy to see how the program works. After a program gets modified multiple times, trying to follow the order of commands the computer follows can be like untangling spaghetti, hence the term spaghetti programming.

As programs kept getting bigger and more complicated, computer scientists realized that just letting programmers rush out to write or modify a program wasn't going to work any more. So that's when computer scientists created the first programming techniques to help programmers write programs that'd be easy to understand and modify later.

Planning Ahead with Structured Programming

The problem with programs created without any planning is that it inevitably leads to a mess. So the first step involves keeping a program organized right from the start.

The three parts of structured programming

To keep programs organized, structured programming teaches programmers that any program can be divided into three distinct parts:

  • Sequences

  • Branches

  • Loops

Sequences

Sequences are simply groups of commands that the computer follows one after another. Most simple programs just consist of a list of commands that the computer follows from start to finish, as shown in Figure 2-2.

Sequences consist of groups of commands that the computer follows one after another.

Figure I.2-2. Sequences consist of groups of commands that the computer follows one after another.

Branches

Branches consist of two or more groups of commands. At any given time, the computer may choose to follow one group of commands or another. Branches allow a program to make a decision based on a certain condition.

For example, at the end of most video games, the program asks you, "Do you want to play again (Yes or No)?" If you choose Yes, the program lets you play the video game again. If you choose No, the program stops running, as shown in Figure 2-3.

Branches let the computer choose which group of commands to run at any given time.

Figure I.2-3. Branches let the computer choose which group of commands to run at any given time.

A branch starts with a command that evaluates a condition (such as determining whether the user chose Yes or No) and then based on this answer, chooses which group of commands to follow next.

Loops

Sometimes you may want the computer to run the same commands over and over again. For example, a program might ask the user for a password. If the user types an invalid password, the program displays an error message and asks the user to type the password again.

If you wanted your program to ask the user for a password three times, you could write the same group of commands to ask for the password three times, but that'd be wasteful. Not only would this force you to type the same commands multiple times, but if you wanted to modify these commands, you'd have to modify them in three different locations as well. Loops are basically a shortcut to writing one or more commands multiple times.

A loop consists of two parts:

  • The group of commands that the loop repeats

  • A command that defines how many times the loop should run

By combining sequences, branches, and loops, you can design any program and understand how the program works at each step.

Dividing a program into sequences, branches, and loops can help you isolate and organize groups of related commands into discrete "chunks" of code. That way, you can yank out a chunk of code, modify it, and plug it back in without affecting the rest of the program.

Top-down programming

For small programs, organizing a program into sequences, branches, and loops works well. But the larger your program gets, the harder it can be to view and understand the whole thing. So a second feature of structured programming involves breaking a large program into smaller parts where each part performs one specific task. This is also known as top-down programming.

The idea behind top-down programming (as opposed to bottom-up programming) is that you design your program by identifying the main (top) task that you want your program to solve.

For example, if you wanted to write a program that could predict the next winning lottery numbers, that is a top design of your program. Of course, you can't just tell a computer, "Pick the next winning lottery numbers." You must divide this single (top) task into two or more smaller tasks.

One of these smaller tasks might be, "Identify the lottery numbers that tend to appear often." A second task might be, "Pick the six numbers that have appeared most often and display those as the potential future winning numbers."

The idea is that writing a large program may be tough, but writing a small program is easy. So if you keep dividing the tasks of your program into smaller and smaller parts, eventually you can write a small, simple program that can solve that task. Then you can paste these small programs together like building blocks, and you'll have a well-organized big program — theoretically.

Now if you need to modify part of the large program, just find the small program that needs changing, modify it, and plug it back into the larger program, and you've just updated the larger program.

Ideally, each small program should be small enough to fit on a single sheet of paper. This makes each small program easy to read, understand, and modify. When you divide a large program into smaller programs, each small program is a subprogram.

If you divide a program into multiple subprograms, you have two options for where to store your subprograms:

  • Store all of your subprograms in a single file.

    Warning

    This option is fine for small programs, but after you start dividing a program into multiple subprograms, trying to cram all of your subprograms into a single file is like trying to cram your entire wardrobe of shirts, pants, and underwear into your sock drawer. It's possible, but it makes finding anything later that much more difficult.

  • Store subprograms in separate files, as shown in Figure 2-4.

You can store subprograms in one big file or in separate files.

Figure I.2-4. You can store subprograms in one big file or in separate files.

Tip

Storing subprograms in separate files offers three huge advantages.

  • The fewer subprograms crammed into a single file, the easier it can be to find and modify any of them.

  • If you store subprograms in a separate file, you can copy that file (and any subprograms stored in that file) and then plug it into another program. In that way, you can create a library of useful subprograms and reuse them later.

  • By reusing subprograms that you've tested already to make sure they work properly, you can write more complicated programs in less time, simply because you're reusing subprograms and not writing everything from scratch.

Making User Interfaces with Event-Driven Programming

In the early days, using a program was fairly simple. After typing the command to run a particular program, that program might ask a question such as

What is your name?

At this point, you had no choice but to type a name, such as Joe Smith. After you typed in your name, the program might respond with

Hello, Joe Smith. What month were you born?

The moment you typed in a month, such as April, the program might respond:

What day were you born?

And so on. If you wanted to type your day of birth before your month of birth, you couldn't because the program controlled your options.

Not surprisingly, using a computer like this was frustrating to most people, so computer scientists soon invented something called a graphical user interface (abbreviated as GUI).

A GUI displays multiple options to the user in the form of pull-down menus, windows, buttons, and check boxes. Suddenly, instead of the computer dictating what the user could do at any given time, the user could tell the computer what to do at any given time, just by choosing one of many available commands.

Forcing each program to display menus and windows had two advantages for users:

  • It made using a computer much easier. Instead of having to type in commands, users could just click the command they wanted to use.

  • It's fairly easy to figure out how to use different types of programs. After you understand that you can choose the Print command in the File menu, you know how to print in any program whether it's a word processor, a database, or an image editing program.

Unfortunately, although pull-down menus made programs easy for users, they made writing programs much harder for the programmers:

  • Programmers had to write extra commands just to display all these fancy pull-down menus and windows. (Even worse, programmers had to make sure all those extra commands used to create pull-down menus and windows actually worked correctly.)

  • Programmers now had to write programs that could react to whatever command the user chose. Rather than present the user with options in a specific, predictable order, programs had to handle the unpredictable choices of the user.

To solve this dual problem of creating pull-down menus and knowing how to handle the different commands the user might choose at any given time, computer scientists developed event-driven programming.

In event-driven programming, an event is something that the user does, like clicking a pull-down menu or clicking a button displayed in a window. Event-driven programming simply focuses on displaying different commands on-screen and then handling these different events when they occur.

Event-driven programming divides programming into three distinct parts:

  • The user interface: The commands the user sees on-screen

  • The event handler: The part of your program that reacts to the commands the user chooses from the user interface

  • The actual program: The part of your program that actually does something useful, such as drawing pictures or predicting the winners of horse races

In the old days, creating a user interface essentially tripled your work:

  1. Write your program.

  2. Write commands to create a user interface.

  3. Write commands to make your user interface actually work.

Event-driven programming eliminates this problem. Instead of forcing you to write commands to display pull-down menus and windows on-screen, event-driven programming lets you visually design your user interface, such as the number, placement, and size of buttons.

After you've designed your user interface (without having to write a single command to do it), you can write short programs that respond to everything the user could possibly do, which is called an event. If the user clicks a pull-down menu, that's an event. If the user clicks a button in a window, that's a different event. When you write a small program to handle an event, the program is called an event handler.

Without event-driven programming, you'd be forced to write commands to create a user interface and more commands to make the user interface work. With event-driven programming, you just have to write commands to make your user interface work. The fewer commands you must write, the faster you can create a program and the easier the program will be to read, understand, and modify later.

Note

The most popular event-driven programming language is Visual Basic, although Microsoft has adopted event-driven programming for their Visual C#, Visual C++, and Visual J++ compilers as well. Other popular event-driven programming languages include REALbasic (www.realbasic.com) and Delphi (www.turboexplorer.com).

Warning

Event-driven programming doesn't replace structured programming; it supplements it. Structured programming techniques are useful for helping you write your program. Event-driven programming is useful for helping you design a user interface for your program.

Basically, event-driven programming divides programming into three distinct steps: designing the user interface, writing event handlers to make the user interface work, and writing the actual program.

Designing a user interface

The main advantage of event-driven programming is how quickly it allows you to design a user interface without writing a single command whatsoever. Instead of writing commands, you create a user interface using a two-step process:

  1. Visually draw your user interface on a window by choosing which user interface parts you want, such as buttons, check boxes, or menus, as shown in Figure 2-5.

    After you've drawn your user interface, you wind up with a generic user interface.

  2. Customize each part of your user interface by defining its appearance and behavior.

To customize part of a user interface, you must modify that user interface's properties. Each part of your user interface contains properties that define its appearance and behavior. For example, if you wanted a button to appear in color, you'd change that button's Color property. If you wanted to change the size of a button, you'd modify that button's Width or Height property, as shown in Figure 2-6.

Designing a user interface involves drawing what you want to appear on your program's user interface.

Figure I.2-5. Designing a user interface involves drawing what you want to appear on your program's user interface.

Properties define how each part of a user interface looks and behaves.

Figure I.2-6. Properties define how each part of a user interface looks and behaves.

With event-driven programming, designing a user interface involves drawing your user interface and then customizing it by changing its properties. After you've designed your user interface, it will appear to work but it won't actually do anything until you write an event handler.

Writing event handlers

The whole purpose of an event handler is to work as a middleman between your actual program and your program's user interface. To create an event handler, you need to identify the following:

  • A user interface item, such as a button or a check box

  • The event, such as a click of the mouse

The combination of a user interface item and a specific event uniquely defines an event handler, as shown in Figure 2-7.

An event handler tells the user interface how to behave when the user does something, such as click the mouse over a button.

Figure I.2-7. An event handler tells the user interface how to behave when the user does something, such as click the mouse over a button.

The user can do dozens of different possible events, but the most common events are clicking the mouse or moving the mouse pointer over an item. Event handlers typically do one of three things:

  • Identify what the user did, such as click a button

  • Retrieve information from the user interface, such as when the user types something in a text box

  • Display information to the user, such as displaying an error message

After you've written one or more event handlers for your user interface, you have a complete working user interface. Now you just have to attach this user interface to a working program.

Writing your program

Some people write their program first and then design a user interface around it. Other people design their user interface first and then write their program to work with it. The whole point of event-driven programming is to separate your program from your user interface so you can focus on making each part work individually.

Event-driven programming focuses mostly on designing a user interface and making it work, but does little to help you write your actual program. To write your program, you can use structured programming or object-oriented programming (or both, or neither).

After you've written your program, you "attach" the program to your user interface by writing event handlers. Event handlers "glue" your user interface to your actual program. With event-driven programming, you can be pretty sure that your user interface will always work perfectly. You just have to worry about errors in your main program.

Organizing a Program with Object-Oriented Programming

Structured programming helps you organize and divide your program into smaller, more manageable pieces. For small- to medium-sized programs, dividing a program into smaller programs is fine, but the larger your program gets, the more smaller programs you'll have to worry about. Eventually, computer scientists discovered that they needed another technique for dividing large programs into parts, so they called this new technique object-oriented programming (often abbreviated as OOP).

Object-oriented programming solves two glaring problems with structured programming: reusability and modeling.

Reusability means that you can collect smaller programs that work together, store them in a larger group called an object, and then plug those objects into different programs like Lego building blocks. Where structured programming encourages reusability by letting you reuse subprograms, object-oriented programming encourages reusability on a larger scale by letting you reuse objects (which contain multiple smaller programs). Reusing individual subprograms is like using bricks to build a house. Reusing objects is more like using pre-manufactured walls to build a house.

Modeling means that programming is more intuitive. One of the reasons why assembly language is so hard to understand is because manipulating data in the processor's registers has nothing to do with solving problems like adding two numbers together. Likewise, dividing a large program into smaller tasks, using structured programming, does nothing to help you understand the actual problem the program is trying to solve.

For example, suppose you had to write a program to land a rocket on the moon. This is how you might write this program using structured programming:

Land a rocket on the moon
    Launch rocket
    Guide rocket through space
    Find a landing area on the moon
    Put rocket down on landing area

So far, structured programming seems logical, but what happens when you keep dividing tasks into smaller tasks? Just focusing on the Guide rocket through space task, we might wind up with the following:

Guide rocket through space
   Get current coordinates
         Compare current coordinates with moon coordinates
   Adjust direction

Dividing the Adjust direction task into smaller tasks, we might get this:

Adjust direction
   Identify current speed and direction
         Determine angle needed to steer towards the moon
         Fire thrusters to change the angle of the rocket

Notice that the deeper you keep dividing tasks, the more removed you get from knowing what the main purpose of the program may be. Just by looking at the task Identify current speed and direction, you have no idea whether this task involves flying a rocket to the moon, driving a car down a road, or controlling a walking robot to an electric outlet to recharge its batteries.

The more you divide a larger task into smaller tasks, the harder it can be to understand what problem you're even trying to solve. This gets even worse when you start writing actual program commands.

The two parts of most programs are the commands that tell the computer what to do and the data that the program manipulates. So if you wrote a program to identify the current speed and direction of a rocket, the commands would tell the computer how to retrieve this information and the speed and direction would be the actual data the program uses.

Essentially, program commands are separate from the data that they manipulate. If one part of a program manipulates data incorrectly, the rest of the program winds up using that contaminated data and you, as the programmer, won't know which part of the program screwed up the data. This is like sitting in a baseball game, ordering a hot dog from a vendor, and having six people pass your hot dog down to you. When you see fingerprints all over your hot dog, can you tell which person touched your food?

Objects isolate data

Object-oriented programming avoids this problem by combining data and the commands that manipulate them into a single entity called (surprise!) an object. With object-oriented programming in the hot dog vendor analogy, instead of passing your hot dog to half a dozen other people, the hot dog vendor comes directly to your seat and hands you your hot dog. Now if you saw fingerprints all over your hot dog, you'd know that the fingerprints could only have come from the hot dog vendor.

Besides keeping data isolated from other parts of your program, object-oriented programming also helps you divide a large program into smaller ones. Although structured programming divides a large program into the tasks that need to be performed, object-oriented programming divides a large program into real-life objects.

So if you were designing a program to launch a rocket to the moon, object-oriented programming would let you divide the program into objects. One object might be the rocket, a second object might be the moon, and a third object might be the Earth.

You can also divide a large object into smaller ones. So the rocket object might be divided into an engine object and a guidance object. The engine object could be further divided into a fuel pump object, a nozzle object, and a fuel tank object.

Suppose you wrote a program to calculate a rocket's trajectory to the moon, and the engineers suddenly designed the rocket with a more powerful engine? With object-oriented programming, you could just yank the engine object out of your program, rewrite or replace it, and plug it back into the program again.

In structured programming, modifying the program to reflect a new rocket engine would mean finding the program commands that manipulate the data that represents the engine's thrust, and then making sure that new data gets fed into the program at the proper location and still works with any other program commands that also handle that same data. (If the explanation in this paragraph sounded confusing and convoluted to you, that just shows you the less-than-intuitive problem of modifying a structured program versus an object-oriented program.)

Objects simplify modifications

Besides organizing a large program into logical pieces, objects have another purpose — code reusability. Just as in high school, it was always easier to copy someone else's homework rather than do it yourself, so programmers find that it's easier to copy and reuse somebody else's program rather than write their own from scratch.

In structured programming, you could divide a large program into subprograms and then store those subprograms in a separate file. Now you could copy that file to reuse those subprograms in another program.

Copying subprograms makes programming easier, but here are two problems:

  • What if you copy a subprogram and then later find an error in that subprogram? Now you'll have to fix that subprogram in every copy. If you made 17 copies of a subprogram, you'll have to fix the same error 17 times in 17 different copies of the same subprogram.

  • What if you want to modify and improve a subprogram? Suppose you find a subprogram that asks the user to type in a password of no more than 10 characters, but you want your program to allow users to type in passwords up to 25 characters. At this point, you could either

    • Write your own password-verifying subprogram from scratch (which would take time).

    • Copy the existing subprogram and modify it (which would take much less time). It's easier to make a copy of an existing subprogram and then modify this copy. Now you'll have two copies of (almost) the same subprogram, but uh oh! Suddenly, you discover an error in the original subprogram. Once again, you have to correct this error in the original subprogram and also in the modified subprogram. If you made 20 different modifications to a subprogram, you now have the problem of not only correcting the error in every copy of the original subprogram, but also fixing that same error in all your modified versions of that original subprogram.

But after you modify a subprogram, will you remember which subprogram you copied and modified originally? Even worse, you could copy a subprogram and modify it, and then copy your modified subprogram and modify that copy. Do this several times and you'll wind up with several slightly different versions of the same subprogram, but now you may not have any idea which subprogram you copied originally.

So now if you find an error in the original subprogram, how can you find and fix that same error in any modified copies of that subprogram? Most likely, you can't because you won't know for sure which modified versions of the subprogram you (or another programmer) might have created.

Because programmers are always going to copy an existing program that works, object-oriented programming helps manage the copying process by using inheritance. The whole idea behind inheritance is that rather than making physical copies of a subprogram, you have only one copy of a subprogram at all times.

Instead of physically copying a subprogram, objects inherit a subprogram by essentially pointing to the subprogram that they want to copy. Not only does this save physical space by eliminating the need to make copies of a subprogram, but this also makes it easy to modify subprograms.

If you find an error in a subprogram, just correct the error in the original subprogram and that's it. Any objects that have inherited commands from that subprogram now point automatically to the modified version of the original subprogram, as shown in Figure 2-8.

By isolating commands in objects and using inheritance, objects can get the advantages of copying subprograms without the disadvantages of having multiple physical copies scattered all over the place.

Object-oriented programming makes programs easier to write (by dividing a large program into parts), easier to understand (by organizing subprograms into objects that mimic the actual problem the program is trying to solve), and easier to modify (by automatically updating any copies of subprograms). All these advantages allow you, as the programmer, to focus more on solving problems and less on keeping track of trivial details.

Object-oriented programming never physically copies a subprogram but "points to" or "inherits" a subprogram.

Figure I.2-8. Object-oriented programming never physically copies a subprogram but "points to" or "inherits" a subprogram.

Warning

Discover more about the details of object-oriented programming in Book II, Chapter 7. For now, it's just important that you understand why programmers use object-oriented programming. Then you can worry about figuring out how to use object-oriented programming.

Designing Programs with Today's Methodology

Each step, from spaghetti programming, to structured programming, to event-driven programming, to object-oriented programming, is meant to guide programmers into writing better-organized programs that can be modified quickly and easily. Today, object-oriented programming is popular, but tomorrow, another programming methodology will likely arrive to deal with the shortcomings of object-oriented programming.

With the exception of spaghetti programming (which you want to avoid), structured programming, event-driven programming, and object-oriented programming can be used by themselves or together. You might use object-oriented programming to divide a program into objects, and then use structured programming to organize the commands you write and store inside each object. Finally, you can use event-driven programming to design a fancy user interface so people know how to use your program.

By using each programming methodology's strengths, you can create a well-designed program, on time, that actually works. Given the track record of government agencies and Fortune 500 corporations, creating working software on time is the closest thing to a miracle that most people will ever experience in a lifetime.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset