Programowanie komponentowe - kik

advertisement
Programowanie komponentowe
Henryk Budzisz
wersja 1.0
Koszalin 2006
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
Program wykładów

Wprowadzenie
 Technologia JavaBeans
 Technologia CORBA
 Technologie COM/DCOM/COM+
 Technologia .NET
 Technologia XML
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
2
Wprowadzenie

Koncepcje
 Komponent programowy
 Technologie
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
3
„Klejenie” aplikacji
Programowanie komponentowe (PK) jest procesem
tworzenie aplikacji (systemu informatycznego) poprzez
„sklejanie” jej z gotowych, odpowiednio przygotowanych,
wymiennych komponentów programowych.
Budowanie aplikacji z komponentów jest często
porównywane do budowania (żargonowo klejenia) układu
(urządzenia) elektronicznego z gotowych, seryjnych
komponentów elektronicznych (scalaków, tranzystorów,
przełączników).
Zarówno w jednym jak i w drugim przypadku, komponenty
muszą być przystosowane do tego „klejenia” (wykonane
według określonych standardów).
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
4
Zastępowalność
Podstawowym celem praktycznym PK, jest stworzenie
warunków do łatwej zastępowalności komponentów.
Systemy informatyczne podlegają ciągłym zmianom.
Jest to proces kosztowny i często naruszający
niezawodność oprogramowania.
Możliwość zastąpienia komponentu przez jego nową
wersję lub zupełnie inną implementację, bez potrzeby
rozmontowywania całego systemu, jest niezwykle
atrakcyjna z praktycznego punktu widzenia.
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
5
Programowanie komponentowe a
programowanie obiektowe
Komponent musi odpowiadać specyfikacjom
określonym dla danej technologii. Sposób
implementacji komponentu nie jest narzucany.
Ponieważ idee programowania komponentowego i
programowania obiektowego w dużym stopniu
pokrywają się, komponenty są zwykle definiowane
poprzez klasy.
Klasy definiujące komponenty muszą spełniać
wymagania określone w specyfikacji, np. zapewnić
serializację, mechanizmy łączenia i komunikacji,
samoinstalację itp.
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
6
Komponent programowy
Komponent jest wyizolowaną (autonomiczną) jednostką
programową, która zgodnie z przyjętymi standardami:
 zawiera dane i metody realizujące operacje na tych
danych – niezależnie od otoczenia (hermetyzacja)
 udostępnia zestaw oferowanych usług poprzez
interfejs programowy.
 zapewnia komunikację z innymi komponentami.
 zapewnia „utrwalanie” i odtwarzanie stanu
wewnętrznego.
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
7
Technologie
JavaBeans – wprowadzona do języka Java już od
pierwszej wersji.
 CORBA (ang. Common Object Request Broker
Architecture )
 COM (ang. Component Object Model) –
opracowana przez Microsoft; następnie
rozszerzona do DCOM i COM+
 .NET – lansowana przez Microsoft jako następca
COM/DCOM/COM+
 XML – nowy zawodnik w grze komponentowej

Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
8
Technologie sieciowe
Wraz z rozwojem programowania sieciowego i
rozproszonego wspomniane technologie zostały
zostały rozszerzone:
 EJB – Enterprice Java Beans firmy Sun
 CCM – CORBA Component Model
 DCOM/COM+ - rozszerzenie technologii COM
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
9
„Mój klej jest lepszy od twojego kleju”
Jaką technologię „klejenia” wybrać?
Patrząc z odległości 1000 m, efekt „klejenia” w każdej
technologii jest podobny. Podstawowe koncepcje są
wspólne.
Z bliska jednak dostrzegamy, że w każdej technologii
stosuje się zdecydowanie różne techniki i style
programowania dla osiągnięcia celu.
Każda technologia komponentowa ma swoich
zwolenników. Na razie nie widać przegranych.
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
10
Technologia JavaBeans

Wprowadzenie
 Budowa komponentu Bean
 Zdarzenia
 Refleksja i introspekcja
 Serializacja
 Podsumowanie
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
11
Wprowadzenie

Definicja komponentu Bean
 Cechy komponentu Bean
 Komponenty wizualne
 Środowiska programowe
 Przykład: pakiet ICEBrowser
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
12
Definicja
Bean jest obiektem, którego właściwości i
funkcjonalność mogą być odczytywane i
zmieniane zgodnie z określonym protokołem
(JavaBean specification). Protokół ten określa
zasady komunikacji pomiędzy Beanem, a
otoczeniem (zwykle środowiskiem wizualnym).
Definiując Bean definiujemy klasę, uwzględniając
konwencje narzucone przez specyfikacje
standardu. Nakład dodatkowej pracy „na bycie
komponentem Bean” jest niewielki.
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
13
Cechy komponentu Bean
Komponenty Bean mają wspólne cechy:




Wsparcie dla introspekcji (ang. introspection) udostępnianie informacji o właściwościach i
obsługiwanych zdarzeniach.
Wsparcie dla dostosowywania (ang. customization) –
ustawiania wyglądu i zachowania komponentu.
Wsparcie dla obsługi zdarzeń (ang. events) – komunikacji
między komponentami
Wsparcie dla utrwalania (ang. persistency) – zapisywania i
odczytywania informacji o stanie komponentu do/z pliku.
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
14
Komponenty wizualne
Większość komponentów Bean jest definiowana
jako komponenty wizualne (zawierają metodę
public void paint(Graphics g) { ... }).
Takie komponenty wyprowadzane są z klasy
java.awt.Component (lub klas pochodnych), aby
mogły być umieszczane w kontenerach. Klasy
bibliotek AWT i Swing są komponentami Bean.
Komponenty, które nie są wizualne (np. Timer lub
ButtonGroup z biblioteki Swing), nie muszą być
wyprowadzone z żadnej konkretnej klasy.
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
15
Środowiska programowe
Przykłady programowe zademonstrowane zostaną w
dwóch środowiskach:
 RealJ – bardzo proste środowisko tekstowe, dające
pełną kontrolę nad tekstem źródłowym programu
(środowisko niczego nie „dopisuje”).

NetBeans – złożone środowisko graficzne – dużo
ułatwień, ale łatwo też stracić kontrolę nad tym co
się dzieje w projekcie i w tekstach źródłowych.
Popularnym środowiskiem graficznym jest Eclipse.
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
16
Pakiet ICEBrowser
Zestaw komponentów do budowania przeglądarki
dokumentów HTML pochodzących z różnych
żródeł (z połączenia HTTP, z anonimowego
połączenia FTP, z lokalnego dyskowego systemu
plikowego, z pliku spakowanego .JAR, ze
strumienia javowego Reader)
Pakiet zrealizowany został w firmie ICEsoft
Technologies, Inc. (www.icesoft.com). Wersja
komercyjna jest bardziej rozbudowana i występuje
w produktach wielu firm (Sun, Novell, Oracle, ...).
Komponenty zawarte są w pliku spakowanym
icebrowserbean.jar. Dostępna jest również
dokumentacja i przykłady użycia komponentów.
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
17
Komponenty ICEBrowser
Pakiet ice.htmlbrowser zawiera oprócz klas
pomocniczych trzy komponenty:
 Document – komponent interpretujący hipertekst i
zarządzający dokumentami HTTP. Zbudowany na
bazie klasy Panel biblioteki AWT.
 Browser – komponent na bazie klasy Document,
umożliwiający dostęp do hipertekstu pochodzącego
z różnych źródeł, prowadzenie historii, itp.
 ICEBrowser – gotowy aplet reprezentujący
przeglądarkę dokumentów hipertekstowych.
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
18
Komponent Document
Dostępnych jest ok. 70 metod. Wybrane metody:











gotoLocation – ustawia wskazany dokument w głównej ramce
get-/setCurrentLocation – pobiera/ustawia dokument w bieżącej ramce
get-/setCurrentFrame – pobiera/ustawia bieżącą ramkę
get-/setDocumentTitle – pobiera/ustawia tytuł dokumentu
getSelectedText – pobiera zaznaczony tekst
reload – ponownie załadowuje dokument (odświeżanie)
add-/removeMouseOverLinkListener – obsługa zdarzeń dot. linków
add-/removePropertyChangeListener – obsługa zdarzeń dot. właściwości
firePropertyChange – wygenerowanie zdarzenia dot. zmiany właściwości
search – wyszukuje podany tekst w dokumencie
printDoc – organizuje wydruk dokumentu HTML lub ramki
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
19
Komponent Browser
Browser dziedziczy metody komponentu Document, a
ponadto zawiera 12 własnych metod.
Wybrane metody:






gotoLocation – załaduj dokument HTML
goBack – wróć do poprzedniego dokumentu
goForward – wróć do dokumentu wcześniejszego
getBackHistory – zwróć historię odwiedzin
getForwardHistory - zwróć historię odwiedzin
get-/setCacheSize – podaj/ustaw rozmiar buforu dokumentów
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
20
Zastosowania komponentów
ICEBrowser

do budowy systemu pomocy on-line dla aplikacji.

dostęp do stron www z poziomu aplikacji.

budowanie interfejsu dla aplikacji rozproszonych.
Przykłady:
HTMLBrowser\demo1 (RealJ)
HTMLBrowser\TestHTMLBrowser1 (NetBeans)
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
21
Budowa komponentu Bean

