.. _object-oriented-programming: ########################### Object Oriented Programming ########################### .. include:: /references.inc .. seealso:: Reading Assignment Text: Chapter 11 So far in this class, we have been working with the most fundamental components of computer programming, and trying to learn how to use those components in common situations. We have worked a number of fairly simple projects (well, as you learn more, they will seem simple!), each designed to explore the basic features of those components in realistic problems. Unfortunately, the problems we need to be able to address are far more complex than those we have experimented with. Over the course of the last 40 or so years that people have been seriously developing computer programs, they have discovered that programmers need help in building really big programs. The basic components are fine as far as they go, but we need more powerful tools to construct the really big programs. This is just like realizing that building a small house is not the same kind of task as building a skyscraper - we need bigger tools! Around the mid 1980s, people started studying how programs were being constructed, and came up with a new way to think about programming components. The old structured approach was fine, as far as it went, but when we looked closer at the process of breaking down real problems, we noticed that we were building program components that represented real things in the world of the user of our programs. But, we did not have a way to represent those things in really efficient ways. The result of all of this study was to start thinking of programming in terms of a new component - an *object* that has both *physical (sort-of) characteristics*, and a *behavior* that says how it acts. We have talked about building functions to capture the details of a particular activity in a way that hides exactly what is going on inside, and instead presents the user of that function with a simple way to work with the function - the *interface definition*. Functions are a great way to break up a complex program into more manageable chunks. Can we do the same thing with these funky objects that model the real world? ******************** A Bank Account class ******************** Suppose we want to build a program to manage a bank. We expect to create a number of accounts and let customers make deposits and withdrawals. We also want to let them check their balances. Here is a start on such a class: .. literalinclude:: code/BankAccount1.py :linenos: :lines: 1-4 A *class* has a name, and may be related to other classes. All Python classes are related to a general class named *object*, so we say this when we set up the new class. In this code, we have provided a special method called a *constructor* to set up a new bank account *object*, which we save in a variable of our own choosing. All classes must have such a *constructor*, and the name is always this funny *__init__* name. This constructor sets up a special variable called an *instance variable* that tracks the balance of this account. We set it with the value we pass in when we build a new account. .. note:: Since Python has no idea what you will call this *object* when you build a new account, it uses a special name, **self** to refer to it in this code. When we build objects and want to interact with them, **self** will be set up to refer to a specific object. Once the *object* has been created, we can send it messages using the defined methods we set up: * deposit - add some amount to this account * withdrawal - remove some amount from this account * check_balance - report how much is in this account Here is the new code we add to our class to implement these *methods*. Note that each one must be indented the same as the *constructor* and each must include that funny *self* parameter so the class will work right. .. literalinclude:: code/BankAccount1.py :linenos: :lines: 6-13 Finally, we set up some example code in this file to test our new class: .. literalinclude:: code/BankAccount1.py :linenos: :lines: 15-19 This code should run, but after looking at this example, it occurs to us that we could improve our class to make it report what is going on as we process customer activities: .. literalinclude:: code/BankAccount2.py :linenos: This looks a little better. Here is a sample run: .. code-block:: text Account opened with balance: 100 Deposited 200 New balance: 300 Balance check: 300 Improving the class =================== This class does work, but it is not a very good manager of bank accounts. For one thing, the customer can withdraw any amount they like (not a good idea for the bank!) We can modify the class to fix this problem, with a few changes: .. literalinclude:: code/BankAccount3.py :linenos: Now, the account cannot be overdrawn, which is better for the bank: .. code-block:: text Account opened with balance: 100 Deposited 200 New balance: 300 Attempt to overdraw: Requested: 600 Available: 300 Balance check: 300 More banking rules ================== Suppose the bank sets up a few more rules. For instance, we might want to mark the account as either open or closed for transactions. If there is an attempt to overdraw the account, we will close it preventing any new transactions, until the customer specifically reactivates the account. (Perhaps the bank manager has to do this step). Here is a new class that implements this new rules: .. literalinclude:: code/BankAccount4.py :linenos: To make this rule work, we need a new instance variable, *closed*, which is a boolean that says the account is active or closed. When we build the new account, we mark it as active. If the account is closed, we reject any transactions. If the account is active, but we attempt to overdraw, we mark the account as closed! .. code-block:: text Account opened with balance: 100 Deposited 200 New balance: 300 Withdrawal: 100 New balance: 200 Attempt to overdraw by 200 Account is closed No deposits allowed on a closed account Account is closed, call bank manager Looks like this is working. .. literalinclude:: code/BankAccount5.py :linenos: :lines: 38-42 Now, let's show an example of the code it will take to process a deposit for a specific account: .. literalinclude:: code/BankAccount5.py :linenos: :lines: 44-55 Here, we need to search the list of accounts to find the one we are interested in, then deposit to that account. Here is the complete program: .. literalinclude:: code/BankAccount5.py