Wednesday, November 18, 2009

Tool 20: Testing



[ Team LiB ]





Tool 20: Testing


Imagine a complex assembly machine that puts together videocassettes. It has 15 assembly steps, each followed by a test step: position, test, position, test.… At one step, a robot puts a part in place; at the next step a sensor checks to be sure that the part is there. As you move down the manufacturing line, you find that every time something is done, a test follows to be sure it was done correctly. These tests assure that there are no missing or misaligned parts and that all the parts fit together as the product is being assembled. When a videocassette passes these tests, you know it was put together the way it was designed.


During manufacturing, a representative sample of videocassettes are pulled off the line and put into a test bed to be sure that they function smoothly and play back high quality video in a variety of machines currently in use by consumers. These tests show that the videocassettes will work correctly when they are actually used.


In software development, we also test that design intent is achieved and that the system does what customers want it to do. When developers write code, there should be a test to be sure that each feature works as intended and that all of the pieces work together. These tests have been categorized as unit tests, system tests, and integration tests. As we move from programming one module at a time to programming entire capabilities and features, the distinction between unit, system, and integration tests has less meaning. A better name for these tests might be developer tests, because their purpose is to assure that code does what the developer intended it to do.


Tests to be sure that the system does what customers want have been called acceptance tests, but this term has traditionally been used to refer to tests that run at the end of development. A better name for tests that make sure that a system does what customers intend is customer tests, since their purpose is to assure that the system will do what customers expect it to do. Customer tests are run throughout the development, not just at the end.


Tests play several pivotal roles during the software development process. First, tests unambiguously communicate how things are supposed to work. Second, they provide feedback on whether the system actually works the way it is supposed to work. Third, tests provide the scaffolding that allows developers to make changes throughout the development process, making tools such as last responsible moment, set-based development, and refactoring useful in practice. When development is done, the test suite provides an accurate representation of how the system was actually built. Finally, by developing and maintaining test suites for all systems in production, making changes to production systems that interact with each other can be done safely by running a full suite of tests for all related applications.


Communication


When a product is released to manufacturing, tests are released along with it to tell the manufacturing organization exactly what constitutes an acceptable product. In the same way, developer tests convey exactly how the system is supposed to work internally, while customer tests convey by example exactly what customers need an application to do.


Customer tests can be a viable replacement for, or supplement to, most requirements documents. Suppose a developer has a conversation with a customer about details of a feature. The conversation should not be considered complete until it is expressed as a customer test. Whether the test is written by a customer representative, tester, or developer, it is a precise description of how the feature should work for the customer.


Now imagine a quick design session among developers determining how the feature will be implemented. The implementation is not complete until the design details are exercised by developer tests. By documenting the design in tests, developers can write code with a clear understanding of exactly what it is supposed to do. This is a good way to refine thinking and help developers write code with conceptual integrity.


There are alternatives to writing tests as a communication device prior to coding, but there is no alternative to writing tests to demonstrate whether the system does what it is supposed to do. So, you may as well get double duty out of tests by using them to document what the system is supposed to do, just as manufacturing often uses tests to convey product specifications.


Feedback


When a developer writes code, she or he should get immediate feedback about whether the code works as intended. In other words, there should be a test for each mechanism the developer implements. In fact, developers will find a way to test their code as soon as it is written anyway; this is how code is developed. Why not capture that test and use it? You are going to test the system anyway, so you may as well capitalize on the fact that development is a cycle of experiments with a successful test at the end of each cycle.


The reason you are developing software in short iterations is so that you can provide feedback about how the system works to the customers or customer representatives and get their input on how to proceed. In order to get that feedback, you need to show them what the software developed during the iteration does for them. In other words, you need a set of demos or scripts that demonstrate the developed functionality. These need to be understood by customers well enough to be sure that everything they care about is successfully implemented in the iteration. So, why not have testers on the team to write customer tests during each iteration? Since you are going to demonstrate features at the end of the iteration anyway, you may as well capture the demonstration in tests.


As long as you've got them, developer and customer tests should be automated as much as possible[24] and run as part of the daily build. If the tests are not automated or if they take too much time, they won't be run often enough. Big batches of changes will be made before testing, which will make failure much more likely, and it will be much more difficult to tell which change caused the tests to fail.

[24] For a discussion on deciding when to automate tests, see Marick, "When Should a Test Be Automated?"


Scaffolding


Scaffolding is a supporting framework that allows workers to do things that would otherwise be dangerous. If you develop software in iterations, delay decisions until the last responsible moment, and use set-based development and refactoring, you are going to be making serious changes to code once it has been written. This is dangerous, as we all know, because changes tend to have unintended consequences. Any nontrivial system requires that hundreds of thousands of details must all be correct at the same time. Many of these details interact with each other in ways far too complex to anticipate. The larger the code base, the more devious the interactions might be. To make changes safely, there must be a way to immediately find and fix unintended consequences. The most effective way to facilitate change is to have an automated test suite that tests the mechanisms the developers intend to implement and the behavior the customers need to have. A test suite will find unintended consequences right away, and if it is good, it will also pinpoint the cause of the problem.


In this sense, automated test suites are scaffolding that provides safety and access to the builders of the software system as they complete the construction of a partially built edifice. You can't effectively use the other tools in this chapter without this scaffolding. It may seem like writing tests slows down development; in fact, testing does not cost, it pays, both during development and over the system's lifecycle.


When you think about it, the tests are there for you to find, formalize, and automate, because developers somehow check their work as they code, and ways are found to demonstrate to customers how the system works at the end of iterations. The thing you need to do is capture those tests, make sure they are correct and complete, put them under version control, automate them, consider them as part of the released product, and continue to use and improve them. You might end up with as many lines of test code as of product code, but the benefit will far outweigh the cost.


As-Built



The Importance of As-Built Drawings


At the end of July 2002, news stories told of the dramatic rescue of nine men from the flooded Quecreek coalmine near Somerset, Pennsylvania. The men were trapped deep in the mine after they drilled into an abandoned mine full of water.


Drawings of the abandoned mine had not been updated to show a large cavern that was dug just before the old mine was closed. An updated map was subsequently found, but it was not available to the miners as they worked. So they drilled into the water-filled cavern, not knowing it was there.


Mining is not the only industry with inaccurate maps. It's a safe bet that your city does not have accurate as-built maps of its underground infrastructure. For that matter, few buildings have accurate as-built or as-maintained drawings.


It doesn't come as a surprise that it is difficult, if not impossible, to maintain accurate as-built documentation of software. Heroic attempts are made to do this with safety-critical software, but as the coalmine accident shows, there can always be lapses. However, if a system has a comprehensive test suite that contains both developer tests and customer tests, those tests will in fact be an accurate as-built reflection of the system. If the tests are clear and well organized, they are an invaluable resource for understanding how the system works from a developer's and a customer's point of view.


The other thing a test suite does is give an indication of the health of the as-built system. Defect counts, types, and trends are a very good indication of whether a system is converging, when a product is ready to ship or deploy, and how robust the system is.


The bottom line is, you should have complete, automated (as far as practical) suites of developer and customer tests. They should be subject to the same discipline in design, semantics, versioning, builds, synchronization, and refactoring as the system itself. If there doesn't seem to be enough time, the first thing to do is reallocate the effort used in requirements documentation to writing customer tests. Require developers to write and automate their own developer tests, while providing training and coaching in test development and automation. You will get more payoffs from an effective test program than from most other investments you might make.


