http://www.developer.com/

Back to article

Objects and Interfaces


October 4, 2006

This series, The Object-Oriented Thought Process, is intended for someone just learning an object-oriented language and who wants to understand the basic concepts before jumping into the code, or someone who wants to understand the infrastructure behind an object-oriented language he or she is already using. These concepts are part of the foundation that any programmer will need to make the paradigm shift from procedural programming to object-oriented programming.

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 standard edition, J2SE 5.0, at http://java.sun.com/j2se/1.5.0/download.jsp to compile and execute these applications. I often reference the Java J2SE 5.0 API documentation and I recommend that you explore the Java API further. Code listings are provided for all examples in this article as well as figures and output (when appropriate). See the first article in this series for detailed descriptions for compiling and running all the code examples.

In a previous column, you continued to learn about the various Java collections by covering the ArrayList collection. Before I proceed with coverage of more collections, I will stop and concentrate on the topic of interfaces. The concept of an interface is core to any object-oriented design or implementation.

Interfaces

Interfaces are an extremely important concept; however, they are also very tricky to understand. One problem is that there are many programming concepts that are referred to as an interface. The following three bullet items are the most common, but not the only, concepts that are called an interface.

  • Graphical User Interface
  • Interface versus Implementation
  • Object-oriented interface (as in Java and/or .NET)

Graphical User Interface

Anyone who has used a computer is familiar with a Graphical User Interface or GUI. This term relates to a screen that contains graphical components—such as buttons, labels, and menu items—that is meant to interface with a user. Filling out an on-line form is a great example of a GUI.

Interface versus Implementation

In a design exercise, the interface versus implementation question is also a key object-oriented concept, but it is not the same thing as a Java/.NET interface. You can go back to the second article in this series and look at the example of generating electricity.

The design interface is the fundamental means of communication between objects. Each class design specifies the interfaces for the proper instantiation and operation of objects. Any behavior that the object provides must be invoked by a message sent using one of the provided interfaces. The interface should completely describe how users of the class interact with the class. In Java, the methods that are part of the interface are designated as public.

Interfaces do not normally include attributes, only methods. If a user needs access to an attribute, a method is created to return the attribute (a getter). If a user wants the value of an attribute, a method that returns the value of the attribute is called. In this way, the object that contains the attribute controls access to it. This is of vital importance, especially in terms of testing and maintenance.

If you control the access to the attribute, when a problem arises, you do not have to worry about tracking down every piece of code that might have changed the attribute. It can only be changed in one place (the setter).

Only the public attributes and methods are considered the interface. The user should not see any part of the implementation interacting with an object solely through class interfaces. In many cases, there will be methods that also should be hidden and not be part of the interface. Thus, the implementation can change and it will not affect the user's code.

Figure 1 illustrates the interface/implementation paradigm using real-world objects rather than code. The toaster obviously requires electricity. To get this electricity, the cord from the toaster must be plugged into the electrical outlet, which is the interface. All the toaster needs to do to get the required electricity is to use a cord that complies with the electrical outlet specifications; this is the interface between the toaster and the electricity.

The fact that the actual implementation is a coal-powered electric plant is not the toaster's concern. In fact, for all the toaster cares, the implementation could be a nuclear power plant or a local power generator. With this model, any appliance can get electricity, as long as it conforms to the interface specification seen in Figure 1.

Figure 1: Interface/Implementation

Java/.NET Interface

In this column, you will concentrate on this type of interface, which I call the object-oriented interface. This type of interface is provided by Java and .NET.

The Interface

The interface is the services that are presented to an end user. In the best case, only the services that the end user needs are presented. Of course, which services the user needs may be a matter of opinion. If you put 10 people in a room and ask each of them to do an independent design, you might receive 10 totally different designs. There is nothing wrong with this. However, as a rule of thumb, the interface to a class should contain only what the user needs to know. In the toaster example, the user only needs to know that the toaster must be plugged into the interface—which in this case is the electrical outlet

Perhaps the most important issue when designing a class is identifying the audience, or users, of the class.

The Implementation

The implementation details of the interface services are hidden from the user. One goal regarding the implementation should be kept in mind: A change to the implementation should not require a change to the user's code. This may seem a bit confusing, but this goal is at the heart of the design issue.

Remember that the interface includes the syntax to call a method and return a value. If this interface does not change, the user does not care whether the implementation is changed. As long as the programmer can use the same syntax and retrieve the same value, that is all that matters.

