Wzorce projektowe 3
Marcin Orchel
AGH University of Science and Technology in Poland
1 / 78
Agenda
1 Wzorce współbieżności
2 Wzorce architektoniczne
3 Pozostałe wzorce
Agenda 3 / 78
Wzorce współbieżności
Pamięć lokalna wątku (ang. thread-local storage)
metoda do przypisania pamięci statycznej lokalnie dla wątku, java.lang.ThreadLocal<T>
Wzorce współbieżności 5 / 78
Pamięć lokalna wątku
import java.util.concurrent.atomic.AtomicInteger;
public class ThreadId {
// Atomic integer containing the next thread ID to be assigned
private static final AtomicInteger nextId = new AtomicInteger(0);
// Thread local variable containing each thread’s ID private static final ThreadLocal<Integer> threadId = new ThreadLocal<Integer>() {
@Override protected Integer initialValue() { return nextId.getAndIncrement();}
};
// Returns the current thread’s unique ID, assigning it if necessary
public static int get() { return threadId.get();
} }
Wzorce współbieżności 6 / 78
Reaktor (ang. reactor )
demultipleksowanie i rozdzielanie żądań, które są dostarczane do aplikacji od jednego, lub wielu klientów
dostarcza asynchronicznyinterfejs do zasobów, do których dostęp musi być synchronizowany
procedura obsługi zdarzenia jest powiązana z zasobem struktura wzorca Reaktor odwraca sterowanie w aplikacji
Wzorce współbieżności 7 / 78
Reaktor
Reactor handleEvents() registerHandler() removeHandler()
Handle
Event Handler handleEvent() getHandle()
Concrete Event Handler A handleEvent()
getHandle() Synchronous Event Demultiplexer
select()
Concrete Event Handler B handleEvent()
getHandle() dispatches
owns notifies handle set
Rysunek 1:Źródło: własne
Wzorce współbieżności 8 / 78
Reaktor
Reactor obsługuje rejestrację i wyrejestrowanie procedur obsługi zdarzeń. Reactor zawiera zbiór uchwytów do zasobów (handle set).
Reactor dla zasobu z demultipleksera, w którym nastąpiło zdarzenie (zasób aktywny) wywołuje procedurę obsługi zdarzenia.
metoda handleEvents dla zasobów z nowymi danymi (sprawdzenie przez demultiplexer) wywołuje procedurę obsługi dla tych danych sekwencyjniei ustawia zasób na nieaktywny.
Pętla zdarzeniowa (ang. event loop) w metodzie select w Synchronous Event Demultiplexer wyszukuje wszystkie uchwyty do zasobów, które są aktywne. Zasoby nie są blokowane.
Event Handler: procedura obsługi zdarzeń i powiązany z nią uchwyt do zasobu (Handle)
Wzorce współbieżności 9 / 78
Reaktor
public interface EventHandler {
void handleEvent(byte[] data);
Socket getHandler();
}
public interface SynchronousEventDemultiplexer {
List<Socket> select(Collection<Socket> listeners);
}
public interface Reactor {
void registerHandle(EventHandler eventHandler);
void removeHandle(EventHandler eventHandler);
void handleEvents();
}
Reaktor
public class MessageEventHandler implements EventHandler {
private Socket socket;
public MessageEventHandler(String ipAddress, int port) {
socket = new Socket(ipAddress, port);
}
public void handleEvent(byte[] data) {
String message = Encoding.UTF8.getString(data);
}
public Socket getHandler() {
return socket;
} }
Wzorce współbieżności 11 / 78
Reaktor
public class DefaultSynchronousEventDemultiplexer implements SynchronousEventDemultiplexer {
public List<Socket> select(Collection<Socket> sockets) {
List<Socket> activeSockets = new ArrayList<Socket>();
for(Socket socket : sockets) { if (socket.pending())
{
activeSockets.add(socket);
} }
return activeSockets;
} }
Reaktor
public class DefaultReactor implements Reactor { private SynchronousEventDemultiplexer
synchronousEventDemultiplexer;
private Map<Socket, EventHandler> handlers;
public Reactor(SynchronousEventDemultiplexer synchronousEventDemultiplexer)
{
this.synchronousEventDemultiplexer = synchronousEventDemultiplexer;
handlers = new HashMap<TcpListener, IEventHandler>();
}
public void registerHandle(EventHandler eventHandler) {
handlers.add(eventHandler.getHandler(), eventHandler);
}
Wzorce współbieżności 13 / 78
Reaktor
public void removeHandle(EventHandler eventHandler) { handlers.remove(eventHandler.getHandler());
}
public void handleEvents() { while (true) {
List<Socket> sockets =
synchronousEventDemultiplexer.select(handlers.keySet());
for (Socket socket : sockets) { int dataReceived = 0;
byte[] buffer = new byte[1];
List<Byte> data = new List<Byte>();
Socket socket = socket.acceptSocket();
do {
dataReceived = socket.receive(buffer);
Reaktor
if (dataReceived > 0) {
data.add(buffer[0]);
}
} while (dataReceived > 0);
socket.close();
handlers[socket].handleEvent(data.toArray());
} } }
Wzorce współbieżności 15 / 78
Reaktor
class Program {
static void main(string[] args) { EventHandler client1 = new
MessageEventHandler("1.1.1.1", 123);
EventHandler client2 = new
MessageEventHandler("2.2.2.2", 123);
EventHandler client3 = new
MessageEventHandler("3.3.3.3", 123);
SynchronousEventDemultiplexer synchronousEventDemultiplexer = new DefaultSynchronousEventDemultiplexer();
Reactor dispatcher = new
DefaultReactor(synchronousEventDemultiplexer);
dispatcher.registerHandle(client1);
dispatcher.registerHandle(client2);
dispatcher.registerHandle(client3);
dispatcher.handleEvents();
} }
Wzorce współbieżności 16 / 78
Observer Pattern vs Reactor Pattern
w Reaktorze mamy centralną obsługę żądań, w obserwatorze, każdy obserwator jest powiązany z podmiotem
Wzorce współbieżności 17 / 78
Aktywny obiekt (ang. active object)
oddzieleniewykonywania metody od wywołania metody w celu usprawnienia wielowątkowości i uproszczenia dostępu równoległego do obiektów, które rezydują w swoich własnych wątkach kontrolujących.
Celem jest użycie aynchronicznego wywołania metodi schedulera
Aktywny obiekt
Client Proxy method1() methodN()
Scheduler dispatch() insert()
Future
ActiveQueue enqueue() dequeue()
MethodRequest canRun() call()
Servant method1() methodN()
ConcreteMethodRequest1 ConcreteMethodRequest2 invoke
obtain result from instantiate
instantiate
execute maintain
write to
Rysunek 2:Źródło: własne
Wzorce współbieżności 19 / 78
Servant
class Servant {
MQ_Servant (size_t mq_size);
// Message queue implementation operations.
void put (Message msg);
Message get ();
// Predicates.
boolean empty ();
boolean full ();
// Internal queue representation, e.g., a circular // array or a linked list, that does not use any // internal synchronization mechanism.
};
Proxy
class Proxy {
// The servant that implements the active object // methods and a scheduler for the message queue.
Servant servant;
Scheduler scheduler;
// Bound the message queue size.
enum { MQ_MAX_SIZE = /* ... */ };
Proxy (size_t size = MQ_MAX_SIZE): scheduler (size), servant (size) { }
// Schedule <put> to execute on the active object.
void put (Message msg) {
MethodRequest mr = new Put (servant, msg);
scheduler.insert (mr);
}
// Return a <MessageFuture> as the "future" result of // an asynchronous <get> method on the active object.
Wzorce współbieżności 21 / 78
Proxy
MessageFuture get () { MessageFuture result;
MethodRequest mr = new Get(servant, result);
scheduler.insert (mr);
return result;
}
// empty() and full() predicate implementations ...
};
Method Request
class MethodRequest implements Runnable { // Evaluate the synchronization constraint.
boolean canRun ();
// Execute the method.
void run();
};
Wzorce współbieżności 23 / 78
Method Request
class Get extends MethodRequest { Servant servant;
MessageFuture result;
Get(Servant rep, MessageFuture f) { servant = rep;
result = f; } bool canRun() {
// Synchronization constraint: cannot call a // <get> method until queue is not empty.
return !servant.empty();
}
void run () {
// Bind dequeued message to the future result.
result.set(servant.get());
} };
Scheduler
class Scheduler {
// List of pending MethodRequests.
private BlockingQueue<Runnable> actList = new LinkedBlockingQueue<Runnable>(); // ActiveQueue Scheduler() {}
// Put <MethodRequest> into <ActiveQueue>. This // method runs in the thread of its client, i.e.
// in the proxy’s thread.
void insert (MethodRequest mr) { actList.put(mr); }
};
Wzorce współbieżności 25 / 78
Scheduler
// Dispatch the method requests on their servant // in its scheduler’s thread of control.
public void dispatch() { new Thread(
new Runnable() {
@Override
public void run() { while (true) {
try {
dispatchQueue.take().run();
} catch (InterruptedException e)
{ // okay, just terminate the dispatcher }
} } }
).start();
}
Wzorce współbieżności 26 / 78
Blokada z podwójnym zatwierdzeniem (ang. double checked locking )
optymalizacja, która sprawdza najpierw czy lock musi być zajęty, przed jego zajęciem
Wzorce współbieżności 27 / 78
Blokada z podwójnym zatwierdzeniem
// Correct but possibly expensive multithreaded version class Foo {
private Helper helper;
public synchronized Helper getHelper() { if (helper == null) {
helper = new Helper();
}
return helper;
}
// other functions and members...
}
Blokada z podwójnym zatwierdzeniem
class Foo {
private volatile Helper helper;
public Helper getHelper() { Helper result = helper;
if (result == null) { synchronized(this) {
result = helper;
if (result == null) {
helper = result = new Helper();
} } }
return result;
}
// other functions and members...
}
Wzorce współbieżności 29 / 78
Balking
wykonanie akcji na obiekcie, tylko jeśli obiekt jest w ustalonym stanie
Balking
public class Example {
private boolean jobInProgress = false;
public void job() { synchronized(this) {
if (jobInProgress) { return;
}
jobInProgress = true;
}
// Code to execute job goes here // ...
}
void jobCompleted() { synchronized(this) {
jobInProgress = false;
} } }
Wzorce współbieżności 31 / 78
Blockchain
zdecentralizowany sposób przechowywania danych
blockchain to lista bloków, każdy blok w łańcuchu ma własny cyfrowy podpis zwany hashem, zawierający cyfrowy podpis poprzedniego bloku oraz dane, np. o transakcjach
hash danego bloku jest obliczany na podstawie poprzedniego hashu oraz danych
złamanie łańcucha następuje po zmianie danych
Blockchain
public class Block { public String hash;
public String previousHash;
private String data;
private long timeStamp;
public Block(String data,String previousHash) { this.data = data;
this.previousHash = previousHash;
this.timeStamp = new Date().getTime();
} }
Wzorce współbieżności 33 / 78
Blockchain
public class StringUtil {
public static String applySha256(String input){
try {
MessageDigest digest =
MessageDigest.getInstance("SHA-256");
//Applies sha256 to our input,
byte[] hash = digest.digest(input.getBytes("UTF-8"));
StringBuffer hexString = new StringBuffer(); // This will contain hash as hexidecimal
for (int i = 0; i < hash.length; i++) {
String hex = Integer.toHexString(0xff & hash[i]);
if(hex.length() == 1) hexString.append(’0’);
hexString.append(hex);
}
return hexString.toString();
}
catch(Exception e) {
throw new RuntimeException(e);
} } }
Wzorce współbieżności 34 / 78
Blockchain
public String calculateHash() {
String calculatedhash = StringUtil.applySha256(
previousHash + Long.toString(timeStamp) + data);
return calculatedhash;
}
Wzorce współbieżności 35 / 78
Blockchain
public Block(String data,String previousHash) { this.data = data;
this.previousHash = previousHash;
this.timeStamp = new Date().getTime();
this.hash = calculateHash();
}
Blockchain
public class FooChain {
public static void main(String[] args) {
Block genesisBlock = new Block("Hi im the first block", "0");
System.out.println("Hash for block 1 : " + genesisBlock.hash);
Block secondBlock = new Block("Im the second block",genesisBlock.hash);
System.out.println("Hash for block 2 : " + secondBlock.hash);
Block thirdBlock = new Block("Im the third block",secondBlock.hash);
System.out.println("Hash for block 3 : " + thirdBlock.hash);
} }
Wzorce współbieżności 37 / 78
Blockchain
public class FooChain {
public static ArrayList<Block> blockchain = new ArrayList<Block>();
public static void main(String[] args) {
blockchain.add(new Block("Hi im the first block",
"0"));
blockchain.add(new Block("Im the second
block",blockchain.get(blockchain.size()-1).hash));
blockchain.add(new Block("Im the third
block",blockchain.get(blockchain.size()-1).hash));
String blockchainJson = new
GsonBuilder().setPrettyPrinting().create().toJson(blockchain);
System.out.println(blockchainJson);
} }
Blockchain
sprawdzanie integralności łańcucha
public static Boolean isChainValid() { Block currentBlock;
Block previousBlock;
//loop through blockchain to check hashes:
for(int i=1; i < blockchain.size(); i++) { currentBlock = blockchain.get(i);
previousBlock = blockchain.get(i-1);
//compare registered hash and calculated hash if(!currentBlock.hash.equals(
currentBlock.calculateHash()) ){
System.out.println("Current Hashes not equal");
return false; }
//compare previous hash and registered previous hash if(!previousBlock.hash.equals(
currentBlock.previousHash) ) {
System.out.println("Previous Hashes not equal");
return false;
} }
return true;
}
Wzorce współbieżności 39 / 78
Blockchain
mining blocks
public class Block { public String hash;
public String previousHash;
private String data;
private long timeStamp;
private int nonce;
Blockchain
mining blocks
public String calculateHash() {
String calculatedhash = StringUtil.applySha256(
previousHash +
Long.toString(timeStamp) + Integer.toString(nonce) + data
);
return calculatedhash;
}
Wzorce współbieżności 41 / 78
Blockchain
mining blocks
public void mineBlock(int difficulty) { String target = new String(new
char[difficulty]).replace(’\0’, ’0’); //Create a string with difficulty * "0"
while(!hash.substring( 0, difficulty).equals(target)) { nonce ++;
hash = calculateHash();
}
System.out.println("Block Mined!!! : " + hash);
}
Blockchain
mining blocks
public static void main(String[] args) {
//add our blocks to the blockchain ArrayList:
blockchain.add(new Block("Hi im the first block", "0"));
System.out.println("Trying to Mine block 1... ");
blockchain.get(0).mineBlock(difficulty);
...
Wzorce współbieżności 43 / 78
Blockchain
mining blocks
public static Boolean isChainValid() { Block currentBlock;
Block previousBlock;
String hashTarget = new String(new char[difficulty]).replace(’\0’, ’0’);
...
//loop through blockchain to check hashes:
for(int i=1; i < blockchain.size(); i++) { currentBlock = blockchain.get(i);
previousBlock = blockchain.get(i-1);
if(!currentBlock.hash.substring( 0, difficulty).equals(hashTarget)) {
System.out.println("This block hasn’t been mined");
return false; } }
Inne wzorce współbieżności
thread pool– m wątków tworzonych jest w celu rozwiązania n zadań współbieżnie
szeregowanie(ang. scheduling ) – kontrolowanie bezpośrednio kiedy wątki mogą wykonać swój kod, np. ScheduledExecutorService, lub java.util.Timer
read/write lock– dostęp wielowątkowy dla operacji odczytu, i wyłączny dla operacji zapisu, ReadWriteLock
monitor– obiekt, które metody mogą być wzajemnie wykluczane messaging pattern – umożliwia wymianę informacji pomiędzy komponentami i aplikacjami
blokada– blokada zasobu przed dostępem lub modyfikacją przez inne wątki
join
guarded suspension– oczekiwanie, aż zostanie spełniony jakiś warunek asynchronous method invocation– nie blokowanie wątku podczas oczekiwania na odpowiedź, klasa FutureTask
Wzorce współbieżności 45 / 78
Inne wzorce współbieżności
binding properties – kombinacja wielu obserwatorów w celu wymuszenia właściwości w różnych obiektach, które muszą być synchronizowane lub koordynowane w pewien sposób
master-slave – komponent główny rozdziela zadania do identycznych komponentów pobocznych i oblicza ostateczny wynik z rezultatów zwróconych przez komponenty poboczne
Wzorce architektoniczne
Wzorce architektoniczne 47 / 78
MVC
architektura MVC (ang. Model/View/Controller ) obejmuje obiekty trzech rodzajów:Modelto obiekt aplikacji,View odpowiada
prezentacji widocznej na ekranie, aController określa, jak interfejs ma reagować na działania użytkownika
Controller i View tworzą interfejs użytkownika
ustanowienie protokołu subskrypcji i powiadamiania służącego do komunikowania się między widokami i modelami (wzorzec
Obserwator)
model komunikuje się z widokami, kiedy zmienią się jego wartości, a widoki komunikują się z modelem, aby uzyskać dostęp do danych.
możliwość zagnieżdżania widoków (przyciski zagnieżdżone w panelu sterowania), traktowanie widoku złożonego w taki sam sposób jak jeden z jego komponentów (wzorzec Kompozyt)
umożliwia zmianę sposobu reagowania widoku na działania użytkownika, bez modyfikowania wizualnej warstwy prezentacji
MVC
widok korzysta z egzemplarza podklasy klasy Controller do realizowania konkretnej strategii reagowania (wzorzecStrategia) Metoda wytwórcza do określania domyślnej klasy kontrolera widoku Dekoratorw celu dodania przewijania do widoku.
Wzorce architektoniczne 49 / 78
MVC
Model coreData setOfObservers attach(Observer) detach(Observer) notify() getData() service()
Observer Update()
View myModel myController update() initialize(Model) makeController() activate() display() call update
attach, getData
Controller myModel myView update() handleEvent() initialize(Model,View) create manipulate, display
attach, call service
Rysunek 3:Źródło: własne
Wzorce architektoniczne 50 / 78
Presentation-Abstract-Control
wzorzec architektoniczny Presentation-Abstract-Control (PAC) definiuje strukturę dla interaktywnych systemów oprogramowania w formie hierarchii współpracujących agentów. Każdy agent jest
odpowiedzialny za specyficzny aspekt funkcjonalności oprogramowania i składa się z trzech komponentów: prezentacji, abstrakcji i kontroli.
Ten podział umożliwia separację interakcji z użytkownikiem, od funkcjonalności i od komunikacji z innymi agentami.
w komponencie prezentacjiznajdują się podkomponenty oferujące funkcjonalność rysowania okien, menu, dialogów i zarządzanie danymi o prezentacji. Mogą być one opakowane za pomocą wzorca fasada.
Komponent kontroli umożliwia mediację pomiędzy komponentami abstrakcji i prezentacji tak aby uniknąć powiązania między nimi. Może być zaimplementowany jako adapter. Drugą rolą komponentu kontroli jest komunikacja z innymi agentami PAC. Zdarzenia odbierane od innych agentów są przekazywane dalej, do innych agentów lub komponentu abstrakcji lub prezentacji. Ta pierwsza rola może być zaimplementowana za pomocą wzorcamediator.
Wzorce architektoniczne 51 / 78
Presentation-Abstract-Control
Rysunek 4:Źródło: wikipedia
Wzorce architektoniczne 52 / 78
Presentation-Abstract-Control
Komunikacja może być zaimplementowana wg wzorcaComposite Message. W ten sposób interfejs między agentami jest niewielki.
Agenci mogą być również połączeni za pomocą wzorcaObserwator służącego do propagacji i zmiany stanów agentów.
możemy użyć wzorca aktywnego obiektudla każdego agenta PAC, wtedy każdy agent żyje we własnym wątku
do obsługi agentów na różnych maszynach/procesach można użyć wzorzec pełnomocnikaw celu reprezentacji lokalnej innych agentów i niezależności od konkretnych maszyn
do komunikacji międzyprocesowej pomiędzy agentami można użyć wzorcówForwarder-Receiver oraz Client-Dispatcher-Server
Wzorce architektoniczne 53 / 78
PAC vs MVC
w MVC Controller służy do obsługi wejścia od użytkownika i
translacji na wewnętrzną semantykę. A więc Controller i View razem odpowiadają komponentowi prezentacji w PAC. W Controller brakuje mediacji, a także MVC nie dzieli samodzielnych podzadań systemu na współpracujących, ale luźno powiązanych agentów.
Odwrócenie sterowania (ang. Inversion of Control, IoC )
przeniesienie na zewnątrz komponentu (np. obiektu) odpowiedzialności za kontrolę wybranych czynności
przykładem wykorzystania tego wzorca jest wzorzecstrategii innym przykładem IoC może być wzorzec metody szablonowej nazywana potocznieHollywood Principle – “Don’t call us, we’ll call you”
Wzorce architektoniczne 55 / 78
Broker
wzorzec broker może być użyty do konstrukcji rozproszonych systemów oprogramowania z oddzielnymi komponentami, które komunikują się ze sobą poprzez zdalne wywołanie serwisów.
Komponent brokera jest odpowiedzialny za koordynowanie
komunikacji takiej jak przekazywanie żądań, przesyłanie rezultatów i wyjątków
Broker
Broker +mainEventLoop() +updateRepository() +registerService() +acknowledgement() +findServer() +findClient() +forwardRequest() +forwardResponse() Client-side proxy
packData() +unpackData() +sendRequest() +return()
Server-side proxy +packData() +unpackData() +callService() +sendResponse()
Client +callServer() +startTask() +useBrokerAPI()
Bridge +packData() +unpackData() +forwardMessage() +transmitMessage()
Server +initialize() +enterMainLoop() +runService() +useBrokerAPI() calls
transfers message
uses API
transfers message
calls
uses API
Rysunek 5:Źródło: własne
Wzorce architektoniczne 57 / 78
Broker
serwery implementują serwisy
klienci to aplikacje, które mają dostęp do serwisów, aby wywołać zdalny serwis klient kieruje żądanie do brokera, po zakończeniu operacji otrzymuje odpowiedź lub wyjątek od brokera
klienci nie muszą znać położenia serwerów
broker jest odpowiedzialny za przesyłanie żądań od klienta do serwera, a także odpowiedzi i wyjątków z powrotem do klienta
proxy od strony klienta ukrywa detale implementacyjne dotyczące komunikacji między klientem a brokerem, konwersji danych do wysłania, podobnie działają proxy od strony serwera
Bridge to opcjonalne komponenty służące do ukrycia detali implementacyjnych kiedy komunikuje się dwóch brokerów
Forwarder-Receiver
wzorzec dostarcza jawną komunikację międzyprocesową dla systemów oprogramowania z modelem interakcji peer-to-peer (P2P). Definiuje komponenty przekazujące i odbierające w celu oddzielenia hostów od mechanizmu komunikacyjnego
każdy host może być zarówno klientem jak i serwerem
Wzorce architektoniczne 59 / 78
Forwarder-Receiver
Peer 1 +service()
Forwarder +marshal() +deliver() +sendMsg()
Receiver +receive() +unmarshal() +receiveMsg()
Peer 2 +service()
Receiver +receive() +unmarshal() +receiveMsg()
Forwarder +marshal() +deliver() +sendMsg() sendMsg
receiveMsg
IPC IPC
receiveMsg
sendMsg
Rysunek 6:Źródło: własne
Wzorce architektoniczne 60 / 78
Client-Dispatcher-Server
wzorzec dostarcza warstwę pośrednią pomiędzy klientami i serwerami, komponent rozdzielający. Dostarcza on transparentność położenia za pomocą nazw serwisu i ukrywa szczegóły nawiązanego połączenia komunikacyjnego pomiędzy klientami i serwerami
rola klienta i serwera może zmieniać się dynamicznie
Wzorce architektoniczne 61 / 78
Client-Dispatcher-Server
Client +doTask() +sendRequest()
Dispatcher +locationMap +registerService() +unregisterServer() +locateServer() +establishChannel() +getChannel()
Server +acceptConnection() +runService() +receiveRequest()
requests connection registers, accepts link
establishes connection
request service returns result
Rysunek 7:Źródło: własne
Wzorce architektoniczne 62 / 78
Client-Dispatcher-Server
celem klienta jest wykonać zadania specyficzne dla domeny. Klient ma dostęp do operacji oferowanych przez serwery w celu wykonania przetwarzanych zadań. Przed wysłaniem żądania do serwera, klient pyta komponent rozdzielający o kanał komunikacyjny.
Serwer dostarcza listę operacji do klientów. Może sam się zarejestrować lub jest rejestrowany przez dispatchera za pomocą nazwy i adresu.
Dispatcher oferuje funkcjonalność do nawiązania kanałów komunikacyjnych
Wzorce architektoniczne 63 / 78
Composite Message
konstrukcja komunikacji między komponentami w systemie tak, że luźno powiązane komponenty tworzą minimalne założenia o
infrastrukturze komunikacji, a ściśle powiązane komponenty zajmują się ich interakcją
wzorzec koncentruje się na organizacji infrastruktury komunikacji używanej przez komponenty tak, że komponenty mogą być dodawane i usuwane z minimalnymi zmianami. W tym samym czasie narzut komunikacyjny powinien być minimalizowany.
Composite Message
Component +sendMsg() +recvMsg() +addDataSet()
DataSetInterface +size()
+add() +remove()
Message +size() +add() +remove() +marshal() +unmarshal()
DataSet +size() currentMessage
for all DataSets { find size and add;
}
Rysunek 8:Źródło: własne
Wzorce architektoniczne 65 / 78
Composite Message
komponent generuje wiadomość i może dodawać i usuwać inne zbiory danych. Niektóre komponenty mogą marshalować i dzielić na
fragmenty wiadomość do transmisji przez sieć i unmarshalować je i składać po otrzymaniu
Message to kompozyt, które może zawierać inne zbiory danych.
Message definiuje standardowe metody do dodawania lub usuwania zbiorów danych, wyznaczania rozmiarów i jeśli to konieczne metody do marshallingu, unmarshallingu, fragmentacji i łączenia
DataSet może byż dodawany lub usuwany z wiadomości przez komponent
DataSetInterface - wspólny interfejs dla DataSets i Messages
Mikrokernel (ang. Microkernel)
Mikrokernel dotyczy systemów oprogramowania, które muszą się dostosowywać do zmian w wymaganiach systemowych. Separuje minimalny funkcjonalnie rdzeń od rozszerzonych funkcjonalności i części specyficznych dla klienta. Mikrokernel jest także gniazdem do którego podłączamy rozszerzenia i koordynujemy ich współpracę.
Wzorce architektoniczne 67 / 78
Mikrokernel
External Server +receiveRequest() +dispatchRequest() +executeService()
Microkernel +executeMechanism() +initCommunication() +findReceiver() +createHandle() +sendMessage() +callInternalServer()
Internal Server +executeService() +receiveRequest()
Adapter +callService() +createRequest()
Client +doTask() calls
activates
initializes communication
calls service send request
Rysunek 9:Źródło: własne
Wzorce architektoniczne 68 / 78
Mikrokernel
Microkernel implementuje centralne serwisy takie jak udogodnienia komunikacji, zarządzanie zasobami. Większość części zależnych od sprzętu jest ukrytych przed innymi częściami. Mikrokernel jest odpowiedzialny także za zarządzanie zasobami systemowymi takimi jak procesy, pliki. Kontroluje dostęp do tych zasobów. Mikrokernel implementuje atomiczne serwisy, które będziemy nazywać
mechanizmami. Na podstawie tych mechanizmów konstruowane są bardziej skomplikowane funkcjonalności, zwane działaniami (ang.
policies).
serwer wewnętrzny zwany także podsystemem rozszerza
funkcjonalność dostarczaną przez mikrokernel. Reprezentuje oddzielny komponent oferujący dodatkową funkcjonalność. Mikrokernel
wywołuje funkcjonalność serwerów wewnętrznych przez żądania serwisowe. Zauważmy, że serwery wewnętrzne są dostępne tylko poprzez mikrokernel dla innych komponentów.
Wzorce architektoniczne 69 / 78
Mikrokernel
serwer zewnętrzny implementuje widok specyficznej dziedziny aplikacji. Każdy serwer zewnętrzny uruchamiany jest jako oddzielny proces. Serwer zewnętrzny otrzymuje żądania serwisowe od aplikacji klienckiej używając do tego udogodnień komunikacji dostarczanych przez mikrokernel, interpretuje te żądania, wykonuje odpowiednie serwisy i zwraca rezultat do klienta.
klient jest aplikacją powiązaną z dokładnie jednym serwerem zewnętrznym
adapter inaczej emulator to interfejs pomiędzy serwerem zewnętrznym a klientem. Używa on mechanizmów komunikacji dostarczanych przez mikrokernel.
Pozostałe wzorce
Pozostałe wzorce 71 / 78
Wstrzykiwanie zależności (ang. Dependency Injection, DI)
sterowanie zostaje odwrócone w obszarze tworzeniapowiązań pomiędzy obiektami, czyli powiązania te są kontrolowane poza tymi obiektami
wzorzec ten jest implementowany za pomocą agregacji, podobnie jak wzorzec strategii
DI vs wzorzec strategii: we wzorcu strategii możemy podmieniać strategie w czasie życia obiektu, a w DI zazwyczaj używamy jednej instancji zależności
wyróżniamy 3 typy wstrzykiwania zależności: wstrzykiwanie konstruktora – zależności są dostarczane przez konstruktor, wstrzykiwanie settera – klient posiada metodę ustawiającą, którą wykorzystuje injector do wstrzyknięcia zależności,wstrzykiwanie interfejsu– zależność posiada metodę, która wstrzykuje zależność do klienta podanego do tej metody, klient musi implementować interfejs, który dostarcza metodę settera, która akceptuje zależność.
Wstrzykiwanie zależności
w przypadku wstrzykiwania interfejsu, asembler pobiera referencję do klienta, przekazuje ją do obiektu zależnego, który przekazuje swoją referencję do klienta
mogą być stosowane różne wzorce konstrukcyjne takie jak budowniczy, fabryka abstrakcyjna itp.
Pozostałe wzorce 73 / 78
Wstrzykiwanie interfejsu
Assembler (Injector) Dependency (Service)
Client +setService() ServiceSetter
setService()
Rysunek 10:Źródło: własne
Pozostałe wzorce 74 / 78
Wstrzykiwanie zależności przykład
// An example without dependency injection public class Client {
// Internal reference to the service used by this client private Service service;
// Constructor Client() {
// Specify a specific implementation in the constructor instead of using dependency injection
service = new ServiceExample();
}
// Method within this client that uses the services public String greet() {
return "Hello " + service.getName();
} }
Pozostałe wzorce 75 / 78
Wstrzykiwanie zależności przykład
// Constructor
Client(Service service) {
// Save the reference to the passed-in service inside this client
this.service = service;
}
// Setter method
public void setService(Service service) {
// Save the reference to the passed-in service inside this client
this.service = service;
}
Wstrzykiwanie zależności przykład
// Service setter interface.
public interface ServiceSetter {
public void setService(Service service);
}
// Client class
public class Client implements ServiceSetter {
// Internal reference to the service used by this client.
private Service service;
// Set the service that this client is to use.
@Override
public void setService(Service service) { this.service = service;
} }
Pozostałe wzorce 77 / 78
Wstrzykiwanie zależności przykład
public class Injector {
public static void main(String[] args) { // Build the dependencies first
Service service = new ServiceExample();
// Inject the service, constructor style Client client = new Client(service);
// Use the objects
System.out.println(client.greet());
} }