JĘzyki programowania obiektowego wiesław Porębski Ada95 Program Wykładu



Pobieranie 187.36 Kb.
Strona7/8
Data28.04.2016
Rozmiar187.36 Kb.
1   2   3   4   5   6   7   8

4.1.Użycie pakietów


Pokazany wyżej pakiet może być używany przez kod klienta, np. przez inny pakiet, który potrzebuje stosu liczb rzeczywistych:

s: REAL_STACKS.STACK(1000);

REAL_STACKS.put(s,3.5);

If REAL_STACKS.empty(s) then ...



  • Uwaga. Środowisko Ady musi być zdolne skompilować taki kod klienta, nawet gdy jest dostępny tylko interfejs pakietu (specyfikacja) bez dostępu do jego ciała.

Zauważmy też, że możemy tutaj uniknąć notacji z kropką, jeżeli dodamy dyrektywę use:

use REAL_STACKS:

s: STACK(1000);

put(s,3.5);

If empty(s) then ...

Jednak ze względu na możliwe konflikty nazw zwykle zaleca się używanie pełnych nazw kwalifikowanych.


4.2.Implementacja pakietów


Ciało pakietu zawiera szczegóły implementacyjne, które nie powinny być widoczne dla klienta danego pakietu. Dlatego zwykle specyfikacja i ciało pakietu są umieszczane w oddzielnych plikach. Jednak istnieją przypadki, gdy pakiet, na przykład zawierający pewną procedurę, nie ma oddzielnej specyfikacji; wówczas ciało pakietu automatycznie podaje swoją specyfikację. Innym przypadkiem szczególnym może być pakiet zawierający wyłącznie deklaracje stałych (np. pi, pierwiastek kwadratowy z dwóch, itp.). Taki pakiet nie może mieć ciała, ponieważ nie jest ono potrzebne. Jednak większość pakietów wymaga zarówno deklaracji, jak i ciała. Kontynuując nasz przykład z pakietem REAL_STACKS zawierającym typ parametryzowany STACK, moglibyśmy zapisać jego ciało z jedną pełną definicją podprogramu:

package body REAL_STACKS is

procedure put(s: in out STACK; x: in Float) is

begin


if s.count = s.capacity then

raise Overflow

endif;

s.count := s.count + 1;



s.table(count) := x;

end put;


procedure remove(s: in out STACK) is

-implementacja remove

end remove;

function item(s: STACK) return Float is

--implementacja item

end item;

function empty(s: STACK) return Boolean is

--implementacja empty

end empty;

end REAL_STACKS;


4.2.1.Kompilacja programu wieloplikowego


Jeżeli specyfikację pakietu Real_Stacks umieścimy w pliku Real_Stacks.ads (rozszerzenie nazwy .ads mówi o tym, że w pliku umieszczono specyfikację pakietu Real_Stacks), ciało w pliku Real_Stacks.adb, zaś procedurę, która wykorzystuje ten pakiet, w pliku Stacks _main.adb, to kompilacja obejmie następujące kroki:

  1. Krok 1: gcc -c Stacks _main.adb

  2. Krok 2: gcc -c Real_Stacks.adb

  3. Krok 2: gnatbind Stacks _main

  4. Krok 2: gnatlink Stacks _main

Wymienione trzy kroki można zastąpić jednym: gnatmake Stacks _main.adb.

4.3.Parametryzacja pakietów


Zdefiniowany wyżej pakiet REAL_STACKS pozwalał deklarować struktury stosowe o elementach typu Float. Stosując przyjętą w języku Ada metodę parametryzacji (genericity) można go przekształcić w pakiet GENERIC_STACKS parametryzowany dowolnym typem, stosując następującą składnię:

generic


type Item is private;

package GENERIC_STACKS is

type STACK(capacity: Positive) is private;

procedure put(s: in out STACK; x: in Item);

procedure remove(s: in out STACK);

function item(s: STACK) return Item;

function empty(s: STACK) return Boolean;

Overflow, Underflow: Exception;

private

type STACK_CONTENTS is array(Positive range<>) of Item;



type STACK(capacity: Positive) is

record


table: STACK_CONTENTS(1..capacity);

count: Natural := 0;

end record;

end GENERIC_STACKS;

Definicja ciała pakietu GENERIC_STACKS jest podobna do REAL_STACKS, przy czym nie poprzedzamy jej klauzulą generic:



package body GENERIC_STACKS is

--Definicje jak poprzednio, zastępując wszędzie typ

--Float przez Item

end GENERIC_STACKS;

Tak zdefiniowany pakiet GENERIC_STACKS jest faktycznie szablonem pakietów, ponieważ jego użycie wymaga podania parametrów aktualnych. Jeżeli np. chcemy mieć pakiet zawierający stos liczb całkowitych, to możemy go zadeklarować instrukcją