Konstruktor bezparametrowy
 Właściwości i akcesory
 Właściwości proste
 Właściwości logiczne
 Właściwości indeksowane
 Właściwości związane i ograniczone
 Metody publiczne komponentu
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
22
Konstruktor bezparametrowy
Komponent JavaBeans musi zawierać konstruktor
bezparametrowy, np.: void Button() { ... }
Umożliwia to dynamiczne ładowanie klasy i
tworzenie obiektu przy użyciu metody
Bean.instantiate(...)
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
23
Właściwości i akcesory
Właściwość (ang. property) jest definiowana poprzez
nazwę funkcji nazywanej akcesorem (ang. accessor).
Nazwa ta rozpoczyna się przedrostkiem get- , set- lub is- .
Getter jest akcesorem służącym do pobierania wartości
właściwości. Przykład: getLabel definiuje właściwość
label (zmiana wielkości pierwszej litery)
Setter służy do ustawiania właściwości, np.: setColor
definiuje właściwość color. Zmiana właściwości jest
często uzgadniana z innymi komponentami.
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
24
Właściwości proste
Właściwość prosta ma pojedynczą wartość.
Getter:
<typ właściwości> get<nazwa właściwości> ()
{ <definicja> }
Setter:
void set<nazwa właściwości> (<typ właściwości>)
{ <definicja> }
Przykład definiowania właściwości prostej foreground:
private Color color;
// zmienna pomocnicza
// definicja akcesorów
public Color getForeground()
// getter
{ return color; }
public void setForeground(Color c) // prosty setter
{ color = c; }
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
25
Fragment kodu klasy AbstractButton.java (Sun Microsystems)
/**
* Returns the button's text.
* @return the buttons text
* @see #setText
*/
public String getText() {
return text;
}
/**
* Sets the button's text.
* @param text the string used to set the text
* @see #getText
* @beaninfo
*
bound: true
*
preferred: true
*
attribute: visualUpdate true
* description: The button's text.
*/
public void setText(String text) {
String oldValue = this.text;
this.text = text;
firePropertyChange(TEXT_CHANGED_PROPERTY, oldValue, text);
updateDisplayedMnemonicIndex(text, getMnemonic());
}
if (accessibleContext != null) {
accessibleContext.firePropertyChange(
AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
oldValue, text);
}
if (text == null || oldValue == null || !text.equals(oldValue)) {
revalidate();
repaint();
}
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
26
Właściwości logiczne
Właściwość logiczna jest właściwością prostą typu boolean,
ale ma inne akcesory.
Getter:
boolean is<nazwa właściwości> ()
{ <definicja> }
Setter:
void set<nazwa właściwości> (boolean)
{ <definicja> }
Przykład:
private boolean selected;
// definicja akcesorów
public boolean isSelected()
{ return selected; }
public void setSelected(boolean sel)
{ selected = sel; }
Wprowadzenie Beans COM NET XML CORBA
// getter
// prosty setter
© H.Budzisz
27
Fragment kodu klasy AbstractButton.java
(Sun Microsystems)
/**
* Returns the state of the button. True if the
* toggle button is selected, false if it's not.
* @return true if the toggle button is selected, otherwise false
*/
public boolean isSelected() {
return model.isSelected();
}
/**
* Sets the state of the button. Note that this method does not
* trigger an <code>actionEvent</code>.
* Call <code>doClick</code> to perform a programatic action change.
*
* @param b true if the button is selected, otherwise false
*/
public void setSelected(boolean b) {
boolean oldValue = isSelected();
model.setSelected(b);
}
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
28
Właściwości indeksowane
Właściwość indeksowana jest tablicą elementów.
Getter elementu:
<typ właściwości> get<nazwa właściwości> (int)
{ <definicja> }
Setter elementu:
void set<nazwa właściwości> (int, <typ właściwości>)
{ <definicja> }
Getter tablicy:
<typ właściwości>[] get<nazwa właściwości> ()
{ <definicja> }
Setter tablicy:
void set<nazwa właściwości> (<typ właściwości>[])
{ <definicja> }
Przykład: void setWykaz (String[]) { ... }
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
29
Właściwości związane i ograniczone
Właściwości komponentu mogą być związane (ang.
bounded), tzn. o zmianie tej właściwości informowane są
inne komponenty i reagują na tą zmianę.
Właściwości komponentu mogą być ograniczane (ang.
constrained), tzn. o zmianie powiadamiane są inne
komponenty; zmiana dochodzi do skutku jeżeli żaden z
powiadomionych komponentów nie zawetuje zmiany.
Mechanizm ten funkcjonuje w oparciu o generowanie i
rozsyłanie odpowiednich zdarzeń.
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
30
Metody publiczne komponentu
Metody publiczne komponentu są często
wykorzystywane w trakcie komunikacji
pomiędzy komponentami (są wywoływane w
trakcie obsługi zdarzeń przychodzących z
innych komponentów).
Metody te powinny być synchronizowane dla
zapewnienia poprawnej obsługi wywołań
pochodzących z różnych wątków.
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
31
Zdarzenia

Koncepcja
 Hierarchia zdarzeń
 Tworzenie słuchacza
 Przyłączanie słuchacza
 Adaptery
 Anonimowe klasy wewnętrzne
 Klasa narzędziowa PropertyChangeSupport
 Rozgłaszanie zmian właściwości
 Wetowanie zmian właściwości
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
32
Koncepcja
Źródło
zdarzenie
Słuchacz
Programowa realizacja:
 Utworzyć obiekt Słuchacza (obiekt klasy implementującej
interfejs nasłuchu lub klasy potomnej z adaptera).
 Przyłączyć obiekt słuchacza do jednego lub większej
liczby komponentów.
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
33
Hierarchia zdarzeń
Object
EventObject
PropertyChangeEvent
AWTEvent
ActionEvent
AdjustmentEvent
ContainerEvent
ItemEvent
WindowEvent
TextEvent
FocusEvent
ComponentEvent
InputEvent
KeyEvent
MouseEvent
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
34
Zdarzenie ActionEvent
ActionEvent(Object source, int id, String command)
gdzie:
source - odniesienie na obiekt, który jest źródłem zdarzenia; słuchacz
może użyć metody getSource aby pobrać to odniesienie.
id – identyfikator liczbowy (zwykle stała ACTION_PERFORMED);
słuchacz może użyć metody getID
command – dowolny łańcuch, np. etykietka przycisku albo nazwa
opcji menu; słuchacz może użyć metody getActionCommand
Przykład generowania zdarzenia:
ActionEvent action = new ActionEvent(this,
ActionEvent.ACTION_PERFORMED, label);
Powiadamianie przyłączonego słuchacza o zdarzeniu action:
listener.actionPerformed(action);
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
35
Zdarzenie MouseEvent
public MouseEvent(Component source, int id,
long when, int modifiers, int x, int y,
int clickCount, boolean popupTrigger)
Parametry:
source – odniesienie na żródło zdarzeń
id – identyfikator liczbowy (patrz: dokumentacja JDK)
when – czas powstania zdarzenia.
modifiers – modyfikator wskazujący na użycie klawisza
modyfikującego (np.: shift, ctr, alt).
x, y- współrzędne kursora myszki.
clickCount – liczba kliknięć myszki związanych ze zdarzeniem.
popupTrigger – zmienna logiczna; prawdziwa jeżeli zdarzenie
wyzwala menu rozwijane.
Parametry te są dostępne z poziomu słuchacza poprzez gettery.
Przykład: demo2.java klasa Nawigacja funkcja mouseReleased (RealJ)
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
36
Zdarzenie PropertyChangeEvent
PropertyChangeEvent(Object source, String propertyName,
Object oldValue, Object newValue)
Parametry:
source – komponent JavaBean, który jest źródłem zdarzenia.
propertyName – nazwa właściwości, która została zmieniona.
oldValue – poprzednia wartość właściwości.
newValue – nowa wartość właściwości.
Przykład generowania zdarzenia:
PropertyChangeEvent propEvt =
new PropertyChangeEvent(this, ”text”, oldTxt,
newTxt);
Jeżeli wartość właściwości jest typu prostego (np. int), musi być
przekształcona w obiekt klasy opakowującej (dla int jest to Integer)
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
37
Tworzenie słuchacza
Słuchacz - to klasa, która może obsługiwać zdarzenie. Każda
klasa, która implementuje interfejs nasłuchu staje się
Słuchaczem, np.:
public class MojaKlasa implements ActionListener {
...
}
Każda klasa implementująca interfejs musi zdefiniować metody
interfejsu. Dla interfejsu ActionListener jest to:
public void actionPerformed(ActionEvent e) {
// instrukcje obsługujące zdarzenie
}
Klasa-słuchacz może też implementować większą liczbę
interfejsów nasłuchu (określamy w ten sposób zestaw
obsługiwanych zdarzeń).
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
38
Interfejsy nasłuchu
EventListener
ActionListener
Action
WindowListener
MouseListener
MouseInputListener
MouseMotionListener
MenuListener
PropertyChangeListener
VetoableChangeListener
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
39
Interfejsy nasłuchu zmian właściwości
Interfejs PropertyChangeListener ma jedną metodę:
public void propertyChange(PropertyChangeEvent e)
Interfejs VetoableChangeListener również ma jedną metodę:
public void vetoableChange(PropertyChangeEvent e)
throws PropertyVetoException
Schemat definiowania słuchacza:
public class Listener implements PropertyChangeListener {
...
public void propertyChange(PropertyChangeEvent e) {
...
} // propertyChange
...
}
Przykład: demo6.java (RealJ)
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
40
Przyłączanie słuchacza
Wszystkie komponenty JavaBeans powinny umożliwiać
przyłączanie/odłączanie określonych typów Słuchaczy. Służą do
tego metody:
addXXXListener() oraz removeXXXListener(), gdzie XXX jest
typem słuchacza.
Np.: zdarzenie ActionEvent może być obsłużone przez Słuchacza
implementującego interfejs ActionListener; Słuchacz taki może być
przyłączony do komponentów, które mają dostęp do metody
addActionListener(). Są to: Button, List, TextField, MenuItem oraz
klasy pochodne.
Przykład:
Słuchacz słuchacz = new Słuchacz();
przyciskOK.addActionListener(słuchacz);
Przykład programowy – Demo2.java (RealJ)
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
41
Rejestracja słuchaczy (AWTEventMulticaster)
Przykład użycia klasy bibliotecznej java.awt.AWTEventMulticaster do
rejestrowania słuchaczy w komponencie AWT (JDK Help)
public myComponent extends Component {
ActionListener actionListener = null;
public synchronized void addActionListener(ActionListener l) {
actionListener = AWTEventMulticaster.add(actionListener, l);
}
public synchronized void removeActionListener(ActionListener l){
actionListener = AWTEventMulticaster.remove(actionListener, l);
}
public void processEvent(AWTEvent e) {
// when event occurs which causes "action" semantic ActionListener
listener = actionListener;
if (listener != null) {
listener.actionPerformed(new ActionEvent());
}
}
}
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
42
Rejestracja słuchaczy w kontenerze
// Utility field holding table of ActionListeners.
// Generic type is used to declare this container.
private Vector<ActionListener> listeners = new Vector<ActionListener>();
// Registers ActionListener to receive events.
public synchronized void addActionListener(ActionListener listener) {
listeners.addElement(listener);
}
// Removes ActionListener from the list of listeners.
public synchronized void removeActionListener(ActionListener listener) {
listeners.removeElement(listener);
}
// Notifies all registered listeners about the event.
private void fireActionPerformed(ActionEvent event) {
Vector<ActionListener> targets;
synchronized (this) {
targets = (Vector<ActionListener>)listeners.clone();
}
Przykład (NetBeans) :
for (ActionListener target : targets)
target.actionPerformed(event);
OvalButton.java
}
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
43
Adaptery
Implementację interfejsu można wykonać w pomocniczej klasie
zwanej adapterem, a następnie zdefiniować klasę słuchacza jako
rozszerzenie adaptera, np.:
class WindowAdapter implements WindowListener {
public void windowActivated(WindowEvent e) {}
public void windowClosed(WindowEvent e) {}
// następne metody
}
class Słuchacz extends WindowAdapter {
// definicja potrzebnej metody
}
Pakiet java.awt.event zawiera takie adaptery.
Przykład: Demo3.java (RealJ)
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
44
Anonimowe klasy wewnętrzne
Klasa wewnętrzna jest definiowana wewnątrz innej klasy, np.:
class KlasaZewnętrzna
{
class KlasaWewnętrzna
{
...
}
...
}
Klasa wewnętrzna może być zdefiniowana nawet wewnątrz funkcji.
Klasa anonimowa, to klasa bez nazwy, definiowana w momencie
tworzenia obiektu (tj. przy użyciu operatora new).
Przykłady:
Demo4.java, Demo5.java (RealJ) i TestHTMLBrowser2 (NetBeans)
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
45
Klasa narzędziowa PropertyChangeSupport
Wybrane metody klasy:
public PropertyChangeSupport(Object sourceBean) // constructor
public void
addPropertyChangeListener(PropertyChangeListener listener)
public void
removePropertyChangeListener(PropertyChangeListener listener)
public void firePropertyChange(String propertyName,
Object oldValue, Object newValue)
public void firePropertyChange(String propertyName,
int oldValue, int newValue)
public void firePropertyChange(String propertyName,
boolean oldValue, boolean newValue)
public void firePropertyChange(PropertyChangeEvent evt)
Dla właściwości ograniczonych stosuje się klasę VetoableChangeSupport.
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
46
Rozgłaszanie zmian właściwości
Setter, który zmienia właściwość związaną powinien powiadomić
o tym wszystkich słuchaczy zmian właściwości (obiekty klas
implementujących interfejs PropertyChangeListener) przyłączonych
do komponentu, np.:
private PropertyChangeSupport propertySupport =
new PropertyChangeSupport(this);
...
public void setURL(String URL) {
String oldURL = this.URL;
this.URL = URL;
propertySupport.firePropertyChange("URL", oldURL, URL);
}
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
47
Przykład: HyperlinkedLabel
Demonstruje:







