http://www.developer.com/

Back to article

Protecting Data through Object Oriented Programming


December 30, 2004

 

 

Introduction

 

This is the eight installment in a series of articles about fundamental object-oriented (OO) concepts. The material presented in these articles is based on material from the second edition of my book, The Object-Oriented Thought Process, 2nd edition. The Object-Oriented Thought Process is intended for anyone who needs to understand the basic object-oriented concepts before jumping into the code. Click here to start at the beginning of the series.

 

In keeping with the code examples used in the previous articles, Java will be the language used to implement the concepts in code. One of the reasons that I like to use Java is because you can download the Java compiler for personal use at the Sun Microsystems Web site http://java.sun.com/. You can download the J2SE 1.4.2 SDK (software development kit) to compile and execute these applications and will provide the code listings for all examples in this article. I have the SDK 1.4.0 loaded on my machine. I will provide figures and the output for these examples. See previous articles in this series for detailed descriptions for compiling and running all the code examples in this series.

 

Checking Account Example

 

In this column we will focus on how to protect data from either unintentional mistakes or intentional malicious behavior. To accomplish this we will continue in our development of an ATM example. In the previous column, we developed a simple checking account example. Let's do a quick review of this class before we proceed.

 

Figure 1 shows the UML class diagram that corresponds to our latest design.

 

Figure 1: UML Diagram for Checking Account Class

 

Listing 1 displays the code that corresponds directly to the class in Figure 1.

 

class CheckingAccount {
 
   private double balance = 0;
 
   private void setBalance(double bal) {
 
      balance = bal;
 
   };
 
   public double getBalance(){
 
      return balance;
 
   };
 
   public void deposit(double dep){
 
        setBalance(balance + dep);
 
 
   };
 
   public void withdrawal(double with){
 
        setBalance(balance - with);
 
 
   };
 
}

Listing 1: CheckingAccount.java

In effect we now have the following components to our CheckingAccount class.

 

  1. The private attributes
    1. balance
  2. The private implementation
    1. setBalance()
  3. The public interfaces
    1. deposit()
    2. withdrawal()
    3. getBalance()

 

Now that we have developed the CheckingAccount class - let's use the class in an actual application.

 

Creating an Application

 

It is important to realize that an application must be created before utilizing a class. In Java, an application is defined by a method called main(). Technically, everything in Java is considered a class (except for some primitives like int and float). To get a application up and running we will create 2 Java files and each of these files will contain a class. These 2 files will contain the CheckingAccount class and the main() application class (I put only one class per each file). We have already seen the CheckingAccount class, the barebones main() application class is contained in listing 2.

 

class TestCheckingAccount {
    public static void main(String args[]) {

    }
}

Listing 2: TestCheckingAccount.java

The line of code that contains main() defines this as an application - basically the entry point to the applications. Notice that the file names must match the class names. Thus, the class TestCheckingAccount is contained in a file called TestCheckingAccount.java. At this point the TestCheckingAccount application will execute but it doesn't do anything. What we want it to do is utilize the CheckingAccount class that we have created. We can instantiate a CheckingAccount object in our application by adding the code in Listing 3.

 

class TestCheckingAccount {
    public static void main(String args[]) {

        CheckingAccount myAccount = new CheckingAccount();

    }
}

 

Listing 3: TestCheckingAccount.java

When this code is executed an actual CheckingAccount object, called myAccount,  is created; however, we still have yet to do anything with it. To illustrate what we can do with the newly created object, let's examine what the value of the current balance is. Since we initialized it to 0, we should expect that to be the value.

This is a great opportunity to explore how data in an object is actually protected. Consider the possibility that a rogue employee decides to write an application which will inspect the balances of all checking accounts. At first, this seems like a very simple problem to solve - simply write code that inspects the attribute balance directly. The code may look something like Listing 4.

class TestCheckingAccount {
    public static void main(String args[]) {

        CheckingAccount myAccount = new CheckingAccount();

        System.out.println("balance = " + myAccount.balance);

    }
}

Listing 4: TestCheckingAccount.java