package Stack_Int is new GENERIC_STACKS(Integer);

i używać, jak pokazano poprzednio.

Kreacja rzeczywistego pakietu z szablonu jest nazywana w języku Ada tworzeniem wystąpienia (instantiating), a rezultat wykonania instrukcji, jak np. package REAL_STACKS_1 is new GENERIC_STACKS(Integer);

wystąpienie (instance).

Użyte w kontekście „generic type Item is private;” słowo kluczowe private oznacza tutaj, że typ Item może być dowolnym typem Ady, ale inne pakiety mogą wykonywać operacje na zmiennych tego typu jedynie za pomocą zadeklarowanych w pakiecie STACKS operacji publicznych oraz operacje przypisania „:=”, przyrównania „=” i przekazania jako argumentu aktualnego innej operacji. Dopuszczalne operacje na typie Item mogą być jeszcze bardziej ograniczone, gdy zadeklarujemy go jako “limited private”; oznacza to, że dla typu Item mogą nie istnieć operacje przypisania i przyrównania.

4.4.Dziedziczenie typów i hierarchie pakietów


W języku Ada 95 dziedziczenie jest rozumiane jako rozszerzenie istniejących typów o nowe pola rekordu i podprogramy, przy czym prawo do rozszerzenia istniejącego typu o nowe składowe (pola rekordu) i nowe operacje mają jedynie tak zwane “typy znakowane”, których deklaracje i definicje zawierają słowo kluczowe tagged. Powstałe przez dziedziczenie typy nazywa się potomnymi lub pochodnymi (derived types), zaś typy podlegające rozszerzaniu określa się jako typy rodzicielskie lub bazowe (base types). Jeżeli typ bazowy nie jest znakowany, to typ pochodny dziedziczy operacje typu bazowego, natomiast nie może dodawać nowych składowych. Mechanizm znakowania jest użyteczny także w tym, że umożliwia wiązanie późne (w fazie wykonania) i polimorfizm. Jak implikuje jego nazwa, z każdym typem znakowanym jest skojarzony pewien znacznik, który w języku Ada 95 identyfikuje dany typ.

Typy pochodne w języku Ada deklaruje się z reguły w części publicznej specyfikacji instrukcją:



type DERIVED is new BASE with private;

Użyta w tej deklaracji fraza with private oznacza, że faktyczna implementacja takiego typu będzie ukryta w prywatnej części specyfikacji i nie może być bezpośrednio używana przez kody klientów. Oznacza to również możliwość późniejszej zmiany implementacji bez wpływu na kod użytkownika obiektów typu DERIVED.

Dziedziczenie w Adzie jest pojedyncze; jeżeli liczba poziomów drzewa dziedziczenia jest stosunkowo niewielka, to całą hierarchię umieszcza się w jednym pakiecie, jak pokazano niżej.

package Accounts is

type MONEY is digits 12 delta 0.01;
type ACCOUNT is tagged private;

procedure deposit(a: in out ACCOUNT; amount: in MONEY);

procedure withdraw(a: in out ACCOUNT; amount: in MONEY);

function balance(a: in ACCOUNT) return MONEY;


type CHECKING_ACCOUNT is new ACCOUNT with private;

function balance(a: in CHECKING_ACCOUNT) return MONEY;


type SAVINGS_ACCOUNT is new ACCOUNT with private;

procedure compound(a:in out SAVINGS_ACCOUNT;period:in Positive);


private

type ACCOUNT is tagged

record

initial_balance: MONEY := 0.0;



owner: String(1..30);

end record;


type CHECKING_ACCOUNT is new ACCOUNT with null record;
type SAVINGS_ACCOUNT is new ACCOUNT with record

rate: Float;

end record;

end Accounts;

W pakiecie Accounts zadeklarowano typ znakowany ACCOUNT o prywatnej implementacji. Na zmiennych tego typu i typów pochodnych mogą operować podprogramy publiczne: procedury deposit i withdraw oraz funkcja balance.



Typy pochodne CHECKING_ACCOUNT i SAVINGS_ACCOUNT dziedziczą atrybuty i metody od typu ACCOUNT, przy czym typ SAVINGS_ACCOUNT rozszerza typ ACCOUNT o atrybut rate i procedurę compound, zaś typ CHECKING_ACCOUNT ma te same atrybuty, co typ bazowy (mówi o tym fraza with null record). Zwróćmy również uwagę na ponowną deklarację funkcji balance dla typu CHECKING_ACCOUNT, co sygnalizuje jej redefinicję (tutaj nie pokazaną). Oznacza to, że w języku Ada 95 istnieje możliwość przeciążania procedur i funkcji.

1   2   3   4   5   6   7   8


©absta.pl 2016
wyślij wiadomość

    Strona główna