Recall that in the toaster example, although the interface is always the electric outlet, the implementation could change from a coal power plant to a nuclear power plant without affecting the toaster. There is one very important caveat to be made here: The coal or nuclear plant must also conform to the interface specification. If the coal plant produces AC power, but the nuclear plant produces DC power, there is a problem. The bottom line is that both the user and the implementation must conform to the interface specification.

To Reuse or Not to Reuse?

One of the major advantages touted by O-O proponents is that you can write code once, and then reuse it to your heart's content. This is true to a certain degree. As with all design approaches, the utility and the reusability of code depends on how well it was designed and implemented. O-O design does not hold the patent on code reuse. There is nothing stopping anyone from writing very robust and reusable code in a non–O-O language. Certainly, there are countless numbers of routines and functions, written in structured languages such as COBOL and C, that are of high quality and quite reusable.

Thus, it is clear that following the O-O paradigm is not the only way to develop reusable code. Yet, the O-O approach does provide several mechanisms for facilitating the development of reusable code. One way to create reusable code is to create frameworks. In this chapter, you focus on using interfaces and abstract classes to create frameworks and encourage reusable code.

What Is a Framework?

Hand-in-hand with the concept of code reuse is the concept of standardization, which is sometimes called plug-and-play. The idea of a framework revolves around these plug-and-play and reuse principles. One of the classic examples of a framework is a desktop application. Take an office suite application as an example. The document editor that I am currently using has a menu bar that includes multiple menu options. These options are similar to those in the presentation package and the spreadsheet software. In fact, the first six menu items (File, Edit, View, Insert, Format, and Tools) are the same in all three programs. Not only are the menu options similar, but the first toolbar looks remarkably alike as well (New, Open, Save, and so on). Below the toolbars is the document area—whether it is for a document, a presentation, or a spreadsheet. The common framework makes it easier to learn various applications within the office suite. It also makes a developer's life easier by allowing maximum code reuse.

The fact that all these menu bars have a similar look and feel is obviously not an accident. In fact, when you develop in most integrated development environments, you get certain things without having to create them yourself. When you create a window in a Windows environment, you get elements such as the main title bar and the file close button in the top-right corner. When you double-click on the main title bar, the screen always minimizes/maximizes. When you click on the close button in the top-right corner, the application always terminates. This is all part of the framework.

For example, to create an applet in Java, you would bring up the API documentation for the Applet class and take a look at the public interfaces it presents. By using these APIs, you can create a valid Java applet and conform to required standards. If you follow these standards, your applet will be set to run in Java-enabled browsers.

What Is a Contract?

In the context of this article, you will consider a contract to be any mechanism that requires a developer to comply with the specifications of an Application Programming Interface (API). Often, an API is referred to as a framework. The online dictionary Dictionary.com defines a contract as "an agreement between two or more parties, especially one that is written and enforceable by law."

This is exactly what happens when a developer uses an API—with the project manager or business owner representing the law. In short, when using contracts, the developer is required to comply with the rules defined in the framework. This includes issues such as method names, number of parameters, and so on. In short, standards are created to facilitate good coding practices.

Note: The term contract is widely used in many aspects of business, including software development. Do not confuse the concept here with other possible software design concepts called contracts.

Enforcement is vital because it is always possible for a developer to break a contract. Without enforcement, a rogue developer could decide to reinvent the wheel and write his or her own code rather than use the code provided by the framework. There is little benefit to a standard if people routinely disregard or circumvent it. In Java and the .NET languages, the two ways to implement contracts are to use abstract classes and interfaces.

Designing with Object-Oriented Interfaces

Let me start right away with an example. Suppose that you work for a software development company that is writing software to play a baseball game. You will create a class called Batter and this Batter class will have a behavior (method) called swing. There are a number of programmers assigned to work on the project. One programmer is assigned the task to design and implement the LeftHandedBatter class, and one is assigned to design and implement the RightHandedBatter class. If you then send them off to their respective cubes, they may, possibly, come back with the following two class designs in Listing 1 and Listing 2.

Listing 1: RightHandBatter.java

public class RightHandBatter {

   public void rightHandBatterSwing () {

      System.out.println("Righty Swing");

   }

}

Listing 2: LeftHandBatter.java

public class Lefty {

   public void leftySwing() {

      System.out.println("Lefty Swing");

   }

}

To use these classes, you create an application called TestBatter that is found in Listing 3.

Listing 3: TestBatter.java