Użycie klasy narzędziowej PropertyChangeSupport.
Setter i getter właściwości hyperlinkColor oraz URL.
Zgłaszanie zmian właściwości hyperlinkColor oraz URL.
Implementację interfejsu MouseListener (jest słuchaczem
zdarzeń MouseEvent).
Sposób dodawania i usuwania słuchaczy akcji.
Generowanie i rozsyłanie zdarzeń ActionEvent w
odpowiedzi na kliknięcie myszki.
Dorysowanie podkreślenia pod etykietką.
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
48
Wetowanie zmian właściwości
Setter, który zmienia właściwość ograniczoną powinien przed tą
zmianą powiadomić wszystkich przyłączonych słuchaczy
implementujących interfejs VetoableChangeListener, np.:
public void setCount(int newCount) throws PropertyVetoException {
int oldCount = count; // count - wartość właściwości
vetoableChangeSupport.fireVetoableChange("count",oldCount,newCount);
count = newCount; // przy braku wyjątku dochodzi do zmiany
}
Słuchacze mogą z przesłanego zdarzenia typu PropertyChangeEvent,
odczytać (getNewValue) proponowaną nową wartość. Jeżeli
proponowana nowa wartość nie może być zaakceptowana,
zgłaszany jest wyjątek PropertyVetoException.
Zgłoszenie wyjątku przerywa wykonywanie dalszych instrukcji (do
zmiany właściwości nie dochodzi) i następuje przejście do obsługi
wyjątku.
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
49
Przykład: Counter
sterowanie
decButton
addActionListener
komponent
klasa
wyjątek
JavaBeans
słuchacza
(exception)
wetowanie
addVetoableChangeListener
(JButton)
limitator
(Limitator)
anonymous
(ActionListener)
decreace
addActionListener
anonymous
setText
(ActionListener)
PropertyVetoExceprion
counter
(Counter)
setCounter
ostrzeganie
(JTextField)
increace
addPropertyChangeListener
anonymous
(ActionListener)
setText
propListener
counterWarning
(PropertyChangeListener)
incButton
(JLabel)
addActionListener
(JButton)
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
50
Podsumowanie
Zasady definiowania komponentu JavaBeans:
Klasę definiującą komponent należy zdefiniować jako
serializowaną.
 Należy zdefiniować konstruktor bezparametrowy.
 Właściwości komponentu są zdefiniowane przez zestaw setterów
i getterów (również odziedziczonych).
 Zdarzenia obsługiwane przez komponent określone są przez parę
metod addXXXListener/removeXXXListener (gdzie: XXX jest
nazwą zdarzenia) zdefiniowanych w komponencie (lub
odziedziczonych).
 Metody publiczne komponentu powinny być synchronizowane.

Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
51
Refleksja i introspekcja

Automatyzacja tworzenia i dostosowywania komponentu
 Klasa Class
 Dynamiczne ładowanie klas
 Dynamiczne pobieranie metody
 Dynamiczne wywołanie metody
 Introspekcja
 Narzędzia graficzne
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
52
Automatyzacja tworzenia i dostosowywania komponentu
Przeciągnięcie ikony komponentu z palety na obszar projektowanego
interfejsu graficznego powoduje utworzenie obiektu tej klasy.
Przy użyciu mechanizmu refleksji rozpoznaje się typ komponentu,
a następnie stosując konstruktor bezparametrowy, tworzy się
obiekt.
Następnie, stosując introspekcję należy z komponentu „wydobyć”
informacje o właściwościach, setterach, getterach, obsługiwanych
zdarzeniach i dostępnych metodach publicznych (bez dostępu do
kodu źródłowego).
W końcu, przy użyciu mechanizmu refleksji, należy udostępnić
wartości właściwości (z możliwością edycji) oraz przyłączyć
stosownych słuchaczy.
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
53
Klasa Class
W trakcie wykonywania instrukcji:
Button anuluj = new Button(”Anuluj”);
potrzebna jest definicja klasy java.awt.Button.
JVM odszukuje plik Button.class, dekoduje binarny format klasy,
sprawdza kompatybilność z innymi klasami, weryfikuje sekwencję
operacji kodu bajtowego, ładuje do pamięci i tworzy obiekt klasy
Class zawierający metadane udostępniające informacje o
modyfikatorach, konstruktorach, metodach i polach klasy Button.
Dostęp do tego obiektu jest możliwy przez użycie metody getClass z
klasy Object lub dodanie literału .class do nazwy klasy. Przykłady:
Class c = anuluj.getClass();
Class c = java.awt.Button.class;
Przykład programowy: Introspekcja\ClassViewer.java (RealJ)
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
54
Dynamiczne ładowanie klas
Jeżeli w trakcie realizacji programu potrzebny jest obiekt
pewnej klasy, można ją odszukać i załadować przy użyciu
metody forName z klasy Class, np.:
Class c = null;
try {
c = Class.forName(”beans.Counter”);
} catch (ClassNotFoundException ex) {
// obsługa wyjątku
}
Obiekt załadowanej klasy można stworzyć przy użyciu metody
newInstance z klasy Class, np.:
Object
object
= =
null;
Objekt
objekt
new c();
try {
object = c.newInstance();
} catch ...
Wprowadzenie Beans COM NET XML CORBA
Nie można użyć
operatora new
© H.Budzisz
55
Dynamiczne pobieranie metody
W załadowanej klasie, dostępnej przez obiekt klasy Class (w
przykładzie beanClass) występują pola i metody. Dostęp do
zdefiniowanej metody uzyskuje się przez podanie jej nazwy i
parametrów jako argumentów metody getMethod z klasy Class.
// tablica parametrów odpowiadających argumentom metody
Class[] parameterTypes = new Class[] {String.class};
Method addTxt = null;
try {
addTxt = beanClass.getMethod("addText", parameterTypes);
} catch (NoSuchMethodException ex) { ...}
Definicja metody jest reprezentowana przez obiekt klasy Method.
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
56
Dynamiczne wywoływanie metody
beanObject.addTxt(”Anuluj”);
Metodę reprezentowaną przez obiekt klasy Method (w
przykładzie addTxt) wywołuje się stosując metodę invoke.
Argumentami jej są: obiekt na rzecz którego jest
wywoływana i argumenty wywołania.
Object[] arguments = ”Anuluj”;
try {
addTxt.invoke(beanObject, arguments);
}
catch (IllegalArgumentException ex) { ... }
catch (InvocationTargetException ex) { ... }
catch (IllegalAccessException ex) { ... }
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
57
Podsumowanie
W mechanizmie refleksji wykorzystuje się następujące klasy:
java.lang.Class – reprezentuje klasę lub interfejs.
 java.lang.Object – udostępnia metodę getClass.
 java.lang.reflect.Constructor – udostępnia konstruktor klasy.
Umożliwia dynamiczne tworzenie obiektu klasy.
 java.lang.reflect.Method – udostępnia metodę klasy. Umożliwia
wywołanie metody.
 java.lang.reflect.Field – udostępnia pole klasy i dynamiczny dostęp
do tego pola.
 java.lang.reflect.Modifier – udostępnia informacje o modyfikatorach
