Επαναχρησιμοποίηση

Διομήδης Σπινέλλης
Τμήμα Διοικητικής Επιστήμης και Τεχνολογίας
Οικονομικό Πανεπιστήμιο Αθηνών
dds@aueb.gr

Τρόποι επαναχρησιμοποίησης

Διακρίνουμε τις παρακάτω κατηγορίες επαναχρησιμοποίησης (reuse) κατά την ανάπτυξη λογισμικού:

Πλεονεκτήματα επαναχρησιμοποίησης

Προβλήματα επαναχρησιμοποίησης

Υποστήριξη από εργαλεία

Παράδειγμα λάθους που εμφανίζεται κατά τη μεταγλώττιση προγράμματος C++ με τη χρήση της βιβλιοθήκης STL κάτω από GNU C++ compiler:
c:\gcc\bin\..\lib\gcc-lib\i386-mingw32msvc\2.95.2\..\..\..\..\include\g++-3\stl_
iterator.h: In method `class ostream_iterator<pair<const basic_string<char,strin
g_char_traits<char>,__default_alloc_template<false,0> >,int> > & ostream_iterato
r<pair<const basic_string<char,string_char_traits<char>,__default_alloc_template
<false,0> >,int> >::operator =(const pair<const basic_string<char,string_char_tr
aits<char>,__default_alloc_template<false,0> >,int> &)':
c:\gcc\bin\..\lib\gcc-lib\i386-mingw32msvc\2.95.2\..\..\..\..\include\g++-3\stl_
algobase.h:129:   instantiated from `__copy<_Rb_tree_iterator<pair<const basic_s
tring<char,string_char_traits<char>,__default_alloc_template<false,0> >,int>,pai
r<const basic_string<char,string_char_traits<char>,__default_alloc_template<fals
e,0> >,int> &,pair<const basic_string<char,string_char_traits<char>,__default_al
loc_template<false,0> >,int> *>, ostream_iterator<pair<const basic_string<char,s
tring_char_traits<char>,__default_alloc_template<false,0> >,int> >, ptrdiff_t>(_
Rb_tree_iterator<pair<const basic_string<char,string_char_traits<char>,__default
_alloc_template<false,0> >,int>,pair<const basic_string<char,string_char_traits<
char>,__default_alloc_template<false,0> >,int> &,pair<const basic_string<char,st
ring_char_traits<char>,__default_alloc_template<false,0> >,int> *>, _Rb_tree_ite
rator<pair<const basic_string<char,string_char_traits<char>,__default_alloc_temp
late<false,0> >,int>,pair<const basic_string<char,string_char_traits<char>,__def
ault_alloc_template<false,0> >,int> &,pair<const basic_string<char,string_char_t
raits<char>,__default_alloc_template<false,0> >,int> *>, ostream_iterator<pair<c
onst basic_string<char,string_char_traits<char>,__default_alloc_template<false,0
> >,int> >, input_iterator_tag, ptrdiff_t *)'
c:\gcc\bin\..\lib\gcc-lib\i386-mingw32msvc\2.95.2\..\..\..\..\include\g++-3\stl_
algobase.h:161:   instantiated from `__copy_dispatch<_Rb_tree_iterator<pair<cons
t basic_string<char,string_char_traits<char>,__default_alloc_template<false,0> >
,int>,pair<const basic_string<char,string_char_traits<char>,__default_alloc_temp
late<false,0> >,int> &,pair<const basic_string<char,string_char_traits<char>,__d
efault_alloc_template<false,0> >,int> *>,ostream_iterator<pair<const basic_strin
g<char,string_char_traits<char>,__default_alloc_template<false,0> >,int> >,__fal
se_type>::copy(_Rb_tree_iterator<pair<const basic_string<char,string_char_traits
<char>,__default_alloc_template<false,0> >,int>,pair<const basic_string<char,str
ing_char_traits<char>,__default_alloc_template<false,0> >,int> &,pair<const basi
c_string<char,string_char_traits<char>,__default_alloc_template<false,0> >,int>
*>, _Rb_tree_iterator<pair<const basic_string<char,string_char_traits<char>,__de
fault_alloc_template<false,0> >,int>,pair<const basic_string<char,string_char_tr
aits<char>,__default_alloc_template<false,0> >,int> &,pair<const basic_string<ch
ar,string_char_traits<char>,__default_alloc_template<false,0> >,int> *>, ostream
_iterator<pair<const basic_string<char,string_char_traits<char>,__default_alloc_
template<false,0> >,int> >)'
c:\gcc\bin\..\lib\gcc-lib\i386-mingw32msvc\2.95.2\..\..\..\..\include\g++-3\stl_
algobase.h:188:   instantiated from `copy<_Rb_tree_iterator<pair<const basic_str
ing<char,string_char_traits<char>,__default_alloc_template<false,0> >,int>,pair<
const basic_string<char,string_char_traits<char>,__default_alloc_template<false,
0> >,int> &,pair<const basic_string<char,string_char_traits<char>,__default_allo
c_template<false,0> >,int> *>, ostream_iterator<pair<const basic_string<char,str
ing_char_traits<char>,__default_alloc_template<false,0> >,int> > >(_Rb_tree_iter
ator<pair<const basic_string<char,string_char_traits<char>,__default_alloc_templ
ate<false,0> >,int>,pair<const basic_string<char,string_char_traits<char>,__defa
ult_alloc_template<false,0> >,int> &,pair<const basic_string<char,string_char_tr
aits<char>,__default_alloc_template<false,0> >,int> *>, _Rb_tree_iterator<pair<c
onst basic_string<char,string_char_traits<char>,__default_alloc_template<false,0
> >,int>,pair<const basic_string<char,string_char_traits<char>,__default_alloc_t
emplate<false,0> >,int> &,pair<const basic_string<char,string_char_traits<char>,
__default_alloc_template<false,0> >,int> *>, ostream_iterator<pair<const basic_s
tring<char,string_char_traits<char>,__default_alloc_template<false,0> >,int> >)'