class TestBatter{

   public static void main(String[] args){

      Lefty lefty = new Lefty();
      RightHandBatter righty = new RightHandBatter();

      lefty.leftySwing();
      righty.rightHandBatterSwing();

   }
}

Perhaps you have noticed a fundamental flaw in the management, or lack of management, involved in this project. The flaw is that the way you swing differs depending on the type of batter you are. If you are a right-handed batter, you must use the rightHandBatterSwing( ) method, and if you are a left-handed batter, you must use the leftySwing ( ) method. At first, this may not seem like much of a problem; however, the names of the methods are not necessarily consistent. In fact, the decision on the name of the methods was left totally up to the discretion of the programmers. While this may be a good way to encourage creativity, it is not a very good design decision. Now, explore this problem more closely.

As the project manager for this application, one thing that I want to ensure is that the naming conventions are standard, clearly specified, and enforced. For example, if you were a third programmer on this team with the responsibility to actually use the two batter classes, you might logically assume that when any batter swings the method would be called swing( ). While this may seem reasonable, the two programmers in this case came up with two totally different method names. Even though these differing method names would work in practice, they are not good design for several reasons, including the fact that swing( ) is a much better choice. So, how would a project manager force all the Batter classes to use a method called swing( )?

Abstract Methods

This is where the object-oriented interface comes in. Short of doing code reviews and disciplining programmers who do not follow the programming standards, you can create an interface called Batter. You can create a class diagram representing the design of this interface, simple as it is. This class diagram is found in Figure 2. Note that the class name and the method swing( ) are presented in italics, which signifies that the class and the method are abstract.

Figure 2: Batter Interface

The corresponding code is found in Listing 4.

Listing 4: Batter.java

public interface Batter {

   public void swing();

}

By itself, this will not directly remedy the inconsistencies; however, as project manager, I now require that all programmers creating any type of Batter class must implement this Batter interface. While there is not much code in Listing 4, it illustrates a very important concept. By creating this interface, the project manager is saying:

To be a valid type of batter class, you must implement the Batter interface that requires you to implement a method called swing( ).

In effect, the Batter interface is a contract that the programmer must follow. The contract states that if you want to be a Batter in this system, you must implement the Batter interface. Abiding by this contract requires that you provide a swing( ) method. The class diagram for the entire Batter system is shown in Figure 3. Note that both the RightHandBatter class and the LeftHandBatter class must implement the swing( ) method.

Figure 3: Batter System

Thus, when the programmer who creates the right hand batter class writes code that does not conform to this model, as seen in Listing 5, you have a problem.

Listing 5: RightHandBatter.java

public class RightHandBatter implements Batter {

   public void rightHandBatterSwing() {

      System.out.println("Righty Swing");

   }

}

With the Batter interface in place, the programmer who attempts to compile the class in Listing 5 will receive the following compiler error:

C:column27>"C:Program FilesJavajdk1.5.0_06binjavac"
   -Xlint -classpath . RightHandBatter.java

RightHandBatter.java:1: RightHandBatter is not abstract and does
not override abstract method swing() in Batter
public class RightHandBatter implements Batter {
    ^
1 error
C:column27>

The error was generated due to the fact that the programmer who designed this class failed to meet the requirements of the contract. The content of the message is informative. The interface Batter contains a single method specification, swing( ). Note that the swing method has no implementation. By this, I mean that there are no curly braces as follows:

public void swing( ){ };

Even though there is no working code between these curly braces, the mere fact that the curly braces are present provides an implementation. It is an empty implementation; however, it is an implementation nonetheless. The code for the interface contains only the method signature and no curly braces.

public void swing( );

This syntax defines the method as abstract. When a method is abstract, this means that any class implementing or inheriting this abstract method must provide the implementation for the abstract method or that class will itself be considered abstract.

Abstract Hierarchies

For example, consider the model where you want to create subclasses of the RightHandBatter class, say a class called GoodRightHandBatter. In this case, you could implement the swing( ) method in the subclass. Note that in Figure 4, both the Batter and the RighHandBatter classes are abstract—only the final class in the hierarchy, GoodRightHandBatter is considered concrete, which means that you can instantiate it.

Figure 4: Abstract Hierarchy

The error message states that "RightHandBatter is not abstract." This is because it is not defined as abstract. If you wanted to make RightHandBatter abstract, the code would look like this:

public abstract class RightHandBatter implements Batter {

}

The error message also states that "RightHandBatter does not override abstract method swing( ) is Batter". This is the important issue in this example. The compiler is catching the fact that RightHandBatter is not satisfying the contract because it is not overriding the abstract method swing( ). Herein lies the power of the interface. By designing the appropriate interfaces and then requiring their use, software developers have a powerful tool.

To remedy the problem in the example above, a simple redesign is in order. The redesign will require the following code change as seen in Listing 6.

Listing 6: RightHandBatter.java

public class RightHandBatter implements Batter {