Maintenance


The software industry needs to find a way to make software easy to change after it is running in production, since well over half of development occurs after initial release. Furthermore, making changes to production software has to be economical�that is, changes must be made relatively quickly and at a reasonable cost. There are many ways to make software more changeable�layering, clumping, and hiding potential variability; components; use of commercial software; and so on. It is also a good idea to have the development team retain responsibility for application maintenance to preserve domain learning. All of these techniques are important, but we must add one other mechanism that to the list: maintaining a set of comprehensive tests throughout the lifecycle of the system. If a scaffolding of tests was built during development, all you have to do is re-erect the scaffolding and proceed with the changes. Then, the system can be safely repaired and refactored throughout its useful life. Scaffolding is as useful for maintenance as it was for the original construction.


Let's say you have a complex system with many applications using common services�a common database, middleware, or hardware, for example. You know enough about complex systems by now to suspect that a change in any one application could have an adverse reaction on an unrelated application. Since you don't have a reliable set of as-built documentation, you have to figure out for yourself how all the systems actually work before you can safely change any of them. No wonder maintenance is so difficult.


What you need is the test suite for each application, developed as scaffolding for change during development. These tests, assuming you keep them healthy, constitute an accurate set of as-built documentation for all the applications in your environment. If each application has an up-to-date test suite to prove its integrity, you can test the entire environment before a change is released.





    [ Team LiB ]



    Section 18.4.  Calculation Tests









    18.4. Calculation Tests


    We now focus on design ideas and smells that apply to calculation tests expressed with ColumnFixture and CalculateFixture.



    18.4.1. Smell: Many Columns


    A ColumnFixture or CalculateFixture with many columns may indicate that something is wrong. Is there a tangle of tests here (see Section 18.2.8)? If so, can they be split into separate tests with distinct tables?


    Are there many repeats in a big table? If so, maybe they can be eliminated by breaking the table up into several smaller tables, with common column values expressed as arguments. For example, one such table from Section 16.3 on p. 131 is shown again as Figure 18.12.



    Figure 18.12. Table with Arguments









    18.4.2. Smell: Not Calculating


    Consider the table shown again in Figure 18.13. This table is using the form of calculation tests, but the intent is to set up for an action test. It's clearer to use a SetUpFixture table, as shown in Section 10.4 on p. 76.


    Figure 18.13. Setup Table Using ColumnFixture

    DiscountGroupsEntry

    future value

    max balance

    min purchase

    discount percent

    add()

    low

    0.00

    0.00

    0

    true

    medium

    0.00

    500.00

    5

    true

    medium

    500.00

    500.00

    3

    true

    high

    500.00

    2000.00

    10

    true

    high

    1000.00

    500.00

    5

    true



    ColumnFixture tables can also be used for actions tests. For example, the ColumnFixture table in Figure 18.14 is testing that a rental return leads to the correct refund. The returns() column checks that the return succeeded; the running total() column checks the amount of the refund. This test can be expressed a little more clearly by using a DoFixture table without being confused as a calculation test.


    Figure 18.14. A ColumnFixture Used for Action Tests

    hire.ChangeRentals

    rental item

    count

    days

    returns()

    running total()

    coffee urn

    5

    1

    true

    -100.00

    hot water urn

    2

    1

    true

    -140.00





    18.4.3. Smell: Many Rows


    As we've already seen (Section 18.2.10), long tables may need to be organized into several tables to help the test reader and the writer. For example, in Section 13.6 on p. 99, we suggested that a separate table be used to define the late-return business rules when the high demand is not zero. Figure 18.12 shows one of several tables that define fair charging, based on time and the various charge rates.


    In general, long tables can be split to create separate tables for the major cases. Order the rows in the tables clearly to make it easier to see the organization and easier to see whether cases are missing. Use blanks with ColumnFixture rather than repeating the same value down a column. Or use table arguments, as we saw in Section 18.4.1.




    18.4.4. Smell: No Related Workflow


    For any business logic that is tested as calculations, we'd expect to see two or more action-based tests to define what happens under the various circumstances. For example, if we have calculation tests that validate some data input, such as a date, we'd expect to see what happens in the workflow when it's valid and when it's not. For example, we have not shown any action tests for the late-returns tests covered in Chapter 13.










      Can't Get No Satisfaction



      [ Team LiB ]





      Can't Get No Satisfaction


      Reich's political predictions may not have withstood the test of time, but his classification of Con I, Con II, and Con III provides a useful model for the software industry today.


      Con I in software is associated with a focus on self-reliance. Software experts often refer to software developers operating at this level of awareness as mavericks, cowboy programmers, Lone Rangers, and prima donnas. Software developers at this level tend to have little tolerance for other people's ideas. They like to work alone. They don't like following standards. The "Not Invented Here" syndrome thrives.


      Con I's advantage is that little training is needed, and the lone wolf approach works adequately in environments that employ only small numbers of programmers who work independently on small projects. Con I's disadvantage is that it scales poorly to projects that need teams of programmers rather than isolated individuals, which means it is valuable only on the smallest projects.


      Con II in software is associated with a focus on rules. Many software developers eventually discover the limitations of Con I's self-reliant development style and see the advantages of working in groups. Over time, they learn rules that allow them to coordinate their work with others. Some groups of developers create their own informal rules through trial and error, and these groups can be highly effective. Other groups buy a prebuilt methodology. Sometimes the rules are provided by consultants�as in the classic "17 three-ring-binders" methodologies. Other times, the rules are taken from books, such as the Rational Unified Process,[2] the Extreme Programming series,[3] or my own Software Project Survival Guide.[4] Developers at this level of awareness tend to focus on details of adherence to the rules. They argue about which interpretations of the rules are correct and focus on "following the methodology."


      Con III in software is associated with a focus on principles. At this level of awareness, developers understand that the rules of any prepackaged methodology are, at best, approximations of principles. Those approximations might apply most of the time, but they won't apply all of the time. Extensive education and training are needed to introduce a developer to the Con III principles underlying effective software development, and that education and training is not easily obtained. Once that education and training has been obtained, however, the developer is equipped with a full range of software engineering tools to support success on a wide range of projects.


      Whereas a Con II approach consists of the repetitive application of techniques and procedures, Con III's focus on principles requires the application of judgment and creativity. With Con II, a developer can be trained to use just a single approach. If a good approach is chosen, a developer can leverage a relatively small amount of training across many projects. The gap between Con II and Con III will appear to be small when a Con II approach is used within its area of applicability. But a Con II developer will be ill-equipped to succeed on projects that fall outside the range of the specific methodology in which the developer was trained. The Con II developer is essentially an individual example of the Cargo Cult organization described in Chapter 3�procedures are followed without a thorough understanding of what makes them work, which leads to spotty project results.





        [ Team LiB ]



        6.1 Save with Each Record the Name of the Last Person Who Edited It and the Date and Time



        6.1 Save with Each Record the Name of the Last
        Person Who Edited It and the Date and Time


        6.1.1 Problem


        Your application is used in a multiuser environment with
        users regularly adding and editing records. Access keeps track of when an object
        was created and last modified. However, it does not track this information at
        the record level. With each record, you want to log who created the record, who
        last edited the record, and the date and time associated with each of these
        actions. Is this possible?


        6.1.2 Solution


        Access has no built-in feature that records who edited a
        record and when the edit was made, but it's fairly easy to create your own.
        You'll need to add four fields to each of your tables to hold this information.
        You'll also need to create two simple procedures and attach them to the
        BeforeInsert and BeforeUpdate events of your forms.


        To add this functionality to your applications, follow these
        steps:





        1. Modify your table to include four new fields, as shown in
          Table 6-1.


           
































          Table 6-1. New fields for tblCustomer


          Field name


          Field type


          Default value


          DateCreated


          Date/Time


          =Now( )


          UserCreated


          Text (20)

           

          DateModified


          Date/Time


          =Now( )


          UserModified


          Text (20)

           

           



        2. Open your form in design view. Add new text box controls,
          as shown in Table 6-2. You can place these controls anywhere on the form; they
          needn't be visible. In the example form, we placed these controls along the
          bottom of the form (see Figure 6-2).


           



























          Table 6-2. New controls for frmCustomer1


          Control name


          Control source


          txtDateCreated


          DateCreated


          txtUserCreated


          UserCreated


          txtDateModified


          DateModified


          txtUserModified


          UserModified


           



        3. Set the Enabled property of these controls to No and the
          Locked property to Yes. This prevents users from modifying the values that
          will be computed automatically. You may also wish to set the TabStop property
          of these controls to No to remove these fields from the normal tab sequence of
          the form.



        4. Create the following event procedure in the form's
          BeforeInsert event, which uses the CurrentUser
          function to insert the user's name. You don't need to insert the date because
          it has already been supplied as a default value in the tblCustomers table.


          Private Sub Form_BeforeInsert(Cancel As Integer)
          Me!UserCreated = CurrentUser( )
          End Sub



        5. Create the following event procedure in the form's
          BeforeUpdate event. This time you must insert both the username and the date
          and time:


          Private Sub Form_BeforeUpdate(Cancel As Integer)
          Me!DateModified = Now( )
          Me!UserModified = CurrentUser( )
          End Sub



        6. The event procedures should show up in the form's
          properties sheet, as shown in Figure 6-1. Save and close the form. Open the
          form and run it to test your new code.



          Figure 6-1. Referencing the event procedures for
          frmCustomer1






        To see an example, load the frmCustomer1 form from
        06-01.MDB
        . This form, shown in Figure 6-2, allows you to enter and edit data
        in the tblCustomer table. Make a change to an existing record, and the
        DateModified and UserModified fields will be updated with the current date and
        time and username. Add a new record, and the DateCreated and UserCreated fields
        will be updated.



        Figure 6-2. The frmCustomer1 form




        6.1.3 Discussion


        To keep track of the username and the date and time a record
        is created and updated, you must do two things:




        • Create additional fields in the table to hold the
          information.



        • Create the application code to ensure that these fields are
          properly updated when a record is added or modified.



        We added four fields to tblCustomer: two fields to hold the
        username and date/time the record was created, and another two fields to hold
        the username and date/time the record was last modified. You don't have to
        create all four fields, only the fields for which you wish to log information.


        We also created event procedures to update these columns
        whenever a record is inserted or updated. The Now
        function supplies the date and time; if you'd prefer to record only the date of
        the change without a time, you can use the Date
        function instead. The built-in CurrentUser
        function saves the name of the current user.


        Access doesn't support the specification of calculated fields
        at the table level, so all of the logic presented in this solution occurs at the
        form level. This means that you must recreate this logic for every form that
        updates the data in this table. It also means that if you add new records or
        update existing records outside of a form梡erhaps by using an update query or by
        importing records from another database梩he fields in Table 6-1 will not all be
        automatically updated.


        You can ensure that one of the fields, DateCreated, is
        correctly updated for every record by adding the following expression to its
        DefaultValue property:


        =Now(  )

        Unfortunately, you can't use the DefaultValue property for
        either of the updated fields, because DefaultValue is evaluated only when the
        record is initially created. You can't use this property to update the
        UserCreated field, either, because DefaultValue cannot call built-in or
        user-defined functions (except for the special Now
        and Date functions).


        You may have noticed that placing the four controls from
        Table 6-2 on the form takes up a considerable amount of screen space.
        Fortunately, you don't need controls to make this technique work, because Access
        lets you refer to a form's record-source fields directly. In the sample database
        you'll find a second version of the form, frmCustomer2, that demonstrates this
        variation of the technique. Notice that there are no txtDateCreated,
        txtUserCreated, txtDateModified, or txtUserModified controls on frmCustomer2,
        yet when you enter or edit a record using this form, the fields in tblCustomer
        are correctly updated. Here's the BeforeUpdate event procedure for this form:


        Private Sub Form_BeforeUpdate(Cancel As Integer)
        Me!DateModified = Now( )
        Me!UserModified = CurrentUser( )
        End Sub

        Access lets you refer to fields in a form's underlying record
        source (in this example, the DateModified and UserModified fields in tblCustomer)
        as if they were controls on the form, even though they're not. Because of this,
        it's a good idea to name the controls on a form differently from the underlying
        fields. Then you can be sure that you are always referring to the correct
        object.


        Another consideration is that the
        CurrentUser
        function is useful only if you have implemented user-level
        security on your database. In an unsecured Access database it will always return
        "Admin", which is not very informative. In that case, you can use Windows API
        calls to retrieve either the computer name or the network login (or both) of the
        current user, instead of the Access security account. In the sample application,
        frmCustomer3 calls acbNetworkUserName when a
        record is inserted or edited. Here are the API declaration and the function,
        which you can find in basNetworkID:


        Private Declare Function acbGetUserName Lib "advapi32.dll" Alias _
        "GetUserNameA" (ByVal lpBuffer As String, nSize As Long) As Long

        Function acbNetworkUserName( ) As String
        ' Returns the network login name.
        Dim lngLen As Long, lngX As Long
        Dim strUserName As String
        strUserName = String$(254, 0)
        lngLen = 255
        lngX = acbGetUserName(strUserName, lngLen)
        If lngX <> 0 Then
        acbNetworkUserName = Left$(strUserName, lngLen - 1)
        Else
        acbNetworkUserName = ""
        End If
        End Function

        The basNetworkID module also includes the following API call,
        which you can use to obtain the name of the current user's computer:


        Private Declare Function acbGetComputerName _
        Lib "kernel32" Alias "GetComputerNameA" _
        (ByVal lpBuffer As String, nSize As Long) As Long

        Private Const acbcMaxComputerName = 15

        Public Function acbComputerName( ) As String
        ' Retrieve the name of the computer.
        Dim strBuffer As String
        Dim lngLen As Long

        strBuffer = Space(acbcMaxComputerName + 1)
        lngLen = Len(strBuffer)
        If CBool(acbGetComputerName(strBuffer, lngLen)) Then
        acbComputerName = Left$(strBuffer, lngLen)
        Else
        acbComputerName = ""
        End If
        End Function

        Another option is to create your own public function called
        CurrentUser that returns the network name. That
        way, you won't need to change any of the code that calls
        CurrentUser in your forms. Access will use your
        function rather than the built-in one, and if you do implement Access security,
        all you need to do is rename or remove the custom
        CurrentUser
        function to have the form code start retrieving Access
        security names using the built-in CurrentUser
        function.



        Lab 19.2 Nested Records



        [ Team LiB ]





        Lab 19.2 Nested Records



        Lab Objective



        After this Lab, you will be able to:


        Use Nested Records



        As mentioned in the introduction to this chapter, PL/SQL allows you to define nested records. These are records that contain other records and collections. The record that contains a nested record or collection is called an enclosing record.


        Consider the following code fragment.


        FOR EXAMPLE





        DECLARE
        TYPE name_type IS RECORD
        (first_name VARCHAR2(15),
        last_name VARCHAR2(30));

        TYPE person_type IS
        (name name_type,
        street VARCHAR2(50),
        city VARCHAR2(25),
        state VARCHAR2(2),
        zip VARCHAR2(5));

        person_rec person_type;

        This code fragment contains two user-defined record types. The second user-defined record type, person_type, is a nested record type because its field name is a record of the name_type type.


        Next, consider the complete version of the preceding example.


        FOR EXAMPLE





        DECLARE
        TYPE name_type IS RECORD
        (first_name VARCHAR2(15),
        last_name VARCHAR2(30));

        TYPE person_type IS RECORD
        (name name_type,
        street VARCHAR2(50),
        city VARCHAR2(25),
        state VARCHAR2(2),
        zip VARCHAR2(5));

        person_rec person_type;

        BEGIN
        SELECT first_name, last_name, street_address, city, state, zip
        INTO person_rec.name.first_name, person_rec.name.last_name, person_rec.street,
        person_rec.city, person_rec.state, person_rec.zip
        FROM student
        JOIN zipcode USING (zip)
        WHERE rownum < 2;

        DBMS_OUTPUT.PUT_LINE ('Name: '||
        person_rec.name.first_name||' '||
        person_rec.name.last_name);
        DBMS_OUTPUT.PUT_LINE ('Street: '||
        person_rec.street);
        DBMS_OUTPUT.PUT_LINE ('City: '||
        person_rec.city);
        DBMS_OUTPUT.PUT_LINE ('State: '||
        person_rec.state);
        DBMS_OUTPUT.PUT_LINE ('Zip: '||
        person_rec.zip);
        END;

        In this example, the person_rec record is a user-defined nested record. As a result, in order to reference its field name that is a record with two fields, the following syntax is used:





        enclosing_record.(nested_record or
        nested_collection).field_name

        In this case, the person_rec is enclosing record because it contains the name record as one of its fields while the name record is nested in the person_rec record.


        This example produces the following output:





        Name: James E. Norman
        Street: PO Box 809 Curran Hwy
        City: North Adams
        State: MA
        Zip: 01247

        PL/SQL procedure successfully completed.




          [ Team LiB ]



          Ongoing Education










          Ongoing Education


          All Microsoft engineering staff must attend security training at least once a year. At first, the only class available was the "The Basics" presentation, but attendance is not appropriate once a person has taken the course, even though the content does change substantially. In fact, the content changes every month to reflect new threats, research, and mitigations. With this in mind, we hired another person and started working on a more in-depth security curriculum to address the needs of specific disciplines. At the time of writing, the following classes are either complete or being developed:


          • The Basics of Secure Software Design, Development, and Testing This class introduces all engineers to the basics of security.

          • Fuzz Testing in Depth Explained in detail in Chapter 12, "Stage 7: Secure Testing Policies," this class is an effective way to find certain classes of security bugs. This class explains how fuzz testing works, how to build effective fuzz tests, and how to identify fuzz-testing failures.

          • Threat Modeling in Depth Explained in detail in Chapter 9, threat modeling is a method for uncovering design flaws in a software component before the component is built. This class, which includes a small exercise at the end, outlines the process.

          • Implementing Threat Mitigations This class begins where the threat-modeling class leaves off. The original threat-modeling class covered the threat-modeling process as well as development tasks and test tasks. This turned out to be a great deal of content, spanning many disciplines, and was simply too unwieldy. The present threat-modeling class covers just the threat-modeling process, and the "Implementing Threat Mitigations" class is aimed mainly at developers and helps them to decide how to choose the appropriate mitigations or countermeasures.

          • Security Design and Architecture: Time-Tested Design Principles We touch on this subject in Chapter 7, "Stage 2: Define and Follow Design Best Practices." Most software developers focus solely on best practice to lift themselves out of the security pit, but we need to go much further than simple best practice. Engineers should learn some of the basic security models, such as the Bell-LaPadula Disclosure model (Wikipedia 2006a) and the Biba integrity model (Wikipedia 2006b), and secure design principles such as Saltzer and Schroeder (Saltzer and Schroeder 1975).

          • Introduction to the SDL and Final Security Review (FSR) Process This class covers the end-to-end SDL process, but most important, it prepares development groups for the final security review, outlining what they can expect during an FSR. The target audience is more senior employees because these folks need to build SDL time into their schedules. A key facet of the class is explaining the importance of building time into the schedule for all the SDL requirements.

          • Security Tools Overview There are many security tools available inside and outside Microsoft. This class covers some of the most important tools for performing code analysis, design analysis, attack surface reviews, penetration testing, threat modeling, and fuzz testing.

          • Performing Security Code Reviews Very few people know how to review code correctly for security bugs. This class teaches some of the critical skills, such as understanding lack of trustworthiness of most incoming data, as well as ranking system entry points by potential "attackability." This in part is driven by the threat model, which identifies the dangerous interfaces into the application.

          • Secure Coding Practices Going beyond "The Basics," this class teaches developers how to create secure software not simply by applying best practice, but also by using good, sound security discipline and secure coding patterns.

          • Security Bugs in Detail This class covers a catalog of security bugs along with their causes, mitigations, and defenses. The class then examines security bugs in more detail, showing specific bugs in various software products.

          • Attack Surface Analysis (ASA) and Attack Surface Reduction (ASR) This class outlines what defines attack surface for common applications and platforms and how to drive attack surface down while trying to keep the application useful for customers. ASA is covered in detail in Chapter 7.

          • Exploit Development This advanced class outlines how to create exploit code to take advantage of vulnerabilities. Obviously, the purpose of the class is to educate, not to attack real systems. When it comes to showing how dangerous security bugs can be, there is nothing quite as effective as seeing an exploit in action.

          • Build Requirements The target audience for this class is people involved in creating the daily build. Admittedly, most companies don't need this class, but for companies like Microsoft, it's important because the build process must be protected, and the correct security tools must be run on the build.

          • Security Response There will be security bugs in products, and it's important that your team understands what the security response is going to be. This subject is covered in detail in Chapter 15, "Stage 10: Security Response Planning."

          • Cryptography by Example This class takes a scenario of two people wishing to communicate securely and builds up to a secure solution using cryptographic primitives to mitigate real threats. The second part of the class covers cryptographic best practice.

          • Customer Privacy This online training class focuses on protecting customer data, most notably protecting private user data maintained by some of the Microsoft online properties, such as MSN (Microsoft 2006a). The basics taught include legal aspects of privacy, privacy statements, the data lifecycle, and privacy-enhancing technologies (PETs), as well as global privacy policy such as notice, choice, access, security, onward transfer, data integrity, and enforcement.


          Important

          If your company creates or uses software that stores and maintains private user data or sensitive or confidential data, your engineers must understand the basics of privacy.



          Note that this is a partial list of classes, and it will be augmented and modified in coming years as threats evolve.


          Important

          Any education you require for your employees must provide new specific skills that they can apply "on the job."













          Section 20.1.&nbsp; Achieving the Right Balance










          20.1. Achieving the Right Balance


          In the preceding parts of this book, we went into great detail on the theoretical basis for aspects. We provided a systematic approach for applying use cases and aspects to achieve better separation of concerns. We demonstrated how to apply the approach from requirements to code and how to establish a resilient architecture based on the approach.


          It is time for you to apply the approach to your project. We recognize that every project is differentdifferent complexity, different technologies, different levels of formality, and so on. Every project team is different as well, with various backgrounds and skill sets. You definitely need to tailor the approach to suit the specific needs of your project.


          In this chapter, we discuss how you can adopt aspect orientation, specifically, the approach described in this book. This chapter is not about adopting a new approach in an organization. Adopting a new technology in an organization means organizational change, and it involves getting a champion, identifying pilot projects, and morethat is an entirely separate topic.


          Rather, the premise is this: given that you want to apply use cases and aspects on an already chosen project, what should you do in that project? Which areas of our approach should you apply? How do you balance your existing approach with the new approach?


          The remainder of this chapter is a guide to adopting the approach to a project discussed in this book. In particular, we look at the following considerations:


          • Selecting the areas (or disciplines) within the methodology you want to adoptrequirements, design, implementation, testing, and so on.

          • Balancing current and proposed approaches for software development at different phases of a project.


          For each consideration, we discuss some of the adoption challenges you are likely to face and show you how to resolve them.










            Section 13.7. Containers and Layout Managers










            [Page 627 (continued)]

            13.7. Containers and Layout Managers


            A Container is a component that can contain other components. Because containers can contain other containers, it is possible to create a hierarchical arrangement of components, as we did in the second version of our Converter interface. In its present form, the hierarchy for Converter consists of a JFrame as the top-level container (Fig. 13.14). Contained within the frame is a KeyPad (subclass of JPanel) that contains 12 JButtons. Most GUIs will have a similar kind of containment hierarchy.


            A Container is a relatively simple object whose main task is primarily to hold its components in a particular order. It has methods to add and remove components (Fig. 13.20). As you can see from these methods, a container keeps track of the order of its elements, and it is possible to refer to a component by its index order.




            Figure 13.20. A Container contains Components.










            [Page 628]

            13.7.1. Layout Managers


            The hard work of organizing and managing the elements within a container is the task of the layout manager. Among other tasks, the layout manager determines


            • The overall size of the container.

            • The size of each element in the container.

            • The spacing between elements.

            • The positioning of the elements.


            Although it is possible to manage your own layouts, it is not easy. For most applications you are much better off by learning to use one of the AWT's built-in layouts. Table 13.3 gives a brief summary of the available layouts. We will show examples of FlowLayout, GridLayout, and BorderLayout. Some of the widely used Swing containers have a default layout manager assigned to them (Table 13.4).


            Table 13.3. Some of Java's AWT and Swing layout managers

            Manager

            Description

            java.awt.BorderLayout

            Arranges elements along the north, south, east, west, and in the center of the container.

            java.swing.BoxLayout

            Arranges elements in a single row or single column.

            java.awt.CardLayout

            Arranges elements like a stack of cards, with one visible at a time.

            java.awt.FlowLayout

            Arranges elements left to right across the container.

            java.awt.GridBagLayout

            Arranges elements in a grid of variably sized cells (complicated).

            java.awt.GridLayout

            Arranges elements into a two-dimensional grid of equally sized cells.

            java.swing.OverlayLayout

            Arranges elements on top of each other.



            Table 13.4. Default layouts for some of the common Swing containers

            Container

            Layout Manager

            JApplet

            BorderLayout (on its content pane)

            JBox

            BoxLayout

            JDialog

            BorderLayout (on its content pane)

            JFrame

            BorderLayout (on its content pane)

            JPanel

            FlowLayout

            JWindow

            BorderLayout (on its content pane)



            To override the default layout for any of the JApplet, JDialog, JFrame, and JWindow containers, you must remember to use the getContentPane(). The correct statement is


            getContentPane().setLayout(new FlowLayout());


            Debugging Tip: Content Pane

            Attempting to add a component directly to a JApplet or a JFrame will cause an exception. For these top-level containers, components must be added to their content panes.







            [Page 629]

            13.7.2. The GridLayout Manager


            It is simple to remedy the layout problem that affected the keypad in the most recent version of the Converter program. The problem was caused by the fact that as a subclass of JPanel, the KeyPad uses a default FlowLayout, which causes its buttons to be arranged in a row. A more appropriate layout for a numeric keypad would be a two-dimensional grid, exactly the kind of layout supplied by the java.awt.GridLayout. Therefore, to fix this problem, we need only set the keypad's layout to a GridLayout. This takes a single statement, which should be added to the beginning of the KeyPad() constructor:


            setLayout(new GridLayout(4,3,1,1));


            This statement creates a GridLayout object and assigns it as the layout manager for the keypad. It ensures that the keypad will have four rows and three columns of buttons (Fig. 13.21). The last two arguments in the constructor affect the relative spacing between the rows and the columns. The higher the number, the larger the spacing. As components are added to the keypad, they will automatically be arranged by the manager into a 4 x 3 grid.




            Figure 13.21. This version of the metric converter GUI uses a keypad for mouse-based input. It has an attractive overall layout.







            Note that for a JPanel, the setLayout() method applies to the panel itself. Unlike the top-level containers, such as JFrame, other containers do not have content panes. The same point would apply when adding components to a JPanel. They are added directly to the panel, not to a content pane. Confusion over this point could be the source of bugs in your programs.


            Debugging Tip: Content Pane

            Top-level containers, such as JFrame, are the only ones that use a content pane. For other containers, such as JPanel, components are added directly to the container.



            As its name suggests, the GridLayout layout manager arranges components in a two-dimensional grid. When components are added to the container, the layout manager starts inserting elements into the grid at the first cell in the first row and continues left to right across row 1, then row 2, and so on. If there are not enough components to fill all the cells of the grid, the remaining cells are left blank. If an attempt is made to add too many components to the grid, the layout manager will try to extend the grid in some reasonable way in order to accommodate the components. However, despite its effort in such cases, it usually fails to achieve a completely appropriate layout.


            [Page 630]

            Java Programming Tip: Grid Layout

            Make sure the number of components added to a GridLayout is equal to the number of rows times the number of columns.





            13.7.3. GUI Design Critique


            Although the layout in Figure 13.21 is much improved, there are still some deficiencies. One problem is that the convert button seems to be misplaced. It would make more sense if it were grouped with the keypad rather than with the input text field.


            A more serious problem results from the fact that we are still using a FlowLayout for the program's main window, the JFrame. Among all of Java's layouts, FlowLayout gives you the least amount of control over the arrangement of the components. Also, FlowLayout is most sensitive to changes in the size and shape of its container.




            13.7.4. The BorderLayout Manager


            One way to fix these problems is to use a BorderLayout to divide the frame into five areas, north, south, east, west, and center, as shown in Figure 13.22. The BorderLayout class contains two constructors:


            public BorderLayout();
            public BorderLayout(int hgap, int vgap);




            Figure 13.22. Arrangement of components in a border layout. The relative sizes of the areas will vary.







            The two parameters in the second version of the constructor allow you to insert spacing between the areas.


            Components are added to a BorderLayout by using the add(Component, String) method found in the Container class. For example, to set the application window to a border layout and add the keypad to its east area, we would use the following statements:


            getContentPane().setLayout(new BorderLayout(2, 2));
            getContentPane().add(keypad,"East");



            [Page 631]

            In this version of the add() method, the second parameter must be a capitalized String with one of the names "North", "South", "East", "West", or "Center". The order in which components are added does not matter.


            One limitation of the BorderLayout is that only one component can be added to each area. That means that if you want to add several components to an area, you must first enclose them within a JPanel and then add the entire panel to the area. For example, let's create a panel to contain the prompt and the text field and place it at the north edge of the frame:


            JPanel inputPanel = new JPanel();          // Create panel
            inputPanel.add(prompt); // Add label
            inputPanel.add(input); // Add textfield
            getContentPane().add(inputPanel,"North"); // Add the panel to the frame



            Containment hierarchy




            The same point would apply if we want to group the keypad with the convert button and place them at the east edge. There are several ways these elements could be grouped. In this example, we give the panel a border layout and put the keypad in the center and the convert button at the south edge:


            JPanel controlPanel= new JPanel(new BorderLayout(0,0));
            controlPanel.add(keypad,"Center");
            controlPanel.add(convert, "South");
            getContentPane.add(controlPanel,"East"); // Add the panel to the frame


            Given these details about the BorderLayout, a more appropriate design for the converter application is shown in Figure 13.23. Note that the border layout for the top-level JFrame uses only the center, north, and east areas. Similarly, the border layout for the control panel uses just the center and south areas.




            Figure 13.23. A border layout design for the metric converter program. The dotted lines show the panels.







            In a BorderLayout, when one or more border areas are not used, one or more of the other areas will be extended to fill the unused area. For example, if West is not used, then North, South, and Center will extend to the left edge of the Container. If North is not used, then West, East, and Center will extend to the top edge. This is true for all areas except Center. If Center is unused, it is left blank.


            [Page 632]

            Figure 13.24 shows the results we get when we incorporate these changes into the program. The only changes to the program itself occur in the constructor method, which in its revised form is defined as follows:


            public Converter() {
            getContentPane().setLayout(new BorderLayout());
            keypad = new KeyPad(this);

            JPanel inputPanel = new JPanel(); // Input panel
            inputPanel.add(prompt);
            inputPanel.add(input);
            getContentPane().add(inputPanel,"North");

            JPanel controlPanel= new JPanel(new BorderLayout(0,0));
            controlPanel.add(keypad, "Center"); // Control panel
            controlPanel.add(convert, "South");
            getContentPane().add(controlPanel, "East");
            getContentPane().add(display,"Center"); // Output display
            display.setLineWrap(true);
            display.setEditable(false);

            convert.addActionListener(this);
            input.addActionListener(this);
            } // Converter()




            Figure 13.24. The metric converter, showing its appearance when a border design is used.







            This layout divides the interface into three main panels, an input panel, display panel, and control panel, and gives each its own layout. The control panel contains the keypad panel. Thus, the containment hierarchy for this design is much more complex than in our original design.



            Self-Study Exercises

            Exercise 13.6

            The border layout for the top window uses the north, center, and east regions. What other combinations of areas might be used for these three components?

            Exercise 13.7

            Why wouldn't a flow layout be appropriate for the control panel?














            File Names and Layout










            [Page 815 (continued)]

            File Names and Layout


            Java source files should have the .java suffix, and Java bytecode files should have the .class suffix. A Java source file can only contain a single public top-level class. Private classes and interfaces associated with a public class can be included in the same file.



            Source File Organization Layout


            All source files should begin with a comment block that contains important identifying information about the program, such as the name of the file, author, date, copyright information, and a brief description of the classes in the file. In the professional software world, the details of this "boilerplate" comment will vary from one software house to another. For the purposes of an academic computing course, the following type of comment block would be appropriate:


            /*
            * Filename: Example.java
            * Author: J. Programmer
            * Date: April, 20 1999
            * Description: This program illustrates basic
            * coding conventions.
            */


            The beginning comment block should be followed by any package and import statements used by the program:


            package java.mypackage;
            import java.awt.*;


            The package statement should only be used if the code in the file belongs to the package. None of the examples in this book use the package statement. The import statement allows you to use abbreviated names to refer to the library classes used in your program. For example, in a program that imports java.awt.* we can refer to the java.awt.Button class as simply Button. If the import statement were omitted, we would have to use the fully qualified name.


            The import statements should be followed by the class definitions contained in the file. Figure A.1 illustrates how a simple Java source file should be formatted and documented.




            [Page 816]

            Figure A.1. A sample Java source file.





            /*
            * Filename: Example.java
            * Author: J. Programmer
            * Date: April, 20 1999
            * Description: This program illustrates basic
            * coding conventions.
            */
            import java.awt.*;

            /**
            * The Example class is an example of a simple
            * class definition.
            * @author J. Programmer
            */
            public class Example {

            /** Doc comment for instance variable, var1*/
            public int var1;
            /**
            * Constructor method document at comment describes
            * what the constructor does.
            */
            public Example () {
            // ... method implementation goes here
            }

            /**
            * An instanceMethod() documentation comment describes
            * what the method does.
            * @param N is a parameter than ....
            * @return This method returns blah blah
            */
            public int instanceMethod( int N ) {
            // ... method implementation goes here}
            }
            } // Example class















            Typing on the TI-99 4A



            Typing on the TI-99 4A


            Around the third or fourth grade,
            someone in my family got me a book on video game programming for the TI-99 4A.
            This book probably made the most significant contribution to my interests in
            computer science. It contained the source code for the video games, but it
            required you to manually type it in, as there wasn't a concept of "installing"
            software on the TI. Either the software came on a cartridge or you had to type
            it in manually. I would two-finger type line after line of code for hours on
            end. I have to thank my older cousins for taking turns helping me type code on
            those evenings.


            There was this one video game's code in
            particular that took us many, many hours to type in. The video game involved
            flying, where you're navigating a fighter-style spaceship through explosive
            mines and other things that are bad for a spaceship to fly into in space. You
            could fly at a constant speed in only one direction. All you could do was arrow
            up or arrow down to avoid destruction. It so ruled.


            Just thinking back on those days makes
            me look at Visual Studio in a much different light. We used a cassette tape deck
            to store the video games, until it caught on fire one night. Also, we didn't
            know whether we typed in a letter wrong until the very end of the our typing
            ordeal, when we got to finally compile. Think of typing on the TI as using
            Notepad to type hours upon hours of source code from a book, without the ability
            to save, until the end. Maybe this is why I "heart" Visual Studio so much.


            One of the lessons we learned the
            hard way was that you lost everything if you turned off the computer or if there
            was a power failure (which of course happened one night). How we were able to
            ever program that game is beyond me, and we did it on several occasions (as we
            didn't realize we had to save to cassette the first time). I definitely learned
            patience, although the adults probably beg to differ, and how to pay attention
            to detail from all that typing.


             


            2.4 Denormalization



            [ Team LiB ]






            2.4 Denormalization






            Denormalization is
            the process of consciously removing entities created through the
            normalization process. An unnormalized database is
            not a denormalized database. A database can be
            denormalized only after it has been sufficiently normalized, and
            solid justifications need to exist to support every act of
            denormalization.



            Nevertheless, fully normalized databases can require complex
            programming and generally require more joins than their unnormalized
            or denormalized counterparts. Joins are resource-intensive
            operations; thus, the more joins, the more time a query will take.



            To deal with queries that take too long or are too complex to be
            maintainable, a database architect denormalizes the database. As we
            have seen from the process of normalization, each lower normal form
            introduces database anomalies that can compromise the integrity,
            maintainability, and extensibility of the database. Denormalization
            is thus a reasoned trade-off between query complexity/performance and
            system integrity, maintenance, and extensibility.




            The Perils of Denormalization



            Denormalization must be approached with caution. In general, a table
            should have proven it requires denormalization in testing or even in
            production before you actually denormalize it. Data architects very
            commonly denormalize based on hunches about performance or experience
            with similar applications in the past梐 practice that leads
            down the path to a poorly designed database.



            Denormalization can in some circumstances incur performance
            penalties. More important, however, most of the time you do not see
            the kinds of performance improvements from denormalization that
            actually make a difference. When you denormalize without concrete
            performance benchmarks backing the denormalization, you end up:



            • Denormalizing tables without appreciable performance improvement

            • Denormalizing again later, after you have done performance testing


            The result is a database that looks more unnormalized than
            denormalized. The best rule of thumb is to prove the database needs
            denormalization and document that need for the people who will be
            maintaining the database. Subsequently, you should prove that your
            denormalization actually improves performance and back out the
            changes if they fail to address the performance concerns.





            In most cases, you can deal with complexity simply by creating views
            that hide the complexity. Performance is thus the general
            driver of denormalization. To determine whether denormalization makes
            sense, I recommend

            Craig
            Mullins's simple guidelines posted in an online
            article for The Data Administration Newsletter
            in an article called "Denormalization
            Guidelines" (http://www.tdan.com/i001fe02.htm):



            • Can you achieve performance goals without denormalization?

            • Will the system still fail to achieve performance goals with
              denormalization?

            • Will the system be less reliable as a result of denormalization?


            If you answer "yes" to any of these
            questions, you should not use denormalization as your performance
            tuning tool.




            BEST PRACTICE: Denormalize only when you have concrete proof that denormalization will boost performance.




            The most common temptation to denormalize comes from queries that
            require joins to retrieve a single value. Any query pulling a
            film's suitability for children along with the film
            from the database would fall into this category. For example:



            SELECT Film.title, Film.language, Film.year, Rating.forChildren
            FROM Film, Rating
            WHERE Film.filmID = 2
            AND Film.rating = Rating.code;


            Denormalization would move the rating code and suitability for
            children back into the Film table.
            Wouldn't the query perform much better without that
            join? Actually, it probably would not perform
            noticeably better梩he join is done using a
            unique index (Rating.code). Denormalization,
            however, would incur all of the anomalies that led us to normalize
            the table in the first place.



            A better candidate for normalization might be pulling a state name
            into an Address table along with the state code
            used in the join. If most queries actually want the state name and
            the query would definitely benefit from avoiding the join to the
            State table, it can make sense to add an extra
            column to Address for
            stateName. You do not, however, remove the
            State table. This denormalization
            works梐ssuming real performance benefits are achieved for the
            application梑ecause the state name is a candidate key for the
            State table. Though stateName
            would technically be a transitive depencency in the
            Address table (and thus violate 3NF), its status
            as a candidate key for State makes it almost a
            functional depencency and consequently almost acceptable to put into
            the Address table.



            A common situation in which performance does truly become a problem
            is reporting. For reporting, database normalization is just one of
            many factors that lead to performance degradation. Because complex
            reports generally eat server resources regardless of normalization
            issues, it is generally a bad idea to empower users to execute
            complex reports against live tables. Instead, you can denormalize by
            replicating the data into special tables designed to support
            reporting needs. To create a table for reporting on westerns, we
            might create a WesternReport table that looks like
            the table in Table 2-9.



            Table 2-9. A table for reporting on westerns

            Attribute



            Domain



            Notes



            NULL?



            filmID



            BIGINT



            PRIMARY KEY



            No



            title



            VARCHAR(100)


             

            No



            rating



            CHAR(5)


             

            Yes



            forChildren



            CHAR(1)



            DEFAULT 'N'



            No



            otherGenres



            VARCHAR(255)


             

            Yes



            directors



            VARCHAR(255)


             

            Yes



            actors



            VARCHAR(255)


             

            Yes



            ranking



            INT


             

            No



            year



            INT


             

            No




            Reporting on all of the westerns from 1992 would look like this:



            SELECT * FROM WesternReport
            WHERE year = 1992;


            The alternative is to have users constantly executing the following
            query against the tables that actually maintain your data:



            SELECT Film.filmID, Film.title, Film.rating,
            IFNULL(Rating.forChildren, 'N'), Film.ranking, Film.year
            FROM Film, FilmGenre
            LEFT OUTER JOIN Rating ON Film.rating = Rating.code

            WHERE FilmGenre.code = 'WES'
            AND year = 1992
            AND Film.filmID = FilmGenre.filmID;


            Use follow-up queries to get other genres, directors, and actors
            associated with the film.







              [ Team LiB ]



              Exercises











              [Page 298]

              Exercises


              Note



              For programming exercises, first draw a UML class diagram describing all classes and their inheritance relationships and/or associations.




              Exercise 6.1

              Explain the difference between the following pairs of terms:


              1. Counting loop and conditional loop.

              2. For statement and while statement.

              3. While statement and do-while statement.

              4. Zero indexing and unit indexing.

              5. Sentinel bound and limit bound.

              6. Counting bound and flag bound.

              7. Loop initializer and updater.

              8. Named constant and literal.

              9. Compound statement and null statement.


              Exercise 6.2

              Fill in the blank.


              1. The process of reading a data item before entering a loop is known as a _______.

              2. A loop that does nothing except iterate is an example of _______.

              3. A loop that contains no body is an example of a statement_______.

              4. A loop whose entry condition is stated as (k < 100 || k >= 0) would be an example of an ________ loop.

              5. A loop that should iterate until the user types in a special value should use a _______ bound.

              6. A loop that should iterate until its variable goes from 5 to 100 should use a _______ bound.

              7. A loop that should iterate until the difference between two values is less than 0.005 is an example of a _______bound.

              Exercise 6.3

              Identify the syntax errors in each of the following:


              1. for (int k = 0; k < 100; k++) System.out.println(k)

              2. for (int k = 0; k < 100; k++); System.out.println(k);

              3. int k = 0 while k < 100 {System.out.println(k); k++;}

              4. int k = 0; do {System.out.println(k); k++;} while k < 100 ;

              Exercise 6.4

              Determine the output and/or identify the error in each of the following code segments:


              1. for (int k = 1; k == 100; k += 2) System.out.println(k);

              2. int k = 0; while (k < 100) System.out.println(k); k++;

              3. for (int k = 0; k < 100; k++) ; System.out.println(k);


              [Page 299]
              Exercise 6.5

              Write pseudocode algorithms for the following activities, paying particular attention to the initializer, updater, and boundary condition in each case.


              1. a softball game

              2. a five-question quiz

              3. looking up a name in the phone book

              Exercise 6.6

              Identify the pre- and postconditions for each of the statements that follow. Assume that all variables are int and have been properly declared.


              1. int result = x / y;

              2. int result = x % y;

              3. int x = 95; do x /= 2; while(x >= 0);

              Exercise 6.7

              Write three different loopsa for loop, a while loop, and a do-while loopto print all the multiples of 10, including 0, up to and including 1,000.



              Exercise 6.8

              Write three different loopsa for loop, a while loop, and a do-while loopto print the following sequence of numbers: 45, 36, 27, 18, 9, 0, -9, -18, -27, -36, -45.

              Exercise 6.9

              Write three different loopsa for loop, a while loop, and a do-while loopto print the following ski-jump design:


              #
              # #
              # # #
              # # # #
              # # # # #
              # # # # # #
              # # # # # # #


              Exercise 6.10

              The Straight Downhill Ski Lodge in Gravel Crest, Vermont, gets lots of college students on breaks. The lodge likes to keep track of repeat visitors. Straight Downhill's database includes an integer variable, visit, which gives the number of times a guest has stayed at the lodge (one or more). Write the pseudocode to catch those visitors who have stayed at the lodge at least twice and send them a special promotional package (pseudocode = send promo). (Note: The largest number of stays recorded is eight. The number nine is used as an end-of-data flag.)

              Exercise 6.11

              Modify your pseudocode in the preceding exercise. In addition to every guest who has stayed at least twice at the lodge receiving a promotional package, any guest with three or more stays should also get a $40 coupon good for lodging, lifts, or food.


              [Page 300]
              Exercise 6.12

              Write a method that is passed a single parameter, N, and displays all the even numbers from 1 to N.

              Exercise 6.13

              Write a method that is passed a single parameter, N, that prints all the odd numbers from 1 to N.

              Exercise 6.14

              Write a method that is passed a single parameter, N, that prints all the numbers divisible by 10 from N down to 1.

              Exercise 6.15

              Write a method that is passed two parametersa char Ch and an int Nand prints a string of N Chs.



              Exercise 6.16

              Write a method that uses a nested for loop to print the following multiplication table:


                1  2  3  4  5  6  7  8  9
              1 1
              2 2 4
              3 3 6 9
              4 4 8 12 16
              5 5 10 15 20 25
              6 6 12 18 24 30 36
              7 7 14 21 28 35 42 49
              8 8 16 24 32 40 48 56 64
              9 9 18 27 36 45 54 63 72 81



              Exercise 6.17

              Write a method that uses nested for loops to print the patterns that follow. Your method should use the following statement to print the patterns: System.out.print('#').


              # # # # # # # #   # # # # # # # #      # # # # # # # #   # # # # # # # #
              # # # # # # # # # # # # # # # # #
              # # # # # # # # # # # # # # #
              # # # # # # # # # # # # #
              # # # # # # # # # # #
              # # # # # # # # #
              # # # # # # #
              # # # # # # # # # # # # # # # # # #



              Exercise 6.18

              Write a program that asks the user for the number of rows and the number of columns in a box of asterisks. Then use nested loops to generate the box.


              [Page 301]
              Exercise 6.19

              Write a Java application that lets the user input a sequence of consecutive numbers. In other words, the program should let the user keep entering numbers as long as the current number is one greater than the previous number.

              Exercise 6.20

              Write a Java application that lets the user input a sequence of integers terminated by any negative value. The program should then report the largest and smallest values that were entered.

              Exercise 6.21

              How many guesses does it take to guess a secret number between 1 and N? For example, I'm thinking of a number between 1 and 100. I'll tell you whether your guess is too high or too low. Obviously, an intelligent first guess would be 50. If that's too low, an intelligent second guess would be 75. And so on. If we continue to divide the range in half, we'll eventually get down to one number. Because you can divide 100 seven times (50, 25, 12, 6, 3, 1, 0), it will take at most seven guesses to guess a number between 1 and 100. Write a Java applet that lets the user input a positive integer, N, and then reports how many guesses it would take to guess a number between 1 and N.

              Exercise 6.22

              Suppose you determine that the fire extinguisher in your kitchen loses X percent of its foam every day. How long before it drops below a certain threshold (Y percent), at which point it is no longer serviceable? Write a Java applet that lets the user input the values X and Y and then reports how many weeks the fire extinguisher will last.

              Exercise 6.23

              Leibnitz's method for computing p is based on the following convergent series:








              How many iterations does it take to compute p to a value between 3.141 and 3.142 using this series? Write a Java program to find out.

              Exercise 6.24

              Newton's method for calculating the square root of N starts by making a (nonzero) guess at the square root. It then uses the original guess to calculate a new guess, according to the following formula:


              guess = (( N / guess) + guess) / 2;


              No matter how wild the original guess is, if we repeat this calculation, the algorithm will eventually find the square root. Write a square root method based on this algorithm. Then write a program to determine how many guesses are required to find the square roots of different numbers. Uses Math.sqrt() to determine when to terminate the guessing.


              [Page 302]
              Exercise 6.25

              Your employer is developing encryption software and wants you to develop a Java applet that will display all of the primes less than N, where N is a number to be entered by the user. In addition to displaying the primes themselves, provide a count of how many there are.

              Exercise 6.26

              Your little sister asks you to help her with her multiplication, and you decide to write a Java application that tests her skills. The program will let her input a starting number, such as 5. It will generate multiplication problems ranging from 5 x 1 to 5 x 12. For each problem she will be prompted to enter the correct answer. The program should check her answer and should not let her advance to the next question until the correct answer is given to the current question.

              Exercise 6.27

              Write an application that prompts the user for four values and draws corresponding bar graphs using an ASCII character. For example, if the user entered 15, 12, 9, and 4, the program would draw


              ******************
              ************
              *********
              ****

              Exercise 6.28

              Revise the application in the preceding problem so that the bar charts are displayed vertically. For example, if the user inputs 5, 2, 3, and 4, the program should display


              **
              ** **
              ** ** **
              ** ** ** **
              ** ** ** **
              -------------

              Exercise 6.29

              The Fibonacci sequence (named after the Italian mathematician Leonardo of Pisa, ca. 1200) consists of the numbers 0, 1, 1, 2, 3, 5, 8, 13, . . . in which each number (except for the first two) is the sum of the two preceding numbers. Write a method fibonacci(N) that prints the first N Fibonacci numbers.

              Exercise 6.30

              The Nuclear Regulatory Agency wants you to write a program that will help determine how long certain radioactive substances will take to decay. The program should let the user input two values: a string giving the substance's name and its half-life in years. (A substance's half-life is the number of years required for the disintegration of half of its atoms.) The program should report how many years it will take before there is less than 2 percent of the original number of atoms remaining.


              [Page 303]
              Exercise 6.31

              Modify the CarLoan program so that it calculates a user's car payments for loans of different interest rates and different loan periods. Let the user input the amount of the loan. Have the program output a table of monthly payment schedules.


              The next chapter also contains a number of loop exercises.