t.cpp:21:   instantiated from here
c:\gcc\bin\..\lib\gcc-lib\i386-mingw32msvc\2.95.2\..\..\..\..\include\g++-3\stl_
iterator.h:890: no match for `ostream & << const pair<const basic_string<char,st
ring_char_traits<char>,__default_alloc_template<false,0> >,int> &'
c:\gcc\bin\..\lib\gcc-lib\i386-mingw32msvc\2.95.2\..\..\..\..\include\g++-3\iost
ream.h:77: candidates are: class ostream & ostream::operator <<(char)
c:\gcc\bin\..\lib\gcc-lib\i386-mingw32msvc\2.95.2\..\..\..\..\include\g++-3\iost
ream.h:78:                 class ostream & ostream::operator <<(unsigned char)
c:\gcc\bin\..\lib\gcc-lib\i386-mingw32msvc\2.95.2\..\..\..\..\include\g++-3\iost
ream.h:79:                 class ostream & ostream::operator <<(signed char)
(Ακολουθούν άλλες 10 γραμμές)

Παράδειγμα λάθους που εμφανίζεται κατά τη μεταγλώττιση προγράμματος C++ με τη χρήση της βιβλιοθήκης STL κάτω από Microsoft C/C++ compiler:

        t.cpp(24) : see reference to function template instantiation 'class std:
:basic_ostream<char,struct std::char_traits<char> > &__cdecl std::operator <<(cl
ass std::basic_ostream<char,struct std::char_traits<char> > &,const char *)' bei
ng compiled
C:\PROGRA~1\MICROS~4\VC98\INCLUDE\iterator(203) : error C2679: binary '<<' : no
operator defined which takes a right-hand operand of type 'const struct std::pai
r<class std::basic_string<char,struct std::char_traits<char>,class std::allocato
r<char> > const ,int>' (or there is no acceptable conversion)
        C:\PROGRA~1\MICROS~4\VC98\INCLUDE\iterator(203) : while compiling class-
template member function 'class std::ostream_iterator<struct std::pair<class std
::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > c
onst ,int>,char,struct std::char_traits<char> > &__thiscall std::ostream_iterato
r<struct std::pair<class std::basic_string<char,struct std::char_traits<char>,cl
ass std::allocator<char> > const ,int>,char,struct std::char_traits<char> >::ope
rator =(const struct std::pair<class std::basic_string<char,struct std::char_tra
its<char>,class std::allocator<char> > const ,int> &)'

Πρότυπα σχέδια

Each pattern describes a problem which occurs over and over again in our environment, and then describes the core of the solution to that problem, in such a way that you can use this solution a million times over, without ever doing it the same way twice.
-- Christopher Alexander et al. A Pattern Language

Τα πρότυπα σχέδια μας επιτρέπουν τη

υπάρχουσας σχεδιαστικής γνώσης.

Σε αντίθεση με αλγορίθμους και δομές δεδομένων τα στοιχεία που περιγράφουν δε χρησιμοποιούνται αυτούσια αλλά περιγράφουν πως θα υλοποιηθεί το σχέδιο του έργου.

Επίσης, τα πρότυπα σχέδια περιγράφονται με τέτοιο τρόπο έτσι ώστε να μπορούν να συνδυάζονται μεταξύ τους.

Κάθε περιγραφή ενός σχεδίου περιλαμβάνει:

Παράδειγμα προτύπου σχεδίου

Το παρακάτω κείμενο (Σπινέλλης και Ράπτης 2000) περιγράφει με τη μορφή ενός προτύπου σχεδίου τη σύνδεση εξαρτημάτων:

Component Composition - Structural

Intent
The component composition pattern identifies the primary methods of encapsulated component composition and integration.

Motivation
Encapsulated components do not operate in a vacuum. They are composed to create more powerful components and integrated within an object-based system to provide specialised services. Moreover, composition of encapsulated components with component glue can be used to provide efficient access to off-line data, graphical user interfaces, and a multitude of other component-based services. As an example a spelling checker can be easily constructed by composing the translate, sort, unique, and common components, while the gluing of a editbox and listbox components can be used to provide a GUI front end.

Applicability
Many of the problems solved under the Unix programming environment using shell programming constructs and pipelines can be transformed to component composition structures. Of particular relevance are sequences of filter type components, where each one receives a data stream, performs some operations on it, and forwards it to another filter to perform some other operations. Examples include pipelines of tools that process text, images, sound, and object code. Meunier [13] describes a complete pattern language for a ``Pipes and Filters Architecture'' that can be used as a base to structure applications.

Structure


Figure 4: A spell checker with a GUI.

Figure 4 depicts the component interaction diagram of a filter-based spell checker built from Unix-mined and glue components. The text to be spell-checked is retrieved from the GUI edit box using a data source glue component. It is transformed into a list of words using the translate component which is a direct equivalent of the Unix tr command. The word list is then transformed into a sorted list of unique words using the sort and unique components which correspond to the Unix sort and uniq commands. At the same time, the system dictionary and a user dictionary are passed using appropriate file connectors to the merge component which merges two sorted streams; the merge component is a specialisation of sort which provides this functionality. Finally, the two sorted streams of words to be spelled and acceptable words are checked by common - derived from the Unix comm command - which outputs a list of words contained in the first stream and not contained in the second one. This stream of misspelled words is sent using the ListBoxSink glue component to a GUI list box. It is important to note that the integration of GUI elements using the same component object paradigm and the merging of two data streams could not be implemented using the standard Unix linear pipeline system.

Participants
The components composed are object instances of either active process components that are connected to existing data sources and sinks, or connector and glue components (pipes and environment interfacing classes) that provide such sources and sinks.

Consequences
Using the component composition pattern it is possible to implement sophisticated component interaction topologies. In addition, it is possible to package together existing components to provide new standard and reusable components.

Implementations
The implementation of the composition pattern is independent of the component-framework used. Most relevant decisions are taken when implementing the encapsulation and the glue patterns. Designs based on the composition pattern should be portable across different component frameworks.

Προγραμματισμός με εξαρτήματα

Ένα εξάρτημα (component) είναι μια μονάδα σύνθεσης που ορίζεται μόνο από: Μπορούν να χρησιμοποιηθούν: Όπως και τα αντικείμενα ένα εξάρτημα: Τα εξαρτήματα επιπλέον:

Παραδείγματα εξαρτημάτων

Κατηγορίες εξαρτημάτων από το δικτυακό τόπο http://www.componentsource.com (http://www.componentsource.com):

Πλαίσια εφαρμογών

Ένα πλαίσιο εφαρμογής: Για παράδειγμα ένα πλαίσιο για εφαρμογές που περιλαμβάνουν γραφική διεπαφή με το χρήστη ορίζεται ως έτοιμο πλαίσιο με βάση το σχέδιο: μοντέλο, εικόνα, ελεγκτής (model view controller (MVC)).

Μελέτη περίπτωσης: Java Beans

Η τεχνολογία JavaBeans επιτρέπει την υλοποίηση και χρήση εξαρτημάτων με οπτική παράσταση στο περιβάλλον της γλώσσας Java. Οι βασικές αρχές της τεχνολογίας είναι οι παρακάτω:
Εμφάνιση ενός bean στο περιβάλλον εργασίας και αλλαγή των ιδιοτήτων του (εικόνα από το δικτυακό τόπο της Sun).

Περιέχοντες και επαναλήπτες στην STL

Η πρότυπη βιβλιοθήκη της C++ περιέχει ένα σύνολο από αλγορίθμους που χρησιμοποιούνται με τη χρήση ενός επαναλήπτη (iterator) πάνω σε έναν περιέχοντα (container). Οι λειτουργίες αυτές αποτελούν τη βιβλιοθήκη με το όνομα Standard Template Library (STL). Η STL ορίζει μια σειρά από περιέχοντες (containers) όπως την ουρά, τη στοίβα, την απεικόνιση και τον πίνακα. Πάνω στους περιέχοντες αυτούς επιτρέπει την εκτέλεση αλγορίθμων, όπως την εύρεση ενός στοιχείου, η ένωση δύο περιεχόντων, η ταξινόμηση, κ.λπ.

Στην STL ορίζονται οι παρακάτω πρότυποι (ως προς τα στοιχεία που περιέχουν) περιέχοντες:

list
διπλά συνδεδεμένη λίστα
queue
ουρά
deque
ουρά με πρόσβαση και στις δύο άκρες
map
απεικόνιση (π.χ. από συμβολοσειρές σε ακέραιους)
set
απεικόνιση με μοναδικά στοιχεία στο πεδίο τιμών
stack
στοίβα
vector
πίνακας
Κάθε περιέχων μπορεί να περιλαμβάνει στοιχεία οποιουδήποτε τύπου.

Η πρόσβαση των στοιχείων ενός περιέχοντα από τους αλγορίθμους γίνεται μέσω επαναληπτών (iterators). Οι επαναλήπτες - ανάλογα με τη δομή των δεδομένων του περιέχοντα - μπορούν να επιτρέπουν την παρακάτω ιεραρχία κινήσεων:

Επίσης, μπορούν να επιτρέπουν την παρακάτω ιεραρχία πρόσβασης στα δεδομένα που δείχνουν: Η κλάση των επαναληπτών ορίζεται στην επικεφαλίδα iterator. Μερικές μέθοδοι που ορίζονται σε επαναλήπτες είναι οι παρακάτω: Για κάθε περιέχοντα ορίζονται μέθοδοι όπως (στην περίπτωση της διπλής ουράς):
assign
ανάθεση τιμής
at
αναφορά σε στοιχείο
back
το τελευταίο στοιχείο
begin
επαναλήπτης που δείχνει στην αρχή της δομής
clear
διαγραφή όλων των στοιχείων
empty
αληθές αν η δομή είναι άδεια
end
επαναλήπτης που δείχνει στο τέλος της δομής
erase
διαγραφή σειράς στοιχείων
front
το πρώτο στοιχείο
insert
προσθήκη στοιχείου
operator[]
πρόσβαση σε στοιχείο
pop_back
αφαίρεση στοιχείου από το τέλος
pop_front
αφαίρεση στοιχείου από την αρχή
push_back
προσθήκη στοιχείου στο τέλος
push_front
προσθήκη στοιχείου στην αρχή
rbegin
ανάστροφος επαναλήπτης που δείχνει στην αρχή της δομής
rend
ανάστροφος επαναλήπτης που δείχνει στο τέλος της δομής
resize
καθορισμός του αριθμού των στοιχείων
size
αριθμός των στοιχείων
swap
εναλλαγή δύο στοιχείων μεταξύ τους

Παράδειγμα STL: απεικόνιση

Το παρακάτω παράδειγμα τυπώνει πόσες φορές εμφανίστηκε κάθε λέξη στην είσοδο του προγράμματος:
#include <map>
#include <iostream>
#include <string>

using namespace std;

typedef map <string, int> smap_t;

int
main()
{
        string s;
        smap_t m;                       // Our map

        while (!cin.eof()) {
                cin >> s;
                m[s]++;                 // Use overloaded [] operator
        };

        smap_t::iterator i;             // Iterator for printing the results
        for (i = m.begin(); i != m.end(); i++)
                cout << i->first << " " << i->second << endl;
        return (0);
}

Πρόσθετες λειτουργίες στην STL

Επικεφαλίδα algorithm

Στην επικεφαλίδα algorithm ορίζονται μέθοδοι που ενεργούν πάνω σε περιέχοντες:
adjacent_find
βρίσκει δύο ίσα στοιχεία σε διπλανές θέσεις
binary_search
δυαδική ανίχνευση
copy
αντιγραφή περιοχής
copy_backward
αντίστροφη αντιγραφή περιοχής
count
μέτρημα
count_if
μέτρημα υπό συνθήκη
equal
σύγκριση περιοχών
equal_range
σύγκριση περιοχών με συγκεκριμένη ακρίβεια
fill
πλήρωση περιοχής με τιμή
fill_n
πλήρωση αριθμού στοιχείων με τιμή
find
εύρεση στοιχείου
find_end
εύρεση στοιχείου από το τέλος
find_first_of
εύρεση στοιχείου ίσου με κάποιο μέλος από σύνολο στοιχείων
find_if
εύρεση στοιχείου που να ικανοποιεί συνθήκη
for_each
εκτέλεση συνάρτησης για όλα τα στοιχεία σε περιοχή
generate
πλήρωση περιοχής με αποτέλεσμα συνάρτησης
generate_n
πλήρωση αριθμού στοιχείων με αποτέλεσμα συνάρτησης
includes
έλεγχος αν μια περιοχή εμπεριέχει μια άλλη
inplace_merge
σύζευξη δεδομένων στον ίδιο περιέχοντα
iter_swap
εναλλαγή δύο τιμών
lexicographical_compare
σύγκριση δύο περιοχών α, β για α < β
lower_bound
εύρεση μιας ελάχιστης τιμής σε περιοχή σε σχέση με μιαν άλλη τιμή
make_heap
μετατροπή περιοχής σε σωρό (heap) (δυαδικό δένδρο στο οποίο τα παιδιά έχουν τιμή μικρότερη ή ίση από αυτή των γονέων τους).
max
το μέγιστο από δύο στοιχεία
max_element
εύρεση του μέγιστου στοιχείου σε περιοχή
merge
σύζευξη δύο περιοχών σε τρίτη
min
το ελάχιστο από δύο στοιχεία
min_element
εύρεση του ελαχίστου στοιχείου σε περιοχή
mismatch
εύρεση του πρώτου διαφορετικού στοιχείου ανάμεσα σε δύο περιοχές
next_permutation
υπολογισμός της επόμενης μετάθεσης σε μια περιοχή
nth_element
θέτει ένα στοιχείο στη θέση που θα έπρεπε να έχει αν η περιοχή ήταν ταξινομημένη
partial_sort
ταξινομεί τα πρώτα στοιχεία μιας περιοχής
partial_sort_copy
ταξινομεί τα πρώτα στοιχεία μιας περιοχής σε μιαν άλλη
partition
χωρίζει μια περιοχή στα δύο με βάση μια συνάρτηση και επιστρέφει το σημείο που είναι ο χωρισμός
pop_heap
αφαίρεση στοιχείου από σωρό
prev_permutation
υπολογισμός της προηγούμενης μετάθεσης σε μια περιοχή
push_heap
προσθήκη στοιχείου από σωρό
random_shuffle
ανακατεύει μια περιοχή
remove
αφαιρεί στοιχεία ίσα με μια τιμή
remove_copy
αφαιρεί στοιχεία ίσα με μια τιμή μεταφέροντας το αποτέλεσμα σε μιαν άλλη περιοχή
remove_copy_if
αφαιρεί στοιχεία για τα οποία μια συνάρτηση είναι αληθής μεταφέροντας το αποτέλεσμα σε μιαν άλλη περιοχή
remove_if
αφαιρεί στοιχεία για τα οποία μια συνάρτηση είναι αληθής
replace
αλλάζει τιμή σε στοιχεία ίσα με μια τιμή
replace_copy
αλλάζει τιμή σε στοιχεία ίσα με μια τιμή μεταφέροντας το αποτέλεσμα σε μιαν άλλη περιοχή
replace_copy_if
αλλάζει τιμή σε στοιχεία για τα οποία μια συνάρτηση είναι αληθής μεταφέροντας το αποτέλεσμα σε μιαν άλλη περιοχή
replace_if
αλλάζει τιμή σε στοιχεία για τα οποία μια συνάρτηση είναι αληθής
reverse
αντιστρέφει τη σειρά σε μια περιοχή
reverse_copy
αντιστρέφει τη σειρά σε μια περιοχή μεταφέροντάς την σε μιαν άλλη περιοχή
rotate
περιστρέφει τη σειρά των στοιχείων σε μια περιοχή
rotate_copy
περιστρέφει τη σειρά των στοιχείων σε μια περιοχή μεταφέροντάς την σε μιαν άλλη περιοχή
search
εύρεση σειράς στοιχείων σε μια περιοχή ίσης με στοιχεία μιας άλλης
search_n
εύρεση σειράς στοιχείων σε μια περιοχή ίσης με αριθμό στοιχείων μιας άλλης
set_difference
θέτει μια περιοχή ίση με τη διαφορά των στοιχείων δύο άλλων περιοχών (διαφορά συνόλων)
set_intersection
θέτει μια περιοχή ίση με την τομή των στοιχείων δύο άλλων περιοχών (τομή συνόλων)
set_symmetric_difference
θέτει μια περιοχή ίση με τα μη κοινά των στοιχείων δύο άλλων περιοχών
set_union
θέτει μια περιοχή ίση με την ένωση των στοιχείων δύο άλλων περιοχών (ένωση συνόλων)
sort
ταξινομεί μια περιοχή
sort_heap
ταξινομεί έναν σωρό
stable_partition
χωρίζει μια περιοχή στα δύο με βάση μια συνάρτηση και επιστρέφει το σημείο που είναι ο χωρισμός. Ο χωρισμός γίνεται χωρίς να αλλάξει η σχετική σειρά των στοιχείων.
stable_sort
ταξινομεί μια περιοχή. Η ταξινόμηση γίνεται χωρίς να αλλάξει η σχετική σειρά των στοιχείων που είναι μεταξύ τους ίσα.
swap
αντιστρέφει μεταξύ τους δύο στοιχεία
swap_ranges
αντιστρέφει μεταξύ τους δύο περιοχές
transform
εφαρμόζει έναν τελεστή σε μια περιοχή ή μεταξύ δύο περιοχών
unique
αφαιρεί τα όμοια στοιχεία από μια περιοχή
unique_copy
αφαιρεί τα όμοια στοιχεία από μια περιοχή μεταφέροντάς την σε μιαν άλλη περιοχή
upper_bound
εύρεση μιας μέγιστης τιμής σε περιοχή σε σχέση με μια άλλη τιμή

Επικεφαλίδα numeric

Στην επικεφαλίδα algorithm ορίζονται αριθμητικές μέθοδοι που ενεργούν πάνω σε περιέχοντες:
accumulate
υπολογίζει ένα σύνολο πάνω σε μια περιοχή
adjacent_difference
υπολογίζει τις διαφορές τιμών μεταξύ στοιχείων μιας περιοχής
inner_product
υπολογίζει ένα εσωτερικό γινόμενο μεταξύ δύο περιοχών
partial_sum
υπολογίζει ένα μερικό άθροισμα τιμών μιας περιοχής σε μιαν άλλη

Άλλες επικεφαλίδες

Ακόμα στην STL ορίζονται οι παρακάτω επικεφαλίδες:
utility
πρότυπη κλάση που ορίζει διάταξη σε ζεύγη τιμών
functional
κλάση που επιτρέπει συναρτησιακό προγραμματισμό
memory
ορίζει την κλάση allocator η οποία κατανέμει τη μνήμη σε όλους τους περιέχοντες. Ο επανακαθορισμός της επιτρέπει την υλοποίηση άλλων στρατηγικών καταμερισμού και πρόσβασης στη μνήμη.

Κατανεμημένα συστήματα βασισμένα σε εξαρτήματα

Πριν δύο δεκαετίες, η φύση των υπολογιστικών συστημάτων εκφράζονταν με την ύπαρξη ισχυρών κεντρικών υπολογιστικών συστημάτων (mainframes) στα οποία ήταν εγκατεστημένες εφαρμογές οι οποίες μπορούσαν να προσπελαστούν από τους χρήστες μέσα από τα τερματικά του συστήματος.

Με την εμφάνιση και την ανάπτυξη των προσωπικών υπολογιστών και καθώς οι εφαρμογές άρχισαν να γίνονται ολοένα και πιο μεγάλες και σύνθετες, άρχισαν να μεταμορφώνονται από ενιαία προγράμματα σε κομμάτια ικανά να συνεργάζονται μεταξύ τους. Στην διάσπαση των εφαρμογών σε περισσότερα από ένα μέρη, συνέβαλλε η ανάπτυξη της αρχιτεκτονικής πελάτη/εξυπηρετητή (client/server) βάσει της οποίας γινόταν η υλοποίησή τους. Κατά την αρχιτεκτονική πελάτη/εξυπηρετητή, μία διεργασία καλείται πελάτης (client process) όταν αιτείται την υλοποίηση κάποιων υπηρεσιών-μεθόδων από μία διεργασία η οποία είναι ικανή να της προσφέρει τις επιθυμητές υπηρεσίες. Η τελευταία αυτή διεργασία καλείται διεργασία του εξυπηρετητή (server process).

Αργότερα με την ανάπτυξη των υπολογιστικών δικτύων τα συνθετικά μέρη των εφαρμογών, προκειμένου να υπάρξει καλύτερη και αποτελεσματικότερη εκμετάλλευση των νέων δυνατοτήτων, άρχισαν να διαμοιράζονται στους υπολογιστές του δικτύου. Με τον τρόπο αυτό αυξανόταν η απόδοση και εκμετάλλευση των πόρων του δικτύου. Αυτού του είδους οι εφαρμογές ονομάζονται κατανεμημένες εφαρμογές (distributed applications).

Η δυνατότητα διασύνδεσης, σε δίκτυα, υπολογιστικών συστημάτων διαφορετικής αρχιτεκτονικής είχε σαν αποτέλεσμα την δημιουργία ενός ανομοιογενούς υπολογιστικού περιβάλλοντος. Θα έπρεπε, λοιπόν, και οι εφαρμογές να μπορέσουν να εξελιχθούν έτσι ώστε να είναι δυνατή η λειτουργία τους σε τέτοια ετερογενή συστήματα υπολογιστών. Αυτή η εξέλιξη οδήγησε στην εμφάνιση των ετερογενών κατανεμημένων εφαρμογών (heterogeneous distributed applications).

Τεχνολογίες διαλειτουργικότητας κατανεμημένων εφαρμογών

Τα βασικά στοιχεία που χαρακτηρίζουν ένα σύγχρονο υπολογιστικό περιβάλλον είναι τα εξής: Με βάση τα παραπάνω στοιχεία η χρησιμοποίηση μοντέλων-μεθοδολογιών τα οποία θα διευκολύνουν την λειτουργία των εφαρμογών κάτω από τις συνθήκες ενός ετερογενούς περιβάλλοντος και θα επιτελούν τις λειτουργίες επικοινωνίας με τρόπο τέτοιο έτσι ώστε να είναι διαφανής προς τους τελικούς χρήστες, είναι επιβεβλημένη και η υιοθέτησή τους αποτελεί πλέον ζήτημα για τους οργανισμούς και τις επιχειρήσεις οι οποίες συντηρούν τέτοιου είδους υπολογιστικά συστήματα και δίκτυα.

Τρία είναι τα βασικά μοντέλα των οποίων σκοπός είναι η υλοποίηση των παραπάνω και αυτά είναι:

Και τα τρία αυτά μοντέλα βασίζονται πάνω στις ίδιες αρχές (και οι τρεις τεχνολογίες λειτουργούν βασιζόμενες στην έννοια του αντικειμένου) και εξυπηρετούν τον ίδιο σκοπό. Κάθε ένα από αυτά έχει όμως τις δικές του ιδιαιτερότητες από τις οποίες κρίνονται και επιλέγονται.

OMG CORBA

Η OMG (Object Management Group) αναπτύσσει πρότυπα βασιζόμενη στην τεχνολογία του αντικειμένου.

Σύμφωνα με την αρχιτεκτονική που προτείνεται από την OMG, την OMA (Object Management Architecture), το κάθε κομμάτι λογισμικού παρουσιάζεται σαν αντικείμενο (Object).

Τα διάφορα αντικείμενα επικοινωνούν μεταξύ τους μέσω του ORB (Object Request Broker), ο οποίος αποτελεί το στοιχείο-κλειδί γι' αυτήν την επικοινωνία. Ο ORB παρέχει τους μηχανισμούς εκείνους μέσω των οποίων τα αντικείμενα κάνουν τις αιτήσεις και συλλέγουν τις αποκρίσεις.

Η CORBA (Common Object Request Broker Architecture) αποτελεί το πρότυπο της OMG για τον ORB.

Κατά την CORBA, ένας πελάτης ο οποίος ζητάει κάποιες υπηρεσίες από κάποιο αντικείμενο, κάνει μία αίτηση (request) η οποία μεταβιβάζεται στον ORB και ο οποίος είναι στη συνέχεια υπεύθυνος για την προώθηση της αίτησης προς τον κατάλληλο εξυπηρετητή.

Η αίτηση αυτή περιέχει όλες τις απαραίτητες πληροφορίες που απαιτούνται προκειμένου να υλοποιηθεί και αυτές είναι:

Για να είναι κατανοητή όμως η αίτηση θα πρέπει να έχει μία κατάλληλη μορφή και γι' αυτό το λόγο γίνεται προς τον ORB μέσω διασυνδετών (interfaces: "Dynamic Invocation" και "IDL Stubs").

Για τον ίδιο λόγο η προώθηση της αίτησης προς τον εξυπηρετητή γίνεται μέσω διασυνδετών ("Static IDL Skeleton" και "IDL Skeleton").

Ένας διασυνδέτης αποτελεί μία περιγραφή ενός συνόλου από πιθανές λειτουργίες τις οποίες ένας πελάτης μπορεί να αιτηθεί ενός αντικειμένου.

Ένα αντικείμενο ικανοποιεί έναν διασυνδέτη εάν μπορεί να προσδιοριστεί σαν το αντικείμενο-στόχος (target object) για κάθε δυνατή αίτηση η οποία περιγράφεται από τον διασυνδέτη.

Στο σημείο αυτό δεν θα πρέπει να αγνοήσουμε την ιδιότητα της κληρονομικότητας που χαρακτηρίζει τους διασυνδέτες και η οποία παρέχει τον συνθετικό εκείνο μηχανισμό που επιτρέπει σ' ένα αντικείμενο να υποστηρίζει πολλαπλούς διασυνδέτες.

Οι διασυνδέτες καθορίζονται πλήρως μέσω της OMG IDL (Interface Definition Language). Η IDL δεν αποτελεί γλώσσα προγραμματισμού, όπως οι γνωστές γλώσσες προγραμματισμού, αλλά μία καθαρά περιγραφική γλώσσα η οποία δεν περιλαμβάνει δομές αλγορίθμων ή μεταβλητές. Η γραμματική της αποτελεί υποσύνολο της ANSI C++ με πρόσθετες κατασκευές για την υποστήριξη μηχανισμών για την επίκληση απομακρυσμένων λειτουργιών.

Οι πελάτες δεν είναι "γραμμένοι" σε OMG IDL αλλά σε γλώσσα για την οποία έχει προσδιοριστεί η αντιστοιχία της με την OMG IDL.

Microsoft COM/DCOM/COM+

Το δεύτερο μοντέλο βασίζεται και αυτό στην τεχνολογία του αντικειμένου με την έννοια όμως εδώ των αυτόνομων συνθετικών εξαρτημάτων μίας εφαρμογής.

Και εδώ, ο βασικός σκοπός του μοντέλου είναι να καταστήσει δυνατή την επικοινωνία δύο ή περισσοτέρων εφαρμογών ή συνθετικών, ακόμα και στην περίπτωση που αυτά διαφέρουν ως προς την προέλευση, τη γλώσσα προγραμματισμού, τα μηχανήματα στα οποία βρίσκονται και τα λειτουργικά συστήματα κάτω από τα οποία τρέχουν.

Ο "μηχανισμός λειτουργίας του μοντέλου είναι παρόμοιος με αυτόν της CORBA. Βασικό ρόλο παίζουν οι διασυνδέτες τα οποία δεν είναι τίποτα άλλο από ένα σαφές διατυπωμένο "συμβόλαιο" μεταξύ των "κομματιών" λογισμικού, το οποίο περιέχει ένα σύνολο από σχετικές λειτουργίες. Όταν λέμε ότι ένα αντικείμενο υλοποιεί έναν διασυνδέτη αυτό σημαίνει ότι το αντικείμενο αυτό υλοποιεί κάθε λειτουργία του.

Πως γίνεται, όμως, η διαδικασία της αιτήσεως κάποιων λειτουργιών από έναν πελάτη;

Για να μπορέσει ένας πελάτης να εξυπηρετηθεί από κάποιο αντικείμενο, θα πρέπει η γλώσσα προγραμματισμού, στην οποία είναι υλοποιημένος, να έχει τη δυνατότητα δημιουργίας δεικτών και να καλεί συναρτήσεις μέσω των δεικτών. Θα πρέπει λοιπόν ο πελάτης να έχει τον κατάλληλο δείκτη προς τον επιθυμητό διασυνδέτη τον οποίο περιέχει τις επιθυμητές από τον πελάτη λειτουργίες.

Στην περίπτωση όμως που ο πελάτης δεν έχει τον κατάλληλο δείκτη προς το επιθυμητό διασυνδέτη, τότε απευθύνεται προς την COM δίνοντας σαν στοιχεία την ταυτότητα της κλάσης του εξυπηρετητή που επιθυμεί ο πελάτης και τον τύπο του, δηλαδή εάν είναι:

Η COM τότε αναλαμβάνει να βρει τον επιθυμητό εξυπηρετητή και να επιστρέψει στον πελάτη τον επιθυμητό δείκτη.

Όπως έγινε σαφές, ο κάθε πελάτης μπορεί να είναι υλοποιημένος σε οποιαδήποτε γλώσσα προγραμματισμού, αρκεί αυτή να υποστηρίζει την κατασκευή και την χρήση δεικτών. Για τα "interfaces", όμως, υπάρχει και εδώ μία "IDL" (interface Definition Language) η οποία επιτρέπει και βοηθάει τους σχεδιαστές να κατασκευάζουν διασυνδέτες.

Sun Java RMI

Το μοντέλο RMI της Sun, βασίζεται και αυτό στην τεχνολογία του αντικειμένου και είναι σχεδιασμένο για να προάγει την διαλειτουργικότητα μεταξύ αντικειμένων, κατασκευασμένων σε Java, μέσα σε ένα κατανεμημένο και ετερογενές περιβάλλον.

Η τεχνολογία RMI αφορά αποκλειστικά αντικείμενα τα οποία είναι κατασκευασμένα με τη γλώσσα προγραμματισμού Java. Αυτό έχει ως αποτέλεσμα να δίνει την δυνατότητα της πλήρους εκμετάλλευσης των δυνατοτήτων της Java αλλά και το πλεονέκτημα του ομοιογενούς, ως προς τη γλώσσα προγραμματισμού, περιβάλλοντος.

Η αρχιτεκτονική ενός RMI συστήματος ακολουθεί την δομή των στρωμάτων-επιπέδων (layers). Υπάρχουν τρία (συν ένα) επίπεδα:

Υπάρχει, επίσης, το επίπεδο της Εφαρμογής (Application) το οποίο όμως δεν αποτελεί καθαρό κομμάτι της τεχνολογίας και το οποίο βρίσκεται πάνω απΤ τα υπόλοιπα.

Η διαδικασία επίκλησης κάποιων υπηρεσιών από έναν πελάτη, δεν διαφέρει, ιδιαίτερα, σε σχέση με τις προαναφερθείσες τεχνολογίες. Ένας πελάτης, ο οποίος επιθυμεί την υλοποίηση κάποιων υπηρεσιών, υποβάλλει μία αίτηση προς το RMI-σύστημα. Στην αίτηση αυτή θα πρέπει να αναφέρονται οι κατάλληλες εκείνες πληροφορίες οι οποίες απαιτούνται για την αποστολή της αίτησης προς τον κατάλληλο εξυπηρετητή. Αυτές οι πληροφορίες-παράμετροι είναι: μία αναφορά του επιθυμητού αντικειμένου (object reference), οι επιθυμητές μέθοδοι και οι κατάλληλες παράμετροι για την υλοποίηση των μεθόδων.

Από τη στιγμή που ο πελάτης καταθέσει την αίτησή του, το RMI-σύστημα είναι υπεύθυνο για την ανεύρεση του κατάλληλου εξυπηρετητή, την μεταβίβαση της αιτήσεων και την επιστροφή των αποτελεσμάτων στον πελάτη.

Όμοια με την CORBA και την COM/DCOM, σε ένα RMI-σύστημα, οι πελάτες δεν έρχονται ποτέ σε απευθείας επικοινωνία με τα επιθυμητά αντικείμενα παρά μόνο μέσω των διασυνδετών αυτών των αντικειμένων. Και εδώ ο διασυνδέτης είναι αυτός που περιέχει τις μεθόδους-υπηρεσίες τις οποίες μπορεί να υλοποιήσει το αντικείμενο.

Η επικοινωνία πελάτη-αντικειμένου, σΤ ένα σύστημα RMI, είναι η ίδια ανεξάρτητα με το που βρίσκεται το επιθυμητό αντικείμενο (αν δηλαδή βρίσκεται στην ίδια διεργασία με τον πελάτη, αν βρίσκεται τοπικά ή απομακρυσμένα).

Βιβλιογραφία

Ασκήσεις

  1. Ερευνήστε το περιβάλλον Windows και τις εφαρμογές που τρέχουν σε αυτό για παραδείγματα επαναχρησιμοποίησης.
  2. Εξετάστε τα εξαρτήματα με τις μεγαλύτερες πωλήσεις που εμφανίζει η εταιρία Componentsource (http://www.componentsource.com). Τι συμπεράσματα βγάζετε για το είδος των εφαρμογών που υλοποιούνται, τα εργαλεία που χρησιμοποιούνται, και τις αντίστοιχες αδυναμίες τους;