Pobieranie prezentacji. Proszę czekać

Pobieranie prezentacji. Proszę czekać

Programowanie komponentowe jesień-zima 2013 Wykład 4 Cykl życia komponentu Wzorce projektowe Programowanie obiektowe w C# dr inż. Wojciech Bieniecki Instytut.

Podobne prezentacje


Prezentacja na temat: "Programowanie komponentowe jesień-zima 2013 Wykład 4 Cykl życia komponentu Wzorce projektowe Programowanie obiektowe w C# dr inż. Wojciech Bieniecki Instytut."— Zapis prezentacji:

1 Programowanie komponentowe jesień-zima 2013 Wykład 4 Cykl życia komponentu Wzorce projektowe Programowanie obiektowe w C# dr inż. Wojciech Bieniecki Instytut Nauk Ekonomicznych i Informatyki 1

2 Cykl życia komponentu Każdy komponent jest zarządzany przez kontener. W czasie swojego życia, komponent może jednak mieć wpływ na swoją inicjalizację, konfigurację i usuwanie. Jest to możliwe dzięki temu, że istnieje kontrakt kontrakt pomiędzy komponentem deklarującym określone metody odpowiadające tym etapom, a kontenerem, który we właściwym momencie je wywoła. Komponent nie ma możliwości wpływania na kolejność ani moment wywoływania tych procedur

3 Reprezentacje cyklu życia komponentu 1. Poprzez specjalizowane interfejsy związane z danym kontenerem, które definiują metody reprezentujące poszczególne etapy życia komponentu 2. Poprzez bezpośrednie podanie nazw metod związanych z konkretną fazą życia komponentu podczas jego konfiguracji. silne powiązanie z kontenerem duża liczba interfejsów elastyczne bardzo prosty mechanizm konfiguracji

4 Cykl życia definiowany przez interfejs 4 1. konstruktor 2. contextualize() 3. compose() 4. configure() 5. parametrize() 6. initialize() 7. start() 1. konstruktor 2. contextualize() 3. compose() 4. configure() 5. parametrize() 6. initialize() 7. start() INICJALIZACJA 1.suspend() 2. recontextualize() 3. recompose() 4. reconfigure() 5. reparametrize() 6. resume() 1.suspend() 2. recontextualize() 3. recompose() 4. reconfigure() 5. reparametrize() 6. resume() OBSŁUGA ŻĄDAŃ 1. stop() 2. dispose() 3. finalizacja 1. stop() 2. dispose() 3. finalizacja USUNIĘCIE Przykład - kontener Apache Avalon Fortress. Duża liczba możliwych do wywołania metod – programista może uwzględnić każdy etap w cyklu życia komponentu. Wada – konieczność implementacji wielu interfejsów związanych z konkretnym kontenerem

5 Cykl życia definiowany przez nazwy metod 5 Rozwiązanie to daje ono potencjalnie większe możliwości konfiguracji komponentu. Polega na określaniu nazw metod wywoływanych w typowych fazach życia komponentu i przekazaniu ich kontenerowi w postaci konfiguracji. Przykład – Spring. komponent Ksiazka posiada metodę inicjuj() wywoływaną tuż po utworzeniu instancji obiektu oraz metodę usun() wykonywaną przed usunięciem instancji komponentu z rejestru.

6 Styl życia komponentu 6 Liczba instancji komponentu prototyp: każde żądanie tworzy nową instancję singleton: istnieje jedna współdzielona instancja Kontener tworzy komponenty-referencje (podobne do obiektów-referencji), których jedyna instancja jest współdzielona przez wszystkich żądających do niej dostępu klientów. Kontener tworzy komponenty-wartości – instancje są tworzone w odpowiedzi na każde żądanie. Moment tworzenia komponentu eager (zachłanny): przy starcie kontenera lazy (leniwy): gdy będzie potrzebny

