mirror of
https://github.com/tiennm99/java-design-patterns.git
synced 2026-09-13 22:19:07 +00:00
* docs: reformulate text, add links to further languages and paragraph about the book in central README.md (#2275) * docs: shorten text for link to payhip (#2275) * docs: reformulate german translation for Abstract Document (#2275) * Create folder for Abstract Factory * Create README.md for Abstract Document * Translate README.md for Abstract Document to German * correct typo * docs: add class diagrams for all patterns (#2275) * docs: add missing words in abstract-factory.README.md (#2275) * docs: Create German README.md for Active Object #2275 first text part * docs: finish German translation of README.md for Active Object * docs: translate actor-model into German #2295 * docs: add German translation for Acyclic Visitor #2295 * correct typo * docs: Translate Singleton into German #2295 * docs: Translate Prototype into German #2295 * Delete localization/de/Abstract Factory * insert line break * correct typos in actor-model * realignment of java code block * docs: translate Adapter Pattern into German #2295 * Update README.md change 'lizenz' to 'Lizenz' in badge * Update README.md translate 'benefits' and 'trade-offs' * remove additional bullet point after line break * correct indentation * docs: translate Dependency Injection into German #2295 * docs: translate Bridge into German #2295 * correct typos * docs: Translate Builder into German * delete english text * little changes in formulation * Update README.md change link to image * docs: remove spanish files (#2275) * docs: remove non-translated patterns (#2275) * Create README.md docs: Translate Step Builder into German #2275 * Create README.md docs: Translate Fluent Interface into German #2275 * eager is eifrig, not gierig * Translate Chain of Responsibility into German #2275
233 lines
9.1 KiB
Markdown
233 lines
9.1 KiB
Markdown
---
|
|
shortTitle: Active Object
|
|
category: Concurrency
|
|
language: de
|
|
tag:
|
|
- Asynchronous
|
|
- Decoupling
|
|
- Messaging
|
|
- Synchronization
|
|
- Thread management
|
|
---
|
|
|
|
## Zweck
|
|
|
|
Active Object bietet eine zuverlässige Methode zur Behandlung asynchroner Prozesse, mit der reaktionsfähige Anwendungen
|
|
und effizientes Thread-Management gesichert werden.
|
|
Dies wird dadurch erreicht, dass die einzelnen Aufgaben in Objekte gekapselt werden, die in eigenen Threads (Steuerungsflüssen)
|
|
mit eigener Nachrichtenwarteschlange aktiv sind. Durch diese Trennung bleibt der Hauptthread
|
|
reaktionsfähig und Probleme wie direkte Threadmanipulation oder gemeinsamer Zugriff auf Zustände werden vermieden.
|
|
|
|
## Detaillierte Erklärung
|
|
|
|
Reales Beispiel
|
|
|
|
> Stellen Sie sich ein gut besuchtes Restaurant vor, in dem die Gäste Bestellungen bei den
|
|
> Kellner aufgeben. Die Kellner gehen nicht selbst in die Küche, um die Essen selbst zuzubereiten,
|
|
> sondern sie schreiben die Bestellungen auf Zettel und geben diese dem Küchenmanager.
|
|
> Der Manager organisiert eine Gruppe von Köchen, die die verschiedenen Mahlzeiten parallel zubereiten.
|
|
> Wenn ein Koch frei ist, nimmt er eine Bestellung aus der Warteschlange, bereitet das Essen zu
|
|
> und benachrichtigt den Kellner, sobald es fertig zum Servieren ist.
|
|
>
|
|
> In dieser Analogie stehen die Kellner für die Client-Threads,
|
|
> der Küchenmanager für den Thread-Scheduler, und die Köche für die Methodenausführung
|
|
> in verschiedenen Threads.
|
|
> Die Organisation ermöglicht es, dass die Kellner immer weiter Bestellungen annehmen können,
|
|
> ohne durch die Essenszubereitung aufgehalten zu werden; so wie das Active-Object-Pattern
|
|
> den Methodenaufruf von der Ausführung trennt, um die Effizienz zu verbessern.
|
|
|
|
In einfachen Worten
|
|
|
|
> Das Active-Object-Pattern trennt Methodenausführung und Methodenaufruf,
|
|
> um Parallelitätsgrad und Reaktionsfähigkeit in Multithread-Anwendungen zu verbessern.
|
|
|
|
Wikipedia sagt
|
|
|
|
> Das Design-Pattern Active Object entkoppelt die Methodenausführung vom Methodenaufruf
|
|
> für Objekte, die in ihrem jeweils eigenen Thread arbeiten.
|
|
> Ziel ist, Parallelität dadurch zu ermöglichen, dass Methoden asynchron aufgerufen werden und ein
|
|
> Scheduler die Anfragen organisiert.
|
|
>
|
|
> Das Pattern besteht aus sechs Elementen.
|
|
>
|
|
> * Ein Proxy stellt für Clients ein Interface mit öffentlich zugänglichen Methoden zur Verfügung.
|
|
> * Ein weiteres Interface definiert die Anfragen an ein aktives Objekt.
|
|
> * Eine Liste offener Client-Anfragen.
|
|
> * Ein Scheduler entscheidet, welche Anfrage als nächstes ausgeführt wird.
|
|
> * Die Implementation der Methoden.
|
|
> * Eine Callback-Variable zur Rückmeldung des Ergebnisses.
|
|
|
|
Ablaufdiagramm
|
|
|
|

|
|
|
|
|
|
## Programmbeispiel in Java
|
|
|
|
Die Orcs sind wilde und nicht zu bändigende Kreaturen. Anscheinend haben sie ihre eigene Steuerung, die nur von ihrem vorherigen Verhalten bestimmt wird.
|
|
Um eine derartige Kreatur zu implementieren, können wir das Active-Objekt-Pattern benutzen.
|
|
|
|
```java
|
|
public abstract class ActiveCreature {
|
|
private final Logger logger = LoggerFactory.getLogger(ActiveCreature.class.getName());
|
|
|
|
private BlockingQueue<Runnable> requests;
|
|
|
|
private String name;
|
|
|
|
private Thread thread;
|
|
|
|
public ActiveCreature(String name) {
|
|
this.name = name;
|
|
this.requests = new LinkedBlockingQueue<Runnable>();
|
|
thread = new Thread(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
while (true) {
|
|
try {
|
|
requests.take().run();
|
|
} catch (InterruptedException e) {
|
|
logger.error(e.getMessage());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
);
|
|
thread.start();
|
|
}
|
|
|
|
public void eat() throws InterruptedException {
|
|
requests.put(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
logger.info("{} is eating!", name());
|
|
logger.info("{} has finished eating!", name());
|
|
}
|
|
}
|
|
);
|
|
}
|
|
|
|
public void roam() throws InterruptedException {
|
|
requests.put(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
logger.info("{} has started to roam the wastelands.", name());
|
|
}
|
|
}
|
|
);
|
|
}
|
|
|
|
public String name() {
|
|
return this.name;
|
|
}
|
|
}
|
|
```
|
|
|
|
Man sieht, dass jede Klasse, die `ActiveCreature` erweitert, ihren eigenen Kontrollfluss für den Aufruf und die Ausführung der Methoden zum Herumstreifen und Essen hat.
|
|
|
|
Beispielsweise die Klasse `Orc`:
|
|
|
|
```java
|
|
public class Orc extends ActiveCreature {
|
|
|
|
public Orc(String name) {
|
|
super(name);
|
|
}
|
|
}
|
|
```
|
|
Nun können wir etliche Kreaturen dieser Art schaffen, sie zum Essen und Herumstreifen auffordern, aber jede von ihnen wird das in Eigenregie (eigener Thread) ausführen.
|
|
|
|
```java
|
|
public class App implements Runnable {
|
|
|
|
private static final Logger logger = LoggerFactory.getLogger(App.class.getName());
|
|
|
|
private static final int NUM_CREATURES = 3;
|
|
|
|
public static void main(String[] args) {
|
|
var app = new App();
|
|
app.run();
|
|
}
|
|
|
|
@Override
|
|
public void run() {
|
|
List<ActiveCreature> creatures = new ArrayList<>();
|
|
try {
|
|
for (int i = 0; i < NUM_CREATURES; i++) {
|
|
creatures.add(new Orc(Orc.class.getSimpleName() + i));
|
|
creatures.get(i).eat();
|
|
creatures.get(i).roam();
|
|
}
|
|
Thread.sleep(1000);
|
|
} catch (InterruptedException e) {
|
|
logger.error(e.getMessage());
|
|
Thread.currentThread().interrupt();
|
|
} finally {
|
|
for (int i = 0; i < NUM_CREATURES; i++) {
|
|
creatures.get(i).kill(0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
Programmausgabe:
|
|
|
|
```
|
|
09:00:02.501 [Thread-0] INFO com.iluwatar.activeobject.ActiveCreature -- Orc0 is eating!
|
|
09:00:02.501 [Thread-2] INFO com.iluwatar.activeobject.ActiveCreature -- Orc2 is eating!
|
|
09:00:02.501 [Thread-1] INFO com.iluwatar.activeobject.ActiveCreature -- Orc1 is eating!
|
|
09:00:02.504 [Thread-0] INFO com.iluwatar.activeobject.ActiveCreature -- Orc0 has finished eating!
|
|
09:00:02.504 [Thread-1] INFO com.iluwatar.activeobject.ActiveCreature -- Orc1 has finished eating!
|
|
09:00:02.504 [Thread-0] INFO com.iluwatar.activeobject.ActiveCreature -- Orc0 has started to roam in the wastelands.
|
|
09:00:02.504 [Thread-2] INFO com.iluwatar.activeobject.ActiveCreature -- Orc2 has finished eating!
|
|
09:00:02.504 [Thread-1] INFO com.iluwatar.activeobject.ActiveCreature -- Orc1 has started to roam in the wastelands.
|
|
09:00:02.504 [Thread-2] INFO com.iluwatar.activeobject.ActiveCreature -- Orc2 has started to roam in the wastelands.
|
|
```
|
|
|
|
## Verwendung
|
|
|
|
* Wenn asynchrone Aufgaben behandelt werden sollen, ohne dass der Hauptthread blockiert wird, um bessere Performance und Reaktionsfähigkeit zu gewährleisten.
|
|
* Bei asynchronen Interaktionen mit externen Ressourcen.
|
|
* Zur Verbesserung der Reaktionsfähigkeit.
|
|
* Zum Management parallel ablaufender Aufgaben in modularer und wartbarer Art und Weise.
|
|
*
|
|
## Tutorials
|
|
|
|
* [Android and Java Concurrency: The Active Object Pattern(Douglas Schmidt)](https://www.youtube.com/watch?v=Cd8t2u5Qmvc)
|
|
|
|
## Reale Anwendungen in Java
|
|
|
|
* Echtzeit-Handelssysteme mit asynchroner Verarbeitung von Transaktionen.
|
|
* GUIs, bei denen langwierige Arbeiten im Hintergrund ablaufen, ohne dass die Benutzeroberfläche einfriert.
|
|
* Spiele, bei denen Aktualisierungen des Spielstatus oder KI-Berechnungen parallel abgearbeitet werden.
|
|
|
|
## Vor- und Nachteile
|
|
|
|
Vorteile
|
|
|
|
* Reaktionsfähigkeit des Hauptthreads wird verbessert
|
|
* Parallelitätsprobleme sind in den Objekten gekapselt
|
|
* Ermöglicht bessere Codeorganisation und -wartbarkeit.
|
|
* Sorgt für Threadsicherheit und vermeidet Probleme beim gemeinsamen Zugriff auf Zustände.
|
|
|
|
Nachteile
|
|
|
|
* Zusatzaufwand für die Übermittlung von Benachrichtigungen und das Threadmanagement.
|
|
* Nicht für alle Arten von Nebenläufigkeitsproblemen geeignet.
|
|
|
|
## Verwandte Patterns
|
|
|
|
* [Command](https://java-design-patterns.com/patterns/command/): Kapselt Anfragen als Objekte, ähnlich wie Active Object es mit Methodenaufrufen macht.
|
|
* [Promise](https://java-design-patterns.com/patterns/promise/): Bietet einen Weg zur Abfrage von Ergebnissen eines asynchronen Methodenaufrufs, oft mit Active Object kombiniert.
|
|
* [Proxy](https://java-design-patterns.com/patterns/proxy/): Active Object kann einen Proxy verwenden, um asynchrone Methodenaufrufe zu behandeln.
|
|
|
|
## Quellen
|
|
|
|
* [Design Patterns: Elements of Reusable Object Software](https://amzn.to/3HYqrBE)
|
|
* [Concurrent Programming in Java: Design Principles and Patterns](https://amzn.to/498SRVq)
|
|
* [Java Concurrency in Practice](https://amzn.to/4aRMruW)
|
|
* [Learning Concurrent Programming in Scala](https://amzn.to/3UE07nV)
|
|
* [Pattern Languages of Program Design 3](https://amzn.to/3OI1j61)
|
|
* [Pattern-Oriented Software Architecture Volume 2: Patterns for Concurrent and Networked Objects](https://amzn.to/3UgC24V)
|