   public void swing() {

      System.out.println("Righty Swing");

   }

}

Now that the swing( ) method has been provided, the code for the RightHandBatter class will compile cleanly. The same approach must be followed for the LeftHandBatter class.

Listing 7: LeftHandBatter.java

public class LefttHandBatter implements Batter {

   public void swing( ) {

      System.out.println("Lefty Swing");

   }

}

At this point, you can standardize the way that you have batters swing by simply using a swing( ) method regardless of what type of batter you have at the plate.

Listing 8: TestBatter.java

class TestBatter{

   public static void main(String[] args){

      LeftHandBatter lefty   = new LeftHandBatter();
      RightHandBatter righty = new RightHandBatter();

      lefty.swing( );
      righty.swing( );

   }
}

No matter who the batter is, when you want the batter to swing, you only have to say "swing". This can cut down on any number of errors; however, perhaps the greatest power of the object-oriented interface is the increased usability. Basically, systems with well designed object-oriented interfaces are much easier to use and have less maintenance cost. To illustrate, you can try to enhance this system.

You can create a third type of batter called switchHitter, as seen in Listing 9. The class diagram representing the updated system is found in Figure 5.

Listing 9: SwitchHitter.java

public class SwitchHitter implements Batter {

   public void swing() {

      System.out.println("Switch Hitter Swing");

   }

}

Because the programmer implementing this class is required to implement the interface Batter, the swing( ) method is required. Thus, when the application uses the new SwitchHitter class, all the application needs to know is: If SwitchHitter is a true Batter, all I have to do is say "swing" and the SwitchHitter will swing, as seen in Listing 10.

Listing 10: TestBatter.java

class TestBatter{

   public static void main(String[] args){

      LeftHandBatter lefty      = new LeftHandBatter();
      RightHandBatter righty    = new RightHandBatter();
      SwitchHitter switchHitter = new SwitchHitter ();

      lefty.swing();
      righty.swing();
      switchHitter.swing();

   }
}

Figure 5: Batter Hierarchy

With the Batter interface in place, you can add as many different types of Batters as you like. And, no matter what the type of Batter is or does, you will know that it will have a swing( ) method.

Interfaces versus implementation

Although the object-oriented interface requires that a prescribed contract be followed, there is no way to enforce the code implementation of the contract.

For example, consider the code in Listing 11. This is a simple example. There are no object-oriented interfaces; however, there is a method defined as add( ). The danger with any method, whether it is part of an interface specification or not, is that the implementation within the method body (the actual code) is at the discretion of the programmer.

Listing 11: TestBatter.java

class TestInterface {

   public static void main(String[] args){

      int value = 0;

      value = add(5, 6);

      System.out.println("value = " + value);

   }

   public static int add (int num1, int num2) {

      int num = num1 - num2;

      return num;
   }
}

As can be seen in the example in Listing 11, a programmer may well follow a method specification, in this case add( ); yet the implementation may be at odds with the interface. In this case, the understandable expectation is that the add( ) method will return the sum of the parameters, not the difference, as this code apparently does. There is not a compiler mechanism that can enforce this. In short, using object-oriented interfaces is a great way to enforce the user interfaces, but they will not control the implementation.

Conclusion

When creating an interface, all of the methods must be abstract. There is no actual implementation code in an object-oriented interface. However, there are times when you want to design a class that has both abstract and concrete methods.

In some languages, like C++, the same mechanism is used to create interfaces, which contain only abstract methods, and abstract classes, which contain both abstract and concrete methods.

In the next article, you will explore the differences between abstract classes and interfaces.

References

  • www.sun.com
  • Just Java 2, 6th Edition. Peter van der Linden. 2004, Sun Microsystems.

About the Author

Matt Weisfeld is a faculty member at Cuyahoga Community College (Tri-C) in Cleveland, Ohio. Matt is a member of the Information Technology department, teaching programming languages such as C++, Java, C#, and .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. Besides The Object-Oriented Thought Process, which is now in its second edition, 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