definicji klasy (abstract, public, final).
 java.lang.reflect.Array – umożliwia dynamiczne deklarowanie tablic.

Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
58
Introspekcja
Dynamicznie załadowany Bean musi zostać „przebadany”. Służy do
tego klasa Introspector, umieszczając wynik „badania” w obiekcie
klasy BeanInfo. Służy do tego metoda:
getBeanInfo(Class<?> beanClass, Class<?> stopClass)
Następuje odczytanie informacji o właściwościach, metodach i
zdarzeniach w podanej klasie beanClass oraz wszystkich klasach
bazowych, aż do klasy stopClass (ale bez niej).
Przykład:
BeanInfo bi = null;
try {
bi = Introspector.getBeanInfo(bean, Object.class);
}
catch(IntrospectionException e) { // obsługa wyjątku }
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
59
Wynik introspekcji
Informacje o właściwościach,
getterach, setterach,
zdarzeniach i metodach
umieszczone są w obiektach
odpowiednich klas (rysunek),
zdefiniowanych w pakiecie
java.beans.
PropertyDescriptor[] descriptors = bi.getPropertyDescriptors();
for(PropertyDescriptor d: descriptors) // extended for
print("Nazwa właściwości:\n " + d.getName());
// inne metody klasy PropertyDescriptor
// getPropertType, getReadMethod, getWriteMethod, ...
Przykład: BeanDumper.java (RealJ)
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
60
Sterowanie introspekcją
Realizuje się przez zdefiniowanie klasy o nazwie:
<nazwa komponentu>BeanInfo, np.: JugglerBeanInfo
implementującej interfejs BeanInfo. Ponieważ interfejs
wymusza implementację ośmiu metod, wygodniejsze jest
rozszerzenie pomocniczej klasy SimpleBeanInfo, np.:
public class JugglerBeanInfo extends SimpleBeanInfo {
}
W klasie tej definiuje się metody wykorzystywane podczas introspekcji:
BeanDescriptor getBeanDescriptor()
PropertyDescriptor[] getPropertyDescriptors();
MethodDescriptor[] getMethodDescriptors();
EventSetDescriptor[] getEventSetDescriptors();
Definiując metodę getIcon, można przypisać do komponentu ikonę.
Przykład: JugglerBeanInfo.java (NetBeans)
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
61
Narzędzia graficzne
W środowisku graficznym nazwy komponentów są zastąpione
przez ikony. Przeciągnięcie ikony z palety do obszaru
projektowego powoduje utworzenie obiektu z użyciem
konstruktora bezparametrowego.
Poprzez introspekcję „odczytane” zostają właściwości komponentu
oraz obsługiwane zdarzenia. Nazwy właściwości tworzą
pierwszą kolumnę tabeli edycyjnej.
Wartości właściwości są odczytywane przez gettery i tworzą drugą
kolumnę tabeli edycyjnej. Do edycji właściwości używane są
wyspecjalizowane edytory (koloru, czcionki, łańcuchów itp.).
Nowe wartości są ustawiane w komponencie przez settery.
W podobny sposób wiąże się zdarzenia z funkcjami odpowiedzi.
Przykład: PropertyViewer.java (NetBeans)
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
62
Serializacja komponentów
Serializacja obiektu, czyli zapis stanu obiektu w pliku dyskowym
realizuje metoda writeObject() z klasy ObjectOutputStream.
Jest to klasa opakowująca dowolny strumień zdefiniowany przez
klasę wyprowadzoną z klasy abstrakcyjnej OutputStream.
Przykład:
JButton przycisk = new JButton(”Pomoc”);
// serializacja obiektu przycisk
ObjectOutputStream out =
new ObjectOutputStream(
new FileOutputStream(”przycisk.ser”));
out.writeObject(przycisk); // pominięto wyjątki
Klasy obiektów serializowanych muszą implementować interfejs
Serializable. Interfejs ten nie zawiera żadnych metod. Składowe
klasy, których nie chcemy serializować (hasła, zmienne
pomocnicze itp.) oznaczamy słowem kluczowym transient.
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
63
Deserializacja komponentów
Deserializacja obiektu, czyli odczytanie stanu obiektu wcześniej
zserializowanego realizuje metoda readObject() z klasy
ObjectInputStream. Jest to klasa opakowująca dowolny strumień
zdefiniowany przez klasę wyprowadzoną z klasy abstrakcyjnej
InputStream.
Przykład:
ObjectInputStream in =
new ObjectInputStream(
new FileInputStream(”przycisk.ser”));
// deserializacja obiektu przycisk
JButton przycisk = (JButton)in.readObject();
Przykład programowy: Serializacja.java i Deserializacja.java (RealJ).
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
64
Serializacja do dokumentu XML
Możliwa jest również serializacja przez utworzenie
dokumentu tekstowego XML. Klasy
ObjectOutputStream i ObjectInputStream zastępuje
się odpowiednio przez klasy XMLEncoder i
XMLDecoder zdefiniowane w pakiecie java.beans.
Klasy te mają takie same metody odczytu i zapisu:
readObject() i writeObject().
Przykład: SerializacjaXML.java i
DeserializacjaXML.java (RealJ)
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
65
Podsumowanie technologii JavaBeans
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
66
Technologia COM
COM – Interfejsy
 Technologia COM w Visual C++
 Globally Unique Identifiers (GUIDs)
 Biblioteka COM
 Tworzenie obiektu COM
 Inteface IUnknown
 Definiowanie komponentu

Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
67
COM – co to jest?
COM (Component Object Model) jest opracowaną
przez Microsoft technologią umożliwiającą
efektywną komunikację między aplikacjami
Component Object Model definiuje:
binarny standard wywoływania funkcji między
komponentami,
 struktury interfejsów udostępnianych przez
poszczególne obiekty,
 mechanizmy jednoznacznej identyfikacji
komponentów i ich interfejsów.

Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
68
Klient
Diagram binarnego standard
wywoływania funkcji
Obiekt komponentowy
(instancja komponentu)
wskaźnik na VTBL
(pole prywatne)
VTBL jest wspólna dla wszystkich
klientów korzystających z danego
komponentu COM
Serwer
wskaźnik na funkcję1
wskaźnik na funkcję2
wskaźnik na funkcję3
VTBL (tablica funkcji wirtualnych)
funkcja1(pObj,arg1,arg2,...)
{
...
}
Implementacja komponentu COM
zawarta zwykle w bibliotece DLL
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
69
Komponenty COM w jednym procesie
Technologia COM definiuje sposób współpracy
pomiędzy komponentem a jego klientem.
W pojedynczym procesie, klient łączy się z
komponentem bez pośrednich komponentów.
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
70
Komponenty COM w różnych procesach
Klient, który chce się skomunikować z
komponentem musi skorzystać z pośrednictwa
tzw. RPC stubs oraz obiektów Proxy, istniejących
po obu stronach połączenia.
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
71
Komponenty COM na różnych
komputerach
Jeżeli klient i komponent rezydują na różnych komputerach,
w technologii DCOM (Distributed COM) komunikację
międzyprocesorową zastępuje się protokołem sieciowym.
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
72
COM – Interfejsy
Obiekty COM udostępniają swoje funkcje obiektom zewnętrznym
za pośrednictwem interfejsów.
 Interfejs jest zestawem prototypów funkcji składowych
komponentu COM (metod).
 Nazwy interfejsów przyjęło się poprzedzać przedrostkiem I,
np.: ILookup
 Interfejs można zdefiniować na bazie innego interfejsu, czyli
zastosować dziedziczenie (ale wyłącznie jednobazowe)
 Interfejs nie posiada własnej implementacji.
 Każdy komponent może implementować wiele interfejsów oferować wiele zestawów usług.
 Klienty (aplikacje lub inne komponenty) odwołują się do
interfejsów za pośrednictwem wskaźników.
 Każdy interfejs posiada własny, unikalny identyfikator
- Globally Unique Identifier (GUID).
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
73
Schemat implementacji interfejsu
Interfejsy
Każdy komponent może implementować wiele interfejsów oferować wiele zestawów usług.
IA
IB
Komponent COM
(coclass)
IC
Komponent lub coclass (skrót od component object class)
zawiera kod wszystkich funkcji udostępnianych przez interfejsy,
funkcje pomocnicze i dane. Komponent jest umieszczony w
serwerze COM - pliku binarnym (DLL lub EXE).
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
74
Implementacja interfejsu
Implementacja interfejsu (definicja komponentu) może być zrealizowana
w różnych językach programowania. Komponenty mogą być
używane przez inne komponenty lub aplikacje realizowane w różnych
językach programowania. Technologia COM definiuje standardy
komunikowania się na poziomie binarnym.
Na poziomie źródłowym ta sama funkcja będzie kodowana zgodnie ze
składnią danego języka, np.:
Visual Basic
object.Add( Time As Double, Name As String ) As Variant
C++
HRESULT Add( double Time, BSTR Name, VARIANT* pVal );
Java
public com.ms.com.Variant Add( double Time, String Name );
Trzeba ponadto zapewnić konwersję typów danych. Szczegóły w dokumentacji
MSDN (COM Language Translation).
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
75
Technologia COM w Visual C++
Preferowanym językiem implementacji jest C++.
 Interfejs jest definiowany jako klasa abstrakcyjna z
czystymi funkcjami wirtualnymi.
 Do tworzonych obiektów automatycznie dołączana jest
Vtable (tablica wywołań funkcji wirtualnych). W C trzeba
taką tablicę samemu zdefiniować.
 Każda metoda musi mieć wskaźnik zwrotny na obiekt. W
C++ automatycznie dodawany jest wskaźnik this. W C
trzeba dodać do każdej funkcji dodatkowy parametr.
 Definicja klasy stanowi też automatycznie przestrzeń nazw
dla składowych.
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
76
Globally Unique Identifiers (GUIDs)
Do identyfikacji każdego interfejsu i każdego komponentu,
używany jest unikalny identyfikator (128-bitowa struktura;
long + uint + uint + 8*char = 16 bajtów). Plik guiddef.h
zawiera definicję struktury opisującej GUID i pomocnicze
makrodefinicje, np.: DEFINE_GUID.
Programista – zamiast tego identyfikatora – używa związanej z
nim stałej symbolicznej IID_<nazwa interfejsu> lub
CLSID_<nazwa komponentu> .
Przykład definicji stałej IID_ILOOKUP:
DEFINE_GUID(IID_ILOOKUP, 0xc4910d71, 0xba7d, 0x11cd,
0x94, 0xe8, 0x08, 0x00, 0x17, 0x01, 0xa8, 0xa3);
Do generowania GUID udostępnione są programy narzędziowe
(w Visual C++ Tools/Create GUID) oraz funkcja CoCreateGuid
z COM API.
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
77
Obsługa błędów
Wszystkie funkcje COM w Visual C++ (z wyjątkiem AddRef i
Release z interfejsu IUnknown) zwracają jako wynik wartość
typu HRESULT zawierającą informacje o błędach (plik
winerror.h wiersz 17 040). Typowy schemat obsługi błędów:
HRESULT hr;
hr = funkcja_COM(parametry);
if (hr == S_OK) {
// dalsze operacje
...
}
else
cerr << ”Wywołanie funkcja_COM nie powiodło się”;
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
78
Makrodefinicje do obsługi błędów
Powszechną praktyką jest wykorzystywanie do obsługi błędów
makrodefinicji SUCCEEDED i FAILED, którym przekazuje
się wynik wywołania funkcji typu HRESULT. Przykład:
if ( FAILED( CoInitialize(NULL) )) {
cerr << "Inicjacja nie powiodła się";
return 1;
}
Kod błędu można odczytać z HRESULT stosując makrodefinicję
HRESULT_CODE. Opis tekstowy błędu (na podstawie jego kodu,
np.: 0x800401F0) można uzyskać przy użyciu narzędzia
Tools/Error Lookup w VC++. Opis jest widoczny również w
debuggerze.
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
79
Biblioteka COM
COM library API udostępnia funkcje i makra do obsługi zadań
związanych z definiowaniem i używaniem obiektów COM.
Bibliotekę trzeba przed użyciem zainicjować przy użyciu funkcji
HRESULT CoInitialize(LPVOID pvReserved);
Schemat inicjacji:
// Inicjacja biblioteki COM - załadowanie plików DLL.
if ( FAILED( CoInitialize(NULL) )) {
cerr << "Inicjacja biblioteki COM nie powiodła się" << endl;
return 1;
}
...
// Zwolnij zasoby przydzielone bibliotece COM.
CoUninitialize();
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
80
Tworzenie obiektu COM
Aby otworzyć obiekt COM i uzyskać wskaźnik na interfejs stosuje
się funkcję CoCreateInstance() z COM API
HRESULT CoCreateInstance( REFCLSID rclsid, LPUNKNOWN
pUnkOuter, DWORD dwClsContext, REFIID riid, LPVOID* ppv);
Parametry:
rclsid – identyfikator komponentu, np.: CLSID_ShellLink.
pUnkOuter – używany przy agregacji obiektu COM (NULL gdy nie jest
stosowana agregacja)
dwClsContext – rodzaj serwera; dla serwera DLL w tym samym procesie
stosuje się stałą CLSCTX_INPROC_SERVER.
riid - identyfikator interfejsu, np.: IID_IShelLink.
ppv - wskaźnik na interfejs; parametr zwrotny funkcji.
Zwracane wartości: S_OK, REGDB_E_CLASSNOTREG,
CLASS_E_NOAGGREGATION lub E_NOINTERFACE.
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
81
Interfejs IUnknown
Podstawowym interfejsem implementowanym przez
każdy obiekt COM jest interfejs IUnknown,
udostępniający trzy funkcje: QueryInterface,
AddRef, Release.
IUnknown
Komponent COM
QueryInterface
implementacja
AddRef
implementacja
Release
implementacja
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
82
IUnknown – funkcje AddRef i Release
Funkcje AddRef i Release zarządzają licznikiem
referencji do interfejsów.
AddRef jest wywoływana, gdy klient używa danego
interfejsu i zwiększa licznik o 1.
Release jest wywoływana, gdy klient nie potrzebuje
już interfejsu i zmniejsza licznik.
Obie zwracają nową wartość licznika referencji.
Funkcja QueryInterface zostanie omówiona w dalszej
części wykładu.
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
83
Schemat tworzenia i użycia obiektu COM
HRESULT hr;
// zmienna przechowująca wynik wywołania
IShellLink* pISL; // wskaźnik na interfejs
hr = CoCreateInstance (
CLSID_ShellLink,
// CLSID komponentu
NULL,
// nie używa się agregacji
CLSCTX_INPROC_SERVER, // typ serwera
IID_IShellLink,
// IID interfejsu
(void**) &pISL );
// zwracany wskaźnik
if ( SUCCEEDED ( hr ) ) {
// Wywołania metod komponentu z użyciem pISL
...
pISL->Release(); // powiadom obiekt COM o zakończeniu
}
else {
// Nie utworzono obiektu COM; hr zawiera kod błędu
}
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
84
Przykład użycia komponentu Active Desktop
Active Desktop jest usługą Internet Explorera pozwalającą na wyświetlanie stron
www, dokumentów HTML, apletów Javy i komponentów ActiveX na tapecie
(bez użycia przeglądarki; Uwaga! Spowalnia system).
Działania zrealizowane w przykładzie:
1. Inicjalizacja biblioteki COM (funkcja CoInitialize).
2. Utworzenie obiektu COM typu ActiveDesktop i pobranie wskaźnika do
interfejsu IActiveDesktop.
3. Wywołanie metody GetWallpaper() komponentu ActiveDesktop.
4. Jeżeli wywołanie zakończyło się powodzeniem – wydrukowanie nazwy pliku
przechowującego tapetę widoczną na pulpicie.
5. Pobranie przy użyciu metody GetDesktopItemCount() liczby elementów (stron
www dodanych do listy) – wydruk informacji.
6. Pobranie (w pętli) informacji o elementach (funkcja GetDesktopItem) do
struktury COMPONENT i wydruk nazw elementów (stron www).
7. Zwolnienie interfejsu (funkcja Release)
8. Zwolnienie zasobów przydzielonych bibliotece COM (funkcja CoUninitialize).
Przykład: C:\HB\Programowanie komponentowe\COM\Pulpit (project Pulpit1)
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
85
Łańcuchy w technologii COM
uzupełnienie przykładu
Łańcuchy zwracane przez funkcje COM kodowane są w
Unikodzie (2 bajty na znak). Znak w Unikodzie definiowany
jest w VC++ przez typ WCHAR, np.:
char tekst1[80]; // zwykła tablica znakowa
WCHAR tekst2[80]; // tablica znaków w Unikodzie
Do przekształcenia tekstu Unikod na tekst ASCII służy funkcja
WideCharToMultiByte(). Prostszym sposobem jest użycie klasy
CString z biblioteki MFC, której konstruktor akceptuje zarówno
łańcuchy ASCII jak i Unikod (konwersja polskich znaków jest
poprawna), np.: CString str1(unicode_text);
Strumień standardowy wcout akceptuje teksty wyłącznie w
Unikodzie. „Zwykły” tekst musi być przekształcony w Unikod
(prefiks L, np.: L”Nazwa: ”). Uwaga: nie radzi sobie z polskimi
znakami – przerywa wyprowadzanie.
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
86
IUnknown – funkcja QueryInferface
Funkcja QueryInterface dostępna w każdym komponencie
(ponieważ zdefiniowana jest w interfejsie IUnknown) pozwala
na uzyskanie dostępu do innych interfejsów danego obiektu. Jej
składnia jest następująca:
HRESULT QueryInterface (REFIID iid, void** ppvObject);
Parametry:
iid - identyfikator GUID żądanego interfejsu
ppvObject - wskaźnik do żądanego interfejsu;
parametr zwrotny.
Gdy interfejs jest dostępny, funkcja zwraca wartość S_OK.
W przypadku, gdy interfejs nie jest zaimplementowany zwracana
jest wartość E_NOINTERFACE.
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
87
Przykład użycia komponentu z wieloma interfejsami
Zadaniem programu (Pulpit2) jest dodanie na pulpicie skrótu do własnego pliku
exe. Etapy realizacji zadania:
1. Pobranie ścieżki do własnego pliku exe (GetModuleFileName).
2. Pobranie ścieżki do foldera pulpitu (SHGetSpecialFolderPath).
3. Uzupełnienie ścieżki o nazwę pliku typu .lnk zawierającego link.
4. Sprawdzenie czy plik już istnieje (CFile::GetStatus).
5. Przekształcenie ścieżki z nazwą pliku na łańcuch w Unikodzie
(MultiByteToWideChar).
6. Inicjacja biblioteki COM (CoInitialize).
7. Utworzenie obiektu COM typu Shell Link (CoCreateInstance) i uzyskanie
wskaźnika na interfejs IShellLink.
8. Ustawienie ścieżki do pliku, dla którego tworzony jest skrót
(IShellLink::SetPath)
9. Uzyskanie wskaźnika na kolejny interfejs IPersistFile obiektu Shell Link
(IShellLink::QueryInterface)
10. Zapisanie pliku skrótu do foldera pulpitu (IPersistFile::Save)
11. Zwolnienie interfejsów IPersistFile i IShellLink (Release) oraz CoUninitialize
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
88
Definiowanie komponentu

Definiowanie interfejsu
 Makra i typy danych stosowane w definicjach
 Implementacja interfejsu
 Użycie biblioteki MFC
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
89
Definiowanie interfejsu
Każdy interfejs komponentu COM musi być bezpośrednio lub
pośrednio wyprowadzony z interfejsu IUnknown. Interfejs jest
definiowany w języku IDL (Microsoft Interface Definition
Language). Przykład:
import "unknwn.idl"; // definicja interfejsu IUnknown
[ object, uuid(4411B7FE-EE28-11ce-9054-080036F12502) ]
interface ISome : IUnknown {
HRESULT SomeMethod(void);
};
[ object, uuid(4411B7FD-EE28-11ce-9054-080036F12502) ]
interface ISomeOther : ISome {
HRESULT SomeOtherMethod([in]long l);
};
Definicja interfejsu zapisywana jest w pliku .idl
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
90
Definiowanie interfejsu w VC++
W SDK VC++ dostępna jest klasa IUnknown (plik nagłówkowy
unknown.h) reprezentująca interfejs IUnknown. Z klasy tej
można wyprowadzić własny interfejs stosując składnię C++, np.:
// {15038B10-3D3E-11ce-9EE5-00AA004231BF}
DEFINE_GUID(IID_IPrintInterface, 0x15038b10, 0x3d3e, 0x11ce, 0x9e,
0xe5, 0x0, 0xaa, 0x0, 0x42, 0x31, 0xbf);
class IPrintInterface : public IUnknown
{
public:
// Standard IUnknown interface functions
virtual HRESULT QueryInterface(REFIID riid, LPVOID ppvObj)=0;
virtual ULONG AddRef(void) = 0;
virtual ULONG Release(void) = 0;
// This interface
virtual HRESULT PrintObject(void) = 0;
};
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
91
Makra i typy danych stosowane w definicjach
typedef struct _GUID {
unsigned long Data1;
// 32 bity
unsigned short Data2;
// 16 bitów
unsigned short Data3;
// 16 bitów
byte
Data4[ 8 ]; // 8*8 = 64 bity
} GUID; // definicja Globally Unique Identifier, 128 bitów
typedef GUID IID - identyfikator interfejsu; synonim GUID
typedef IID* LPIID; - wskaźnik daleki na ID interfejsu
#define REFIID const IID& - referencja na identyfikator interfejsu
typedef GUID CLSID - identyfikator co-klasy; synonim GUID
typedef CLSID* LPCLSID; - wskaźnik daleki na ID co-klasy
typedef long HRESULT; - typ wyniku funkcji COM; synonim long
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
92
Konwencja wywoływania metod interfejsu
W Microsoft C++ wprowadzono kilkanaście dodatkowych słów
kluczowych (rozpoczynających się od __ ), które służą do
sterowania kompilacją. Słowo kluczowe __stdcall określa
sposób wywołania wymagany przez funkcje Win32 API.
class IPrintInterface : public IUnknown
{
public:
// Standard IUnknown interface functions
virtual HRESULT __stdcall QueryInterface(REFIID riid,
LPVOID ppvObj)=0;
virtual ULONG __stdcall AddRef(void) = 0;
virtual ULONG __stdcall Release(void) = 0;
// This interface
virtual HRESULT __stdcall PrintObject(void) = 0;
};
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
93
Makrodefinicja STDMETHODCALLTYPE
Definicja:
#ifdef _WIN32 // Win32 doesn't support __export
#define STDMETHODCALLTYPE __stdcall
#else
#define STDMETHODCALLTYPE __export __stdcall
#endif
class IPrintInterface : public IUnknown
{
public:
// Standard IUnknown interface functions
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid,
LPVOID ppvObj)=0;
virtual ULONG STDMETHODCALLTYPE AddRef(void) = 0;
virtual ULONG STDMETHODCALLTYPE Release(void) = 0;
// This interface
virtual HRESULT STDMETHODCALLTYPE PrintObject(void) = 0;
};
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
94
Makra STDMETHODIMP_ oraz
STDMETHODIMP
#define STDMETHODIMP HRESULT STDMETHODCALLTYPE
#define STDMETHODIMP_(type) type STDMETHODCALLTYPE
Pierwsze makro definiuje konwencję i wynik wywołania
HRESULT, drugie podany typ wyniku. Przykład:
class IPrintInterface : public IUnknown
{
public:
// Standard IUnknown interface functions
virtual STDMETHODIMP QueryInterface(REFIID riid,
LPVOID ppvObj)=0;
virtual STDMETHODIMP_(ULONG) AddRef(void) = 0;
virtual STDMETHODIMP_(ULONG) Release(void) = 0;
// This interface
virtual STDMETHODIMP PrintObject(void) = 0;
};
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
95
Makra STDMETHOD_ i STDMETHOD
#define STDMETHOD(method) virtual HRESULT STDMETHODCALLTYPE method
#define STDMETHOD_(type,method) virtual type STDMETHODCALLTYPE method
Pierwsza wersja definiuje funkcję wirtualną interfejsu, która zwraca wynik typu
HRESULT. Druga definiuje funkcję, która zwraca wynik wskazanego typu.
class IPrintInterface : public IUnknown
{
public:
// Standard IUnknown interface functions
STDMETHOD(QueryInterface)(REFIID riid, LPVOID ppvObj)=0;
STDMETHOD_(ULONG, AddRef)(void) = 0;
STDMETHOD_(ULONG, Release)(void) = 0;
// This interface
STDMETHOD(PrintObject)(void) = 0;
};
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
96
Makrodefinicja PURE
Definiuje czystą funkcję wirtualną:
#define PURE = 0
class IPrintInterface : public IUnknown
{
public:
// Standard IUnknown interface functions
STDMETHOD(QueryInterface)(REFIID riid, LPVOID ppvObj) PURE;
STDMETHOD_(ULONG, AddRef)(void) PURE;
STDMETHOD_(ULONG, Release)(void) PURE;
// This interface
STDMETHOD(PrintObject)(void) PURE;
};
Przykład: SimpleComServ, projekt: MsgBoxServ, plik: ISimpleMsgBox.h
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
97
Implementacja interfejsu
W C++ interfejs jest implementowany przez klasę. Przykład:
class CPrintObject : public IPrintInterface
{
public:
CPrintObject() : refCount(0) {}
// konstruktor
virtual ~CPrintObject() {}
// destruktor
// Standardowe funkcje interfejsu IUnknown
virtual STDMETHODIMP QueryInterface(REFIID riid,
LPVOID ppvObj);
virtual STDMETHODIMP_(ULONG) AddRef(void);
virtual STDMETHODIMP_(ULONG) Release(void);
// Funkcje interfejsu IPrintInterface
virtual STDMETHODIMP PrintObject(void);
Przykład:
protected: CPrintObject();
Projekt: MsgBoxServ
ULONG refCount; // licznik referencji
SimpleMsgBoxImpl.h
};
Metody już nie są czystymi funkcjami wirtualnymi i muszą zostać zdefiniowane.
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
98
Definicja funkcji AddRef i Release
Funkcje AddRef i Release zarządzają licznikiem referencji.
Każde użycie interfejsu powoduje zwiększenie stanu
licznika (wywołanie AddRef), a kiedy już nie jest potrzebny
– aplikacja klienta ma obowiązek wywołać funkcję Release.
STDMETHODIMP_(ULONG) CPrintObject::AddRef(void)
{
return ++refCount; // zwiększa licznik referencji
}
STDMETHODIMP_(ULONG) CPrintObject:: Release(void)
{
if (--refCount == 0)
delete this; // usunięcie obiektu !!!
return refCount;
}
Przykład – projekt: MsgBoxServ, plik: SimpleMsgBoxImpl.cpp
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
99
Funkcja QueryInterface
Zadaniem funkcji jest zwrócenie wskaźnika na interfejs o
podanym identyfikatorze GUID. Przykład użycia funkcji
(założenie: pUnk jest wskaźnikiem na obiekt IUnknown):
IPrintInterface* pPrint = NULL;
if (pUnk->QueryInterface(IID_IPrintInterface,
(void**)&pPrint) == NOERROR)
{
pPrint->PrintObject();
pPrint->Release();
// release pointer obtained via QueryInterface !
}
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
100
Definicja QueryInterface
STDMETHODIMP CPrintObject::QueryInterface(REFIID iid,
void FAR* FAR* ppvObj)
{
if (iid == IID_IUnknown || iid == IID_IPrintInterface)
{
*ppvObj = this;
AddRef(); // zwiększenie licznika referencji
return NOERROR;
}
return E_NOINTERFACE; // zgłoszenie błędu
}
Przykład – projekt: MsgBoxServ, plik: SimpleMsgBoxImpl.cpp
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
101
Klasa CCmdTarget z biblioteki MFC
Klasa zawiera standardową implementację
IUnknown (licznik referencji modyfikowany przez
AddRef i Release oraz definicję QueryInterface)
class CPrintObject : public CCmdTarget
{
public:
// definicja składowych klasy
...
protected:
DECLARE_INTERFACE_MAP()
BEGIN_INTERFACE_PART(PrintObject, IPrintInterface)
STDMETHOD_(void, PrintObject)();
END_INTERFACE_PART(PrintObject)
};
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
102
Makra BEGIN_ i END_INTERFACE_PART
#define BEGIN_INTERFACE_PART(localClass, baseClass) \
class X##localClass : public baseClass \
{ \
public: \
STDMETHOD_(ULONG, AddRef)(); \
STDMETHOD_(ULONG, Release)(); \
STDMETHOD(QueryInterface)(REFIID iid, LPVOID* ppvObj); \
#define END_INTERFACE_PART(localClass) \
} m_x##localClass; \
friend class X##localClass; \
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
103
Komponenty .NET

