Implementing Inheritance

 <  Day Day Up  >  

Before we can implement inheritance, we should understand the basic guidelines for architecting superclasses and subclasses. The basic guidelines are as follows :

  • Common attributes and methods are generalized in the superclass.

  • Specific attributes and methods are specialized in the subclass.

In the Loan class, we have methods and properties that are common to all types of loans. Listing 5.1 shows the Loan class.

Listing 5.1. Revisiting the Loan Class
 class Loan{       private var principal:Number ;       private var rate:Number ;       private var years:Number ;       public function Loan(){             trace("in loan constuctor");       }       public function setPrincipal(thePrincipal:Number ) {             principal = thePrincipal;       }       public function setRate(theRate:Number) {             rate=theRate;       }       public function setYears(theYears:Number ) {             years=theYears;       }       public function getYears( ):Number {             return years;       }       public function  getMonthlyPayment( ):Number {             var months:Number = this.getYears() *12;             var monthlyRate:Number = this.rate /(12*100);             var payment:Number = this.principal * (monthlyRate / (1 - Math.pow(1 + monthlyRate,-months)));             return payment;       }       public function getTotalPayment( ):Number {             var months:Number= this.getYears() * 12;             return getMonthlyPayment() * months;       } } 

What if we wanted to specialize the Loan class for a car or a mortgage? We could add additional properties and methods to the Loan class for each type of loan or we could use inheritance to extend the loan definition for each new type of loan. In Figure 5.3, you can see the hierarchy we might use to solve the problem. Notice that the generalized properties and methods exist in the ancestor , Loan , while the properties and methods specific to cars or mortgages are in the descendants only. The descendants also have all the members of the ancestor.

Figure 5.3. Loan hierarchy.

graphics/05fig03.gif

N OTE

Unified Modeling Language (UML) is the notation used in Figure 5.3. It is a standard used for describing object relationships and hierarchies.


extends Keyword

To implement the inheritance hierarchy for the loan solution in Figure 5.3, we can use the extends keyword, as seen in Listing 5.2.

Listing 5.2. Subclassing the Loan Class
 class CarLoan extends Loan{       private var bankFee:Number ;       public function  getBankFee( ):Number {             return this.bankFee;       }       public function setBankFee( fee:Number):Void {             this.bankFee = fee;       } } class Mortgage extends Loan{       private var insurancePremium:Number ;       public function  getInsurancePremium( ):Number {             return this.insurancePremium;       }       public function setInsurancePremium( fee:Number):Void {             this.insurancePremium = fee;       } } 

When a class is created using the extends keyword, the class inherits all the methods and properties of the ancestor class. In Listing 5.2, you can see that the CarLoan and Mortgage classes extend the Loan class. In both cases, we have added the members that are specific to the type of loan.

At this point, we have made each loan type unique by adding the attributes. To use the attributes, the implementing application, LoanCalc , has to be modified. Additional logic also has to be added to determine which type of loan is required.

If you recall, LoanCalc.fla has a method called calc() , as shown in Listing 5.3.

