Part 5: Behaviour

 
 
 
 

5.1   Specification

        The problem specification is unchanged.
 
 

5.2   Analysis

        The problem solution is improved in three ways, by making as many features as possible private within their class, and by adding assertions to check that the code executes as advertised.

5.2.1         Creation status

        The creation status of a routine specifies who can call the routine as a creation routine. Here it is simple: bank creates customer, and customer creates account.

5.2.2         Export policies

        The default design rule is to hide everything. An attribute should definitely be hidden, along with the routines that set and show that attribute. If an attribute has to be used outside its class, then there is no choice and the attribute is exported. A routine should be hidden. If a routine is called from outside the class then there is no choice, and the routine has to be exported. These rules lead to very simple class interfaces.

        The only feature that "should" be private but is not is the routine in ACCOUNT to show the balance. The bank wants to show the balance, so we have two choices: export the routine to BANK, or place a feature show_balance in CUSTOMER and export the ACCOUNT routine to CUSTOMER, and the CUSTOMER routine to BANK. Some authors (Lieberherr, 1996) believe that compound calls (that skip classes) are bad style, because they are invisible in the middle class’s definition. The price for adding a routine is an additional routine, instead of a different export policy. The only effective feature is the one in class ACCOUNT, so the export policy of this feature is changed.

5.2.3         Assertions

        A class invariant defines what must be true about the class. Here, the balance of an account must never be negative.

        A pre-condition contrains the value of an argument. Here, constraints can be placed on the arguments to deposit (the amount must be positive) and to withdraw (the amount must positive and less than or equal to the balance of the account). A pre-condition cannot be placed on the creation routine, because it receives no arguments.

        All the functions in the system are simple expressions, with no post-conditions.

        A creation procedure has a post-condition that describes the new state of the attributes it creates or sets. Usually the condition asserts that the identifier contains a value (is not Void, NUL, or 0). A more precise assertion should be made if possible: here, we can assert the exact range of values for gender, because the valid values are known. A procedure that changes a value has a post-condition that describes the change, by comparing the old and current values of the identifier.

        Nothing can be asserted about the output routines, because there is no way to test the "value" of the terminal screen. Nothing can be asserted about a routine call, except that the routine was called when the system ran.
 
 

5.3   Design

        Creation status, export policies, and assertions are added. The system can store invalid values and even crash at run time with invalid input, for the current code and assertions. The next part of the case study shows how guards are added to stop the assertions from failing.
 
 

5.4   Client chart and class diagrams

        The client chart is unchanged from the previous case study, and is repeated below
 
 


 
 
 

        A class diagram for each class in this version of the case study is shown below. The interface for BANK is trivial, because the code in the class is always controlled by a single make routine. The interface of CUSTOMER is very simple, because the use of a customer was fixed by the specification. A more common and flexible use would require more of the CUSTOMER private features to be exported.
 
 


 
 

5.5 Solution code

        Only routine headers and assertions are shown below; the routine bodies are unchanged.
 

class BANK

creation
      make

feature {ANY}
      patron: CUSTOMER

      make is
                      -- create, show and use a customer
              ensure patron_exists: patron /= Void

end -- class BANK
 

class CUSTOMER

creation {BANK}
      make

feature {NONE}
      name: STRING

      get_name is
                      -- read in and set the name
              ensure name_exists: name /= Void

      gender: CHARACTER

      get_gender is
                      -- read in and set the gender
              ensure valid_gender: gender.upper = ‘M’ or gender.upper = ‘F’

      address: STRING

      get_address is
                      -- read in and set the address
              ensure address_exists: address /= Void

      account: ACCOUNT

feature {BANK}
      make is
                      -- create the customer from data input by the user
              ensure account_exists: account /= Void

      use is
                      -- deposit, withdraw, and show the balance
                      -- add interest and show the balance

      show is
                      -- show the customer details

feature {NONE}
      deposit is
                      -- read in an amount and deposit it

      withdraw is
                      -- read in an amount and withdraw it

end -- class CUSTOMER
 

class ACCOUNT

creation {CUSTOMER}
      make

feature {NONE}
      balance: REAL

      get_balance is
                      -- read in a balance from the user and store it

      show_balance is
                      -- show the balance

      rate: REAL is 4.5

      interest: REAL is
                      -- the interest for today

      day_rate: REAL is
                      -- daily interest rate

      show_rate is
                      -- show the interest rate

feature {CUSTOMER}
      make is
                      -- read in and set the initial balance

      show is
                      -- show the balance and interest_rate

      deposit (amount: REAL) is
                      -- add this amount to the balance
              require
                      positive: amount > 0
              ensure
                      more: balance = old balance + amount

      enough (amount: REAL): BOOLEAN is
                      -- is there at least this amount in the account?
              do
                      Result := amount <= balance
              end -- enough

      withdraw (amount: REAL) is
                      -- subtract this amount from the balance
              require
                      positive: amount > 0
                      enough: enough
              ensure
                      less:balance = old balance - amount
              end -- deposit

      add_interest is
                      -- add the daily interest to the balance

invariant
        not_negative_balance: balance >= 0.0

end -- class ACCOUNT
 
 

5.6   Common errors

1.     Export the attributes. This forces a client to know about the internal details of the class, and makes a system hard to maintain and extend for that reason. The rule is to export the behaviour, and hide the implementation. The default design choice is to hide an attribu