Struktura platformy .NET
 Interfejsy w .NET
 Definiowanie właściwości komponentów
 Delegacyjny model obsługi zdarzeń
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
104
Struktura platformy .NET
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
105
Biblioteki klas
Biblioteka klas, dostępna w pakiecie .NET Framework SDK,
składa się z klas, interfejsów i stałych. Elementy biblioteki
są pogrupowane w hierarchiczną strukturę przestrzeni nazw
(ang. namespace). Biblioteki klas umożliwiają dostęp do
usług systemowych i stanowią podstawę tworzenia
aplikacji, komponentów i kontrolek .NET.
Zadaniem przestrzeni nazw jest zawężenie zakresu, w którym
obowiązują nazwy typów. Dzięki temu mogą istnieć dwie
klasy o tej samej nazwie pod warunkiem, że są
zdefiniowane w różnych przestrzeniach nazw.
Podstawowe klasy zgrupowane są w przestrzeni System.
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
106
Przestrzeń nazw System
Przestrzeń nazw System zawiera klasy podstawowe oraz klasy
bazowe, które definiują najczęściej używane referencyjne i
skalarne typy danych, zdarzenia, procedury obsługi zdarzeń,
interfejsy, atrybuty i klasy przeznaczone do obsługi wyjątków.
Inne klasy tej przestrzeni nazw udostępniają usługi związane z
konwersją typów danych, manipulowaniem parametrami
metod, obliczeniami i stałymi matematycznymi, zdalnym i
lokalnym przekazywaniem wywołań, zarządzaniem
środowiskiem wykonywania aplikacji oraz nadzorowaniem
zarządzanego i niezarządzanego kodu.
Jedną ze składowych jest kolejna przestrzeń o nazwie
System.ComponentModel, zawierająca klasy i interfejsy
pomocne w tworzeniu komponentów i kontrolek.
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
107
Przestrzeń System.ComponentModel
Przestrzeń System.ComponentModel udostępnia klasy (135 klas) i
interfejsy (27) zapewniające funkcjonowanie komponentów i
kontrolek zarówno w trybie projektowania (design-time), jaki w
trybie uruchomieniowym (run-time).
Podstawowe klasy to: Component i Container.
Podstawowe interfejsy to: IComponent i IContainer.
Pozostałe klasy umożliwiają implementację atrybutów i
deskryptorów, konwersję typów (TypeConverter), dowiązanie
do źródeł danych oraz licencjonowanie komponentów (License,
LicenseManager, LicenseProvider i LicenseProviderAttribute).
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
108
Komponent w .NET
Z punktu widzenia programisty
Komponent w .NET jest klasą, która w sposób
bezpośredni lub pośredni implementuje interfejs
System.ComponentModel.IComponent
Co to jest interfejs?
Jak klasa implementuje interfejs?
Jak definiuje się właściwości komponentu?
Jak obsługiwać zdarzenia?
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
109
C++/CLI
C++/CLI (Common Language Infrastructure) to język programowania oparty
na C++, umożliwiający programowanie aplikacji korzystających za
wspólnego środowiska uruchomieniowego CLR, jak i bez użycia CLR.
C++ zarządzany
C++ natywny
C++ natywny
Biblioteka klas .NET
Biblioteka MFC
CLR
System operacyjny
Sprzęt (hardware)
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
110
Kod natywny i zarządzany
W kodzie natywnym, obiekty tworzy się przy użyciu operatora new. Tak
utworzone obiekty dostępne są przez wskaźnik. Po wykorzystaniu
obiektu, trzeba pamiętać o usunięciu go z pamięci (operator delete).
std::string* stdstr; // deklaracja wskaźnika
stdstr = new std::string("String niezarządzany");
...
delete stdstr; // usunięcie łańcucha
W kodzie zarządzanym, obiekty tworzy się przy użyciu operatora gcnew.
Tak utworzone obiekty dostępne są przez odniesienie. Obiekty
wykorzystane są automatycznie usuwane z pamięci (odśmiecanie).
System::String^ clrstr;
// deklaracja odniesienia
clrstr = gcnew System::String("String zarządzany");
...
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
111
Klasa zarządzana
Klasa poprzedzona słowem ref class (jedno słowo kluczowe) staje się klasą
zarządzaną (garbage-collected). Czas życia obiektu tej klasy kontrolowany
jest przez CLR. Nie ma potrzeby jawnego wywołania operatora delete.
Przykład:
ref class Test {
public:
Test():i(0){}
private:
int i;
}; // Test
Klasa zarządzana nie ma domyślnego
konstruktora kopiującego ani domyślnego
operatora przypisania. Parametry funkcji
(również konstruktorów) nie mogą mieć
wartości domyślnych. Można definiować
właściwości (słowo kluczowe property).
void main () {
while(true) { // pętla bez końca
Test^ tst = gcnew Test;
} // tworzone obiekty są automatycznie usuwane
}
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
112
Klasa wartości
Klasa poprzedzona słowem value class (jedno słowo kluczowe) jest
przeznaczona do grupowania pewnej liczby danych w jedną całość. Obiekt
klasy wartości może być umieszczony zarówno na stosie (jak zmienne typów
standardowych) jak i na stercie CLR (operator gcnew). Przykład:
value class Wynik
{
public:
Wynik(double czas, double Par) : t(czas),par(Par) {}
private:
double t;
// czas od początku pomiaru
double par; // wartość mierzonego parametru
}; // Wynik
void main () {
Wynik wynik(0.5, 0.342);
}
Wprowadzenie Beans COM NET XML CORBA
// umieszczenie na stosie
© H.Budzisz
113
Interfejs w .NET
Interfejs zawiera deklaracje metod, zdarzeń i
właściwości, ale nie może zawierać ich
implementacji. Nie może również zawierać
deklaracji zmiennych (pól klasy) – z wyjątkiem
pól statycznych. Oprócz pól statycznych może
zawierać także deklaracje innych składowych
statycznych (metod, zdarzeń lub właściwości).
Wszystkie składowe interfejsu mają publiczne
prawa dostępu. Interfejs podlega dziedziczeniu.
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
114
Deklaracja interfejsu
prawa_dostępu interface class nazwa {};
prawa_dostępu interface class nazwa : interfejs_bazowy {};
gdzie: prawa_dostępu - prawa dostępu do interfejsu spoza
podzespołu .NET (.NET assembly): public lub private (domyślne)
Przykład:
public interface class Interface_A {
void Function_1();
void Function_2();
}; // Interface_A
Nazwy interfejsów przyjęło się rozpoczynać od litery I, np.:
IContainer.
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
115
Implementacja interfejsu
Implementacja interfejsu jest realizowana w oparciu o składnię
dziedziczenia – przykład:
public ref class MyClass : public Interface_A
{
virtual void Function_1()
{ Console::WriteLine(“Function_1”); }
virtual void Function_2()
{ Console::WriteLine(“Function_2”); }
}; // MyClass
Ponieważ interfejs jest w Visual C++ typem zarządzanym, klasa
implementująca interfejs musi być również klasą zarządzaną.
Klasa może implementować wiele interfejsów.
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
116
Właściwości
Definiowanie właściwości
 Funkcje dostępowe
 Właściwości proste
 Właściwości indeksowane

Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
117
Rozwiązanie klasyczne – pola klasy
Pola klasy przechowują dane reprezentujące atrybuty obiektu opisywanego przez tą klasę.
class Okno
{
public:
void Szer(int nowa_szer)
{
szer = nowa_szer;
Rysuj();
}
int Szer(void)
{
return szer;
}
// podobnie dla pola wys
private:
void Rysuj() { ... }
int szer, wys;
}; // Okno
Wprowadzenie Beans COM NET XML CORBA
// ustaw szerokość i odrysuj
// pobierz szerokość
© H.Budzisz
118
Rozwiązanie z użyciem właściwości
Definicja właściwości zawiera standardowe funkcje dostępowe (akcesory), o z góry
określonych nazwach: set i get. Przykład:
public ref class Okno {
public:
property int Szer {
void set(int nowa_szer) { // ustaw szerokość i odrysuj
szer = nowa_szer;
Rysuj();
}
int get(void) {
// pobierz szerokość
return szer;
}
} // szer
// podobnie dla właściwości Wys
private:
void Rysuj() { ... }
int szer, wys;
}; // Okno
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
119
Definiowanie właściwości
Właściwość definiuje się w klasie lub interfejsie przy użyciu słowa kluczowego property.
modyfikator property typ nazwa {
<deklaracja akcesorów>
} // brak średnika
gdzie: modyfikator – opcjonalny modyfikator static lub virtual,
Przykład:
public ref class Okno {
public:
property int Szer {
// deklaracja akcesorów
}
property int Wys {
// deklaracja akcesorów
}
};
Właściwość jest objęta prawami dostępu na takich samych zasadach jak inne składowe
klasy lub interfejsu.
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
120
Funkcje dostępowe (akcesory)
Właściwość może zawierać funkcję pobierającą (getter), funkcję ustawiającą
(setter) lub obie funkcje. Składnia deklaracji:
modyfikator void set(typ);
modyfikator typ get();
gdzie: modyfikator – opcjonalny modyfikator static lub virtual.
Typ argumentu settera i wynik gettera muszą być jednakowe i zgodne z
typem właściwości. Setter nie zwraca żadnego wyniku. Getter nie ma
natomiast argumentów. Poza tymi ograniczeniami definicje funkcji mogą
być dowolne.
Deklaracja akcesorów może być poprzedzona deklaracją praw dostępu. Przy
braku deklaracji, prawa dostępu są takie same jak dla właściwości. Prawa
dostępu dla settera i gettera mogą być różne, ale w żadnym przypadku nie
mogą rozszerzać praw dostępu określonych dla właściwości.
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
121
Przykład definiowania akcesorów
Definicja akcesorów może być połączona z deklaracją (jak w poprzednim
przykładzie) lub znajdować się poza klasą, z zastosowaniem nazwy
kwalifikowanej (tak jak dla zwykłych metod). Przykład:
public ref class Okno {
public:
property int Szer {
void set(int);
int get(void);
}
// podobnie dla właściwości Wys
private:
void Rysuj() { ... }
int szer, wys;
}; // Okno
void Okno::Szer::set(int nowa_szer) {
szer = nowa_szer;
Rysuj();
}
int Okno::Szer::get(void) { return szer; }
Wprowadzenie Beans COM NET XML CORBA
// ustaw szerokość i odrysuj
// pobierz szerokość
© H.Budzisz
122
Właściwości proste
Deklaracja właściwości, dla której setter kopiuje wartość do pomocniczej
zmiennej prywatnej, a getter pobiera wartość z tej samej zmiennej, może być
uproszczona do postaci:
modyfikator property typ nazwa;
Przykład:
public ref class Vector {
public:
property double x;
property double y;
property double z;
};
Kompilator tworzy zmienną pomocniczą oraz domyślne akcesory. Nie ma
bezpośredniego dostępu do automatycznie utworzonej zmiennej pomocniczej.
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
123
Właściwości indeksowane
Właściwość indeksowana reprezentuje strukturę danych, w której dane
dostępne są poprzez operator indeksowania [ ]. Właściwości indeksowanych
używa się zwykle do udostępniania elementów tablicy zadeklarowanej
wewnątrz klasy lub składowych wewnętrznych kontenerów.
Składnia:
modyfikator property typ_właściwości nazwa[typ_indeksu] {
modyfikator void set(typ_indeksu, typ_właściwości);
modyfikator typ_właściwości get(typ_indeksu);
} // brak średnika
W kodzie klienta, właściwość z indeksem jest używana jak zwykła tablica.
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
124
Zdarzenia
Delegacyjny model obsługi zdarzeń
 Deklarowanie delegata
 Deklaracja zdarzenia

Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
125
Delegacyjny model obsługi zdarzeń
Subskrybenci
Subskrybent 1
funkcja Sub1A
delegat
źródło
zdarzenia
Subskrybent 2
zgłoszenie
zdarzenia
invocation
funkcja Sub2B
list
Subskrybent3
funkcja Sub3C
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
126
Deklaracja delegata
Delegat jest typem obiektowym. Do deklaracji delegata nie używa się jednak
żadnej klasy bibliotecznej, ale słowa kluczowego delegate, według składni:
prawa_dostępu delegate deklaracja_funkcji;
Przykład:
using namespace System;
void Drukuj(char* tekst)
{ Console::WriteLine("Wydruk - {0}\n", gcnew String(tekst)); }
void Zapisz(char* tekst)
{ Console::WriteLine("Zapis - {0}\n", gcnew String(tekst)); }
delegate void Operacja(char*);
// deklaracja delegata
void main() {
Operacja^ opr;
opr += gcnew Operacja(&Drukuj);
opr += gcnew Operacja(&Zapisz);
opr("wywołanie z delegata");
}
Wynik działania programu:
Wydruk – wywołanie z delegata
Zapis – wywołanie z delegata
Typ argumentów i wyniku w deklaracji delegata i deklaracji dodawanych funkcji
muszą być zgodne.
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
127
Rejestrowanie metod klasy
using namespace System;
ref class Dokument {
public:
void Czytaj(String^ nazwa)
{ Console::WriteLine("Czytaj dokument: {0}\n", nazwa); }
void Drukuj(String^ nazwa)
{ Console::WriteLine("Drukuj dokument: {0}\n", nazwa); }
void Zapisz(String^ nazwa)
{ Console::WriteLine("Zapisz dokument: {0}\n", nazwa); }
}; // Dokument
ref class Obraz {
public:
static void Rysuj(String^ nazwa)
{ Console::WriteLine("Rysuj obraz: {0}\n", nazwa); }
}; // Obraz
delegate void Operacja(String^);
void main() {
Dokument^ dok = gcnew Dokument();
Operacja^ opr = gcnew Operacja(dok, &Dokument::Czytaj);
opr += gcnew Operacja(dok, &Dokument::Drukuj);
opr += gcnew Operacja(dok, &Dokument::Zapisz);
opr += gcnew Operacja(&Obraz::Rysuj); // funkcja statyczna
Console::WriteLine("Operacje:\n");
opr(gcnew String("Rysunek"));
}
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
128
Deklaracja zdarzenia
W C++/CLI koncepcja delegacyjnego modelu obsługi zdarzeń została
zrealizowana poprzez deklarację zdarzenia (słowo kluczowe event) i
powiązanie go z delegatem.
modyfikator event nazwa_delegatu ^ nazwa_zdarzenia;
Wersja rozszerzona deklaracji umożliwia zdefiniowanie sposobu realizacji
metod add, remove i raise.
modyfikator event nazwa_delegatu ^ nazwa_zdarzenia
{
modyfikator typ_zwracany add (nazwa_delegatu ^ nazwa);
modyfikator typ_zwracany remove(nazwa_delegatu ^ nazwa);
modyfikator typ_zwracany raise(parametry);
} // event block
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
129
Przykład
using namespace System;
// deklaracja delegatów
delegate void ZoomEventHandler(double);
delegate void PrintEventHandler(String^);
int main()
{
// utworzenie żródła i subskrybenta
EventSource ^ EventSourceObject =
gcnew EventSource();
// klasa źródło zdarzeń
ref class EventSource {
public:
event ZoomEventHandler^ OnZoom;
event PrintEventHandler^ OnPrint;
void FireEvents() {
// symulacja zdarzeń
OnZoom(1.5);
OnPrint("diagram");
}
};
// klasa subskrybent
ref class EventReceiver {
public:
void OnZoomClick(double scale) {
Console::WriteLine(„Skala {0}", scale);
}
void OnPrintClick(String^ document) {
Console::WriteLine(“Drukuj {0}", document);
}
};
Wprowadzenie Beans COM NET XML CORBA
EventReceiver^ EventReceiverObject =
gcnew EventReceiver();
// rejestracja funkcji obsługujących
EventSourceObject->OnZoom +=
gcnew ZoomEventHandler(EventReceiverObject,
&EventReceiver::OnZoomClick);
EventSourceObject->OnPrint +=
gcnew PrintEventHandler(EventReceiverObject,
&EventReceiver::OnPrintClick);
// wygeneruj zdarzenia
EventSourceObject->FireEvents();
}
Wynik na ekranie:
Skala 1.5
Drukuj diagram
© H.Budzisz
130
Przykład: ExcelIntegration
Przykład demonstruje następujące konstrukcje programowe:





Definiowanie klasy wartości Wynik do przechowywania dwóch pól typu double.
Definiowanie klasy referencyjnej Pomiar symulującej przeprowadzanie
pomiarów. Wynik każdego pomiaru przechowywany jest w obiekcie klasy
Wynik. Po każdym pomiarze generowane jest zdarzenie PomiarEvent,
obsługiwane przez delegata PomiarEventHeandler.
Tworzenie graficznego interfejsu użytkownika – klasa Form1. W obsłudze
zdarzenia Click przycisku pomiarBtn, tworzony jest obiekt klasy Pomiar,
rejestrowana jest funkcja OnPomiar obsługująca zdarzenie PomiarEvent i
wywoływana jest funkcja wykonująca symulację pomiarów.
Definiowanie funkcji odpowiedzi OnPomiar, która kolejne wyniki umieszcza
na liście przewijanej – pomiarListBox.
Wywołanie funkcji transfer z klasy Pomiar, powoduje utworzenie obiektu
komponentu Excel i transfer wyników do arkusza kalkulacyjnego.
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
131
Definiowanie komponentu
Komponent w .NET jest klasą, która w sposób bezpośredni lub pośredni implementuje interfejs System.ComponentModel.IComponent
Interfejs IComponent definiuje standard komponentu .NET.
Nowy komponent definiuje się zwykle w oparciu o klasę Component,
która implementuje interfejs IComponent, a ponadto definuje
typową dla komponentu funkcjonalność (np.: metodę Dispose
zwalniającą przydzielone komponentowi zasoby). Schemat
definicji komponentu:
public ref class NowyKomponent :
public System::ComponentModel::Component
{
}
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
132
Komponenty a kontenery
Komponenty zwykle umieszcza się w kontenerze (w obiekcie
klasy Conteiner (System::ComponentModel::Container).
Użycie kontenera (chociaż nie jest obowiązkowe) ułatwia
wzajemną komunikację pomiędzy komponentami,
zarządzanie zasobami przydzielonymi komponentom oraz
umożliwia integrację komponentu ze środowiskiem
programistycznym (design-time environment).
Kreator aplikacji typu Windows Forms Applications tworzy
klasę formularza w oparciu o klasę biblioteczną Form. W
klasie tej tworzony jest kontener components, przeznaczony
do przechowywania komponentów.
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
133
Szkielet aplikacji typu Windows Forms
public ref class Form1 : public System::Windows::Forms::Form
{
public:
Form1(void) {
InitializeComponent();
}
protected:
~Form1() {
if (components) delete components;
}
// usunięcie kontenere
private:
System::ComponentModel::Container ^components; // deklaracja odniesienia
void InitializeComponent(void) {
this->components = gcnew System::ComponentModel::Container();
}
};
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
134
Kontener w komponencie
Szkielet klasy definiującej komponent najłatwiej można
wygenerować przy użyciu odpowiedniego kreatora
(Project/Add Class…/Component Class).
Konstruktor klasy zawiera operację dodania komponentu do
kontenera zewnętrznego:
NowyKomponent(System::ComponentModel::IContainer ^container)
{
container->Add(this);
// inne operacje do wykonania przez konstruktor
}
W klasie jest również definiowany kontener wewnętrzny do
przechowania innych komponentów.
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
135
Szkielet definicji komponentu
public ref class NowyKomponent : public System::ComponentModel::Component
{
public:
NowyKomponent(void) {
InitializeComponent();
}
NowyKomponent(System::ComponentModel::IContainer ^container) {
container->Add(this);
InitializeComponent();
}
protected:
~NowyKomponent() {
if (components) delete components;
}
private:
System::ComponentModel::Container ^components;
void InitializeComponent(void) {
components = gcnew System::ComponentModel::Container();
}
};
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
136
Przykład: komponent HiResTimer
Zadaniem komponentu jest pomiar czasu pomiędzy wywołaniem funkcji
składowych Start() i Stop().
Właściwości statyczne Counter i TicksPerSecond zwracają odpowiednio stan i
częstotliwość licznika procesora, udostępniane przez funkcje API
QueryPerformanceCounter i QueryPerformanceFrequency.
Częstotliwość licznika jest dla danego procesora stała i dlatego zostaje
odczytana tylko jeden raz i zapamiętana w zmiennej statycznej frequency.
Właściwości Seconds, Milliseconds i Microseconds zwracają w odpowiednich
jednostkach interwał czasu jaki upłynął pomiędzy Start () i Stop(), a
właściwość Ticks liczbę tyknięć licznika.
Solution: PerformanceTesting; Project: Timer.
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
137
Komponent w trybie projektowania
Właściwości i zdarzenia zdefiniowane w komponencie można w trybie
projektowania odczytywać i ustawiać w przeglądarce właściwości
(property browser). Procesem tym można sterować przy użyciu
odpowiednich atrybutów (design-time attributes).
Atrybuty definiowane są przez klasy biblioteczne (np.: CategoryAttribute)
wyprowadzone z klasy System::Attribute. Można też zdefiniować własne
klasy opisujące niestandardowe atrybuty.
W definicji komponentu atrybuty umieszcza się według składni:
[nazwa_atrybutu(argumenty), nazwa_atrybutu(argumenty), … ]
Przykład:
[Category("Format"), Description("Returns time interval")]
Atrybut Category określa przynależność do grupy atrybutów w przeglądarce.
Atrybut Description definiuje tekst wyświetlany na dole okienka przeglądarki.
Atrybut Editor umożliwia przypisanie do właściwości odpowiedniego edytora
(np.: do wyboru koloru)
Wprowadzenie Beans COM NET XML CORBA
© H.Budzisz
138
Download