7 Dygresja – wzorce projektowe 7 Wzorzec projektowy (design pattern) – w inżynierii oprogramowania, uniwersalne rozwiązanie powtarzalnych problemów projektowych. Pokazuje powiązania i zależności pomiędzy klasami oraz obiektami i ułatwia tworzenie, modyfikację oraz pielęgnację kodu źródłowego. Jest opisem rozwiązania. Kreacyjne – opisują proces tworzenia, inicjalizacji i konfiguracji nowych obiektów. Przykłady: Budowniczy, Fabryka abstrakcyjna, Metoda wytwórcza, Prototyp, Singleton strukturalne – opisują struktury powiązanych ze sobą obiektów; Przykłady: Adapter, Dekorator, Fasada, Kompozyt, Most, Pełnomocnik, Pyłek. czynnościowe – opisują zachowanie współpracujących ze sobą obiektów. Przykłady: Interpreter, Iterator, Łańcuch zobowiązań, Mediator, Metoda szablonowa, Obserwator, Odwiedzający, Pamiątka, Polecenie, Stan, Strategia

8 Przykłady wzorców projektowych - Singleton 8 Singleton to obiekt, który może istnieć tylko w jednej instancji (tak jak np. obiekt aplikacji w aplikacji MFC lub obiekt Servletu w kontenerze serwletów. Implementacja: Statyczna metoda getInstance. Sprawdza, czy istnieje już instancja tej klasy, w razie potrzeby tworząc ją. Następnie instancja zwracana jest przez referencję. Instancję przechowuje się w prywatnym statycznym polu klasy. Konstruktor klasy jest prywatny – nie można go wywołać operatorem new Przykład w C# sealed class Singleton { private static readonly Singleton instance = new Singleton(); private Singleton() { / * pusty konstruktor prywatny */ } static Singleton() { /* pusta metoda statyczna */ } public static Singleton Instance { /* metoda getInstance */ get { return instance; } sealed class Singleton { private static readonly Singleton instance = new Singleton(); private Singleton() { / * pusty konstruktor prywatny */ } static Singleton() { /* pusta metoda statyczna */ } public static Singleton Instance { /* metoda getInstance */ get { return instance; }

9 Przykłady wzorców projektowych – Adapter 9 Adapter o obiekt, który implementuje w sposób domyślny (lub pusty) wszystkie metody Interfejsu, dzięki czemu łatwiej jest napisać klasę obsługującą ten interfejs. Przykład w Java – obsługa myszy public interface MouseListener extends EventListener { public void mouseClicked(MouseEvent e); public void mousePressed(MouseEvent e); public void mouseReleased(MouseEvent e); public void mouseEntered(MouseEvent e); public void mouseExited(MouseEvent e); } public interface MouseListener extends EventListener { public void mouseClicked(MouseEvent e); public void mousePressed(MouseEvent e); public void mouseReleased(MouseEvent e); public void mouseEntered(MouseEvent e); public void mouseExited(MouseEvent e); } public abstract class MouseAdapter implements MouseListener { public void mouseClicked(MouseEvent e) {} public void mousePressed(MouseEvent e) {} public void mouseReleased(MouseEvent e) {} public void mouseEntered(MouseEvent e) {} public void mouseExited(MouseEvent e) {} } public abstract class MouseAdapter implements MouseListener { public void mouseClicked(MouseEvent e) {} public void mousePressed(MouseEvent e) {} public void mouseReleased(MouseEvent e) {} public void mouseEntered(MouseEvent e) {} public void mouseExited(MouseEvent e) {} }

10 Przykłady wzorców projektowych – Adapter 10 public class MyApp1 implements MouseListener { JFrame f = new Jframe("Hello"); MyApp1(){ f.setVisible(true); f.addMouseListener(this); } public void mouseClicked(MouseEvent e) { f.setBackground(Color.red); } public void mousePressed(MouseEvent e) {} public void mouseReleased(MouseEvent e) {} public void mouseEntered(MouseEvent e) {} public void mouseExited(MouseEvent e) {} } public class MyApp1 implements MouseListener { JFrame f = new Jframe("Hello"); MyApp1(){ f.setVisible(true); f.addMouseListener(this); } public void mouseClicked(MouseEvent e) { f.setBackground(Color.red); } public void mousePressed(MouseEvent e) {} public void mouseReleased(MouseEvent e) {} public void mouseEntered(MouseEvent e) {} public void mouseExited(MouseEvent e) {} } public class MyApp2 extends MouseAdapter{ JFrame f = new Jframe("Hello"); MyApp2(){ f.setVisible(true); f.addMouseListener(this); } public void mouseClicked(MouseEvent e) { f.setBackground(Color.red); } public class MyApp2 extends MouseAdapter{ JFrame f = new Jframe("Hello"); MyApp2(){ f.setVisible(true); f.addMouseListener(this); } public void mouseClicked(MouseEvent e) { f.setBackground(Color.red); }

11 Przykłady wzorców projektowych – obserwator 11 Obserwator to obiekt, który raportuje zmiany stanu obiektu obserwowanego. Działa tak, że zmiana w obiekcie obserwowanym wywołuje specyficzne zdarzenia, na które możemy reagować. Przykład – użycie obiektu ImageObserwer w Javie, pozwalającego obserwować proces ładowania obrazu Aby obserwować ładowanie obrazka, należy zdefiniować metodę imageUpdate(...) interfejsu ImageObserver. Każdy komponent jest z natury ImageObserverem. Metoda imageUpdate jest wołana za każdym razem, gdy informacja o obrazie jest dostępna. Uzyskuje ona tę informację w postaci infoflag. WartośćStałaOpis 128ABORTProces ładowania obrazka został przerwany 64ERRORNapotkano błąd w czasie odczytu obrazu 1WIDTHOdczytano szerokość obrazu 2HEIGHTOdczytano wysokość obrazu 8SOMEBITSCzęść obrazu została załadowana (w przypadku progresywnych obrazków) 16FRAMEBITSZaładowano następną ramkę obrazu (w przypadku obrazu wieloramkowego) 32ALLBITSWszystkie bity obrazka zostały załadowane. Można wyświetlić obraz w ostatecznej formie

12 Użycie obiektu ImageObserver Aby obserwować ładowanie obrazka, należy zdefiniować metodę imageUpdate(...) interfejsu ImageObserver. Każdy komponent jest z natury ImageObserverem. Metoda imageUpdate jest wołana za każdym razem, gdy informacja o obrazie jest dostępna. Uzyskuje ona tę informację w postaci infoflag. import javax.swing.*; class Kanwa extends JPanel { Image img; String itit; int width, height; boolean imageReady = false, imageError = false, firstPaintDone = false; Kanwa (String s,Image im, int w, int h) { itit=s; img = im; width = w; height = h; } import javax.swing.*; class Kanwa extends JPanel { Image img; String itit; int width, height; boolean imageReady = false, imageError = false, firstPaintDone = false; Kanwa (String s,Image im, int w, int h) { itit=s; img = im; width = w; height = h; }

13 Użycie obiektu ImageObserver public boolean imageUpdate(Image theImg, int Infoflags, int x, int y, int w,int h) { if (infoflags & (ERROR)) != 0) ImageError=true; else if ((infoflags & (WIDTH | HEIGHT)) != 0) { width = w; height = h; } imageReady=((infoflags & (ERROR|FRAMEBITS|ALLBITS)) != 0); if (imageReady) repaint(); return !imageReady; } Ta metoda jest automatycznie wywoływana przez proces ładowania obrazu i program może odczytywać stan obiektu (tu: aktualizować zmienne imageReady, width oraz height)

14 Przykłady public void paint(Graphics g) { int w = getSize().width; int h = getSize().height; g.setFont(new Font("Helvetica", Font.BOLD, 14)); g.drawRect(0, 0, w-1, h-1); g.drawLine(0,30, w-1,30); g.drawLine(0, h-40, w, h-40); g.drawString(name,(w-g.getFontMetrics().stringWidth(name))/2,20); String s = "Gotowe!"; if (!imageReady){ s = "Czekaj... "; g.drawString(s,(w - g.getFontMetrics().stringWidth(s))/2,h-10); } else { if (imageError) s = "Wadliwy obrazek!"; g.drawString(s,(w - g.getFontMetrics().stringWidth(s))/2,h-10); if (!firstPaintDone){ ((JFrame) getParent()).pack(); firstPaintDone = true; } if (!imageError) g.drawImage(img, 5, 40, w-10, h-90, this); }

15 Przykłady wzorców - Budowniczy 15 //Przykład tworzenia pizzy wC#. class Pizza { string ciasto; string wierzch; string sos; public string Ciasto {get{return ciasto;}set{ciasto=value; } } public string Wierzch {get{return wierzch;}set{wierzch=value; } } public string Sos {get{return sos;}set{sos=value; } } public override string ToString() { return string.Format( "Pizza z ciasta {0}, z sosem {1} i wierzchem {2}", Ciasto, Sos, Wierzch); } Budowniczy (Builder) – element programowania komponentowego. Obiekt, który konstruuje inny obiekt (najczęściej kilkuetapowo, gdyż złożony jest z mniejszych komponentów). Rozdziela algorytm tworzenia obiektu od reprezentacji obiektów. Różne warianty omawianego wzorca wykorzystywane są w bibliotece MFC, implementując architekturę dokument/widok. Obiekt klasy CDocument oraz jego podobiekty tworzone są poprzez wywołanie metody tworzącej z trzema parametrami typu CRuntimeClass. Klasa ta zawiera metodę CreateObject, umożliwiającą jej tworzenie obiektów różnych klas (m.in. CDocument, CFrameWnd oraz CView). Dzięki takiemu zachowaniu CRuntimeClass może być nazwana klasą Budowniczego.

16 Przykłady wzorców - Budowniczy 16 //Budowniczy abstrakcyjny abstract class PizzaBuilder { protected Pizza pizza; public Pizza Pizza { get { return pizza; } } public void CreateNewPizza() { pizza = new Pizza(); } public abstract void BudujCiasto(); public abstract void BudujSos(); public abstract void BudujWierzch (); } //Budowniczy konkretny class Hawajska_PizzaBuilder : PizzaBuilder { public override void BudujCiasto() { pizza.Ciasto = "grube"; } public override void BudujSos() { pizza.Sos = "majonez"; } public override void BudujWierzch() { pizza.Wierzch = "szynka/ananas"; } } //Budowniczy konkretny class Pikantna_PizzaBuilder : PizzaBuilder { public override void BudujCiasto() { pizza.Ciasto = "cienkie"; } public override void BuildSos() { pizza.Sos = "ostry ketchup"; } public override void BuildWierzch() { pizza.Wierzch = "pepparoni+salami"; } }

17 Przykłady wzorców - Budowniczy 17 class Kucharz { private PizzaBuilder pizzaBuilder; public PizzaBuilder PizzaBuilder { get { return pizzaBuilder; } set { pizzaBuilder = value; } } public Pizza Pizza { get { return pizzaBuilder.Pizza; } } public void ConstructPizza() { pizzaBuilder.CreateNewPizza(); pizzaBuilder.BudujCiasto(); pizzaBuilder.BudujSos(); pizzaBuilder.BudujWierzch(); } } public class TestPizza { private static void BuildAndDisplayPizza(Kucharz k) { k.ConstructPizza(); System.Console.WriteLine(k.Pizza); } public static void Main() { Kucharz k = new Kucharz(); k.PizzaBuilder = new Hawajska_PizzaBuilder(); BuildAndDisplayPizza(k); k.PizzaBuilder = new Pikantna_PizzaBuilder(); BuildAndDisplayPizza(k); } }

18 Budowniczy a Fabryka obiektów 18 W przypadku korzystania z Budowniczego obiekt może być budowany na raty. Budowniczy udostępnia kilka metod, którymi stopniowo buduje obiekt. Porównaj StringBuilder. abstract class GUIFactory { public static GUIFactory getFactory() { int sys = readFromConfigFile("OS_TYPE"); if (sys == 0) return new WinFactory(); else return new OSXFactory(); } public abstract Button createButton(); } Fabryka zwraca nam gotowy obiekt przy jednym wywołaniu. Przykład – Java – Zakładamy istnienie fabryki abstrakcyjnej i fabryk konkretnych (dla poszczególnych platform systemowych. Fabryka tworzy obiekty – przyciski.

19 Fabryka obiektów 19 class WinFactory extends GUIFactory { public Button createButton() { return new WinButton(); } } class WinFactory extends GUIFactory { public Button createButton() { return new WinButton(); } } class OSXFactory extends GUIFactory { public Button createButton() { return new OSXButton(); } } class OSXFactory extends GUIFactory { public Button createButton() { return new OSXButton(); } } abstract class Button { public abstract void paint(); } class WinButton extends Button { public void paint() { System.out.println("Przycisk WinButton"); } } class WinButton extends Button { public void paint() { System.out.println("Przycisk WinButton"); } } class OSXButton extends Button { public void paint() { System.out.println("Przycisk OSXButton"); } } class OSXButton extends Button { public void paint() { System.out.println("Przycisk OSXButton"); } } public class Application { public static void main(String[] args) { GUIFactory factory = GUIFactory.getFactory(); Button button = factory.createButton(); button.paint(); } public class Application { public static void main(String[] args) { GUIFactory factory = GUIFactory.getFactory(); Button button = factory.createButton(); button.paint(); }

20 Dekorator 20 Klasę możemy wzbogacić poprzez dziedziczenie. Można to robić na etapie kompilacji. Dekorator pozwala na wzbogacenie istniejącego w pamięci obiektu nie naruszając stanu obiektu. Obiekt można dekorować wielokrotnie i w dowolnej kolejności Przykład w Javie – obsługa strumieni File plik = new File("test.txt"); //Otwieramy plik // dekorujemy plik, jako strumień wejściowy FileInputStream fstream = new FileInputStream(plik); // dekorujemy strumień – odczyt liczb zapisanych binarnie DataInputStream in = new DataInputStream(fstream); //ponowna dekoracja – umożliwia zmianę kodowania bajty na znaki InputStreamReader is = new InputStreamReader(in); //ponowna dekoracja – umożliwia odczyt linia po linii BufferedReader br = new BufferedReader(is);

21 Dekorator – implementacja 21 Zwykle dekorator implementujemy jako klasę dziedziczącą po udekorowanej klasie, zawierającą dodatkowo wskaźnik (lub referencję) na dekorowany obiekt /** Klasa, którą będziemy dekorować */ public class Book{ String author; String title; int pages; public String toString(){ return ("|" + author + "|" + title + "|" + pages + "|"); } public Book(String author, String title, int pages){ this.author = author; this.title = title; this.pages = pages; } public String getAuthor(){return author;} public String getTitle() {return title; } public int getPages() {return pages; } }

22 Dekorator – implementacja 22 /** abstrakcyjny dekorator */ public abstract class CoverIndex extends Book{ protected Book decorBook; //referencja na obiekt public CoverIndex(Book decorBook) { this.decorBook = decorBook;} } public class CoverClassic extends CoverIndex{ public CoverClassic(Book decorBook){super(decorBook);} public String toString(){ return decorBook.toString() + "| Classic Cover"; } } public class CoverHard extends CoverIndex{ public CoverHard(Book decorBook){super(decorBook);} public String toString(){ return decorBook.toString() + "| Hard Cover"; } } public class CoverDecorative extends CoverIndex{ public CoverDecorative(Book decorBook){super(decorBook);} public String toString(){ return decorBook.toString() + "| Decorative Cover"; } }

23 Dekorator – implementacja 23 class Main{ public static void main(String args[]) { Book b = new Book("Mickiewicz", "Dziady", 300); b = new CoverDecorative(b); //Dekoracja w oprawę ozdobną b = new CoverHard(b); //dekoracja w oprawę twardą System.out.println(b); // Mickiewicz | Dziady | 300 | Decorative Cover | Hard Cover }

24 Konfiguracja rejestru kontenera w Spring 24 W Java Spring komponenty konfigurujemy w pliku applicationContext.xml Plik definiuje dwa komponenty typu Samochód. Komponent dostępny pod nazwą samochod1 wykorzystuje wstrzyknięcie komponentu Silnik przez konstruktor, natomiast pod nazwą samochod2 znajduje się komponent korzystający z wstrzykiwania przez właściwość o nazwie silnik.

25 Programowanie w C# - część 3 25

26 Użycie konsoli 26 Metody klasy Console: BeepGra sygnał poprzez głośnik konsolowy. ClearCzyści bufor konsoli i odpowiadające mu okno. ResetColor przywraca domyślne kolory czcionki i tła dla konsoli SetCursorPosition – ustawia kursor na podanej pozyji SetWindowPosition – ustawia pozycję okna konsolowego. SetWindowSize – ustawia szerokość i wysokość okienka konsolowego (liczba wierszy i kolumn) Właściwości klasy Console: Console.Title = "Hello World"; Console.BackgroundColor = ConsoleColor.DarkYellow; bool Console.GetCapsLock(); sprawdza czy jest wciśnięty klasisz CapsLock In Console.GetCursorSize(); //wyświetla wielkość kursora w procentach wielkości komórki

27 Zasady obiektowości w C# 27 Większość zasad tworzenia i używania klas jest taka sama jak w Javie lub C++ Korzystanie z obiektu. W C# tak jak w Javie, wszystkie metody oraz zmienne muszą być składowymi klas lub struktur. Obiekt tworzymy poprzez operator new. Nie ma operatora delete, ani destruktora. Każdy obiekt dziedziczy z object Klasa1 zmienna1 = new Klasa1(); Odwołanie się do składowych (czyli metod, zmiennych lub właściwości zmienna1.X = 20; //odwołanie się do pola (lub właściwości zmienna1.Metoda1(); //wywołanie metody Ukrywanie informacji – modyfikatory dostępu privateDostępne tylko z metod danej klasy publicOgólnie dostępne protecteddostępne dla klas i potomków klasy internaldostęp dla klas z pakietu internal protected dostęp dla klas z pakietu lub potomnych Metody klasy object ToString - domyślna wersja tej metody zwraca w pełni kwalifikowaną nazwę klasy Equals - domyślnie porównuje referencje GetHashCode – wartość skrótu dla obiektu

28 Zasady obiektowości w C# 28 Właściwości (Properties) Właściwość to metoda składowa klasy, wraz z elastycznym mechanizmem odczytu, zapisu i przetwarzania prywatnego pola klasy class Prostokat{ private int a; private int b; public int A{ get{ return a; } set{ a = (value>0?value:0);} } public int B{ get{ return b; } set{ b = (value>0?value:0);} } public int P{ get { return a*b; } } A, B i P to są właściwości klasy Prostokąt. Używamy ich tak jakby to były pola klasy, w rzeczywistości są to metody Prostokat pr = new Prostokat(); pr.A = 10; pr.B = 20; Console.writeln("Dane prostokąta A={0}, B={1}, pole={2}", pr.A, pr.B, pr.P); Właściwości to bezpieczne akcesory (settery, gettery), które w kontrolowany sposób dają dostęp do pól prywatnych, jednocześnie tak łatwe w użyciu jak pola

29 Właściwości automatycznie implementowane 29 Gdy deklarujesz właściwości tak, jak w poniższym przykładzie kompilator sam tworzy prywatne anonimowe pola, do których dostęp możliwy jest poprzez akcesory get i set. class Klient{ public double SumaZakupow{ get; set; } public string Nazwisko { get; set; } public int IdKlienta {get; set; } Klient(double zakupy, string nazwisko, int id) { SumaZakupow = zakupy; Nazwisko = nazwisko; IdKlienta = id; }

30 Indekser 30 Indeksery to właściwości sparametryzowane definiowane wewnątrz klas Indekser może otrzymywać na wejściu jeden lub wiele parametrów, a nazwa indeksera jest reprezentowana prze słowo kluczowe this. public class TablicaKsiazek { Ksiazka[] ksiazki; public Ksiazka this [int index] { get { return ksiazki[index]; } set { if (value!=null) { ksiazki[index] = value; } TablicaKsiazek polka = new TablicaKsiazek(); polka[155] = new Ksiazka("Pan Tadeusz"); polka[0] = new Ksiazka("Dziady"); Oferuje sposób dostępu do kolekcji wartości utrzymywanych w ramach pojedynczego obiektu klasy (jest to odpowiednik przeciążonego operatora indeksowania w C++)

31 Klasy wieloplikowe 31 W C# definicja klasy nie musi znajdować się w całości w jednym pliku Załóżmy, że jeden plik z definicją klasy jest do naszej dyspozycji i możemy dodawać tam własny kod, drugi jest uaktualniany przez środowisko Visual Studio i nie powinniśmy go modyfikować. Drugie zastosowanie – praca grupowa Obie części będą tworzyły jedną klasę pod warunkiem, że będą miały tę samą, w pełni kwalifikowaną nazwę, czyli będą znajdować się w tej samej przestrzeni nazw. Wszystkie części muszą być dostępne w czasie kompilacji – nie możemy w ten sposób rozszerzać klas już skompilowanych. //Plik1.cs partial class Klasa1 { //częściowa definicja klasy } //Plik2.cs partial class Klasa1 { //częściowa definicja klasy }


Pobierz ppt "Programowanie komponentowe jesień-zima 2013 Wykład 4 Cykl życia komponentu Wzorce projektowe Programowanie obiektowe w C# dr inż. Wojciech Bieniecki Instytut."

Podobne prezentacje


Reklamy Google