Listing 5.3. calc Method in LoanCalc.fla
 function calc(){ //function called by the button handler       var loan:Loan = new Loan();       //set the loan properties from the text field in the form       loan.setPrincipal(Number(loanAmt.text));       loan.setYears(Number(term.text));       loan.setRate(Number(rate.text));       //calculate the monthly payment       var monthlyPmtVal:Number =  loan.getMonthlyPayment();       //display monthly payment       monthlyPmt.text = monthlyPmtVal;       var totalPmtVal:Number =  loan.getTotalPayment();       //display total payments       totalPmt.text = totalPmtVal; } 

To determine the loan type, we have to add the selection capability to the user interface, as shown in Figure 5.4:

Figure 5.4. Selection capability in the user interface.

graphics/05fig04.jpg

After the user has indicated a loan type, the selected value is used to determine which type of loan to instantiate. Listing 5.4 shows the additional logic needed to implement the correct type of loan object.

Listing 5.4. calc Method in LoanCalc.fla
 function calc(){ //function called by the button handler       var loan;       if (loanType.selectedData == "mortgage"){             loan = new Mortgage();             loan.setPropertyTaxRate(15);             //set the loan properties from the text field in the form             loan.setPrincipal(Number(loanAmt.text));             loan.setYears(Number(term.text));             loan.setRate(Number(rate.text));             //calculate the monthly payment             var monthlyPmtVal:Number =  loan.getMonthlyPayment()+loan.getPropertyTax();             var totalPmtVal:Number =  loan.getTotalPayment();       }else if(loanType.selectedData == "car"){             loan = new CarLoan();             loan.setBankFee(25);             //set the loan properties from the text field in the form             loan.setPrincipal(Number(loanAmt.text));             loan.setYears(Number(term.text));             loan.setRate(Number(rate.text));             //calculate the monthly payment             var monthlyPmtVal:Number =  loan.getMonthlyPayment()+loan.getBankFee();             var totalPmtVal:Number =  loan.getTotalPayment();       }      //display monthly payment       monthlyPmt.text = monthlyPmtVal;       //display total payments       totalPmt.text = totalPmtVal; } 

To calculate the car loan, add the bank fee to the monthly payment before displaying the results. In the case of a mortgage, add the propertyTax property to the monthly payment. The only problem with this approach is code redundancy. In both cases, we are calling all the same methods except those that make the loan unique.

A better solution would be to hide the complexity of the differences in the objects themselves . For example, we could apply the property tax and the bank fees in the individual classes. To do this, we can use a concept called overriding .

Method and Property Overriding

When we describe inheritance as extending a definition, in some cases, that might mean changing an original member. In the subclass, we have identified a need to hide the complexity of the new elements of the class within the subclass. Note that we can hide or limit access to class members using access modifiers such as private or protected. Table 5.2 shows the restrictions access modifiers place on objects as part of an inheritance hierarchy.

Table 5.2. Property and Method Accessibility

A CCESS M ODIFIER

C LASS I TSELF

S AME P ACKAGE

S UBCLASS

A NY C LASS

Public

X

X

X

X

Private

X

 

X

 

In the subclass, we can make the new property private and limit access to it from within the containing class. Listing 5.5 shows the CarLoan class hiding the bankFee property and getBankFee() method by making them private. We can still set the property from another object, but the only place where we can read it is in the object itself.

Listing 5.5. CarLoan Class in CarLoan.as
 class CarLoan extends Loan{       private var bankFee:Number ;       public function setBankFee( fee:Number):Void {             this.bankFee = fee;       }       public function  getMonthlyPayment( ):Number {             var months:Number = years*12;             var monthlyRate:Number = rate /(12*100);             var payment:Number = principal * (monthlyRate / (1 - Math.pow(1 + monthlyRate,-months)));             return payment + this.bankFee;       }       public function getTotalPayment( ):Number {             var months:Number= this.getYears() * 12;             return (this.getMonthlyPayment()+this.bankFee)* months ;       } } 

We can provide the same encapsulation for the Mortgage class, as shown in Listing 5.6.

Listing 5.6. Mortgage Class in Mortgage.as
 class Mortgage extends Loan{       private var insurancePremium:Number ;       public function setInsurancePremium( fee:Number):Void {             this.insurancePremium = fee;       }       public function  getMonthlyPayment( ):Number {             var months:Number = years*12;             var monthlyRate:Number = rate /(12*100);             var payment:Number = principal * (monthlyRate / (1 - Math.pow(1 + monthlyRate,-months)));             return payment + this.insurancePremium;       }       public function getTotalPayment( ):Number {             var months:Number= this.getYears() * 12;             return (getMonthlyPayment()+this.insurancePremium)  * months;       } } 

By doing this, we will have less redundancy in the client, LoanCalc . In Listing 5.7, we have eliminated the need for separating the calls that calculate monthly payment by loan type because we have moved the type-specific properties into the subclass.

Listing 5.7. calc Method in LoanCalc.fla
 function calc(){ //function called by the button handler       var loan;       if (loanType.selectedData == "m"){             loan = new Mortgage();             loan.setInsurancePremium(15);       }else{             loan = new CarLoan();             loan.setBankFee(25);       }       //set the loan properties from the text field in the form       loan.setPrincipal(Number(loanAmt.text));       loan.setYears(Number(term.text));       loan.setRate(Number(rate.text));       //calculate the monthly payment       var monthlyPmtVal:Number =  loan.getMonthlyPayment();       //display monthly payment       monthlyPmt.text = monthlyPmtVal;       var totalPmtVal:Number =  loan.getTotalPayment();       //display total payments       totalPmt.text = totalPmtVal; } 

We are almost done implementing a sound inheritance structure. If you revisit Listings 5.5 and 5.6, you will notice that we still have a lot of redundancy. The code in getMonthlyPayments() and getTotalPayments() is almost identical to that of the code's ancestor, and it appears in both subclasses. Because a primary goal of OOP is to prevent coding redundancy, we need one more super object-oriented concept to get us there.

super ”Thanks for Asking

One more little thing to add to our inheritance model is the ability to refer to a superclass from a subclass. In Listing 5.8, we can see that the getMonthlyPayment() method has changed. It is now calling the method in its ancestor, Loan , rather than having the code itself. This truly centralizes the method code in one object, the ancestor, so that the only class members needed in the descendant object are the new members.

To call the ancestor method, we use the super keyword. super always refers to the direct ancestor of the calling object. In Listing 5.8, we are using the super keyword to call the Loan class method that returns the base amount. We can then add the bank fee and return the value to the client.

Listing 5.8. CarLoan Extending Loan
 class CarLoan extends Loan{       private var bankFee:Number ;       public function  getBankFee( ):Number {             return this.bankFee;       }       public function setBankFee( fee:Number):Void {             this.bankFee = fee;       }       public function  getMonthlyPayment( ):Number {             var monthlyPayment:Number = super.getMonthlyPayment() + this.getBankFee();             return monthlyPayment;       }       public function getTotalPayment( ):Number {             var months:Number= super.getYears() * 12;             var totalPayments:Number = super.getTotalPayment() + ( months *  this.getBankFee());             return totalPayments;       } } 

The Mortgage class can use the same construct and reduce redundancy, as we see in Listing 5.9.

Listing 5.9. Mortgage Extending Loan
 class Mortgage extends Loan{       private var insurancePremium:Number ;       public function  getInsurancePremium( ):Number {             return this.insurancePremium;       }       public function setInsurancePremium( fee:Number):Void {             this.insurancePremium = fee;       }       public function  getMonthlyPayment( ):Number {             var monthlyPayment:Number = super.getMonthlyPayment() + this.getInsurancePremium();             return monthlyPayment;       }       public function getTotalPayment( ):Number {             var months:Number= super.getYears() * 12;             var totalPayments:Number = super.getTotalPayment() + ( months *  this.getInsurancePremium());             return totalPayments;       } } 

super and Constructors

Constructors are special methods. They are used to initialize an object. If we don't explicitly create a constructor, one is created for us by the compiler at compile time. The implicit constructor is called a no-argument , default constructor. Just as the compiler creates this constructor for us, it will also implicitly call it for us when the object is instantiated at runtime.

This has an impact on our inheritance hierarchy because there will be times when we will have a constructor with arguments that will often need to call its ancestor. There are special rules that govern this process.

As with any object, if you don't place a call to super() in the constructor function of a subclass, the compiler automatically generates a call to the constructor of its immediate superclass with no parameters as the first statement of the function. If the superclass doesn't have a constructor, the compiler creates an empty constructor function and then generates a call to it from the subclass. The problem arises when the superclass constructor takes parameters in its definition. In Listing 5.10, we have modified the constructor to take the necessary arguments to initialize the Loan object.

Listing 5.10. Loan Constructor Modified
 class Loan{       private var principal:Number ;       private var rate:Number ;       private var years:Number ;       function Loan(principal:Number, rate:Number, years:Number){             trace("in loan constuctor");             this.setPrincipal(principal);             this.setNumber(rate);             this.setYears(years);       } .. .. } 

Because the superclass constructor now takes arguments, we must add a constructor to the subclass to pass in the required parameters. Remember that the compiler will add a call to the no-argument constructor implicitly if we do not have a constructor at all. In Listing 5.11, we have added a constructor to the subclass that calls the superclass constructor with the required parameters.

Listing 5.11. Constructors Using super() in Subclasses
 class CarLoan extends Loan{       private var bankFee:Number ;       function CarLoan(principal:Number, rate:Number, years:Number){             super(principal, rate, years);       } .. ... } 

You might have noticed that the first line of the subclass constructor seems different than superclass method calls we saw earlier. This is a special syntax used only for superclass constructor calls. The superclass constructor call must always be the first call in a subclass constructor, as shown.

A Word on Multiple Inheritance

Many object-oriented languages support multiple inheritance. That is, they inherit from more than one class. In Listing 5.12, class C is trying to inherit from A and B. This is not allowed in ActionScript.

Listing 5.12. Illegal Multiple Inheritance
 // not allowed class C extends A, B {} 

In Flash 2004, multiple inheritance is not allowed. However, we can achieve a similar result using the inheritance chain shown in Listing 5.13.

Listing 5.13. Pseudo Multiple Inheritance
 // allowed class B extends A {} class C extends B {} //c inherits from both a and b. 

Will the Real Method Please Stand Up

At runtime, when a method is called, there is a particular look-up through the inheritance chain to determine which method to execute. For example, let's consider the object hierarchy in Figure 5.5. Consider an object structure within a retail business application for all persons. All objects are persons. Some are employees, some are customers, and some employees are managers. This also shows how we might implement human resource methods in this hierarchy to calculate salaries, vacations , and bonuses for employees .

Figure 5.5. Person-object hierarchy.

graphics/05fig05.gif

At each level, methods are added or overridden as needed. It is important to understand how methods are executed at runtime. Figure 5.6 depicts the method lookup for methods when they are called from a Manager class instance.

Figure 5.6. Method Lookup for the Manager class.

graphics/05fig06.gif

 <  Day Day Up  >  


Object-Oriented Programming with ActionScript 2.0
Object-Oriented Programming with ActionScript 2.0
ISBN: 0735713804
EAN: 2147483647
Year: 2004
Pages: 162

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net