The logic of this code dictates that the balance will be printed when the println() is executed.

        System.out.println("balance = " + myAccount.balance);

This line attempts to access the attribute balance of the myAccount object. In a proper object-oriented design, it is unacceptable for an application to have uncontrolled access to an attribute. This is a security issue. You never want uncontrolled access to data in an object. In fact, this data protection is already designed into the CheckingAccount class. Remember that the balance attribute was declared as private. Thus, when we try to compile the code in Listing 4 the compiler acts as a security guard and will not let the code compile. The compiler produces the following error:

TestCheckingAccount\TestCheckingAccount.java:6: balance has private access in CheckingAccount
System.out.println("balance = " + myAccount.balance);
 

Pay special attention to the word private. Since we (properly) declared balance as private, the attribute is protected from uncontrolled access. You may be wondering what the term uncontrolled access actually means since we have used it several times already.

Data Protection

Consider what happens when you use an ATM and you want to inspect your account balance. After you swipe your card, do you have uncontrolled access to your balance at that time? No. You still need to do at least one more thing - enter your PIN. It is only after you enter your PIN that you gain access to you account and start posting transactions. This is what is meant by uncontrolled access. In the vast majority of  business transactions, controlled access to data is vital - even required by law. This is why attributes in an object are accessed via methods and not referenced directly. We have no mechanism to control access to public attributes. However, we can insert code into methods to control access to attributes in objects. Perhaps the most common example would be that of passwords - which are similar to the PIN example.

Consider your email client. What happens when you want to change your password? Are you able to directly change your password? Absolutely not. You must execute a change password routine to accomplish the task. In fact, it is not quite as simple as that. You not only have to execute the change password routine, you must also know your original password. This is an obvious security issue. You don't want to walk away from your computer for a break and then have someone sit down at your machine and directly reset your password. What you do want is for a change password routine to require the original password be entered before the new password is set.

We will explore this password issue later, however; we can now return to the problem of how to inspect the balance. At this point the answer should be obvious. The answer is a method and in this case the getBalance() method. This code is shown in Listing 5.

class TestCheckingAccount {
    public static void main(String args[]) {

        CheckingAccount myAccount = new CheckingAccount();

        System.out.println("balance = " + myAccount.getBalance());

    }
}

Listing 5: TestCheckingAccount.java

Thus, the way to access (inspect) the balance attribute is through the  method getBalance().

        System.out.println("balance = " + myAccount.getBalance());
 

When this code executes, the output produced is presented in Figure 2. Here we can see that the initial value of balance is indeed 0.

Figure 2: TestCheckingAccount Application

There is one more level of data protection that was designed into the CheckingAccount class. Consider that our rogue programmer figures out pretty quickly that there is absolutely no way to directly access the balance attribute. If we need to access via a method that is what we will do. There is a method called setBalance() so let's set the balance to $1000. Consider the code in Listing 6.

class TestCheckingAccount {
    public static void main(String args[]) {

        CheckingAccount myAccount = new CheckingAccount();

        System.out.println("balance = " + myAccount.getBalance());

        myAccount.setBalance(1000);

    }
}

Listing 6: TestCheckingAccount.java

Here the programmer attempts to set the balance through the setBalance() method. However, the compiler will flag this as well because the method was declared as private. Basically , the designers of the CheckingAccount class wanted this method to be used only by objects of this class.

        myAccount.setBalance(1000);
 

This code produces the following compiler error:

TestCheckingAccount\TestCheckingAccount.java:8: setBalance(double) has private access in CheckingAccount
myAccount.setBalance(1000);
 

Thus, while we can say that all attributes should be private, we cannot say that all methods are public. Perhaps most methods will be public; however,  there are appropriate design situations for private methods. In the case of the CheckingAccount class, the only way to change the balance attribute is to either make a deposit or a withdrawal.

In Listing 7 the code is included to make a deposit and then a withdrawal.

