Interfaces

When designing an object-oriented system, there often arises a need to define some functionality or some pattern for class behavior without providing any implementation. Interfaces provide us this feature in a .NET environment. Generally, an interface is a set of methods. Implementing an interface by a class means providing implementations for all methods of that interface. This is very similar to inheritance from an abstract class, but unlike in the case of inheritance, you may implement multiple interfaces in your classes. Unfortunately, PerlNET does not allow us to define our own .NET interfaces, but we can easily implement existing ones. You can create your own interfaces in other .NET languages, such as C#. You can see how that is done later in this chapter and in Appendix B.

Implementing .NET Interfaces

To better understand implementing .NET interfaces, let us take the existing .NET interface, IComparable (code convention is that interface names should start with I). This interface is exposed by the System namespace and consists of only one method, CompareTo, with the following prototype:

int CompareTo(Object o);

This method takes an object (other object) as an argument and compares it to the object on behalf of which the method was called (this object). If this object is greater than the other object, then the method should return a positive value. If this object is less than the other object, then the return value should be negative. In case of equality of this object and the other object, we should return zero. If some class implements the IComparable interface (i.e., provides implementation of CompareTo method), then we can easily sort our objects of this class inside ArrayList by applying the Sort method to the ArrayList object. Internally, the Sort method will call the CompareTo method on the objects inside the list and, according to comparison results, will sort the objects.

Generally, to implement some .NET interface, we should use the [implements: …] attribute inside our PerlNET class =for interface POD blocks. Additionally, we should enumerate all the methods of the interface inside POD blocks, even if we provide an empty implementation for some methods.

package MyPackage;

=for interface
   [implements: SomeInterface]
							SomeInterfaceMethod_1;
							SomeInterfaceMethod_2;
							. . .
							SomeInterfaceMethod_N;
=cut

# Other definitions
. . .

SomeInterfaceMethod_X is the prototype of method number X defined by the interface. We may implement several interfaces. In this case we can list them in the single implements attribute:

[implements: IComparable, IEnumerable, ICollection]

Example and Discussion

If we want our StockItem class to implement the IComparable interface, then we should alter the StockItem module in the following manner (StockItemStep5):


#
# StockItem.pm
#
package OI::Samples::StockItem;
use namespace "System";

=for interface
   [implements: IComparable]
							int CompareTo(Object o);
=cut

# IComparable implementation
sub CompareTo
{
   my($this, $other) = @_;

   # Return comparison result
   return($this->{f_ID} - $other->{ID});
}
# The rest of the module
. . .

To provide implementation for the methods of the interface, we should define subroutines with the methods' names inside the Perl module, as we did in the above fragment for the CompareTo method—we compare IDs of two items and return the result of the comparison.

The following PerlNET program (SortItems) demonstrates the easy way to sort the StockItem class objects after we implement the IComparable interface.


#
# SortItems.pl
#
use namespace "System.Collections";
use namespace "OI.Samples";
use PerlNET qw(in false);

my $items = ArrayList->new();
$items->Add(StockItem->new(int(2), "Item 2"));
$items->Add(StockItem->new(int(1), "Item 1"));
$items->Add(StockItem->new(int(3), "Item 3"));

print "Before Sort:
";
foreach my $item (in $items)
{
   $item->OutputItem(false);
}
print "
";

# Sort Array
$items->Sort();

print "After Sort:
";
foreach my $item (in $items)
{
   $item->OutputItem(false);
}

The output of the SortItems program will look like this:

Before Sort:
2 Item 2
1 Item 1
3 Item 3

After Sort:
1 Item 1
2 Item 2
3 Item 3

All the descendant classes automatically implement all the interfaces of the base class with no need of additional definition inside the descendant or the base classes. Therefore, the BookItem class implements the IComparable interface as the descendant of StockItem.

Defining Interfaces

If we have to define our own interface, we do so in C# or in any other .NET-compliant language that supports creating .NET interfaces. The implementation technique is the same as for all .NET interfaces.

ROULETTE SAMPLE

We stated that Mixed types enjoy the same benefits as .NET types. We decided to combine the demonstration of defining our own interface with implementing it in the Mixed type Roulette, which we presented previously in this chapter.

We want to define a new interface, IResettable. It will have one method, Reset. Calling this method on an implementing class will cause the object to reset its state. In the example of the Roulette class, we will erase all the stakes that were previously made.

We have written the following interface definition in C#:


//
// IResettable.cs
//
namespace OI.Samples
{
   interface IResettable
   {
      void Reset();
   }
}

Refer to the IResettable folder for the code and Visual C# project. We compile this file into a DLL (IResettable.dll) using the csc.exe C# compiler:

csc /target:library IResettable.cs

When rewriting and rebuilding the Roulette class, we will reference this library.

Our next step is to alter the Roulette code to provide the implementation of the IResettable interface. Remember that we deal with Mixed type, and all its nonstatic methods are automatically provided with two arguments that are not listed in the interface definition: $this and $self. The Reset method is not an exception.

We made the following additions to the code to implement the IResettable interface (the full code of the new Roulette class resides in the ResettableRoulette folder):


#
# Roulette.pm
#
package OI::Samples::Roulette;
use namespace "OI.Samples";
. . .
# Implements IResettable interface
=for interface
    [implements: IResettable]
    void Reset();
=cut
. . .
# IResettable implementation
sub Reset
{
    my ($this, $self) = @_;
    $this->{f_LastGambler} = 0;
    $self->{Stakes} = [];
}

To build the newly written Roulette class, run PerlNET compiler (don't forget to build IResettable.dll and copy it to the ResettableRoulette folder).

plc Roulette.pm -target="library"
                -reference="IResettable.dll"

After the above modifications, we may clear all the stakes after each round in our roulette game (ResettableRouletteGame).


#
# RouletteGame.pl
#

use strict;
use namespace "OI.Samples";

# Instantiate Roulette
my $roulette = Roulette->new();

# Make two stakes
$roulette->AddStake(20, 3);
$roulette->AddStake(5, 2.5);

# Roll it
$roulette->Roll();

# Clear the stakes
								$roulette->Reset();

# Make new stake
$roulette->AddStake(10, 4);

# Roll again
$roulette->Roll();

The output, depending on the number generated, may look like the following:

**************************
The winning number is: 25!
**************************
--------------------------
Gambler 1:
Your prize is: 0.00
--------------------------
Gambler 2:
Your prize is: 0.00
--------------------------
**************************
The winning number is: 10!
**************************
--------------------------
Gambler 1:
Your prize is: 144.00
--------------------------

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
13.59.122.162