Po zidentyfikowaniu metod wspólnych dla wszystkich kształtów możemy przystąpić do definiowania klas samych kształtów. Pod względem liczby klas, jakie należy zaimplementować, najprostszym kształtem jest koło — klasa Circle:
package com.example.programmingas3.geometricshapes
{
public class Circle implements IGeometricShape
{
public var diameter:Number;
public function Circle(diam:Number = 100):void
{
this.diameter = diam;
}
public function getArea():Number
{
// The formula is Pi * radius * radius.
var radius:Number = diameter / 2;
return Math.PI * radius * radius;
}
public function getCircumference():Number
{
// The formula is Pi * diameter.
return Math.PI * diameter;
}
public function describe():String
{
var desc:String = "This shape is a Circle.\n";
desc += "Its diameter is " + diameter + " pixels.\n";
desc += "Its area is " + getArea() + ".\n";
desc += "Its circumference is " + getCircumference() + ".\n";
return desc;
}
}
}
Klasa Circle implementuje interfejs IGeometricShape, a więc musi zawierać zarówno kod metody
getArea()
, jak i kod metody
describe()
. Ponadto definiuje metodę
getCircumference()
występującą tylko w klasie Circle. Klasa Circle zawiera także deklarację właściwości
diameter
, której nie maja klasy wieloboków.
Pozostałe dwa typy kształtów — kwadraty i trójkąty równoboczne — mają kilka innych cech wspólnych: mają boki równej długości oraz wspólne wzory na obliczanie obwodu i sumy kątów wewnętrznych. W istocie te wspólne wzory będą miały zastosowanie do wszystkich innych wieloboków foremnych, które być może zdefiniujemy w przyszłości.
Klasa RegularPolygon jest nadklasą dla klasy Square i klasy EquilateralTriangle. Nadklasa pozwala na zdefiniowanie w jednym miejscu wszystkich wspólnych metod, przez co nie trzeba definiować ich osobno w każdej podklasie. Oto kod klasy RegularPolygon:
package com.example.programmingas3.geometricshapes
{
public class RegularPolygon implements IPolygon
{
public var numSides:int;
public var sideLength:Number;
public function RegularPolygon(len:Number = 100, sides:int = 3):void
{
this.sideLength = len;
this.numSides = sides;
}
public function getArea():Number
{
// This method should be overridden in subclasses.
return 0;
}
public function getPerimeter():Number
{
return sideLength * numSides;
}
public function getSumOfAngles():Number
{
if (numSides >= 3)
{
return ((numSides - 2) * 180);
}
else
{
return 0;
}
}
public function describe():String
{
var desc:String = "Each side is " + sideLength + " pixels long.\n";
desc += "Its area is " + getArea() + " pixels square.\n";
desc += "Its perimeter is " + getPerimeter() + " pixels long.\n";
desc += "The sum of all interior angles in this shape is " + getSumOfAngles() + " degrees.\n";
return desc;
}
}
}
W klasie RegularPolygon najpierw deklarowane są dwie właściwości wspólne dla wszystkich wieloboków foremnych: długość boku (właściwość
sideLength
) oraz liczba boków (właściwość
numSides
).
Klasa RegularPolygon implementuje interfejs IPolygon i zawiera deklaracje wszystkich czterech metod określonych w interfejsie IPolygon. Dwie z tych metod —
getPerimeter()
oraz
getSumOfAngles()
— implementuje przy użyciu wspólnych wzorów.
Ponieważ wzór używany w metodzie
getArea()
jest różny w poszczególnych kształtach, wersja tej metody w klasie bazowej nie może zawierać wspólnej logiki dziedziczonej przez metody w podklasach. Dlatego metoda ta zwraca wartość domyślną 0, która oznacza, że pole powierzchni nie zostało obliczone. Aby prawidłowo obliczać pola powierzchni poszczególnych kształtów, podklasy klasy RegularPolygon muszą przesłaniać metodę
getArea()
.
Poniższy kod klasy EquilateralTriangle ilustruje przesłanianie metody
getArea()
:
package com.example.programmingas3.geometricshapes
{
public class EquilateralTriangle extends RegularPolygon
{
public function EquilateralTriangle(len:Number = 100):void
{
super(len, 3);
}
public override function getArea():Number
{
// The formula is ((sideLength squared) * (square root of 3)) / 4.
return ( (this.sideLength * this.sideLength) * Math.sqrt(3) ) / 4;
}
public override function describe():String
{
/* starts with the name of the shape, then delegates the rest
of the description work to the RegularPolygon superclass */
var desc:String = "This shape is an equilateral Triangle.\n";
desc += super.describe();
return desc;
}
}
}
Słowo kluczowe
override
oznacza, że metoda
EquilateralTriangle.getArea()
celowo przesłania metodę
getArea()
z nadklasy RegularPolygon. Po wywołaniu metody
EquilateralTriangle.getArea()
pole powierzchni jest obliczane według wzoru zawartego w powyższym przykładzie, a kod w metodzie
RegularPolygon.getArea()
nigdy nie jest wykonywany.
Natomiast klasa EquilateralTriangle nie definiuje własnej wersji metody
getPerimeter()
. Wywołanie metody
EquilateralTriangle.getPerimeter()
jest przekazywane w górę w łańcuchu dziedziczenia i wykonywany jest kod metody
getPerimeter()
z nadklasy RegularPolygon.
W konstruktorze
EquilateralTriangle()
użyto instrukcji
super()
w celu jawnego wywołania konstruktora
RegularPolygon()
z nadklasy. Gdyby oba konstruktory miały ten sam zestaw parametrów, można byłoby pominąć konstruktor
EquilateralTriangle()
, z zamiast niego wykonywany byłby konstruktor
RegularPolygon()
. Jednak konstruktor
RegularPolygon()
potrzebuje dodatkowego parametru
numSides
. Dlatego konstruktor
EquilateralTriangle()
zawiera wywołanie
super(len, )
, które przekazuje parametr wejściowy
len
oraz wartość 3 oznaczającą, że trójkąt ma 3 boki.
Metoda
describe()
również wykorzystuje instrukcję
super()
, ale w inny sposób. Wykorzystuje ją w celu wywołania wersji nadklasy RegularPolygon metody
describe()
. Metoda
EquilateralTriangle.describe()
najpierw przypisuje zmiennej
desc
typu String informację o typie kształtu. Następnie pobiera wynik metody
RegularPolygon.describe()
, wywołując
super.describe()
, po czym dołącza ten wynik do ciągu znaków
desc
.
Klasy Square nie będziemy tutaj szczegółowo omawiać, ale jest ona podobna do klasy EquilateralTriangle i zawiera konstruktor oraz własne implementacje metod
getArea()
i
describe()
.