class TestCheckingAccount {
    public static void main(String args[]) {

        CheckingAccount myAccount = new CheckingAccount();

        System.out.println("balance = " + myAccount.getBalance());

        myAccount.deposit(1000);

        myAccount.withdrawal(100);

        System.out.println("balance = " + myAccount.getBalance());

   
    }
}

Listing 7: TestCheckingAccount.java

In Listing 7 we make a deposit of $1000 and then withdrawal $100. This should leave us with a balance of $900. By executing the application we can verify this. Figure 3 shows what happens when we do execute this application.

Figure 3: TestCheckingAccount Application

Note: You may be wondering why our rogue programmer would even know the names of private attributes and methods. The answer is that the programmer would not - or at least should not. If the application environment is properly designed, the application programmer would not have access to the design or the source code of the CheckingAccount class. The programmer's use of the CheckingAccount class would be via class packages (in some ways analogous to libraries). Only the documentation of the public methods would be made available to the programmer. In theory, the programmer would not even know the name of the balance attribute or the setBalance() method. You may not even want to choose common names for these attributes and methods.

Adding a Password

Finally, let's implement a simple password example to demonstrate some of the data protection concepts discussed earlier. This will be a very primitive example, but it illustrates the point.

Listing 8 includes new code in the CheckingAccount class that implements a method to change a password attribute. The password is initially set to a blank.

class CheckingAccount {

    private double balance = 0;
    private String password = " ";

    public void setPassword(String s1, String s2) {
        if (password = = s1) {
            password = s2;
            System.out.println("Password changed");
        } else {
            System.out.println("Bad password");
        }
    };
    private void setBalance(double bal) {
        balance = bal;
    };
    public double getBalance(){
        return balance;
    };
    public void deposit(double dep){
        setBalance(balance + dep);
    };
    public void withdrawal(double with){
        setBalance(balance - with);
    };


}

Listing 8: CheckingAccount.java

This is a very simplistic implementation intended to illustrate the concepts. In this code, we call setPassword() with 2 parameters. The first is the old password and the second is the new password. If the old password is incorrect, an error message is printed. If the old password in entered correctly , then the password is changed and a message indicating this is printed.

    public void setPassword(String s1, String s2) {
        if (password = = s1) {
            password = s2;
            System.out.println("Password changed");
        } else {
            System.out.println("Bad password");
        }
    };

This is a good example of how access to an attribute is controlled. And this entire article is all about stressing the importance of controlling access to all attributes. Listing 9 shows the an application that does not use the proper old password.

class TestCheckingAccount {
    public static void main(String args[]) {

        CheckingAccount myAccount = new CheckingAccount();

        myAccount.setPassword("x", "1234");

    }
}

Listing 9: TestCheckingAccount.java

As we can see when we run the application, the class will not allow the password to be changed if the old password is incorrectly entered. figure 4 shows the output.

Figure 4: TestCheckingAccount Application

Conclusion

In this month's article, we continued our discussion about encapsulation and data hiding. Controlling access to all attributes is one of the most important concepts of object-oriented design. By using methods to control access to attributes we can provide a much higher level of security for your class as well as providing many programming advantages.

Next month we will discuss the importance of putting an object into what is called a safe state. Not only is it important for an object to have the appropriate security, but the object must also be robust.

About the Author

Matt Weisfeld is an Assistant Professor at Cuyahoga Community College (Tri-C) in Cleveland, Ohio. Matt is a part of the Information Technology department, teaching programming languages such as C++, Java, and C# .NET as well as various Web technologies. Prior to joining Tri-C, Matt spent 20 years in the information technology industry gaining experience in software development, project management, business development, corporate training, and part-time teaching. Matt holds an MS in computer science and an MBA in project management.

The articles in this series are adapted from The Object-Oriented Thought Process 2nd Edition (published by Sams Publishing). Matt has published two other computer books, and more than a dozen articles in magazines and journals such as Dr. Dobb's Journal, The C/C++ Users Journal, Software Development Magazine, Java Report, and the international journal Project Management. Matt has presented at conferences throughout the United States and Canada.

Sitemap | Contact Us

Thanks for your registration, follow us on our social networks to keep up-to-date