Designing Software  «Prev  Next»
Lesson 4Lessons learned from patterns
ObjectiveExamine how Patterns can be used in unexpected Ways

Lessons Learned from Design Patterns

One way patterns help is by introducing you to new tricks and techniques that can be used in other places, even though the patterns may not even be used.

alert dialog
Alert dialog

For example, one of the most difficult problems for students learning to program in Java is to write a class, that brings up a standard alert dialog with a single method. Furthermore, I specify that the class should use only this one public method that sets the message.
This makes the class extremely simple to use for client programmers. Most students begin by extending Java's Dialog class. However they rapidly discover that this requires them to override and expose many public methods. They tend to end up with results like this:

AlertDialog a = new AlertDialog("That action is forbidden");
a.init();
a.show();

This is just too complicated. The correct, simpler solution (at least simpler for the client programmer who actually has to use this class) is to wrap a Dialog object inside another class, then construct it and display it through a static method like this:

Message.alert("That action is forbidden");

The use of a static method to create instances that are otherwise hidden in an object's private areas is found in almost every creational pattern including Singleton and Factory Method. This solution does not use the Singleton or Factory Method patterns, but it uses the concepts learned from these patterns.

Lessons learned from Design Patterns

Actually, if creating new dialogs is expensive, but changing the message they display is not, you might indeed want to use the Singleton pattern here. You might need to modify it if it is possible for two different dialogs to be displayed simultaneously. With the Singleton design pattern you can:
  1. Ensure that only one instance of a class is created
  2. Provide a global point of access to the object
  3. Allow multiple instances in the future without affecting a singleton class's clients
Ensure a class has only one instance, and provide a global point of access to it.
The figure below illustrates the Singleton design pattern class diagram.

Singleton Design Pattern
Singleton Design Pattern

As you can see from the figure above, the Singleton design pattern is relatively simple. Singletons maintain a static reference to the sole singleton instance and return a reference to that instance from a static instance() method.
Example 1 shows a classic Singleton design pattern implementation:

Example 1. The classic singleton
public class ClassicSingleton {
   private static ClassicSingleton instance = null;
   protected ClassicSingleton() {
      // Exists only to defeat instantiation.
   }
   public static ClassicSingleton getInstance() {
      if(instance == null) {
         instance = new ClassicSingleton();
      }
      return instance;
   }
}

JavaFX Design Patterns

JavaFX, a framework for building rich client applications, incorporates a multitude of design patterns to facilitate development, manage complexity, and ensure software quality. These design patterns are not unique to JavaFX but are adapted to its specific architecture and needs.
  1. MVC (Model-View-Controller): JavaFX naturally aligns with the MVC pattern, separating the presentation layer (View), the data (Model), and the logic to glue both (Controller). The FXML files often represent the View, Java classes serve as Controllers, and POJOs (Plain Old Java Objects) or properties act as the Model. This separation enhances maintainability and testability.
  2. Singleton Pattern: JavaFX uses the Singleton pattern for specific services, ensuring that only a single instance exists for a given class. For example, the `Toolkit` and `Platform` classes follow this pattern to provide a unified point of access to various underlying services.
  3. Factory Method: JavaFX employs the Factory Method pattern to create instances of objects without specifying their concrete classes. This is particularly useful for instantiating appropriate platform-specific implementations of certain features. For instance, the `Shape` class and its subclasses like `Circle`, `Rectangle`, etc., often employ factory methods for object creation.
  4. Observer Pattern: JavaFX's property and binding mechanisms are a quintessential example of the Observer pattern. Components can observe changes in properties and get notified when a property changes, thereby promoting a decoupled design.
  5. Composite Pattern: JavaFX employs the Composite pattern in its scene graph architecture, where a `Parent` node can contain multiple child nodes, and each child node can also be a parent. This forms a tree-like hierarchy where operations can be performed on both individual nodes and composites, treating both uniformly.
  6. Command Pattern: The framework leverages the Command pattern through its event handling mechanism. For example, actions like button clicks or key presses trigger events that are encapsulated as objects and can be handled in a decoupled manner.
  7. Builder Pattern: To facilitate the creation of complex objects with numerous optional parameters, JavaFX offers a Builder pattern for classes like `Font`, `Image`, and many UI components. This enhances code readability and maintainability.
  8. Decorator Pattern: JavaFX uses the Decorator pattern to add responsibilities to objects dynamically without altering their structure. For instance, layout managers like `HBox` or `VBox` decorate their children nodes by adding positional logic while leaving the original nodes unmodified.
  9. Adapter Pattern: JavaFX often utilizes the Adapter pattern to allow incompatible interfaces to work together. This is particularly useful when integrating with older Java libraries that do not use JavaFX properties and bindings.
In conclusion, JavaFX is a showcase of effective design pattern utilization, each serving specific architectural needs. These patterns not only assist developers in writing robust and maintainable code but also epitomize best practices in object-oriented software design.