Detailed description of Java design patterns (3)

 1. Decorator pattern

The Decorator Pattern allows adding new functionality to an existing object without changing its structure. This type of design pattern is a structural pattern, which acts as a wrapper around an existing class.

scenes to be used:

  • Add responsibilities to individual objects dynamically and transparently without affecting other objects.

  • Functions need to be added to an object dynamically, and these functions can also be revoked dynamically. When the system cannot be expanded through inheritance or inheritance is not conducive to system expansion and maintenance.

This pattern creates a decorator class that wraps the original class and provides additional functionality while maintaining the integrity of the class method signature.

Example:

interface Coffee {
    double getCost();
    String getIngredients();
}

class SimpleCoffee implements Coffee {
    @Override
    public double getCost() {
        return 1;
    }

    @Override
    public String getIngredients() {
        return "Coffee";
    }
}

abstract class CoffeeDecorator implements Coffee {
    private final Coffee decoratedCoffee;

    public CoffeeDecorator(Coffee coffee) {
        this.decoratedCoffee = coffee;
    }

    @Override
    public double getCost() {
        return decoratedCoffee.getCost();
    }

    @Override
    public String getIngredients() {
        return decoratedCoffee.getIngredients();
    }
}

class MilkCoffee extends CoffeeDecorator {
    public MilkCoffee(Coffee coffee) {
        super(coffee);
    }

    @Override
    public double getCost() {
        return super.getCost() + 0.5;
    }

    @Override
    public String getIngredients() {
        return super.getIngredients() + ", Milk";
    }
}

class WhipCoffee extends CoffeeDecorator {
    public WhipCoffee(Coffee coffee) {
        super(coffee);
    }

    @Override
    public double getCost() {
        return super.getCost() + 0.7;
    }

    @Override
    public String getIngredients() {
        return super.getIngredients() + ", Whip";
    }
}

public class DecoratorExample {
    public static void main(String[] args) {
        Coffee c = new SimpleCoffee();
        System.out.println("Cost : " + c.getCost() + "; Ingredients : " + c.getIngredients());

        c = new MilkCoffee(c);
        System.out.println("Cost : " + c.getCost() + "; Ingredients : " + c.getIngredients());

        c = new WhipCoffee(c);
        System.out.println("Cost : " + c.getCost() + "; Ingredients : " + c.getIngredients());
    }
}

This example demonstrates a simple coffee beverage shop, which defines an abstract interface Coffee , which has two methods: getCost() and getIngredients() .

Then we define a simple coffee drink: SimpleCoffee , which implements the Coffee interface and has default prices and ingredients.

Advantages and Disadvantages

advantage

  1. Decorator pattern can provide more flexibility than inheritance

  2. You can extend the functionality of an object in a dynamic way, selecting different decorators at runtime to achieve different behaviors.

  3. By using different concrete decoration classes and permutations of these decoration classes, many combinations of different behaviors can be created. You can use multiple specific decoration classes to decorate the same object to get a more powerful object.

  4. Specific component classes and specific decoration classes can be changed independently. Users can add new specific component classes and specific decoration classes as needed, and then combine them when using them. The original code does not need to be changed, which is in line with the "opening and closing principle".

shortcoming

  1. A lot of small objects will be generated, increasing the complexity of the system.

  2. This more flexible feature than inheritance also means that decoration mode is more error-prone than inheritance, and troubleshooting is also difficult. For objects decorated multiple times, finding errors during debugging may require step-by-step troubleshooting, which is more cumbersome.

2. Strategy mode

Strategy Pattern is a common design pattern that defines a family of algorithms and encapsulates each algorithm so that they can be replaced with each other. The strategy pattern allows the algorithm to change independently of the client using it, allowing the client to change the way the algorithm is used without modifying the source code.

Here is a sample code that uses the strategy pattern to implement sorting. Suppose there is a Sorter class that can sort an integer array using different sorting algorithms, where the specific algorithm used is specified by the client code.

First, you need to define an interface SortStrategy , which represents a sorting algorithm. This interface contains a sort method , which accepts an integer array as a parameter and returns the sorted array:

public interface SortStrategy {
    int[] sort(int[] array);
}

Next, the specific sorting algorithm can be defined. Here, we implement two sorting algorithms: bubble sort and quick sort. They all implement the SortStrategy interface:

public class BubbleSort implements SortStrategy {
    public int[] sort(int[] array) {
        // 冒泡排序算法的具体实现
        // ...
        return sortedArray;
    }
}

public class QuickSort implements SortStrategy {
    public int[] sort(int[] array) {
        // 快速排序算法的具体实现
        // ...
        return sortedArray;
    }
}

Now, we can define the Sorter class. It contains a sort method that accepts an array of integers and a sorting strategy as parameters. A sorting strategy is an instance of a class that implements the SortStrategy interface. The sort method of the Sorter class sorts the array using the passed sorting strategy.

public class Sorter {
    private SortStrategy strategy;

    public void setStrategy(SortStrategy strategy) {
        this.strategy = strategy;
    }

    public int[] sort(int[] array) {
        return this.strategy.sort(array);
    }
}

Finally, you can use the Sorter class in client code to sort the array. First create an integer array, then create a Sorter object, and set the sorting strategy to bubble sort. Then, use the sort method of the Sorter object to sort the array and print the results. Next, set the sorting strategy to quick sort, sort the array again, and print the results

public static void main(String[] args) {
    int[] array = {5, 2, 4, 6, 1, 3};

    Sorter sorter = new Sorter();

    sorter.setStrategy(new BubbleSort());
    int[] sortedArray = sorter.sort(array);
    System.out.println(Arrays.toString(sortedArray)); // 输出 [1, 2, 3, 4, 5, 6]

    sorter.setStrategy(new QuickSort());
    sortedArray = sorter.sort(array);
    System.out.println(Arrays.toString(sortedArray)); // 输出 [1, 2, 3, 4, 5, 6]
}

Strategy pattern is a behavioral design pattern that can dynamically change the behavior of a class.

In this code, we have two sorting algorithms: BubbleSort and QuickSort.

In the Sorter class, we use the Strategy pattern to dynamically select the sorting algorithm to use.

In the main class Main, we create a Sorter object and sort it using the BubbleSort algorithm.

We can then change the sorting algorithm to use by using the setSortStrategy() method.

In fact, the strategy pattern and the template pattern are very similar, but they are not the same. Let’s look at the next chapter: Detailed description of Java design patterns (3)

Guess you like

Origin blog.csdn.net/qq_18235445/article/details/129315280