天天看点

Are You Using Abstract Classes, Polymorphism, and Interfaces? @ JDJ

<script type="text/javascript"> </script> <script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> </script> <script type="text/javascript"> </script><script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> </script>

  If the answer is no, at a minimum Your project needs a code review.

  Let's work on the following assignment: a company has

  employees and consultants. Design Classes with and without the use of

  inheritance to represent the people who work for this company. The

  Classes should have the following methods:

  changeAddress

  promote

  giveDayOff

  raiseSalary

  Promotion means giving one day off and raising the salary by

  a specified percentage. For employees, the method raiseSalary should

  raise the yearly salary and, for consultants, it should increase

  their hourly rate.

  Abstract Classes

  A class is called Abstract if it has at least one Abstract

  (not implemented) method. The keyword Abstract has to be placed in

  the definition of the method(s) and the class itself. For example,

  the class in Listing 1 has three concrete methods and one Abstract.

  (Listings 1-11 can be downloaded from

  http://www.sys-con.com/java/sourcec.cfm.)

  Abstract Classes cannot be instantiated, but they allow You

  to create superClasses that implement some of the functionality,

  while leaving one or more methods to be implemented in subClasses.

  The class Person can contain dozens of concrete methods that

  Are the same for every person, such as changeAddress and giveDayOff,

  but since the process of raising a salary is different for employees

  and consultants, the method raiseSalary should remain Abstract.

  Please note that even though this method is Abstract, it could be

  called in an Abstract class because by the time the concrete class is

  instantiated, the method will be already implemented. Since we have

  two types of workers, let's create subClasses Employee and Consultant

  and implement the method raiseSalary based on different rules (see

  Listings 2 and 3).

  The designer of the class Person may not know the specifics

  of the raising salary process, but this does not stop him or her from

  calling the method raiseSalary. Programmers writing subClasses Are

  forced to write an implementation of this method according to its

  signature declAred in the Abstract class. If they declAre a method

  raiseSalary with a different argument list, this will be considered

  method overloading and the subclass will remain Abstract. The class

  Promoter in Listing 4 shows how to use the Classes Employee and

  Consultant for promoting workers.

  Polymorphism

  A programming language could be considered object-oriented if

  it supports inheritance, encapsulation, and polymorphism. The first

  two notions can be easily defined:

  Inheritance lets You design a class by deriving it from an

  existing one. This feature allows You to reuse existing code without

  doing copy and paste. Java provides the keyword extends for declaring

  inheritance.

  Encapsulation is the ability to hide and protect data. Java

  has access-level qualifiers such as public, private, and protected to

  control who can access class variables and methods. There is also

  so-called package-level protection, which is automatically engaged if

  You don't use any of the access-level keywords.

  Polymorphism, though, is easier to understand through an

  example. Let's look at the Classes Person, Employee, and Consultant

  from a different angle. We'll populate a Vector, mixing up the

  instances of Classes Employee and Consultant - in real life this

  information usually comes from a database. For example, a program

  could get the person's work status from the database and instantiate

  an appropriate concrete class. The class Promoter (see Listing 4)

  will give an additional vacation day and increase the salary or

  hourly rate of every worker by 5%.

  Please note that even though we cast every object from the

  collection workers to the ancestor's type Person in line 17, Listing

  4, the variable pers can hold references to its descendent objects.

  The actual object type will be evaluated during runtime only. This

  feature of object-oriented languages is called runtime or late

  binding.

  The output of the class Promoter will look as follows:

  Class Person: Promoting a worker...

  Class Person: Adding a day off

  Class Employee:Increasing salary by 5%

  Class Person: Promoting a worker...

  Class Person: Adding a day off

  Class Consultant: Increasing hourly rate by 5%

  Class Person: Promoting a worker...

  Class Person: Adding a day off

  Class Employee:Increasing salary by 5%

  Class Person: Promoting a worker...

  Class Person: Adding a day off

  Class Employee:Increasing salary by 5%

  Both Classes Employee and Consultant Are inherited from the

  same base class Person. Instead of having different methods for

  increasing the worker's compensation based on its type, we give a

  polymorphic behavior to the method raiseSalary, which applies

  different business logic depending on the type of object from the

  collection. Even though it looks as if we're calling the same method

  promote, this is not the case. Since the actual object type is

  evaluated during runtime, the salary is raised properly according to

  this particular object's implementation of the method raiseSalary.

  This is polymorphism in action.

  The while loop in the class Promoter will remain the same

  even if we add some other types of workers inherited from the class

  Person. For example, to add a new category of worker - a foreign

  contractor - we'll have to create a class Foreign-

  Contractor derived from the class Person and implement the method

  raiseSalary there. The class Promoter will keep casting all these

  objects to the type Person during runtime and call the method

  raiseSalary of the proper object.

  Polymorphism allows You to avoid Using switch or if

  statements with the expensive operator instanceof. Listing 5 shows an

  ugly alternative to our while loop from the class Promoter that

  assumes there is no Abstract method raiseSalary, but we have separate

  promote methods in each subclass of the Person. This code would work

  slower than the polymorphic version from the class Promoter, and the

  if statement would have to be modified every time a new type of

  worker is added.

  Interfaces

  A similar functionality could be implemented Using Java

  interfaces. We'll keep working with a modified version of the

  ancestor class Person because it has such useful methods as

  changeAddress and giveDayOff. But this class doesn't have to be

  Abstract anymore because the method raiseSalary will be moved to a

  Java interface. The method promote won't be needed; we'd rather make

  the method giveDayOff available to descendants of the class Person by

  changing the private access level to protected (see line 8 in Listing

  6).

  Here's the "interface way" to ensure that each person in the

  firm receives the proper salary raise despite the differences in

  payroll calculation.

  Let's define an interface Payable in Listing 7. More than one

  class can implement this interface (see Listing 8). When the class

  Consultant declAres that it implements interface Payable, it promises

  to write implementations for all methods declAred in this interface -

  in our case it's just one method raiseSalary. Why is it so important

  that the class will "keep the promise" and implement all the

  interface's methods? In many cases interface is a description of some

  behavior. In our case behavior Payable means the existence of the

  method boolean raiseSalary(int percent). If any other class knows

  that Employee implements Payable, it can safely call any method

  declAred in the Payable interface (see the interface example in

  Listing 9).

  Let's forget for a moment about employees and consultants and

  switch to the Java AWT listeners and events. When a class declAres

  that it implements the interface java.awt.Action-

  Listener, a JVM will call the method actionPerformed on this class

  whenever the user clicks on the window's button, and in some other

  cases as well. Try to imagine what would happen if You forgot to

  include the method actionPerformed in Your class. The good news is

  that Your class won't even compile if not all methods declAred in the

  interface were implemented. The java.awt.WindowListener interface

  declAres seven methods, and even if You Are interested only in the

  windowClosing one, You must include six additional empty-bodied

  methods to compile the class (window adapters simplify this process,

  but they Are beyond the scope of this article).

  While both Abstract Classes and interfaces can ensure that a

  concrete class will have all required methods, Abstract Classes can

  also contain implemented methods, but interfaces can't.

  Beside method declarations, interfaces can contain final

  static variables. For example, let's say we have multiple bonus-level

  codes used in several Classes during the calculation of new salaries.

  Instead of redefining these constants in every class that needs them,

  we can create the interface shown in Listing 10.

  Now a small change in the class declaration will allow us to

  use these bonus levels as if they were declAred in the class Employee:

  public class Employee

  implements Payable, Bonus {

  S

  if (empLevel==JUNIOR_LVL){

  //apply the rules for juniors

  }

  }

  public class Consultant

  implements Payable, Bonus {

  S

  }

  Java does not allow multiple inheritance, which means a class

  can't have two independent ancestors, but You can use interfaces as a

  workaround. As You've seen in the example above, a class can

  implement multiple interfaces; it just needs to implement all methods

  declAred in all interfaces. If Your window needs to process button

  clicks and window closing events, You can declAre a class as follows:

  class MyWindow implements ActionListener, WindowListener{S}

  During evolution, an Employee can obtain multiple behaviors,

  for example

  class Employee extends Person

  implements Payable, Transferable,

  Sueable, Bonus {S}

  Consultants such as myself Are usually more primitive

  creatures and can be defined as follows:

  class Consultant extends Person

  implements Payable, Sueable {S}

  But if a program such as Promoter is interested only in

  Payable functions, it can cast the object only to those interfaces it

  intends to use, for example:

  Employee emp = new Employee();

  Consultant con = new Consultant();

  Payable person1 = (Payable) emp;

  Payable person2 = (Payable) con;

  Now we're ready to write a second version of the class

  Promoter that will use the Classes Employee and Consultant defined in

  Listings 8 and 11.

  The output of this program will look similar to the output of

  the class Promoter from Listing 4:

  Class Employee:Increasing salary by 5%

  Class Consultant: Increasing hourly rate by 5%

  Class Employee:Increasing salary by 5%

  Class Employee:Increasing salary by 5%

  Line 18 of Listing 9 may look a little confUsing: How can we

  call a concrete method raiseSalary on a variable of an interface

  type? Actually we call a method on a concrete instance of the

  Employee or a Consultant, but by casting this instance to the type

  Payable we Are just letting the JVM know that we're only interested

  in the methods that were declAred in this particular interface.

  Java Technical Interviews

  During the technical interviews, probably the most frequently

  asked question is, "What's the difference between Java Abstract

  Classes and interfaces?" While interviewing Java programmers, I also

  found out that only half of the job applicants could properly

  complete the assignment described at the beginning of this article.

  During the job interview Your answers should be clear and

  short; You won't even have a chance to use all the material presented

  here. Here's one version of the answer to our problem.

  If two Classes have lots of common functionality, but some

  methods should be implemented differently, You could create a common

  Abstract ancestor Person and two subClasses Employee and Consultant.

  The method raiseSalary must be declAred Abstract in the class Person

  while other methods should be concrete. This way we ensure that the

  subClasses do have the method named raiseSalary with a known

  signature, so we could use it in the ancestor without knowing its

  implementation. Java interfaces should also be considered in cases

  when the same method must be implemented in multiple Classes - in

  this case we do not need to use Abstract ancestors. Actually,

  interfaces could be Your only option if a class already has an

  ancestor that can not be changed.

  One good interview technique is to impress the interviewer by

  elaborating on a related topic. Discussion of Abstract Classes and

  interfaces gives You a good opportunity to show Your understanding of

  polymorphism.

  Summary

  Use of Abstract Classes, interfaces, and polymorphism

  improves the design of any project by making it more readable and

  easily extensible. This also makes Your code more compact and elegant.

<script type="text/javascript"> </script> <script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> </script> <script type="text/javascript"> </script><script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> </script>