diff --git a/README.md b/README.md index a0a54f7..5c8419e 100644 --- a/README.md +++ b/README.md @@ -5,64 +5,188 @@ ![Branches](.github/badges/branches.svg) # Java Design Patterns and Constructs -Collection of patterns and other constructs in Java for educational purposes. (See *Contributing* section if you would like to contribute). + +A curated collection of object-oriented design patterns and concurrency constructs implemented in modern Java, intended as teaching material. Each example is reduced to the smallest set of classes that still exhibits the essential structure of the pattern, and is accompanied by a written explanation, a class diagram and a unit test. The examples are hosted in a small Spring Boot application with a Swing front end, so that they can be executed one at a time and their output observed. + +## Contents + +1. [Quickstart](#quickstart) +2. [Catalogue](#catalogue) +3. [Design of the host application](#design-of-the-host-application) +4. [Building and quality gates](#building-and-quality-gates) +5. [Adding a new example](#adding-a-new-example) +6. [Documentation conventions](#documentation-conventions) +7. [Related projects](#related-projects) +8. [References](#references) +9. [Contributing](#contributing) ## Quickstart -* Install a Java 25 implementation (like openjdk25, see `.sdkmanrc`) -* Clone and run the spring-boot maven goal: + +### Requirements + +* A Java 25 JDK. The repository ships an `.sdkmanrc` file, so `sdk env install` will fetch a matching distribution if you use [SDKMAN!](https://sdkman.io/). +* Nothing else: the Maven wrapper (`./mvnw`) downloads the build tool on first use. + +### Running the examples + ```bash git clone https://github.com/lpenap/java-patterns-and-constructs cd java-patterns-and-constructs ./mvnw spring-boot:run ``` -## Da List -### Patterns -* [Abstract Factory](src/main/java/com/penapereira/example/constructs/abstractfactory/) -* [Adapter](src/main/java/com/penapereira/example/constructs/adapter/) -* [Chain of Responsibility](src/main/java/com/penapereira/example/constructs/chainofresponsibility/) -* [Decorator](src/main/java/com/penapereira/example/constructs/decorator/) -* [Factory](src/main/java/com/penapereira/example/constructs/factory/) -* [Factory Method](src/main/java/com/penapereira/example/constructs/factorymethod/) -* [Observer](src/main/java/com/penapereira/example/constructs/observer/) -* [Singleton](src/main/java/com/penapereira/example/constructs/singleton/) -* [Strategy](src/main/java/com/penapereira/example/constructs/strategy/) -* [Template Method](src/main/java/com/penapereira/example/constructs/templatemethod/) -### Constructs and Problems -* [Producer/Consumer](src/main/java/com/penapereira/example/constructs/producerconsumer/), a multi-process synchronization problem. - -## Caveats -Change the log level to TRACE in `application.properties` if you would like to see the output from examples: + +A window opens with one button per example and a **Run All** button. Clicking a button runs that example on a background thread and writes its trace output to the **Logger Output** panel. Tick **Preserve log** to keep the output of previous runs instead of clearing the panel before each one. + +The examples can also be run without the window by enabling the console runner in `src/main/resources/application.properties`: + +```properties +app.enableCommandLineRunner=true +``` + +Example output is written at `TRACE` level. The default configuration already enables it for the project packages: + ```properties logging.level.com.penapereira.example.*=TRACE ``` -## Contributing -If you have an interesting idea but don't know how to implement it, feel free to open an issue to request a new feature. - -If you would like to contribute additional constructs or patterns follow this steps to add an additional package with the additional example: - -1. Create an additional nested package (i.e. `newexample`) as `com.penapereira.example.constructs.newexample` -2. Place the interfaces and classes you need there. -3. Implement an example runner by giving it a name ending in `ExampleRunner`, implementing the `ExampleRunnerInterface` and adding the `@Component` annotation: -```java -@Component -public class FooBarExampleRunner implements ExampleRunnerInterface { - @Override - public void runExample() { - // place your code here - } -} + +## Catalogue + +The patterns follow the classification of Gamma, Helm, Johnson and Vlissides [1]. Each entry links to a folder containing the source code and a README that discusses intent, structure, participants, consequences and references. + +### Creational patterns + +| Pattern | Intent | +|---|---| +| [Abstract Factory](src/main/java/com/penapereira/example/constructs/abstractfactory/) | Provide an interface for creating families of related objects without naming their concrete classes. | +| [Factory Method](src/main/java/com/penapereira/example/constructs/factorymethod/) | Define an interface for creating an object, but let subclasses decide which class to instantiate. | +| [Factory (Simple Factory)](src/main/java/com/penapereira/example/constructs/factory/) | Centralise the creation of related products behind a single method that selects the concrete class. | +| [Singleton](src/main/java/com/penapereira/example/constructs/singleton/) | Ensure a class has exactly one instance and provide a global point of access to it. | + +### Structural patterns + +| Pattern | Intent | +|---|---| +| [Adapter](src/main/java/com/penapereira/example/constructs/adapter/) | Convert the interface of a class into another interface clients expect. | +| [Decorator](src/main/java/com/penapereira/example/constructs/decorator/) | Attach additional responsibilities to an object dynamically. | + +### Behavioural patterns + +| Pattern | Intent | +|---|---| +| [Chain of Responsibility](src/main/java/com/penapereira/example/constructs/chainofresponsibility/) | Pass a request along a chain of handlers until one of them handles it. | +| [Observer](src/main/java/com/penapereira/example/constructs/observer/) | Define a one-to-many dependency so that dependents are notified when a subject changes state. | +| [Strategy](src/main/java/com/penapereira/example/constructs/strategy/) | Define a family of interchangeable algorithms and let the client choose one at run time. | +| [Template Method](src/main/java/com/penapereira/example/constructs/templatemethod/) | Define the skeleton of an algorithm and defer some steps to subclasses. | + +### Concurrency constructs + +| Construct | Problem | +|---|---| +| [Producer/Consumer](src/main/java/com/penapereira/example/constructs/producerconsumer/) | Coordinate threads that generate data with threads that process it through a bounded, thread-safe buffer. | + +## Design of the host application + +The application exists only to discover, run and display the examples. Its design is deliberately small so that it does not distract from the patterns themselves. + ``` -Additional notes: -* Keep it simple, the idea is to implement the minimum from an educational point of view. -* Try to keep your runtime in less than 1000 milliseconds. -* If you need to output text to the console, try not to print more than 5 lines, you can add a slf4j logger this way and use `trace`: -```java -@Component -public class FooBarExampleRunner implements ExampleRunnerInterface { - private static final Logger log = LoggerFactory.getLogger(FooBarExampleRunner.class); - @Override - public void runExample() { - log.trace("Executing FooBar example:"); - } -} +com.penapereira.example.constructs +├── JavaPatternsAndConstructsApplication Spring Boot entry point (headless mode disabled) +├── app +│ ├── ExampleRunnerInterface Contract every example implements +│ ├── AppCommandLineRunner Opens the main window on the AWT event thread +│ ├── ExamplesCommandLineRunner Optional console runner (app.enableCommandLineRunner) +│ ├── properties +│ │ ├── ApplicationProperties Typed binding of the app.* properties +│ │ └── Messages Typed binding of the msg.* user-facing strings +│ └── ui +│ ├── MainWindow Swing frame: one button per example, output panel +│ ├── OutputSink Interface through which text reaches the window +│ ├── GuiAppender Logback appender that forwards log events to an OutputSink +│ └── HyperlinkMouseListener Opens the project URL in the system browser +├── abstractfactory, adapter, ... One package per pattern or construct +└── producerconsumer ``` + +The moving parts and how they fit together: + +* **Discovery by type.** Every example is a Spring `@Component` implementing `ExampleRunnerInterface`, whose single method is `runExample()`. Both runners ask the `ApplicationContext` for all beans of that type, so adding an example never requires touching the host. The bean name, minus the `ExampleRunner` suffix, becomes the button label. +* **Output through logging.** Examples do not print; they log at `TRACE` through SLF4J. `GuiAppender` registers itself on the Logback root logger at start-up and forwards each formatted message to an `OutputSink`. `MainWindow` implements `OutputSink` by appending to its text area on the AWT event thread. The indirection through an interface keeps the appender testable without a display. +* **Configuration binding.** Margins, colours and all user-visible strings live in `application.properties` and are bound to `ApplicationProperties` and `Messages` through `@ConfigurationProperties`. Lombok's `@Data` generates the accessors. +* **Dependency injection.** Beans receive their collaborators through constructors generated by Lombok's `@RequiredArgsConstructor`. There is no field injection. +* **Threading.** Each example runs on its own thread so a slow example cannot freeze the window. The producer/consumer example is the only one that creates threads of its own, and it shuts them down before returning. + +## Building and quality gates + +| Tool | Role | +|---|---| +| Maven Wrapper 3.9 | Reproducible builds without a local Maven installation | +| Spring Boot 4.1 parent | Dependency management and the `spring-boot:run` goal | +| JUnit Jupiter | Unit tests, one test class per pattern plus one per example runner | +| JaCoCo | Coverage report and enforcement | +| Lombok | Boilerplate generation (`@Data`, `@RequiredArgsConstructor`) | + +`./mvnw verify` compiles, runs the tests and fails the build if instruction coverage drops below 95% or branch coverage below 90%. Only the Swing shell (`MainWindow`, `AppCommandLineRunner` and the application class) is excluded from the measurement, because it cannot be exercised without a display. + +Continuous integration runs on two services: + +* **GitHub Actions** (`.github/workflows/maven.yml`) builds every pull request. On pushes to `master` it additionally refreshes the dependency graph and regenerates the coverage badges in `.github/badges/`, committing them through a short-lived pull request that the workflow merges itself. +* **CircleCI** (`.circleci/config.yml`) runs `mvn verify` on every push as an independent check. + +If you open the project in an IDE, install its Lombok plugin so the generated constructors and accessors resolve. + +## Adding a new example + +The host discovers examples by type, so a new example is a self-contained package plus a test. Follow the existing packages as templates. + +1. **Create the package** `com.penapereira.example.constructs.` under `src/main/java`, using a single lower-case word or compound (for example `chainofresponsibility`). +2. **Implement the pattern** with the fewest classes that still show its structure. Name the classes after the roles in the reference literature (`Product`, `ConcreteProductA`, `Handler`, and so on) so the README can map them directly to the participants. +3. **Add the runner.** A class named `ExampleRunner`, annotated `@Component`, implementing `ExampleRunnerInterface`. Log at `TRACE`, start with a one-line heading and indent the details by two spaces. Keep total output to a handful of lines and the run time under a second. + + ```java + @Component + public class FooBarExampleRunner implements ExampleRunnerInterface { + + private static final Logger log = LoggerFactory.getLogger(FooBarExampleRunner.class); + + @Override + public void runExample() throws Exception { + log.trace("Executing FooBar Pattern Implementation:"); + log.trace(" " + new FooBar().operation()); + } + } + ``` + +4. **Write the tests** under the matching package in `src/test/java`: one class exercising the pattern's behaviour and one asserting that the runner executes without error. The coverage gate applies to new code. +5. **Document it** with a `README.md` in the package following the [documentation conventions](#documentation-conventions), and a class diagram: add the PlantUML source to the README and export it as `assets/images/.png`. +6. **List it** in the [Catalogue](#catalogue) above under the appropriate category. + +If the example needs threads, make sure every thread it starts has finished or been interrupted before `runExample()` returns, so that repeated runs from the window do not leak resources. + +## Documentation conventions + +Each pattern README follows the same outline, adapted from the pattern template of Gamma et al. [1]: + +* **Intent**: the problem the pattern solves, in one or two sentences. +* **Motivation**: the forces that make the naive solution inadequate. +* **Structure**: the class diagram, as an image and as PlantUML source. +* **Participants**: the roles in the pattern and the class in this package that plays each one. +* **The example**: what the runner does and what output to expect. +* **Consequences**: benefits, costs and common misuses. +* **Related patterns** and **References**, with numbered citations in the text. + +## Related projects + +**[java-monitor-example](https://github.com/lpenap/java-monitor-example)** is a companion repository by the same author dedicated to a single concurrency construct: the *monitor*, as formulated by Hoare [5] and Brinch Hansen [6], and its realisation in Java through intrinsic locks, `synchronized` and `wait`/`notifyAll`. It works through mutual exclusion and condition synchronisation on a shared pool of integers consumed exactly once each by competing threads, and visualises the contention in real time. Readers who want to understand what happens inside the `BlockingQueue` used by the [Producer/Consumer](src/main/java/com/penapereira/example/constructs/producerconsumer/) example here should start there. + +## References + +1. E. Gamma, R. Helm, R. Johnson and J. Vlissides, *Design Patterns: Elements of Reusable Object-Oriented Software*. Addison-Wesley, 1994. +2. J. Bloch, *Effective Java*, 3rd ed. Addison-Wesley, 2018. +3. E. Freeman and E. Robson, *Head First Design Patterns*, 2nd ed. O'Reilly, 2020. +4. B. Goetz, T. Peierls, J. Bloch, J. Bowbeer, D. Holmes and D. Lea, *Java Concurrency in Practice*. Addison-Wesley, 2006. +5. C. A. R. Hoare, "Monitors: An Operating System Structuring Concept," *Communications of the ACM*, vol. 17, no. 10, pp. 549–557, 1974. +6. P. Brinch Hansen, *Operating System Principles*. Prentice-Hall, 1973. + +## Contributing + +Issues are welcome for new examples, corrections to the explanations or additional references. Pull requests should target `master`, include tests, keep the coverage gate green and follow the conventions above. If you have an idea but are unsure how to implement it, open an issue describing the pattern or construct and the source you learned it from. diff --git a/src/main/java/com/penapereira/example/constructs/abstractfactory/README.md b/src/main/java/com/penapereira/example/constructs/abstractfactory/README.md index de6918b..1feb287 100644 --- a/src/main/java/com/penapereira/example/constructs/abstractfactory/README.md +++ b/src/main/java/com/penapereira/example/constructs/abstractfactory/README.md @@ -1,8 +1,18 @@ -# Abstract Factory Pattern +# Abstract Factory -The abstract factory pattern provides an interface for creating families of related objects without specifying their concrete classes. This example defines two factories that each create a pair of products. +*Creational pattern. Also known as* Kit. -## Class diagram +## Intent + +Provide an interface for creating families of related or dependent objects without specifying their concrete classes [1, p. 87]. + +## Motivation + +Some systems must be configurable with one of several *families* of products, where the members of a family are designed to be used together. A user interface toolkit that supports several look-and-feel standards is the canonical case: a window, a scroll bar and a button must all belong to the same standard. Instantiating concrete classes throughout the client code makes it hard to guarantee that consistency and hard to switch families later. + +Abstract Factory moves all creation into one object per family. The client is written against the abstract factory and the abstract products only, so an entire family can be exchanged by substituting a single factory instance. + +## Structure ![Class diagram](/assets/images/abstractfactory.png) @@ -30,3 +40,47 @@ class ProductB1 implements ProductB class ProductB2 implements ProductB @enduml ``` + +## Participants + +| Role [1] | Class in this package | Responsibility | +|---|---|---| +| AbstractFactory | `AbstractFactory` | Declares one creation operation per abstract product: `createProductA()`, `createProductB()`. | +| ConcreteFactory | `Factory1`, `Factory2` | Each implements the operations for one family, returning `ProductA1`/`ProductB1` and `ProductA2`/`ProductB2` respectively. | +| AbstractProduct | `ProductA`, `ProductB` | Declare the interface of each kind of product. Here both expose only `name()`. | +| ConcreteProduct | `ProductA1`, `ProductA2`, `ProductB1`, `ProductB2` | The products themselves. The digit denotes the family. | +| Client | `AbstractFactoryExampleRunner` | Uses only the abstract types. | + +## The example + +`AbstractFactoryExampleRunner` creates one instance of each concrete factory and asks each for both of its products, logging the product names. The output shows four products, two per family: + +``` +Executing Abstract Factory Pattern Implementation: + ProductA1 + ProductB1 + ProductA2 + ProductB2 +``` + +Note that the runner never mentions a concrete product class. Replacing `new Factory1()` with `new Factory2()` is the only change needed to switch the whole family. + +## Consequences + +* **Isolates concrete classes.** Product class names appear only inside the concrete factories. +* **Makes exchanging product families easy** and **promotes consistency among products**, since a factory can only produce members of its own family. +* **Supporting new kinds of products is difficult.** Adding a `ProductC` means extending the `AbstractFactory` interface and every concrete factory. The pattern fixes the *set of product kinds* while leaving the *set of families* open, which is the opposite trade-off from Factory Method. + +In standard Java, `javax.xml.parsers.DocumentBuilderFactory` and the other JAXP factories follow this pattern: the client obtains a factory and receives a coherent family of parser objects without knowing the provider. In applications built on a dependency-injection container, the container itself often plays the abstract factory role, which is one reason the pattern appears less often in hand-written code today [2]. + +## Related patterns + +* **Factory Method**: concrete factories are frequently implemented with factory methods, one per product. +* **Singleton**: an application usually needs a single instance of a given concrete factory. +* **Prototype**: an alternative way to implement the concrete factories when families are many. + +## References + +1. E. Gamma, R. Helm, R. Johnson and J. Vlissides, *Design Patterns: Elements of Reusable Object-Oriented Software*. Addison-Wesley, 1994, pp. 87–95. +2. J. Bloch, *Effective Java*, 3rd ed. Addison-Wesley, 2018, Item 1 and Item 5. +3. E. Freeman and E. Robson, *Head First Design Patterns*, 2nd ed. O'Reilly, 2020, ch. 4. diff --git a/src/main/java/com/penapereira/example/constructs/adapter/README.md b/src/main/java/com/penapereira/example/constructs/adapter/README.md index 22c5669..0983607 100644 --- a/src/main/java/com/penapereira/example/constructs/adapter/README.md +++ b/src/main/java/com/penapereira/example/constructs/adapter/README.md @@ -1,8 +1,18 @@ -# Adapter Pattern +# Adapter -The adapter pattern allows incompatible interfaces to work together. It wraps an existing class with a new interface so that it can be used as another type. +*Structural pattern. Also known as* Wrapper. -## Class diagram +## Intent + +Convert the interface of a class into another interface clients expect. Adapter lets classes work together that could not otherwise because of incompatible interfaces [1, p. 139]. + +## Motivation + +A useful class often has an interface that does not match the one a client was written against, and neither can be changed: the client may be third-party code and the class may come from a library. Rewriting either to fit the other is costly or impossible. An adapter sits between them, implementing the interface the client expects and translating each call into the operations the existing class provides. + +Gamma et al. distinguish the **class adapter**, which inherits from both the target and the adaptee, from the **object adapter**, which implements the target and *holds* the adaptee [1, p. 141]. Java's single inheritance makes the object adapter the usual choice, and it is the form shown here. + +## Structure ![Class diagram](/assets/images/adapter.png) @@ -21,3 +31,42 @@ class Adapter implements Target { Adapter --> Adaptee @enduml ``` + +## Participants + +| Role [1] | Class in this package | Responsibility | +|---|---|---| +| Target | `Target` | The domain-specific interface the client uses: `request()`. | +| Client | `AdapterExampleRunner` | Collaborates with objects through `Target` only. | +| Adaptee | `Adaptee` | An existing class with a useful but incompatible operation, `specificRequest()`. | +| Adapter | `Adapter` | Implements `Target` by delegating to the `Adaptee` it was constructed with. | + +## The example + +The runner creates an `Adaptee`, wraps it in an `Adapter` and calls `request()` through the `Target` reference. The adapter's answer makes the delegation visible: + +``` +Executing Adapter Pattern Implementation + Adapter(Adaptee) +``` + +## Consequences + +For the object adapter form used here: + +* **One adapter serves many adaptees.** Because it holds a reference rather than inheriting, the same `Adapter` works with any `Adaptee` or subclass of it. +* **Overriding adaptee behaviour is harder.** Changing what the adaptee does requires subclassing it and passing the subclass in, whereas a class adapter could override directly. +* **How much adapting is needed varies** from renaming a method, as here, to synthesising an entirely different protocol. + +The Java class library contains many adapters. `java.io.InputStreamReader` adapts a byte-oriented `InputStream` to the character-oriented `Reader` interface, and `java.util.Arrays.asList` adapts an array to the `List` interface. Spring MVC's `HandlerAdapter` lets the dispatcher invoke handlers of different shapes through one interface. + +## Related patterns + +* **Bridge** has a similar structure but a different purpose: it separates an abstraction from its implementation up front, whereas Adapter reconciles interfaces after the fact. +* **Decorator** also wraps an object, but preserves its interface and adds behaviour rather than translating. +* **Proxy** wraps an object with the same interface to control access to it. + +## References + +1. E. Gamma, R. Helm, R. Johnson and J. Vlissides, *Design Patterns: Elements of Reusable Object-Oriented Software*. Addison-Wesley, 1994, pp. 139–150. +2. E. Freeman and E. Robson, *Head First Design Patterns*, 2nd ed. O'Reilly, 2020, ch. 7. diff --git a/src/main/java/com/penapereira/example/constructs/chainofresponsibility/README.md b/src/main/java/com/penapereira/example/constructs/chainofresponsibility/README.md index 8ef4061..b2aa409 100644 --- a/src/main/java/com/penapereira/example/constructs/chainofresponsibility/README.md +++ b/src/main/java/com/penapereira/example/constructs/chainofresponsibility/README.md @@ -1,8 +1,16 @@ -# Chain of Responsibility Pattern +# Chain of Responsibility -The chain of responsibility pattern passes a request along a chain of handlers until one of them deals with it. +*Behavioural pattern.* -## Class diagram +## 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 until an object handles it [1, p. 223]. + +## Motivation + +Consider a context-sensitive help system: the object that should answer a help request depends on where the user is and how specific the available help is, and the requester cannot know in advance which object that is. Rather than encoding that knowledge in the sender, each candidate receiver is linked to a successor. A receiver either handles the request or forwards it, so the sender only needs a reference to the first link. + +## Structure ![Class diagram](/assets/images/chainofresponsibility.png) @@ -27,3 +35,46 @@ class PositiveHandler extends AbstractHandler { AbstractHandler --> Handler : next @enduml ``` + +## Participants + +| Role [1] | Class in this package | Responsibility | +|---|---|---| +| Handler | `Handler`, `AbstractHandler` | `Handler` declares `setNext()` and `handle()`. `AbstractHandler` stores the successor and implements the default behaviour: forward if there is a successor, otherwise answer `"unhandled"`. | +| ConcreteHandler | `NegativeHandler`, `ZeroHandler`, `PositiveHandler` | Each handles the requests it is responsible for (negative, zero or positive integers) and defers the rest to `super.handle()`. | +| Client | `ChainOfResponsibilityExampleRunner` | Assembles the chain and sends requests to its first link. | + +Gamma et al. observe that the successor link and the default forwarding are best placed in the abstract handler so that concrete handlers only decide whether to act [1, p. 226]. `AbstractHandler` plays that role here. + +## The example + +The runner links the handlers in the order negative, zero, positive, then sends the requests −1, 0 and 1 to the head of the chain: + +``` +Executing Chain of Responsibility Pattern Implementation + -1 is negative + 0 is zero + 1 is positive +``` + +A request that no handler claims reaches the end of the chain and is reported as `unhandled`. The tests exercise both a request that falls off a shortened chain and a lone handler with no successor. + +## Consequences + +* **Reduced coupling.** The sender knows neither which handler will act nor how many exist. +* **Added flexibility.** Responsibilities can be redistributed by relinking the chain at run time. +* **Receipt is not guaranteed.** A request may fall off the end. The explicit `"unhandled"` result in `AbstractHandler` makes that outcome visible rather than silent. +* **Cost.** Each request may traverse several handlers before finding a taker. + +The literature distinguishes the *pure* form, in which exactly one handler acts and stops the chain, from the *impure* form, in which every handler may act and then pass the request on. This example is pure. Servlet filters (`javax.servlet.FilterChain`), Spring's `HandlerInterceptor` chain and the logger hierarchy of Logback, in which an event propagates to parent loggers' appenders, are impure variants in everyday Java. + +## Related patterns + +* **Composite**: a component's parent can act as its successor, so the chain follows the object tree. +* **Command**: the request travelling down the chain is often reified as a command object. +* **Decorator**: has the same "linked objects of one interface" shape, but every decorator always acts, whereas a handler chooses. + +## References + +1. E. Gamma, R. Helm, R. Johnson and J. Vlissides, *Design Patterns: Elements of Reusable Object-Oriented Software*. Addison-Wesley, 1994, pp. 223–232. +2. E. Freeman and E. Robson, *Head First Design Patterns*, 2nd ed. O'Reilly, 2020, appendix "Leftover Patterns". diff --git a/src/main/java/com/penapereira/example/constructs/decorator/README.md b/src/main/java/com/penapereira/example/constructs/decorator/README.md index 2472b26..5d3c682 100644 --- a/src/main/java/com/penapereira/example/constructs/decorator/README.md +++ b/src/main/java/com/penapereira/example/constructs/decorator/README.md @@ -1,8 +1,16 @@ -# Decorator Pattern +# Decorator -The decorator pattern attaches additional responsibilities to an object dynamically by wrapping it with decorator classes. +*Structural pattern. Also known as* Wrapper. -## Class diagram +## Intent + +Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality [1, p. 175]. + +## Motivation + +Adding a border or a scroll bar to any visual component through inheritance would require one subclass per combination of component and feature, and the choice would be fixed at compile time. Enclosing the component in another object that conforms to the same interface, forwards every request to the component and adds its own behaviour before or after, allows features to be combined freely and chosen at run time. Because the decorator has the same interface as what it wraps, it is transparent to clients and decorators can be nested. + +## Structure ![Class diagram](/assets/images/decorator.png) @@ -19,3 +27,44 @@ class ConcreteDecoratorA extends Decorator ConcreteDecoratorA --> ComponentIF @enduml ``` + +## Participants + +| Role [1] | Class in this package | Responsibility | +|---|---|---| +| Component | `ComponentIF` | The interface shared by objects that can have responsibilities added: `operation()`. | +| ConcreteComponent | `ConcreteComponent` | The object being decorated. | +| Decorator | `Decorator` | Abstract; implements `ComponentIF` and holds a reference to the wrapped component. | +| ConcreteDecorator | `ConcreteDecoratorA` | Adds its responsibility around the delegated call. | + +The interface is named `ComponentIF` rather than `Component` to avoid confusion with Spring's `@Component` stereotype, which the example runners use. + +## The example + +The runner wraps a `ConcreteComponent` in a `ConcreteDecoratorA` and invokes `operation()` on the outer object. The nesting is visible in the result: + +``` +Executing Decorator Pattern Implementation + ConcreteDecoratorA(ConcreteComponent) +``` + +## Consequences + +* **More flexible than static inheritance.** Responsibilities are added and removed at run time, and the same decorator can be applied twice. +* **Avoids feature-laden classes high in the hierarchy.** Functionality is paid for only where it is used. +* **A decorator and its component are not identical.** Code that relies on object identity should not be given decorated objects. +* **Many small objects.** Systems built this way are easy to customise but can be hard to learn and debug. + +The `java.io` stream classes are the standard Java example: `new BufferedInputStream(new FileInputStream(f))` decorates a byte source with buffering, and further wrappers add decompression or checksumming without changing the type the reader sees. `Collections.unmodifiableList` and `Collections.synchronizedList` are decorators as well. Bloch presents the forwarding-class technique behind the pattern as the recommended alternative to inheritance across package boundaries [2, Item 18]. + +## Related patterns + +* **Adapter** changes an object's interface; Decorator keeps it and changes behaviour. +* **Composite**: a decorator is a degenerate composite with one child, but its purpose is augmentation rather than aggregation. +* **Strategy** changes the guts of an object; Decorator changes its skin. + +## References + +1. E. Gamma, R. Helm, R. Johnson and J. Vlissides, *Design Patterns: Elements of Reusable Object-Oriented Software*. Addison-Wesley, 1994, pp. 175–184. +2. J. Bloch, *Effective Java*, 3rd ed. Addison-Wesley, 2018, Item 18. +3. E. Freeman and E. Robson, *Head First Design Patterns*, 2nd ed. O'Reilly, 2020, ch. 3. diff --git a/src/main/java/com/penapereira/example/constructs/factory/README.md b/src/main/java/com/penapereira/example/constructs/factory/README.md index 9c6b0a1..352ce07 100644 --- a/src/main/java/com/penapereira/example/constructs/factory/README.md +++ b/src/main/java/com/penapereira/example/constructs/factory/README.md @@ -1,8 +1,18 @@ -# Factory Pattern +# Factory (Simple Factory) -The factory pattern encapsulates object creation logic in a dedicated factory class. The factory decides which concrete product to instantiate. +*Creational idiom.* -## Class diagram +## Intent + +Centralise the creation of a family of related products behind a single method that decides which concrete class to instantiate, so that clients depend only on the product interface. + +## Motivation + +Code that instantiates concrete classes with `new` is tied to them: adding a product, renaming one or changing how it is built means editing every client. Gathering those decisions into one place removes the duplication and gives the rest of the program a single point through which products are obtained. + +The Simple Factory is not one of the twenty-three patterns catalogued by Gamma et al. Freeman and Robson describe it as "not actually a Design Pattern; it's more of a programming idiom" [3, ch. 4]. It is nevertheless the form most programmers meet first, and it is the stepping stone to the two genuine creational patterns in this collection: the *parameterised factory method* variant of Factory Method [1, p. 110] and Abstract Factory. Bloch's discussion of static factory methods covers the same ground from the API designer's point of view [2, Item 1]. + +## Structure ![Class diagram](/assets/images/factory.png) @@ -20,3 +30,44 @@ ProductFactory --> ConcreteProductA ProductFactory --> ConcreteProductB @enduml ``` + +## Participants + +| Role | Class in this package | Responsibility | +|---|---|---| +| Product | `Product` | The interface every product implements: `name()`. | +| ConcreteProduct | `ConcreteProductA`, `ConcreteProductB` | The classes the factory can instantiate. | +| Factory | `ProductFactory` | `createProduct(type)` maps a type code to a concrete product with a `switch` expression, and rejects unknown codes with `IllegalArgumentException`. | +| Client | `FactoryExampleRunner` | Requests products by type code and uses them through `Product`. | + +## The example + +The runner asks the factory for a product of type `"A"` and one of type `"B"` and logs their names: + +``` +Executing Factory Pattern Implementation: + Concrete Product A + Concrete Product B +``` + +The tests also cover the rejection of an unknown type. + +## Consequences + +* **Creation is encapsulated** in one class, and clients are written against `Product` alone. +* **The factory violates the open-closed principle** [4]: supporting a new product requires modifying the `switch`. Factory Method addresses this through subclassing and Abstract Factory through substituting whole factories, which is precisely what distinguishes them from the idiom. +* **The selection is data-driven**, which is convenient when the type code comes from configuration or user input. + +Since Java 14 a `switch` *expression* must be exhaustive, which is why the `default` branch is required here. If the type codes were modelled as an `enum` or the products as a `sealed` hierarchy (Java 17), the compiler could verify that every case is handled and the runtime exception would become unnecessary. + +## Related patterns + +* **Factory Method** moves the decision into subclasses of a creator. +* **Abstract Factory** groups several factory operations into one object per product family. + +## References + +1. E. Gamma, R. Helm, R. Johnson and J. Vlissides, *Design Patterns: Elements of Reusable Object-Oriented Software*. Addison-Wesley, 1994, pp. 107–116. +2. J. Bloch, *Effective Java*, 3rd ed. Addison-Wesley, 2018, Item 1. +3. E. Freeman and E. Robson, *Head First Design Patterns*, 2nd ed. O'Reilly, 2020, ch. 4. +4. B. Meyer, *Object-Oriented Software Construction*. Prentice Hall, 1988, §2.3 (the open-closed principle). diff --git a/src/main/java/com/penapereira/example/constructs/factorymethod/ConcreteProductA.java b/src/main/java/com/penapereira/example/constructs/factorymethod/ConcreteProductA.java index a62e9ed..c898c8e 100644 --- a/src/main/java/com/penapereira/example/constructs/factorymethod/ConcreteProductA.java +++ b/src/main/java/com/penapereira/example/constructs/factorymethod/ConcreteProductA.java @@ -4,6 +4,6 @@ public class ConcreteProductA extends GenericProduct { @Override public String factoryMethod() { - return "ConcretepProduct A"; + return "Concrete Product A"; } } diff --git a/src/main/java/com/penapereira/example/constructs/factorymethod/ConcreteProductB.java b/src/main/java/com/penapereira/example/constructs/factorymethod/ConcreteProductB.java index 062977c..8fae4c7 100644 --- a/src/main/java/com/penapereira/example/constructs/factorymethod/ConcreteProductB.java +++ b/src/main/java/com/penapereira/example/constructs/factorymethod/ConcreteProductB.java @@ -4,6 +4,6 @@ public class ConcreteProductB extends GenericProduct { @Override public String factoryMethod() { - return "ConcretepProduct B"; + return "Concrete Product B"; } } diff --git a/src/main/java/com/penapereira/example/constructs/factorymethod/README.md b/src/main/java/com/penapereira/example/constructs/factorymethod/README.md index 8040ebf..b329162 100644 --- a/src/main/java/com/penapereira/example/constructs/factorymethod/README.md +++ b/src/main/java/com/penapereira/example/constructs/factorymethod/README.md @@ -1,8 +1,16 @@ -# Factory Method Pattern +# Factory Method -The factory method pattern defines an interface for creating an object but lets subclasses decide which class to instantiate. +*Creational pattern. Also known as* Virtual Constructor. -## Class diagram +## Intent + +Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses [1, p. 107]. + +## Motivation + +A framework often knows *when* an object must be created but not *which* class it should be, because that class is application-specific. The framework class therefore declares a creation operation, calls it at the appropriate point in its own algorithms, and leaves its implementation to subclasses supplied by the application. Gamma et al. remark that "Factory Methods are usually called within Template Methods" [1, p. 116], and that relationship is the essence of this example. + +## Structure ![Class diagram](/assets/images/factorymethod.png) @@ -20,3 +28,44 @@ class ConcreteProductB extends GenericProduct { } @enduml ``` + +## Participants + +| Role [1] | Class in this package | Responsibility | +|---|---|---| +| Creator | `GenericProduct` | Declares the abstract `factoryMethod()` and calls it from `build()`, its template operation. | +| ConcreteCreator | `ConcreteProductA`, `ConcreteProductB` | Override `factoryMethod()` to decide what is created. | +| Product | the value returned by `factoryMethod()` | In this reduced example the product is represented by a `String` naming what was built, so the product hierarchy of the canonical diagram collapses to a value. | + +The class names emphasise the creator hierarchy; readers comparing this with the textbook structure should map `GenericProduct` to *Creator* rather than to *Product*. + +## The example + +The runner instantiates both concrete creators through the `GenericProduct` type and calls `build()` on each. The base class logs what the subclass decided to produce: + +``` +Executing Factory Method pattern implementation: + Building Concrete Product A + Building Concrete Product B +``` + +## Consequences + +* **Eliminates the need to bind application-specific classes into framework code.** The base class works with whatever the subclass returns. +* **Provides hooks for subclasses.** A factory method is a natural extension point. +* **Connects parallel class hierarchies**, letting a creator hierarchy mirror a product hierarchy. +* **Clients may have to subclass just to create a product**, which is the pattern's main drawback when the creator hierarchy does not already exist. + +`Iterable.iterator()` is the most pervasive factory method in Java: every collection decides which `Iterator` implementation to instantiate, and clients never name it. Bloch's *static factory method* [2, Item 1] is a different technique despite the similar name: it is a static method with no subclassing involved, closer to the Simple Factory idiom in this collection. + +## Related patterns + +* **Template Method**: the operation that calls the factory method is typically a template method, as `build()` is here. +* **Abstract Factory**: often implemented with factory methods. +* **Prototype**: avoids subclassing the creator by cloning a prototype instead. + +## References + +1. E. Gamma, R. Helm, R. Johnson and J. Vlissides, *Design Patterns: Elements of Reusable Object-Oriented Software*. Addison-Wesley, 1994, pp. 107–116. +2. J. Bloch, *Effective Java*, 3rd ed. Addison-Wesley, 2018, Item 1. +3. E. Freeman and E. Robson, *Head First Design Patterns*, 2nd ed. O'Reilly, 2020, ch. 4. diff --git a/src/main/java/com/penapereira/example/constructs/observer/README.md b/src/main/java/com/penapereira/example/constructs/observer/README.md index f653cc0..6cccbe2 100644 --- a/src/main/java/com/penapereira/example/constructs/observer/README.md +++ b/src/main/java/com/penapereira/example/constructs/observer/README.md @@ -1,8 +1,16 @@ -# Observer Pattern +# Observer -The observer pattern defines a one-to-many dependency so that when one object changes state, its dependents are notified automatically. +*Behavioural pattern. Also known as* Dependents, Publish-Subscribe. -## Class diagram +## Intent + +Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically [1, p. 293]. + +## Motivation + +A spreadsheet cell and the charts drawn from it must stay consistent, yet the cell should not know which charts exist or how they render. Observer separates the *subject* holding the state from the *observers* interested in it. Observers register with the subject; the subject notifies every registered observer when its state changes and each observer pulls or receives whatever it needs. The subject depends only on an abstract observer interface, so observers can be added without modifying it. + +## Structure ![Class diagram](/assets/images/observer.png) @@ -23,3 +31,47 @@ class Observer implements PropertyChangeListener { Observable --> Observer : notifies @enduml ``` + +## Participants + +| Role [1] | Class in this package | Responsibility | +|---|---|---| +| Subject | `ObservableInterface`, `ObservableAbstract` | Know their observers and offer registration. `ObservableAbstract` delegates the bookkeeping to a `java.beans.PropertyChangeSupport` and adds `removeAllListeners()`. | +| ConcreteSubject | `Observable` | Holds the state of interest. `doSomethingWith(n)` derives a new value and fires a `"myProperty"` change event. | +| Observer | `java.beans.PropertyChangeListener` | The notification interface, taken from the standard library. | +| ConcreteObserver | `Observer` | Logs the property name and its old and new values. | + +The example uses the *push* model: the event carries the old and new values, so observers need not query the subject. `PropertyChangeSupport` is the JavaBeans realisation of the pattern [2] and remains the idiomatic choice for in-process observation. The older `java.util.Observable` is deprecated since Java 9 because it is a class rather than an interface, offers no thread-safety guarantees and does not specify notification order [3]. + +## The example + +The runner creates two subjects and one observer, registers the observer with both, and triggers a change on each: + +``` +Executing Observer pattern: + Property updated!. "myProperty": 5->7 + Property updated!. "myProperty": 10->16 +``` + +The new value is the old one plus a random increment, so the exact numbers vary between runs. A subtlety worth knowing: `PropertyChangeSupport.firePropertyChange` suppresses the event when the old and new values are equal, so on the rare run where the random increment is zero no line is printed. The unit test accounts for this by retrying until an event is observed. + +## Consequences + +* **Abstract coupling** between subject and observer: the subject knows only that it has a list of `PropertyChangeListener`s. +* **Support for broadcast.** The subject does not care how many observers there are. +* **Unexpected updates.** Because observers are ignorant of one another, a seemingly innocent change can cascade, and the cost of an update is not visible at the call site. +* **The lapsed-listener problem.** An observer that is never unregistered stays reachable from the subject and cannot be garbage collected. `removeAllListeners()` exists so a subject can release its observers when it is done with them. + +Swing's event listeners, Spring's `ApplicationEvent` mechanism and the `java.util.concurrent.Flow` interfaces introduced in Java 9 for reactive streams are all instances of the pattern at different scales. + +## Related patterns + +* **Mediator**: when the update logic between many subjects and observers becomes complex, a mediator can centralise it. +* **Singleton**: a mediator or change manager is often unique. + +## References + +1. E. Gamma, R. Helm, R. Johnson and J. Vlissides, *Design Patterns: Elements of Reusable Object-Oriented Software*. Addison-Wesley, 1994, pp. 293–303. +2. G. Hamilton (ed.), *JavaBeans API Specification*, version 1.01. Sun Microsystems, 1997, §7 (Properties, bound properties). +3. Oracle, *Java Platform SE API Specification*, class `java.util.Observable`, deprecation note. +4. E. Freeman and E. Robson, *Head First Design Patterns*, 2nd ed. O'Reilly, 2020, ch. 2. diff --git a/src/main/java/com/penapereira/example/constructs/producerconsumer/README.md b/src/main/java/com/penapereira/example/constructs/producerconsumer/README.md index 711fba0..4fc3909 100644 --- a/src/main/java/com/penapereira/example/constructs/producerconsumer/README.md +++ b/src/main/java/com/penapereira/example/constructs/producerconsumer/README.md @@ -1,10 +1,14 @@ -# Producer / Consumer +# Producer/Consumer -The Producer/Consumer is a concurrency model where producer threads generate data and place it into a shared, thread-safe buffer (like a queue), while consumer threads retrieve and process that data. It helps decouple the creation and processing of data, allowing both to operate at different speeds. Synchronization mechanisms like locks or semaphores are used to ensure safe access to the buffer, preventing race conditions and deadlocks. This pattern is commonly used in logging systems, task queues, and real-time processing pipelines to improve scalability and responsiveness in multithreaded applications. +*Concurrency construct. Also known as the* bounded-buffer problem. -In this example implementation a java `BlockingQueue` is used as the thread-safe buffer while a thread pool of 2 consumers and 1 producer is launched through the `Executors` factory. +## Problem -## Class diagram +Two kinds of threads share a buffer of finite capacity. *Producers* generate items and place them in the buffer; *consumers* remove items and process them. Correctness requires that a producer wait while the buffer is full, that a consumer wait while it is empty, and that concurrent access never corrupts the buffer or loses or duplicates an item. + +The problem was posed by Dijkstra in 1965 as the motivating example for semaphores [1], and the same year's work on mutual exclusion frames the general difficulty of coordinating cooperating sequential processes. Hoare [2] and Brinch Hansen [3] later introduced the *monitor*, which bundles the shared data, the operations on it and the condition synchronisation into one construct, and the bounded buffer is the standard illustration in both papers. + +## Structure ![Class diagram](/assets/images/producerconsumer.png) @@ -20,3 +24,51 @@ Producer --> BlockingQueue Consumer --> BlockingQueue @enduml ``` + +## Participants + +| Role | Class in this package | Responsibility | +|---|---|---| +| Producer | `Producer` | Puts the integers 1 to 3 into the queue. `put()` blocks while the queue is full. If interrupted, it restores the interrupt flag and returns. | +| Consumer | `Consumer` | Repeatedly `take()`s an item and logs it. `take()` blocks while the queue is empty. The loop ends when the thread is interrupted. | +| Buffer | `java.util.concurrent.LinkedBlockingDeque` (capacity 3) | A `BlockingQueue` implementation that provides the mutual exclusion and the two wait conditions. | +| Coordinator | `ProducerConsumerExampleRunner` | Creates a fixed pool of three threads, starts two consumers and one producer, waits for the producer to finish and the queue to drain, then interrupts the consumers and shuts the pool down. | + +## The example + +``` +Executing Producer/Consumer implementation: + 1: Consumed [ 1] + 2: Consumed [ 2] + 1: Consumed [ 3] +``` + +Which consumer takes which item is decided by thread scheduling and differs between runs. What does not vary is that each item is consumed exactly once and that `runExample()` returns only after every thread it started has stopped, so the example can be run repeatedly from the window without leaking threads. + +## How the buffer works + +`BlockingQueue`, added in Java 5 with the `java.util.concurrent` package designed by Lea and specified through JSR 166 [4, 5], encapsulates the monitor. Inside `LinkedBlockingDeque` a `ReentrantLock` provides mutual exclusion and two `Condition` objects, one signalled when the deque becomes non-empty and one when it becomes non-full, provide the condition synchronisation. `put()` acquires the lock, waits on the *not full* condition while the deque is at capacity, links the item and signals *not empty*; `take()` is its mirror image. That is Hoare's monitor with condition variables, realised in library code. + +**Companion project.** The repository [java-monitor-example](https://github.com/lpenap/java-monitor-example) by the same author implements this coordination by hand, using Java's intrinsic locks with `synchronized`, `wait()` and `notifyAll()`, and visualises threads contending for a shared pool of integers in real time. It is the recommended next step for readers who want to see what `BlockingQueue` hides. + +## Consequences and design notes + +* **Decoupling of rates.** Producers and consumers run at their own pace; the buffer absorbs short-term differences. +* **Backpressure.** A *bounded* buffer makes a fast producer wait rather than exhaust memory. Goetz et al. recommend bounded queues by default for exactly this reason [4, §5.3]. +* **Thread safety by delegation.** No class in this package contains a lock; safety is delegated entirely to the queue, which is the simplest correct design. +* **Shutdown by interruption.** A consumer blocked in `take()` has no natural end. The coordinator uses `ExecutorService.shutdownNow()`, which interrupts the pool threads; `Consumer` treats interruption as the request to stop, and `Producer` propagates it by re-asserting the flag. This follows the cancellation policy described by Goetz et al. [4, ch. 7]. The alternative is a *poison pill*, a sentinel item that tells a consumer to exit. +* **Lost signals** and **spurious wake-ups**, the classic hazards of hand-written monitors, cannot occur here because the queue's implementation handles them. + +## Related constructs + +* **Monitor** (see the companion project above): the underlying synchronisation construct. +* **Semaphore**: Dijkstra's original solution uses two counting semaphores, *empty* and *full*, plus a binary semaphore for mutual exclusion; `java.util.concurrent.Semaphore` allows the same construction. +* **Observer**: an asynchronous variant in which consumers are notified rather than blocked. + +## References + +1. E. W. Dijkstra, "Cooperating Sequential Processes," EWD 123, Technological University Eindhoven, 1965. Reprinted in F. Genuys (ed.), *Programming Languages*, Academic Press, 1968, pp. 43–112. +2. C. A. R. Hoare, "Monitors: An Operating System Structuring Concept," *Communications of the ACM*, vol. 17, no. 10, pp. 549–557, 1974. +3. P. Brinch Hansen, *Operating System Principles*. Prentice-Hall, 1973. +4. B. Goetz, T. Peierls, J. Bloch, J. Bowbeer, D. Holmes and D. Lea, *Java Concurrency in Practice*. Addison-Wesley, 2006, ch. 5 and ch. 7. +5. D. Lea, *Concurrent Programming in Java: Design Principles and Patterns*, 2nd ed. Addison-Wesley, 1999. diff --git a/src/main/java/com/penapereira/example/constructs/singleton/README.md b/src/main/java/com/penapereira/example/constructs/singleton/README.md index 7f99a5a..731710f 100644 --- a/src/main/java/com/penapereira/example/constructs/singleton/README.md +++ b/src/main/java/com/penapereira/example/constructs/singleton/README.md @@ -1,8 +1,16 @@ -# Singleton Pattern +# Singleton -The singleton pattern ensures a class has only one instance while providing a global point of access to it. +*Creational pattern.* -## Class diagram +## Intent + +Ensure a class has only one instance, and provide a global point of access to it [1, p. 127]. + +## Motivation + +Some objects must be unique: a print spooler, a window manager, a registry of configuration. A global variable makes the instance accessible but does not stop a second one from being created. Making the class itself responsible for its sole instance solves both problems: the constructor is hidden, and a class operation creates the instance on first use and returns it thereafter. + +## Structure ![Class diagram](/assets/images/singleton.png) @@ -16,3 +24,47 @@ class Singleton { } @enduml ``` + +## Participants + +| Role [1] | Class in this package | Responsibility | +|---|---|---| +| Singleton | `Singleton` | Private constructor, private static `uniqueInstance`, and the static `instance()` operation that lazily creates and returns it. `doSomething()` stands for the instance's real responsibilities. | + +## The example + +The runner obtains the instance through `instance()` and calls `doSomething()`. The test asserts that two calls to `instance()` return the same object. + +``` +Instantiating a Singleton +``` + +## Consequences + +* **Controlled access to the sole instance** and **reduced namespace pollution** compared with a global variable. +* **Permits refinement.** The class can be subclassed and the subclass chosen at run time inside `instance()`. +* **Permits a variable number of instances** by changing only `instance()`, should the requirement change. +* **Hidden dependencies and global state.** Every class that calls `Singleton.instance()` depends on it invisibly, which hampers testing and substitution. This is why the pattern is often called an anti-pattern in modern practice, and why dependency-injection containers prefer to manage uniqueness as a *scope* instead. The Spring context hosting these examples treats every `@Component` as a singleton in exactly this sense, without any of the classes implementing the pattern. + +## A note on thread safety + +The lazy initialisation in `instance()` is the textbook form and is **not thread-safe**. Two threads that both observe `uniqueInstance == null` before either assigns it will create two instances: a check-then-act race [3, §2.2]. The example is written this way to show the canonical structure. The correct alternatives in Java are: + +1. **Eager initialisation**: `private static final Singleton INSTANCE = new Singleton();`. The Java Language Specification guarantees that class initialisation is performed exactly once and is visible to all threads [4, §12.4.2]. +2. **The initialisation-on-demand holder idiom**: place the field in a private static nested class, so that the JVM's class-initialisation guarantee provides lazy, thread-safe creation without explicit locking [3, §16.2.3]. +3. **An enum with a single constant** [2, Item 3]: concise, serialisation-safe and immune to reflective attacks on the private constructor. +4. **Double-checked locking** with a `volatile` field, which is correct only under the memory model introduced by JSR 133 in Java 5 [5]. The pre-2004 form was famously shown to be broken [6]. + +## Related patterns + +* **Abstract Factory**, **Builder** and **Prototype**: the factories these patterns introduce are frequently singletons. +* **Facade**: usually a singleton. + +## References + +1. E. Gamma, R. Helm, R. Johnson and J. Vlissides, *Design Patterns: Elements of Reusable Object-Oriented Software*. Addison-Wesley, 1994, pp. 127–134. +2. J. Bloch, *Effective Java*, 3rd ed. Addison-Wesley, 2018, Item 3. +3. B. Goetz, T. Peierls, J. Bloch, J. Bowbeer, D. Holmes and D. Lea, *Java Concurrency in Practice*. Addison-Wesley, 2006, §2.2 and §16.2. +4. J. Gosling, B. Joy, G. Steele, G. Bracha, A. Buckley, D. Smith and G. Bierman, *The Java Language Specification*, Java SE 25 edition, §12.4. +5. J. Manson, W. Pugh and S. V. Adve, "The Java Memory Model," in *Proc. 32nd ACM SIGPLAN-SIGACT Symposium on Principles of Programming Languages (POPL '05)*, 2005, pp. 378–391. +6. D. Bacon et al., "The 'Double-Checked Locking is Broken' Declaration," 2001. Available online. diff --git a/src/main/java/com/penapereira/example/constructs/singleton/SingletonExampleRunner.java b/src/main/java/com/penapereira/example/constructs/singleton/SingletonExampleRunner.java index ac8f22b..e3511b5 100644 --- a/src/main/java/com/penapereira/example/constructs/singleton/SingletonExampleRunner.java +++ b/src/main/java/com/penapereira/example/constructs/singleton/SingletonExampleRunner.java @@ -13,7 +13,7 @@ public class SingletonExampleRunner implements ExampleRunnerInterface { @Override public void runExample() throws Exception { - log.trace("Instanciating a Singleton"); + log.trace("Instantiating a Singleton"); Singleton myInstance = Singleton.instance(); myInstance.doSomething(); diff --git a/src/main/java/com/penapereira/example/constructs/strategy/README.md b/src/main/java/com/penapereira/example/constructs/strategy/README.md index 01d1eb6..ee4b650 100644 --- a/src/main/java/com/penapereira/example/constructs/strategy/README.md +++ b/src/main/java/com/penapereira/example/constructs/strategy/README.md @@ -1,8 +1,16 @@ -# Strategy Pattern +# Strategy -The strategy pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable at runtime. +*Behavioural pattern. Also known as* Policy. -## Class diagram +## Intent + +Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it [1, p. 315]. + +## Motivation + +A text composer may break lines with several algorithms of differing cost and quality. Hard-wiring them into the composer makes it larger, harder to maintain and impossible to extend without editing. Extracting each algorithm into its own class behind a common interface lets the composer hold a reference to *some* strategy and delegate to it, so algorithms can be added, removed or switched at run time without the composer changing. + +## Structure ![Class diagram](/assets/images/strategy.png) @@ -21,3 +29,44 @@ class Context { Context --> Strategy @enduml ``` + +## Participants + +| Role [1] | Class in this package | Responsibility | +|---|---|---| +| Strategy | `Strategy` | The interface common to all algorithms: `executeAlgorithm()`. | +| ConcreteStrategy | `StrategyImpl1`, `StrategyImpl2` | Two interchangeable algorithms. | +| Context | `Context` | Configured with a strategy at construction, exposes `setStrategy()` to change it, and delegates to it from `operation()`. | +| Client | `StrategyExampleRunner` | Chooses the strategies and hands them to the context. | + +## The example + +The runner builds a context with the first strategy, invokes the operation, swaps in the second strategy and invokes it again: + +``` +Executing Strategy Pattern Implementation + Operation with --> Algorithm from Strategy Implementation 1 + Operation with ==> Algorithm from Strategy Implementation 2 +``` + +## Consequences + +* **Families of related algorithms** can be organised and reused independently of the context. +* **An alternative to subclassing the context**, which would fix the algorithm at compile time and mix it with the context's other responsibilities. +* **Eliminates conditional statements** that would otherwise select behaviour. +* **Clients must be aware of the strategies** in order to choose one, which exposes implementation detail. +* **Communication overhead and object proliferation.** Every strategy shares one interface even if some need less information, and each algorithm is a class. + +Since Java 8 a single-method strategy interface is a *functional interface*, so a lambda or method reference can serve as a concrete strategy without a named class. `java.util.Comparator` passed to `List.sort` is the everyday instance: the sort is the context, the comparator the strategy. Bloch discusses this collapse of the pattern's class count as one of the main benefits of lambdas [2, Item 42]. `Strategy` in this package is deliberately kept as an explicit interface with named implementations so that the structure is visible. + +## Related patterns + +* **Template Method** varies steps of an algorithm through inheritance; Strategy varies the whole algorithm through composition. +* **State** has the same structure, but the context changes its state object as a consequence of its own behaviour, whereas a strategy is chosen by the client. +* **Flyweight**: stateless strategies can be shared. + +## References + +1. E. Gamma, R. Helm, R. Johnson and J. Vlissides, *Design Patterns: Elements of Reusable Object-Oriented Software*. Addison-Wesley, 1994, pp. 315–323. +2. J. Bloch, *Effective Java*, 3rd ed. Addison-Wesley, 2018, Item 42. +3. E. Freeman and E. Robson, *Head First Design Patterns*, 2nd ed. O'Reilly, 2020, ch. 1. diff --git a/src/main/java/com/penapereira/example/constructs/templatemethod/README.md b/src/main/java/com/penapereira/example/constructs/templatemethod/README.md index 8eb2c4f..35744c2 100644 --- a/src/main/java/com/penapereira/example/constructs/templatemethod/README.md +++ b/src/main/java/com/penapereira/example/constructs/templatemethod/README.md @@ -1,8 +1,16 @@ -# Template Method Pattern +# Template Method -The template method pattern defines the skeleton of an algorithm in a base class and lets subclasses override specific steps. +*Behavioural pattern.* -## Class diagram +## Intent + +Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure [1, p. 325]. + +## Motivation + +An application framework that opens a document must always perform the same sequence, such as checking the file, creating the document object, adding it to the open set and reading its contents, but only the application knows how to create and read *its* documents. Writing the sequence once in an abstract base class, with the variable steps declared abstract, fixes the order and reuses the invariant parts while leaving the specifics to subclasses. The base class calls the subclass, not the other way round, which Freeman and Robson call the Hollywood principle: "don't call us, we'll call you" [3, ch. 8]. + +## Structure ![Class diagram](/assets/images/templatemethod.png) @@ -23,3 +31,43 @@ class ConcreteClassB extends AbstractClass { } @enduml ``` + +## Participants + +| Role [1] | Class in this package | Responsibility | +|---|---|---| +| AbstractClass | `AbstractClass` | `templateMethod()` fixes the algorithm as `stepOne()` followed by `stepTwo()`. Both steps are abstract *primitive operations*. | +| ConcreteClass | `ConcreteClassA`, `ConcreteClassB` | Implement the primitive operations. | + +Two details of the implementation carry meaning. `templateMethod()` is declared `final`, following the advice that the template method itself should not be overridable, so that subclasses can vary the steps but not the skeleton [1, p. 328]. The primitive operations are `protected`, which signals that they exist to be overridden and are not part of the public interface. + +## The example + +The runner instantiates each concrete class through the abstract type and calls the template method: + +``` +Executing Template Method Pattern Implementation + A step one then A step two + B step one then B step two +``` + +The word "then" comes from the base class, the rest from the subclasses. + +## Consequences + +* **Code reuse.** The invariant part of the algorithm is written once. +* **Inverted control structure.** The parent class calls operations of the subclass. Subclass authors must understand which operations are *hooks* (may be overridden, often with a default) and which are *abstract* (must be overridden). +* **Rigidity.** The variation is chosen at compile time through inheritance. When the steps must be swappable at run time, or when a class would need to vary along several independent axes, Strategy's composition-based approach is preferable. Bloch's advice to favour composition over inheritance and to document a class's self-use pattern if it is designed for inheritance both bear directly on this pattern [2, Items 18 and 19]. + +`java.util.AbstractList` is a large-scale template: `iterator()`, `indexOf()` and the rest are written in terms of the abstract `get(int)` and `size()`. `javax.servlet.http.HttpServlet.service()` dispatches to `doGet()`, `doPost()` and friends in the same way, and `java.io.InputStream.read(byte[], int, int)` is implemented by repeated calls to the abstract single-byte `read()`. + +## Related patterns + +* **Factory Method** is often called from within a template method; the Factory Method example in this collection is structured that way. +* **Strategy** uses delegation to vary the entire algorithm; Template Method uses inheritance to vary part of it. + +## References + +1. E. Gamma, R. Helm, R. Johnson and J. Vlissides, *Design Patterns: Elements of Reusable Object-Oriented Software*. Addison-Wesley, 1994, pp. 325–330. +2. J. Bloch, *Effective Java*, 3rd ed. Addison-Wesley, 2018, Items 18–20. +3. E. Freeman and E. Robson, *Head First Design Patterns*, 2nd ed. O'Reilly, 2020, ch. 8. diff --git a/src/test/java/com/penapereira/example/constructs/factorymethod/FactoryMethodTests.java b/src/test/java/com/penapereira/example/constructs/factorymethod/FactoryMethodTests.java index b87ba00..738d9f9 100644 --- a/src/test/java/com/penapereira/example/constructs/factorymethod/FactoryMethodTests.java +++ b/src/test/java/com/penapereira/example/constructs/factorymethod/FactoryMethodTests.java @@ -8,8 +8,8 @@ class FactoryMethodTests { void productsReturnName() { GenericProduct a = new ConcreteProductA(); GenericProduct b = new ConcreteProductB(); - assertEquals("ConcretepProduct A", a.factoryMethod()); - assertEquals("ConcretepProduct B", b.factoryMethod()); + assertEquals("Concrete Product A", a.factoryMethod()); + assertEquals("Concrete Product B", b.factoryMethod()); a.build(); b.build(); }