diff --git a/docs/anchors/gof-abstract-factory-pattern.adoc b/docs/anchors/gof-abstract-factory-pattern.adoc new file mode 100644 index 0000000..bafb8f1 --- /dev/null +++ b/docs/anchors/gof-abstract-factory-pattern.adoc @@ -0,0 +1,32 @@ += GoF-Abstract Factory Pattern +:categories: design-principles +:roles: software-developer, software-architect +:umbrella: gof-design-patterns +:tier: 2 +:proponents: Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides +:tags: GoF, design pattern, creational, abstract factory, family, platform independence + +[%collapsible] +==== +Full Name:: GoF Abstract Factory Pattern (Creational) + +Also known as:: Kit + +[discrete] +== *Intent*: +Provide an interface for creating families of related or dependent objects without specifying their concrete classes. + +[discrete] +== *When to Use*: +* When a system should be independent of how its products are created +* When a system should be configured with one of multiple families of products +* When related product objects are designed to be used together and you need to enforce this constraint + +[discrete] +== *Prompt Example*: +"Erstelle eine Abstract Factory nach GoF, die UI-Komponenten für verschiedene Plattformen (Web, Mobile, Desktop) erzeugt, ohne dass der Client die konkreten Klassen kennen muss." + +[discrete] +== *Related Anchors*: +* <> (Umbrella) +==== diff --git a/docs/anchors/gof-adapter-pattern.adoc b/docs/anchors/gof-adapter-pattern.adoc new file mode 100644 index 0000000..8fa1349 --- /dev/null +++ b/docs/anchors/gof-adapter-pattern.adoc @@ -0,0 +1,31 @@ += GoF-Adapter Pattern +:categories: design-principles +:roles: software-developer, software-architect +:umbrella: gof-design-patterns +:tier: 1 +:proponents: Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides +:tags: GoF, design pattern, structural, adapter, wrapper, interface conversion, compatibility + +[%collapsible] +==== +Full Name:: GoF Adapter Pattern (Structural) + +Also known as:: Wrapper + +[discrete] +== *Intent*: +Convert the interface of a class into another interface clients expect. + +[discrete] +== *When to Use*: +* When you want to use an existing class whose interface doesn't match what you need +* When you want to create a reusable class that cooperates with unrelated classes + +[discrete] +== *Prompt Example*: +"Erstelle einen Adapter nach GoF, der die Legacy-API auf unser neues Interface mappt, ohne den bestehenden Code zu ändern." + +[discrete] +== *Related Anchors*: +* <> (Umbrella) +==== diff --git a/docs/anchors/gof-bridge-pattern.adoc b/docs/anchors/gof-bridge-pattern.adoc new file mode 100644 index 0000000..58bb6fd --- /dev/null +++ b/docs/anchors/gof-bridge-pattern.adoc @@ -0,0 +1,30 @@ += GoF-Bridge Pattern +:categories: design-principles +:roles: software-developer, software-architect +:umbrella: gof-design-patterns +:tier: 2 +:proponents: Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides +:tags: GoF, design pattern, structural, bridge, abstraction, implementation, decoupling + +[%collapsible] +==== +Full Name:: GoF Bridge Pattern (Structural) + +[discrete] +== *Intent*: +Decouple an abstraction from its implementation so that the two can vary independently. + +[discrete] +== *When to Use*: +* When you want to avoid a permanent binding between an abstraction and its implementation +* When both the abstractions and their implementations should be extensible by subclassing +* When changes in the implementation should have no impact on clients + +[discrete] +== *Prompt Example*: +"Trenne die Rendering-Abstraktion von der plattformspezifischen Implementierung nach dem GoF-Bridge Pattern, damit beide unabhängig erweitert werden können." + +[discrete] +== *Related Anchors*: +* <> (Umbrella) +==== diff --git a/docs/anchors/gof-builder-pattern.adoc b/docs/anchors/gof-builder-pattern.adoc new file mode 100644 index 0000000..eb95e50 --- /dev/null +++ b/docs/anchors/gof-builder-pattern.adoc @@ -0,0 +1,29 @@ += GoF-Builder Pattern +:categories: design-principles +:roles: software-developer, software-architect +:umbrella: gof-design-patterns +:tier: 1 +:proponents: Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides +:tags: GoF, design pattern, creational, builder, construction, fluent interface, complex objects + +[%collapsible] +==== +Full Name:: GoF Builder Pattern (Creational) + +[discrete] +== *Intent*: +Separate the construction of a complex object from its representation so that the same construction process can create different representations. + +[discrete] +== *When to Use*: +* When the algorithm for creating a complex object should be independent of the parts and how they are assembled +* When the construction process must allow different representations + +[discrete] +== *Prompt Example*: +"Implementiere einen Builder nach GoF für das QueryObject, mit Fluent API für Filter, Sortierung und Pagination." + +[discrete] +== *Related Anchors*: +* <> (Umbrella) +==== diff --git a/docs/anchors/gof-chain-of-responsibility-pattern.adoc b/docs/anchors/gof-chain-of-responsibility-pattern.adoc new file mode 100644 index 0000000..1510357 --- /dev/null +++ b/docs/anchors/gof-chain-of-responsibility-pattern.adoc @@ -0,0 +1,30 @@ += GoF-Chain of Responsibility Pattern +:categories: design-principles +:roles: software-developer, software-architect +:umbrella: gof-design-patterns +:tier: 2 +:proponents: Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides +:tags: GoF, design pattern, behavioral, chain of responsibility, handler, pipeline, middleware + +[%collapsible] +==== +Full Name:: GoF Chain of Responsibility Pattern (Behavioral) + +[discrete] +== *Intent*: +Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain. + +[discrete] +== *When to Use*: +* When more than one object may handle a request, and the handler isn't known a priori +* When you want to issue a request to one of several objects without specifying the receiver explicitly +* When the set of objects that can handle a request should be specified dynamically + +[discrete] +== *Prompt Example*: +"Implementiere eine Middleware-Pipeline nach dem GoF-Chain of Responsibility Pattern, bei der jeder Handler die Anfrage entweder verarbeitet oder an den nächsten weitergibt." + +[discrete] +== *Related Anchors*: +* <> (Umbrella) +==== diff --git a/docs/anchors/gof-command-pattern.adoc b/docs/anchors/gof-command-pattern.adoc new file mode 100644 index 0000000..8a106d7 --- /dev/null +++ b/docs/anchors/gof-command-pattern.adoc @@ -0,0 +1,30 @@ += GoF-Command Pattern +:categories: design-principles +:roles: software-developer, software-architect +:umbrella: gof-design-patterns +:tier: 1 +:proponents: Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides +:tags: GoF, design pattern, behavioral, command, undo, queue, encapsulation, action + +[%collapsible] +==== +Full Name:: GoF Command Pattern (Behavioral) + +[discrete] +== *Intent*: +Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations. + +[discrete] +== *When to Use*: +* When you want to parameterize objects with an action to perform +* When you need undo/redo functionality +* When you need to queue, log, or schedule requests + +[discrete] +== *Prompt Example*: +"Implementiere ein Undo/Redo-System nach dem GoF-Command Pattern, bei dem jede Benutzeraktion als Command-Objekt gekapselt wird." + +[discrete] +== *Related Anchors*: +* <> (Umbrella) +==== diff --git a/docs/anchors/gof-composite-pattern.adoc b/docs/anchors/gof-composite-pattern.adoc new file mode 100644 index 0000000..2d2be21 --- /dev/null +++ b/docs/anchors/gof-composite-pattern.adoc @@ -0,0 +1,29 @@ += GoF-Composite Pattern +:categories: design-principles +:roles: software-developer, software-architect +:umbrella: gof-design-patterns +:tier: 2 +:proponents: Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides +:tags: GoF, design pattern, structural, composite, tree, hierarchy, part-whole + +[%collapsible] +==== +Full Name:: GoF Composite Pattern (Structural) + +[discrete] +== *Intent*: +Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions uniformly. + +[discrete] +== *When to Use*: +* When you want to represent part-whole hierarchies of objects +* When you want clients to be able to ignore the difference between compositions of objects and individual objects + +[discrete] +== *Prompt Example*: +"Modelliere das Dateisystem nach dem GoF-Composite Pattern, sodass Dateien und Ordner einheitlich behandelt werden können." + +[discrete] +== *Related Anchors*: +* <> (Umbrella) +==== diff --git a/docs/anchors/gof-decorator-pattern.adoc b/docs/anchors/gof-decorator-pattern.adoc new file mode 100644 index 0000000..2c83194 --- /dev/null +++ b/docs/anchors/gof-decorator-pattern.adoc @@ -0,0 +1,31 @@ += GoF-Decorator Pattern +:categories: design-principles +:roles: software-developer, software-architect +:umbrella: gof-design-patterns +:tier: 1 +:proponents: Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides +:tags: GoF, design pattern, structural, decorator, wrapper, dynamic behavior, composition + +[%collapsible] +==== +Full Name:: GoF Decorator Pattern (Structural) + +Also known as:: Wrapper + +[discrete] +== *Intent*: +Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing. + +[discrete] +== *When to Use*: +* To add responsibilities to objects dynamically and transparently +* When extension by subclassing is impractical + +[discrete] +== *Prompt Example*: +"Verwende das GoF-Decorator Pattern, um dem Logger dynamisch Formatierung, Timestamp und Filterung hinzuzufügen." + +[discrete] +== *Related Anchors*: +* <> (Umbrella) +==== diff --git a/docs/anchors/gof-design-patterns.adoc b/docs/anchors/gof-design-patterns.adoc index 03d4ac2..0e897dc 100644 --- a/docs/anchors/gof-design-patterns.adoc +++ b/docs/anchors/gof-design-patterns.adoc @@ -4,6 +4,7 @@ :proponents: Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides :related: solid-principles, fowler-patterns, clean-architecture :tags: design-patterns, gang-of-four, oop, creational, structural, behavioral +:sub-anchors: gof-strategy-pattern, gof-observer-pattern, gof-factory-method-pattern, gof-singleton-pattern, gof-adapter-pattern, gof-decorator-pattern, gof-command-pattern, gof-facade-pattern, gof-template-method-pattern, gof-builder-pattern, gof-state-pattern, gof-proxy-pattern, gof-abstract-factory-pattern, gof-composite-pattern, gof-iterator-pattern, gof-mediator-pattern, gof-chain-of-responsibility-pattern, gof-bridge-pattern, gof-prototype-pattern, gof-flyweight-pattern, gof-interpreter-pattern, gof-memento-pattern, gof-visitor-pattern [%collapsible] ==== diff --git a/docs/anchors/gof-facade-pattern.adoc b/docs/anchors/gof-facade-pattern.adoc new file mode 100644 index 0000000..8cf098a --- /dev/null +++ b/docs/anchors/gof-facade-pattern.adoc @@ -0,0 +1,29 @@ += GoF-Facade Pattern +:categories: design-principles +:roles: software-developer, software-architect +:umbrella: gof-design-patterns +:tier: 1 +:proponents: Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides +:tags: GoF, design pattern, structural, facade, simplification, subsystem, unified interface + +[%collapsible] +==== +Full Name:: GoF Facade Pattern (Structural) + +[discrete] +== *Intent*: +Provide a unified interface to a set of interfaces in a subsystem. Facade defines a higher-level interface that makes the subsystem easier to use. + +[discrete] +== *When to Use*: +* When you want to provide a simple interface to a complex subsystem +* When there are many dependencies between clients and implementation classes + +[discrete] +== *Prompt Example*: +"Erstelle eine Facade nach GoF für das Payment-Subsystem, die Validierung, Autorisierung und Abrechnung hinter einem einfachen Interface kapselt." + +[discrete] +== *Related Anchors*: +* <> (Umbrella) +==== diff --git a/docs/anchors/gof-factory-method-pattern.adoc b/docs/anchors/gof-factory-method-pattern.adoc new file mode 100644 index 0000000..ea52fcf --- /dev/null +++ b/docs/anchors/gof-factory-method-pattern.adoc @@ -0,0 +1,31 @@ += GoF-Factory Method Pattern +:categories: design-principles +:roles: software-developer, software-architect +:umbrella: gof-design-patterns +:tier: 1 +:proponents: Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides +:tags: GoF, design pattern, creational, factory, polymorphism, instantiation + +[%collapsible] +==== +Full Name:: GoF Factory Method Pattern (Creational) + +Also known as:: Virtual Constructor + +[discrete] +== *Intent*: +Define an interface for creating an object, but let subclasses decide which class to instantiate. + +[discrete] +== *When to Use*: +* When a class can't anticipate the class of objects it must create +* When a class wants its subclasses to specify the objects it creates + +[discrete] +== *Prompt Example*: +"Extrahiere die Objekterzeugung in eine Factory Method nach GoF, damit neue Typen ohne Änderung des bestehenden Codes hinzugefügt werden können." + +[discrete] +== *Related Anchors*: +* <> (Umbrella) +==== diff --git a/docs/anchors/gof-flyweight-pattern.adoc b/docs/anchors/gof-flyweight-pattern.adoc new file mode 100644 index 0000000..9c045c2 --- /dev/null +++ b/docs/anchors/gof-flyweight-pattern.adoc @@ -0,0 +1,16 @@ += GoF-Flyweight Pattern +:categories: design-principles +:roles: software-developer, software-architect +:umbrella: gof-design-patterns +:tier: 3 +:proponents: Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides +:tags: GoF, design pattern, structural, flyweight, memory optimization, sharing + +[%collapsible] +==== +Full Name:: GoF Flyweight Pattern (Structural) + +[discrete] +== *Intent*: +Use sharing to support large numbers of fine-grained objects efficiently. +==== diff --git a/docs/anchors/gof-interpreter-pattern.adoc b/docs/anchors/gof-interpreter-pattern.adoc new file mode 100644 index 0000000..a69e9e7 --- /dev/null +++ b/docs/anchors/gof-interpreter-pattern.adoc @@ -0,0 +1,16 @@ += GoF-Interpreter Pattern +:categories: design-principles +:roles: software-developer, software-architect +:umbrella: gof-design-patterns +:tier: 3 +:proponents: Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides +:tags: GoF, design pattern, behavioral, interpreter, grammar, DSL, parsing + +[%collapsible] +==== +Full Name:: GoF Interpreter Pattern (Behavioral) + +[discrete] +== *Intent*: +Given a language, define a representation for its grammar along with an interpreter that uses the representation to interpret sentences in the language. +==== diff --git a/docs/anchors/gof-iterator-pattern.adoc b/docs/anchors/gof-iterator-pattern.adoc new file mode 100644 index 0000000..cd31c53 --- /dev/null +++ b/docs/anchors/gof-iterator-pattern.adoc @@ -0,0 +1,30 @@ += GoF-Iterator Pattern +:categories: design-principles +:roles: software-developer, software-architect +:umbrella: gof-design-patterns +:tier: 2 +:proponents: Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides +:tags: GoF, design pattern, behavioral, iterator, traversal, collection, aggregate + +[%collapsible] +==== +Full Name:: GoF Iterator Pattern (Behavioral) + +[discrete] +== *Intent*: +Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation. + +[discrete] +== *When to Use*: +* When you need to access an aggregate object's contents without exposing its internal representation +* When you want to support multiple traversals of aggregate objects +* When you want to provide a uniform interface for traversing different aggregate structures + +[discrete] +== *Prompt Example*: +"Implementiere einen Iterator nach GoF für die benutzerdefinierte Baumstruktur, der verschiedene Traversierungsstrategien (Tiefe, Breite) unterstützt." + +[discrete] +== *Related Anchors*: +* <> (Umbrella) +==== diff --git a/docs/anchors/gof-mediator-pattern.adoc b/docs/anchors/gof-mediator-pattern.adoc new file mode 100644 index 0000000..d95062f --- /dev/null +++ b/docs/anchors/gof-mediator-pattern.adoc @@ -0,0 +1,30 @@ += GoF-Mediator Pattern +:categories: design-principles +:roles: software-developer, software-architect +:umbrella: gof-design-patterns +:tier: 2 +:proponents: Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides +:tags: GoF, design pattern, behavioral, mediator, loose coupling, coordination, hub + +[%collapsible] +==== +Full Name:: GoF Mediator Pattern (Behavioral) + +[discrete] +== *Intent*: +Define an object that encapsulates how a set of objects interact. Mediator promotes loose coupling by keeping objects from referring to each other explicitly. + +[discrete] +== *When to Use*: +* When a set of objects communicate in well-defined but complex ways +* When reusing an object is difficult because it refers to and communicates with many other objects +* When behavior distributed between several classes should be customizable without excessive subclassing + +[discrete] +== *Prompt Example*: +"Führe einen Mediator nach GoF ein, der die Kommunikation zwischen den UI-Komponenten (Formular, Liste, Filter) zentral koordiniert und die direkte Kopplung beseitigt." + +[discrete] +== *Related Anchors*: +* <> (Umbrella) +==== diff --git a/docs/anchors/gof-memento-pattern.adoc b/docs/anchors/gof-memento-pattern.adoc new file mode 100644 index 0000000..ea5a849 --- /dev/null +++ b/docs/anchors/gof-memento-pattern.adoc @@ -0,0 +1,16 @@ += GoF-Memento Pattern +:categories: design-principles +:roles: software-developer, software-architect +:umbrella: gof-design-patterns +:tier: 3 +:proponents: Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides +:tags: GoF, design pattern, behavioral, memento, snapshot, undo, state preservation + +[%collapsible] +==== +Full Name:: GoF Memento Pattern (Behavioral) + +[discrete] +== *Intent*: +Without violating encapsulation, capture and externalize an object's internal state so that the object can be restored to this state later. +==== diff --git a/docs/anchors/gof-observer-pattern.adoc b/docs/anchors/gof-observer-pattern.adoc new file mode 100644 index 0000000..5ab1d5d --- /dev/null +++ b/docs/anchors/gof-observer-pattern.adoc @@ -0,0 +1,31 @@ += GoF-Observer Pattern +:categories: design-principles +:roles: software-developer, software-architect +:umbrella: gof-design-patterns +:tier: 1 +:proponents: Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides +:tags: GoF, design pattern, behavioral, observer, publish-subscribe, event, notification, reactive + +[%collapsible] +==== +Full Name:: GoF Observer Pattern (Behavioral) + +Also known as:: Publish-Subscribe, Event-Listener + +[discrete] +== *Intent*: +Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically. + +[discrete] +== *When to Use*: +* When changes to one object require changing others, and you don't know how many objects need to change +* When an object should notify others without knowing who they are + +[discrete] +== *Prompt Example*: +"Implementiere ein Event-System nach dem GoF-Observer Pattern, damit Änderungen am Datenmodell automatisch die UI aktualisieren." + +[discrete] +== *Related Anchors*: +* <> (Umbrella) +==== diff --git a/docs/anchors/gof-prototype-pattern.adoc b/docs/anchors/gof-prototype-pattern.adoc new file mode 100644 index 0000000..f6b4d3f --- /dev/null +++ b/docs/anchors/gof-prototype-pattern.adoc @@ -0,0 +1,30 @@ += GoF-Prototype Pattern +:categories: design-principles +:roles: software-developer, software-architect +:umbrella: gof-design-patterns +:tier: 2 +:proponents: Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides +:tags: GoF, design pattern, creational, prototype, cloning, copy, object creation + +[%collapsible] +==== +Full Name:: GoF Prototype Pattern (Creational) + +[discrete] +== *Intent*: +Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype. + +[discrete] +== *When to Use*: +* When a system should be independent of how its products are created and represented +* When classes to instantiate are specified at runtime +* When you want to avoid building a class hierarchy of factories that parallels the class hierarchy of products + +[discrete] +== *Prompt Example*: +"Verwende das GoF-Prototype Pattern, um neue Konfigurationsobjekte durch Klonen eines Prototypen zu erzeugen, statt sie jedes Mal von Grund auf zu konstruieren." + +[discrete] +== *Related Anchors*: +* <> (Umbrella) +==== diff --git a/docs/anchors/gof-proxy-pattern.adoc b/docs/anchors/gof-proxy-pattern.adoc new file mode 100644 index 0000000..5caa80c --- /dev/null +++ b/docs/anchors/gof-proxy-pattern.adoc @@ -0,0 +1,28 @@ += GoF-Proxy Pattern +:categories: design-principles +:roles: software-developer, software-architect +:umbrella: gof-design-patterns +:tier: 1 +:proponents: Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides +:tags: GoF, design pattern, structural, proxy, access control, lazy loading, caching, virtual proxy + +[%collapsible] +==== +Full Name:: GoF Proxy Pattern (Structural) + +[discrete] +== *Intent*: +Provide a surrogate or placeholder for another object to control access to it. + +[discrete] +== *When to Use*: +* When you need a more versatile or sophisticated reference to an object than a simple pointer (remote, virtual, protection, or smart reference proxy) + +[discrete] +== *Prompt Example*: +"Implementiere einen Caching-Proxy nach GoF für den API-Client, der wiederholte Anfragen aus dem Cache beantwortet." + +[discrete] +== *Related Anchors*: +* <> (Umbrella) +==== diff --git a/docs/anchors/gof-singleton-pattern.adoc b/docs/anchors/gof-singleton-pattern.adoc new file mode 100644 index 0000000..2f21626 --- /dev/null +++ b/docs/anchors/gof-singleton-pattern.adoc @@ -0,0 +1,29 @@ += GoF-Singleton Pattern +:categories: design-principles +:roles: software-developer, software-architect +:umbrella: gof-design-patterns +:tier: 1 +:proponents: Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides +:tags: GoF, design pattern, creational, singleton, global state, instance control + +[%collapsible] +==== +Full Name:: GoF Singleton Pattern (Creational) + +[discrete] +== *Intent*: +Ensure a class only has one instance, and provide a global point of access to it. + +[discrete] +== *When to Use*: +* When there must be exactly one instance of a class +* When the sole instance should be extensible by subclassing + +[discrete] +== *Prompt Example*: +"Stelle sicher, dass der ConfigManager als GoF-Singleton implementiert ist, mit thread-sicherem Lazy Loading." + +[discrete] +== *Related Anchors*: +* <> (Umbrella) +==== diff --git a/docs/anchors/gof-state-pattern.adoc b/docs/anchors/gof-state-pattern.adoc new file mode 100644 index 0000000..763d99a --- /dev/null +++ b/docs/anchors/gof-state-pattern.adoc @@ -0,0 +1,31 @@ += GoF-State Pattern +:categories: design-principles +:roles: software-developer, software-architect +:umbrella: gof-design-patterns +:tier: 1 +:proponents: Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides +:tags: GoF, design pattern, behavioral, state, state machine, finite automaton, context + +[%collapsible] +==== +Full Name:: GoF State Pattern (Behavioral) + +Also known as:: Objects for States + +[discrete] +== *Intent*: +Allow an object to alter its behavior when its internal state changes. The object will appear to change its class. + +[discrete] +== *When to Use*: +* When an object's behavior depends on its state and must change at runtime +* When operations have large conditional statements that depend on the object's state + +[discrete] +== *Prompt Example*: +"Ersetze die verschachtelten if/switch-Statements durch das GoF-State Pattern, sodass jeder Zustand sein eigenes Verhalten kapselt." + +[discrete] +== *Related Anchors*: +* <> (Umbrella) +==== diff --git a/docs/anchors/gof-strategy-pattern.adoc b/docs/anchors/gof-strategy-pattern.adoc new file mode 100644 index 0000000..2c1e65b --- /dev/null +++ b/docs/anchors/gof-strategy-pattern.adoc @@ -0,0 +1,30 @@ += GoF-Strategy Pattern +:categories: design-principles +:roles: software-developer, software-architect +:umbrella: gof-design-patterns +:tier: 1 +:proponents: Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides +:tags: GoF, design pattern, behavioral, strategy + +[%collapsible] +==== +Full Name:: GoF Strategy Pattern (Behavioral) + +[discrete] +== *Intent*: +Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it. + +[discrete] +== *When to Use*: +* Multiple related classes differ only in behavior +* You need different variants of an algorithm +* An algorithm uses data that clients shouldn't know about + +[discrete] +== *Prompt Example*: +"Refactore diese Klasse nach dem GoF-Strategy Pattern, um die verschiedenen Berechnungsarten austauschbar zu machen." + +[discrete] +== *Related Anchors*: +* <> (Umbrella) +==== diff --git a/docs/anchors/gof-template-method-pattern.adoc b/docs/anchors/gof-template-method-pattern.adoc new file mode 100644 index 0000000..ae96843 --- /dev/null +++ b/docs/anchors/gof-template-method-pattern.adoc @@ -0,0 +1,29 @@ += GoF-Template Method Pattern +:categories: design-principles +:roles: software-developer, software-architect +:umbrella: gof-design-patterns +:tier: 1 +:proponents: Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides +:tags: GoF, design pattern, behavioral, template method, algorithm skeleton, hook, inheritance + +[%collapsible] +==== +Full Name:: GoF Template Method Pattern (Behavioral) + +[discrete] +== *Intent*: +Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. + +[discrete] +== *When to Use*: +* When you want to implement the invariant parts of an algorithm once and leave it to subclasses to implement varying behavior +* When common behavior among subclasses should be factored and localized in a common class + +[discrete] +== *Prompt Example*: +"Refactore die Report-Generierung nach dem GoF-Template Method Pattern. Der Algorithmus (Daten laden, transformieren, formatieren) bleibt fix, aber die einzelnen Schritte sind austauschbar." + +[discrete] +== *Related Anchors*: +* <> (Umbrella) +==== diff --git a/docs/anchors/gof-visitor-pattern.adoc b/docs/anchors/gof-visitor-pattern.adoc new file mode 100644 index 0000000..bfb874f --- /dev/null +++ b/docs/anchors/gof-visitor-pattern.adoc @@ -0,0 +1,16 @@ += GoF-Visitor Pattern +:categories: design-principles +:roles: software-developer, software-architect +:umbrella: gof-design-patterns +:tier: 3 +:proponents: Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides +:tags: GoF, design pattern, behavioral, visitor, double dispatch, operations, traversal + +[%collapsible] +==== +Full Name:: GoF Visitor Pattern (Behavioral) + +[discrete] +== *Intent*: +Represent an operation to be performed on the elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates. +==== diff --git a/scripts/extract-metadata.js b/scripts/extract-metadata.js index c95e74d..f7328d5 100755 --- a/scripts/extract-metadata.js +++ b/scripts/extract-metadata.js @@ -79,6 +79,16 @@ function parseAnchorFile(filePath) { filePath: `docs/anchors/${id}.adoc`, } + // Umbrella anchor support + const subAnchors = parseList(attributes['sub-anchors']) + if (subAnchors.length > 0) anchor.subAnchors = subAnchors + + const umbrellaAttr = attributes.umbrella + if (umbrellaAttr) anchor.umbrella = umbrellaAttr.trim() + + const tierAttr = attributes.tier + if (tierAttr) anchor.tier = parseInt(tierAttr, 10) + // Validation const errors = [] const warnings = [] diff --git a/scripts/extract-metadata.test.js b/scripts/extract-metadata.test.js new file mode 100644 index 0000000..f0b95ae --- /dev/null +++ b/scripts/extract-metadata.test.js @@ -0,0 +1,34 @@ +import { describe, it, expect } from 'vitest' +import { execFileSync } from 'child_process' +import fs from 'fs' +import path from 'path' +import { fileURLToPath } from 'url' + +const __dirname = path.dirname(fileURLToPath(import.meta.url)) + +describe('extract-metadata', () => { + it('should extract umbrella, sub-anchors, and tier attributes', () => { + execFileSync('node', ['scripts/extract-metadata.js'], { + cwd: path.join(__dirname, '..'), + stdio: 'pipe', + }) + + const anchors = JSON.parse( + fs.readFileSync( + path.join(__dirname, '..', 'website', 'public', 'data', 'anchors.json'), + 'utf-8' + ) + ) + + const gof = anchors.find((a) => a.id === 'gof-design-patterns') + expect(gof).toBeDefined() + expect(gof.subAnchors).toBeDefined() + expect(gof.subAnchors.length).toBeGreaterThan(0) + expect(gof.subAnchors).toContain('gof-strategy-pattern') + + const strategy = anchors.find((a) => a.id === 'gof-strategy-pattern') + expect(strategy).toBeDefined() + expect(strategy.umbrella).toBe('gof-design-patterns') + expect(strategy.tier).toBe(1) + }) +}) diff --git a/website/src/components/anchor-modal.js b/website/src/components/anchor-modal.js index a6fba09..7d2976d 100644 --- a/website/src/components/anchor-modal.js +++ b/website/src/components/anchor-modal.js @@ -1,4 +1,5 @@ import { i18n } from '../i18n.js' +import { fetchAnchorsData } from '../utils/data-loader.js' let asciidoctor = null @@ -109,11 +110,74 @@ export function closeModal() { const SAFE_ANCHOR_ID = /^[a-z0-9]+(?:-[a-z0-9]+)*$/ const SAFE_LANG = /^[a-z]{2}$/ +function renderSubAnchorList(subAnchorIds, allAnchors) { + if (!subAnchorIds || subAnchorIds.length === 0) return '' + + const items = subAnchorIds.map((id) => { + const anchor = allAnchors.find((a) => a.id === id) + if (!anchor) return '' + const tier = anchor.tier || 2 + + if (tier === 3) { + return `
  • + ${escapeHtml(anchor.title)} + ${i18n.t('umbrella.notAnAnchor')} +
  • ` + } + + const tierClass = tier === 1 ? 'tier-1' : 'tier-2' + return `
  • + ${escapeHtml(anchor.title)} +
  • ` + }) + + return ` +
    +

    ${i18n.t('umbrella.subAnchors')}

    +
      ${items.join('')}
    +
    + ` +} + export async function loadAnchorContent(anchorId) { const modal = document.getElementById('anchor-modal') const titleEl = modal.querySelector('#modal-title') const contentEl = modal.querySelector('#modal-content') + // Back button for sub-anchor navigation + const umbrellaId = modal.dataset.umbrellaAnchor + let existingBackBtn = modal.querySelector('#modal-back') + + if (umbrellaId && anchorId !== umbrellaId) { + if (!existingBackBtn) { + existingBackBtn = document.createElement('button') + existingBackBtn.id = 'modal-back' + existingBackBtn.className = + 'text-[var(--color-text-secondary)] hover:text-[var(--color-text)] transition-colors p-2 mr-2' + // Safe: static SVG content, no user input + const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg') + svg.setAttribute('class', 'w-5 h-5') + svg.setAttribute('fill', 'none') + svg.setAttribute('stroke', 'currentColor') + svg.setAttribute('viewBox', '0 0 24 24') + const path = document.createElementNS('http://www.w3.org/2000/svg', 'path') + path.setAttribute('stroke-linecap', 'round') + path.setAttribute('stroke-linejoin', 'round') + path.setAttribute('stroke-width', '2') + path.setAttribute('d', 'M15 19l-7-7 7-7') + svg.appendChild(path) + existingBackBtn.appendChild(svg) + existingBackBtn.addEventListener('click', () => { + delete modal.dataset.umbrellaAnchor + loadAnchorContent(umbrellaId) + }) + titleEl.parentElement.insertBefore(existingBackBtn, titleEl) + } + } else { + delete modal.dataset.umbrellaAnchor + if (existingBackBtn) existingBackBtn.remove() + } + if (!SAFE_ANCHOR_ID.test(anchorId)) { contentEl.innerHTML = '
    Invalid anchor ID.
    ' return @@ -159,8 +223,28 @@ export async function loadAnchorContent(anchorId) { const title = titleMatch ? titleMatch[1] : anchorId titleEl.textContent = title + // Safe: htmlContent is generated by asciidoctor from .adoc files served from our own origin contentEl.innerHTML = String(htmlContent) + // Umbrella anchor support: show sub-anchor list + const allAnchors = await fetchAnchorsData() + const currentAnchor = allAnchors.find((a) => a.id === anchorId) + + if (currentAnchor?.subAnchors) { + // Safe: renderSubAnchorList uses escapeHtml for all dynamic values + contentEl.innerHTML += renderSubAnchorList(currentAnchor.subAnchors, allAnchors) + + // Add click handlers for sub-anchor links + contentEl.querySelectorAll('[data-sub-anchor]').forEach((link) => { + link.addEventListener('click', (e) => { + e.preventDefault() + const subId = link.dataset.subAnchor + modal.dataset.umbrellaAnchor = anchorId + loadAnchorContent(subId) + }) + }) + } + // Auto-expand all collapsible sections contentEl.querySelectorAll('details').forEach((details) => { details.setAttribute('open', '') diff --git a/website/src/components/anchor-modal.test.js b/website/src/components/anchor-modal.test.js index 4eaa818..59f1e3c 100644 --- a/website/src/components/anchor-modal.test.js +++ b/website/src/components/anchor-modal.test.js @@ -2,6 +2,10 @@ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest' import { JSDOM } from 'jsdom' import { createModal, openModal, closeModal, showAnchorDetails } from './anchor-modal.js' +vi.mock('../utils/data-loader.js', () => ({ + fetchAnchorsData: vi.fn(), +})) + describe('anchor-modal', () => { let dom let document @@ -91,9 +95,11 @@ describe('anchor-modal', () => { }) describe('showAnchorDetails', () => { - beforeEach(() => { + beforeEach(async () => { global.fetch = vi.fn() createModal() + const { fetchAnchorsData } = await import('../utils/data-loader.js') + fetchAnchorsData.mockResolvedValue([]) }) afterEach(() => { @@ -149,4 +155,67 @@ describe('anchor-modal', () => { expect(content.innerHTML).toContain('Failed to load') }) }) + + describe('umbrella anchors', () => { + beforeEach(async () => { + global.fetch = vi.fn() + createModal() + + const { fetchAnchorsData } = await import('../utils/data-loader.js') + fetchAnchorsData.mockResolvedValue([ + { + id: 'umbrella-anchor', + title: 'Umbrella Anchor', + subAnchors: ['sub-one', 'sub-two', 'sub-three'], + }, + { id: 'sub-one', title: 'Sub One', tier: 1 }, + { id: 'sub-two', title: 'Sub Two', tier: 2 }, + { id: 'sub-three', title: 'Sub Three', tier: 3 }, + ]) + }) + + afterEach(() => { + delete global.fetch + }) + + it('should render sub-anchor list when anchor has subAnchors', async () => { + global.fetch.mockResolvedValue({ + ok: true, + text: async () => '= Umbrella Anchor\n\nUmbrella content', + }) + + await showAnchorDetails('umbrella-anchor') + + const content = document.getElementById('modal-content') + expect(content.innerHTML).toContain('sub-anchor-list') + expect(content.innerHTML).toContain('Sub One') + expect(content.innerHTML).toContain('Sub Two') + expect(content.innerHTML).toContain('Sub Three') + }) + + it('should show back button when viewing sub-anchor from umbrella context', async () => { + // First load umbrella anchor + global.fetch.mockResolvedValue({ + ok: true, + text: async () => '= Umbrella Anchor\n\nUmbrella content', + }) + await showAnchorDetails('umbrella-anchor') + + // Click on a sub-anchor link + const subLink = document.querySelector('[data-sub-anchor="sub-one"]') + expect(subLink).toBeTruthy() + + global.fetch.mockResolvedValue({ + ok: true, + text: async () => '= Sub One\n\nSub one content', + }) + subLink.click() + + // Wait for async load + await new Promise((r) => setTimeout(r, 50)) + + const backBtn = document.getElementById('modal-back') + expect(backBtn).toBeTruthy() + }) + }) }) diff --git a/website/src/components/card-grid.js b/website/src/components/card-grid.js index 015e38e..dcb8952 100644 --- a/website/src/components/card-grid.js +++ b/website/src/components/card-grid.js @@ -64,7 +64,10 @@ export function renderCardGrid(categories, anchors) { */ function renderCategorySection(category, allAnchors) { const categoryAnchors = allAnchors.filter( - (anchor) => anchor.categories && anchor.categories.includes(category.id) + (anchor) => + anchor.categories && + anchor.categories.includes(category.id) && + !anchor.umbrella ) if (categoryAnchors.length === 0) return '' @@ -91,6 +94,8 @@ function renderCategorySection(category, allAnchors) { * Render a single anchor card */ function renderAnchorCard(anchor, categoryColor) { + const isUmbrella = anchor.subAnchors && anchor.subAnchors.length > 0 + const umbrellaClass = isUmbrella ? ' anchor-card-umbrella' : '' const rolesCount = anchor.roles ? anchor.roles.length : 0 const githubEditUrl = `https://github.com/LLM-Coding/Semantic-Anchors/edit/main/docs/anchors/${anchor.id}.adoc` const roleText = rolesCount === 1 ? i18n.t('card.roles') : i18n.t('card.rolesPlural') @@ -101,7 +106,7 @@ function renderAnchorCard(anchor, categoryColor) { return `
    + + + + ${anchor.subAnchors.length} Sub-Anchors + + ` + : '' + }
    diff --git a/website/src/components/card-grid.test.js b/website/src/components/card-grid.test.js new file mode 100644 index 0000000..e7ad400 --- /dev/null +++ b/website/src/components/card-grid.test.js @@ -0,0 +1,37 @@ +import { describe, it, expect, vi } from 'vitest' +import { renderCardGrid } from './card-grid.js' + +// Mock i18n module +vi.mock('../i18n.js', () => ({ + i18n: { + t: (key) => key, + }, +})) + +// Mock search-index module +vi.mock('../utils/search-index.js', () => ({ + search: () => [], + isIndexReady: () => false, +})) + +describe('umbrella anchors', () => { + it('should not render sub-anchors in the main catalog', () => { + const categories = [{ id: 'design-principles', name: 'Design Principles' }] + const anchors = [ + { id: 'gof-design-patterns', title: 'GoF', categories: ['design-principles'], roles: ['software-developer'], subAnchors: ['gof-strategy-pattern'], tags: [], proponents: [] }, + { id: 'gof-strategy-pattern', title: 'GoF-Strategy', categories: ['design-principles'], roles: ['software-developer'], umbrella: 'gof-design-patterns', tier: 1, tags: [], proponents: [] }, + ] + const html = renderCardGrid(categories, anchors) + expect(html).toContain('gof-design-patterns') + expect(html).not.toContain('data-anchor="gof-strategy-pattern"') + }) + + it('should add umbrella class to umbrella cards', () => { + const categories = [{ id: 'design-principles', name: 'Design Principles' }] + const anchors = [ + { id: 'gof-design-patterns', title: 'GoF', categories: ['design-principles'], roles: ['software-developer'], subAnchors: ['gof-strategy-pattern'], tags: [], proponents: [] }, + ] + const html = renderCardGrid(categories, anchors) + expect(html).toContain('anchor-card-umbrella') + }) +}) diff --git a/website/src/styles/main.css b/website/src/styles/main.css index fbe6233..9c659fb 100644 --- a/website/src/styles/main.css +++ b/website/src/styles/main.css @@ -295,3 +295,29 @@ body { .animate-fade-out { animation: fade-out 0.3s ease-out; } + + +/* Sub-anchor tier styling */ +.sub-anchor-item.tier-1 { + font-weight: 600; + border-left: 3px solid var(--color-primary); + padding-left: 0.75rem; +} + +.sub-anchor-item.tier-2 { + padding-left: calc(0.75rem + 3px); +} + +.sub-anchor-item.tier-3 { + padding-left: calc(0.75rem + 3px); + opacity: 0.5; +} + +.sub-anchor-link { + color: var(--color-primary); + text-decoration: none; +} + +.sub-anchor-link:hover { + text-decoration: underline; +} diff --git a/website/src/translations/de.json b/website/src/translations/de.json index f07a1b1..ade9f8b 100644 --- a/website/src/translations/de.json +++ b/website/src/translations/de.json @@ -52,5 +52,7 @@ "onboarding.text4": "Statt lange Prompts zu schreiben, sagst du einfach den richtigen Anker \u2013 und die KI liefert.", "onboarding.cta": "Katalog erkunden", "onboarding.watchVideo": "Erkl\u00e4rvideo ansehen", - "onboarding.infoButton": "Was sind Semantic Anchors?" + "onboarding.infoButton": "Was sind Semantic Anchors?", + "umbrella.subAnchors": "Patterns in dieser Sammlung", + "umbrella.notAnAnchor": "(kein semantischer Anker)" } diff --git a/website/src/translations/en.json b/website/src/translations/en.json index 22a551d..b82921c 100644 --- a/website/src/translations/en.json +++ b/website/src/translations/en.json @@ -52,5 +52,7 @@ "onboarding.text4": "Instead of writing long prompts, just use the right anchor \u2013 and the AI delivers.", "onboarding.cta": "Explore Catalog", "onboarding.watchVideo": "Watch explainer video", - "onboarding.infoButton": "What are Semantic Anchors?" + "onboarding.infoButton": "What are Semantic Anchors?", + "umbrella.subAnchors": "Patterns in this collection", + "umbrella.notAnAnchor": "(not a semantic anchor)" } diff --git a/website/src/utils/data-loader.js b/website/src/utils/data-loader.js index 0d40463..8fbbafb 100644 --- a/website/src/utils/data-loader.js +++ b/website/src/utils/data-loader.js @@ -107,6 +107,16 @@ export async function fetchData() { return dataPromise } +let cachedAnchorsOnly = null + +export async function fetchAnchorsData() { + if (cachedAnchorsOnly) return cachedAnchorsOnly + const { anchors } = await fetchData() + cachedAnchorsOnly = anchors + return cachedAnchorsOnly +} + export function __resetDataCacheForTests() { dataPromise = null + cachedAnchorsOnly = null } diff --git a/website/tests/e2e/website.spec.js b/website/tests/e2e/website.spec.js index 3216dba..34aadec 100644 --- a/website/tests/e2e/website.spec.js +++ b/website/tests/e2e/website.spec.js @@ -410,3 +410,57 @@ test.describe('Performance', () => { await expect(searchInput).toHaveAttribute('placeholder', /full-text/, { timeout: 15000 }) }) }) + +test.describe('Umbrella Anchors', () => { + test.beforeEach(async ({ page }) => { + await page.goto('/') + }) + + test('should show umbrella card with stacked visual', async ({ page }) => { + await page.waitForSelector('.anchor-card', { timeout: 10000 }) + const umbrellaCard = page.locator('.anchor-card-umbrella') + await expect(umbrellaCard.first()).toBeVisible() + }) + + test('should not show sub-anchors in main catalog', async ({ page }) => { + await page.waitForSelector('.anchor-card', { timeout: 10000 }) + const strategyCard = page.locator('[data-anchor="gof-strategy-pattern"]') + await expect(strategyCard).toHaveCount(0) + }) + + test('should show sub-anchor list in umbrella modal', async ({ page }) => { + await page.waitForSelector('.anchor-card', { timeout: 10000 }) + await page.locator('[data-anchor="gof-design-patterns"]').click() + await page.waitForSelector('.sub-anchor-list', { timeout: 10000 }) + await expect(page.locator('.sub-anchor-item').first()).toBeVisible() + }) + + test('should navigate to sub-anchor and show back button', async ({ page }) => { + await page.waitForSelector('.anchor-card', { timeout: 10000 }) + await page.locator('[data-anchor="gof-design-patterns"]').click() + await page.waitForSelector('.sub-anchor-list', { timeout: 10000 }) + await page.locator('.sub-anchor-link').first().click() + await expect(page.locator('#modal-back')).toBeVisible({ timeout: 10000 }) + }) + + test('should navigate back to umbrella from sub-anchor', async ({ page }) => { + await page.waitForSelector('.anchor-card', { timeout: 10000 }) + await page.locator('[data-anchor="gof-design-patterns"]').click() + await page.waitForSelector('.sub-anchor-list', { timeout: 10000 }) + await page.locator('.sub-anchor-link').first().click() + await expect(page.locator('#modal-back')).toBeVisible({ timeout: 10000 }) + await page.locator('#modal-back').click() + await page.waitForSelector('.sub-anchor-list', { timeout: 10000 }) + await expect(page.locator('.sub-anchor-list')).toBeVisible() + }) + + test('should show tier-3 items as greyed out and not clickable', async ({ page }) => { + await page.waitForSelector('.anchor-card', { timeout: 10000 }) + await page.locator('[data-anchor="gof-design-patterns"]').click() + await page.waitForSelector('.sub-anchor-list', { timeout: 10000 }) + const tier3Item = page.locator('.sub-anchor-item.tier-3') + await expect(tier3Item.first()).toBeVisible() + const linkCount = await tier3Item.first().locator('a').count() + expect(linkCount).toBe(0) + }) +})