【设计模式】Java设计模式详细讲解

一、概述

Java设计模式是Java程序设计中一种重要的最佳实践,它提供了一种框架和结构,可以帮助开发者更好地理解和设计复杂的系统。设计模式不仅仅是一种语法规则,更是一种思想和方法论,它能够帮助开发者更好地分析、设计和实现软件系统。

设计模式的概念最早由GOF( Gang of Four)在1994年出版的《设计模式:可复用的面向对象软件设计》一书中提出。这本书中介绍了23种经典的设计模式,这些设计模式是根据面向对象编程的原则和最佳实践总结出来的,旨在帮助开发者更好地设计和实现可扩展、可维护和可重用的软件系统。

本文将介绍Java设计模式的基本概念、特点和分类,并通过具体的示例来解释不同类型的设计模式。

二、设计模式的定义和分类

设计模式是一种经过验证的最佳实践,用于解决软件开发中常见的重复出现的问题。它提供了一种框架和结构,可以帮助开发者更好地设计和实现软件系统。

设计模式通常被分为三种类型:创建型模式结构型模式行为型模式

1.1、创建型模式

创建型模式关注对象创建的方式,主要解决对象创建的复杂性问题。以下是一些常见的创建型模式:

1.1.1、单例模式:确保只有一个特定类型的对象实例。

以下是一个简单的 Java 单例模式的实现:

public class Singleton {private static Singleton instance;private Singleton() {}public static synchronized Singleton getInstance() {if (instance == null) {instance = new Singleton();}return instance;}
}

在这个实现中,我们使用一个静态变量 instance 来保存单例对象。构造函数是私有的,因此无法从外部创建实例。getInstance() 方法是公共的,并且返回单例对象。如果单例对象尚未创建,则会创建一个新的实例并将其存储在 instance 变量中。由于 getInstance() 方法是同步的,因此它确保了在多线程环境中只有一个线程可以创建或获取单例对象。

以下是另一种更优雅的 Java 单例模式的实现,使用了双重检查锁定(double-checked locking):

public class Singleton {private static volatile Singleton instance;private Singleton() {}public static Singleton getInstance() {if (instance == null) {synchronized (Singleton.class) {if (instance == null) {instance = new Singleton();}}}return instance;}
}

在这个实现中,我们使用了双重检查锁定来确保只有在第一次调用 getInstance() 方法时才会创建单例对象。在第一次检查 instance 是否为 null 时,没有同步,因此只有单个线程可以进入同步块并创建单例对象。这样可以避免在每次调用 getInstance() 方法时都进行同步,从而提高性能。但是,需要注意的是,如果单例对象的创建非常耗时或需要执行一些耗时的初始化操作,那么这个实现可能会导致性能问题。

1.1.2、原型模式:通过复制对象来创建新对象。

在Java中,可以通过实现 Cloneable 接口并重写 clone() 方法来使用原型模式。以下是一个简单的示例:

public class Circle implements Cloneable {private int x;private int y;private int radius;public Circle(int x, int y, int radius) {this.x = x;this.y = y;this.radius = radius;}public int getX() {return x;}public int getY() {return y;}public int getRadius() {return radius;}@Overridepublic Object clone() throws CloneNotSupportedException {Circle circle = (Circle) super.clone();return circle;}
}

在上面的示例中,Circle 类实现了 Cloneable 接口并重写了 clone() 方法。这个方法返回一个与原始对象具有相同值的新对象。需要注意的是,如果对象中有引用类型的成员变量,这些成员变量也需要使用同样的方式进行克隆。

1.1.4、工厂模式:通过工厂方法来创建对象,而不是直接实例化对象。

在工厂模式中,我们创建对象时不会直接使用 new 操作符,而是通过调用一个工厂方法来创建对象。这个工厂方法通常是一个静态方法,它返回一个实现了特定接口或继承了特定类的对象。

以下是一个简单的示例:

public interface Shape {void draw();
}public class Circle implements Shape {public void draw() {System.out.println("Circle draw method");}
}public class Rectangle implements Shape {public void draw() {System.out.println("Rectangle draw method");}
}public class ShapeFactory {public static Shape getShape(String shapeType) {if ("circle".equals(shapeType)) {return new Circle();} else if ("rectangle".equals(shapeType)) {return new Rectangle();}return null;}
}

在上面的示例中,我们定义了一个 Shape 接口和两个实现了 Shape 接口的类:Circle 和 Rectangle。我们还定义了一个 ShapeFactory 类,它提供了一个静态方法 getShape,该方法接受一个字符串参数 shapeType,并根据该参数创建相应的 Shape 对象。

使用工厂模式的好处是,我们可以在运行时动态创建不同类型的对象,而不需要在代码中硬编码它们。此外,工厂模式还可以帮助我们实现依赖注入和单元测试等其他功能。

1.1.5、建造者模式:通过逐步构建对象来创建复杂对象。

在建造者模式中,我们创建对象时不会直接使用 new 操作符来创建对象,而是通过一系列的建造方法来逐步构建对象。建造者模式通常用于创建复杂的对象,尤其是那些具有很多属性并且这些属性的初始化依赖于其他属性的情况。

以下是一个简单的示例:

public class Car {private String color;private String model;private int year;public Car(Builder builder) {this.color = builder.color;this.model = builder.model;this.year = builder.year;}public void drive() {System.out.println("Driving " + this.color + " " + this.model + " car");}
}public class CarBuilder {private String color;private String model;private int year;public CarBuilder setColor(String color) {this.color = color;return this;}public CarBuilder setModel(String model) {this.model = model;return this;}public CarBuilder setYear(int year) {this.year = year;return this;}public Car build() {return new Car(this);}
}

在上面的示例中,我们定义了一个 Car 类和一个 CarBuilder 类。Car 类具有三个属性:color、model 和 year。CarBuilder 类提供了设置这些属性的方法,并且还有一个 build 方法,该方法返回一个 Car 对象。通过使用 CarBuilder,我们可以逐步构建 Car 对象,而不需要在代码中硬编码它们。

1.1.6、抽象工厂模式:创建一组相关对象,并为其提供接口。

在抽象工厂模式中,我们定义了一个抽象的工厂接口,该接口声明了一组创建对象的方法。然后,我们定义了多个实现该接口的具体工厂类,每个工厂类都提供了特定于该主题的对象。

以下是一个简单的示例:

public interface Shape {void draw();
}public class Circle implements Shape {@Overridepublic void draw() {System.out.println("Drawing Circle");}
}public class Rectangle implements Shape {@Overridepublic void draw() {System.out.println("Drawing Rectangle");}
}public interface ShapeFactory {Shape getShape();
}public class CircleFactory implements ShapeFactory {@Overridepublic Circle getShape() {return new Circle();}
}public class RectangleFactory implements ShapeFactory {@Overridepublic Rectangle getShape() {return new Rectangle();}
}

在上面的示例中,我们定义了一个抽象的 Shape 接口和两个实现该接口的具体类:Circle 和 Rectangle。我们还定义了一个抽象的 ShapeFactory 接口,它声明了一个 getShape 方法。然后,我们定义了两个具体工厂类:CircleFactory 和 RectangleFactory,它们实现了 ShapeFactory 接口,并提供了特定于该主题的 Shape 对象。

在使用抽象工厂模式时,我们可以创建不同的工厂对象来获取不同类型的对象。例如,我们可以创建一个 CircleFactory 对象来获取 Circle 对象,或者创建一个 RectangleFactory 对象来获取 Rectangle 对象。这样,我们就可以在不指定具体类的情况下创建不同类型的对象。

1.2、结构型模式

结构型模式关注对象之间的组合关系,主要解决如何组合对象以形成更大的结构。以下是一些常见的结构型模式:

1.2.1、适配器模式:将一个类的接口转换成另一个客户端所期望的接口形式。

在适配器模式中,我们定义了一个适配器类,该类实现了目标接口,并将不兼容的接口转换为兼容的接口。通常,适配器类会接收一个实现了不兼容接口的对象,并将其转换为另一个实现了目标接口的对象。

以下是一个简单的示例:

public interface Target {void doSomething();
}public class Adaptee implements Target {@Overridepublic void doSomething() {System.out.println("Adaptee's doSomething method");}
}public class Adapter implements Target {private Adaptee adaptee;public Adapter(Adaptee adaptee) {this.adaptee = adaptee;}@Overridepublic void doSomething() {adaptee.doSomething();}
}

在上面的示例中,我们定义了一个 Target 接口,以及一个实现了该接口的 Adaptee 类。我们还定义了一个适配器类 Adapter,该类实现了 Target 接口,并将 Adaptee 类的 doSomething 方法转换为 Target 接口的 doSomething 方法。

在使用适配器模式时,我们可以创建一个 Adaptee 对象,并将其传递给 Adapter 构造函数。然后,我们可以使用 Adapter 对象来调用 Target 接口的 doSomething 方法,而实际执行的是 Adaptee 类的 doSomething 方法。

适配器模式提供了一种灵活的方式来处理不兼容的接口,它可以将不同的接口集成到同一个系统中,并使它们能够相互协作。

1.2.2、桥接模式:将一个复杂的类层次结构分解为多个简单的接口,以便于独立升级和扩展。

Java 桥接模式提供了一种方式来将一个类的接口与另一个不相关的接口关联起来。

在桥接模式中,我们定义了一个桥接类,该类将一个类的接口与另一个不相关的接口关联起来。通常,桥接类会包含一个实现了不相关接口的对象,并将该对象的操作转换为原始接口的操作。

以下是一个简单的示例:

public interface Shape {void draw();
}public class Circle implements Shape {@Overridepublic void draw() {System.out.println("Drawing Circle");}
}public interface Color {void fill();
}public class Red implements Color {@Overridepublic void fill() {System.out.println("filling with red color");}
}public class Bridge implements Shape, Color {private Circle circle;private Red color;public Bridge(Circle circle, Red color) {this.circle = circle;this.color = color;}@Overridepublic void draw() {circle.draw();}@Overridepublic void fill() {color.fill();}
}

在上面的示例中,我们定义了两个接口,Shape 和 Color,分别表示图形和颜色。我们还定义了 Circle 和 Red 类,分别实现了 Shape 和 Color 接口。最后,我们定义了一个 Bridge 类,该类实现了 Shape 和 Color 接口,并将 Circle 和 Red 类的操作转换为这两个接口的操作。

在使用桥接模式时,我们可以创建一个 Circle 对象和一个 Red 对象,并将它们传递给 Bridge 构造函数。然后,我们可以使用 Bridge 对象来调用 Shape 和 Color 接口的方法,而实际执行的是 Circle 和 Red 类的操作。

桥接模式提供了一种灵活的方式来将不同的接口关联起来,它可以将不相关的接口集成到同一个对象中,并使它们能够相互协作。

1.2.3、组合模式:将一组相关对象组合成一个树形结构,以便于客户端以统一的方式处理对象。

Java 组合模式提供了一种方式来组合多个对象,从而形成一个树形结构,并可以递归地调用这些对象的操作。

在组合模式中,我们定义了一个抽象的节点类,该节点类包含了子节点的引用。然后,我们定义了两个具体的节点类,一个是叶节点,另一个是组合节点。叶节点表示树的末端,它没有子节点。组合节点表示树的分支,它可以包含多个子节点。

以下是一个简单的示例:

public abstract class Component {protected Component left;protected Component right;public Component(Component left, Component right) {this.left = left;this.right = right;}public abstract void operation();
}public class Leaf extends Component {private String name;public Leaf(String name) {this.name = name;}@Overridepublic void operation() {System.out.println("Leaf " + name + ": operation()");}
}public class Composite extends Component {private List<Component> children;public Composite() {children = new ArrayList<>();}public void add(Component component) {children.add(component);}@Overridepublic void operation() {for (Component child : children) {child.operation();}}
}

在上面的示例中,我们定义了一个抽象的节点类 Component,它包含了左右子节点的引用。然后,我们定义了两个具体的节点类,一个是叶节点 Leaf,另一个是组合节点 Composite。叶节点表示树的末端,它没有子节点。组合节点表示树的分支,它可以包含多个子节点。

在使用组合模式时,我们可以创建一个树形结构,每个节点可以是叶节点或组合节点。组合节点可以递归地调用其子节点的操作。例如,我们可以创建一个包含多个叶节点和组合节点的树,并调用根节点的 operation() 方法,该方法将递归地调用所有子节点的 operation() 方法。

1.2.4、装饰器模式:动态地给一个对象添加额外的职责,同时保持对象的接口不变。

Java 装饰器模式提供了一种方式来动态地给一个对象添加一些额外的职责。这通过创建一个包装对象,它包装了原始对象并提供一个新的接口,经常用于改变一个或多个类的行为或为其添加新的行为。

装饰器模式包含以下四个角色:

  • Component:定义一个接口,这个接口表示可以被装饰的行为。
  • ConcreteComponent:实现 Component 接口,表示需要被装饰的具体对象。
  • Decorator:继承 Component 接口,并且包含一个对 ConcreteComponent 的引用。Decorator 提供了一种方法来将自身添加到被装饰的对象的引用中。
  • ConcreteDecorator:实现 Component 接口,并添加额外的功能。在需要的时候,可以调用 ConcreteComponent 的方法。
    下面是一个简单的 Java 示例代码:
// Component
interface Coffee {double getCost();String getIngredients();
}// ConcreteComponent
class SimpleCoffee implements Coffee {public double getCost() {return 1;}public String getIngredients() {return "Coffee";}
}// Decorator
abstract class CoffeeDecorator implements Coffee {protected Coffee decoratedCoffee;public CoffeeDecorator(Coffee c) {this.decoratedCoffee = c;}public double getCost() {return decoratedCoffee.getCost();}public String getIngredients() {return decoratedCoffee.getIngredients();}
}// ConcreteDecorator A
class WhippedCoffee extends CoffeeDecorator {public WhippedCoffee(Coffee c) {super(c);}public double getCost() {return decoratedCoffee.getCost() + 0.5;}public String getIngredients() {return decoratedCoffee.getIngredients() + ", Whipped Cream";}
}// ConcreteDecorator B
class MochaCoffee extends CoffeeDecorator {public MochaCoffee(Coffee c) {super(c);}public double getCost() {return decoratedCoffee.getCost() + 1;}public String getIngredients() {return decoratedCoffee.getIngredients() + ", Mocha";}
}

在这个示例中,我们首先定义了一个 Coffee 接口和它的实现类 SimpleCoffee。然后,我们创建了一个抽象的 Decorator 类,它实现了 Coffee 接口并包含一个对被装饰对象的引用。接下来,我们创建了两个具体的装饰器类 WhippedCoffee 和 MochaCoffee,它们分别添加了 “Whipped Cream” 和 “Mocha” 的成分和价格。最后,我们可以使用这些装饰器来动态地给咖啡添加额外的职责。

1.2.5、外观模式:为子系统提供单一的入口点,简化子系统的使用。

外观模式(Facade Pattern)提供了一个简化的接口,将一些复杂的子系统或类的使用方式变得简单。外观模式的主要目的是简化接口,以便客户端代码能够更方便地使用子系统或类。

在 Java 中,外观模式通常使用类来实现。外观类提供了一些简单的方法,这些方法将客户端代码与子系统或类的复杂实现隔离开来。外观类通常只暴露子系统或类的一小部分功能,并且隐藏了内部实现的细节。

下面是一个简单的 Java 代码示例,演示了外观模式的使用:

public class Facade {private SubSystem1 subSystem1;private SubSystem2 subSystem2;public Facade() {subSystem1 = new SubSystem1();subSystem2 = new SubSystem2();}public void simplifiedMethod() {subSystem1.complexMethod1();subSystem2.complexMethod2();}
}public class SubSystem1 {public void complexMethod1() {// complex implementation of method 1}
}public class SubSystem2 {public void complexMethod2() {// complex implementation of method 2}
}

在上面的代码中,Facade 类是外观类,它简化了 SubSystem1 和 SubSystem2 的使用方式。Facade 类只提供了一个 simplifiedMethod() 方法,该方法调用了 SubSystem1 和 SubSystem2 的复杂方法。这样,客户端代码只需要调用 Facade 类的 simplifiedMethod() 方法,而不需要直接调用 SubSystem1 和 SubSystem2 的复杂方法。

使用外观模式可以降低客户端代码与子系统或类的耦合度,提高了代码的可维护性和可读性。同时,外观模式还可以隐藏子系统或类的内部实现细节,提高了系统的安全性。

1.2.6、享元模式:通过共享对象来减少系统中的对象数量,以降低系统的内存占用。

享元模式用于减少创建对象的数量,以便降低内存消耗和提高性能。享元模式通过共享对象来实现这一目标,使得多个客户端可以共享同一个对象,而不是为每个客户端创建新的对象。

享元模式主要适用于以下情况:

  • 对象创建成本较高:如果对象的创建成本较高,例如需要消耗大量的内存或网络带宽,那么使用享元模式可以降低对象的创建和销毁开销。
  • 对象数量可能非常大:如果需要创建的对象数量可能非常大,例如在图形渲染或数据处理中,使用享元模式可以避免大量的对象创建,从而减少内存消耗。
    在 Java 中,享元模式通常使用一个享元工厂类来管理共享对象。享元工厂类负责创建和管理共享对象,并确保多个客户端共享同一个对象。

下面是一个简单的 Java 代码示例,演示了享元模式的使用:

import java.util.HashMap;
import java.util.Map;public class FlyweightFactory {private Map<String, Flyweight> flyweights = new HashMap<>();public synchronized Flyweight getFlyweight(String key) {if (!flyweights.containsKey(key)) {Flyweight flyweight = new Flyweight(key);flyweights.put(key, flyweight);}return flyweights.get(key);}
}public abstract class Flyweight {protected String key;public Flyweight(String key) {this.key = key;}public abstract void operation(int x, int y);
}public class ConcreteFlyweight1 extends Flyweight {public ConcreteFlyweight1(String key) {super(key);}@Overridepublic void operation(int x, int y) {// implementation of operation for ConcreteFlyweight1}
}public class ConcreteFlyweight2 extends Flyweight {public ConcreteFlyweight2(String key) {super(key);}@Overridepublic void operation(int x, int y) {// implementation of operation for ConcreteFlyweight2}
}

在上面的代码中,FlyweightFactory 类是享元工厂类,它负责创建和管理共享对象。Flyweight 是一个抽象类,表示享元对象。ConcreteFlyweight1 和 ConcreteFlyweight2 是具体的享元实现类。每个享元实现类对应不同的操作。通过调用 FlyweightFactory 的 getFlyweight() 方法,可以获取共享的享元对象。在获取对象时,如果该对象不存在,则创建一个新的对象并将其添加到共享对象池中。如果该对象已经存在,则直接返回已经存在的对象。通过这种方式,多个客户端可以共享同一个享元对象,从而减少了对象的创建和销毁开销。

1.2.7、代理模式:通过代理来控制对另一个对象的访问。

代理模式是提供了一种将实际操作隐藏在代理对象之后的机制。代理模式通常用于控制对对象的访问,提供额外的功能或者修改对象的行为。

代理模式包括以下两种类型:

  • 静态代理:静态代理是指在编译期间代理类和被代理类之间的关系就已经确定。代理类需要实现与被代理类相同的接口,并在代理类中调用被代理类的方法。在调用过程中,可以执行额外的操作。
  • 动态代理:动态代理是指在运行时创建代理类。Java 中的动态代理主要通过 java.lang.reflect 包中的 Proxy 类和 InvocationHandler 接口实现。动态代理允许在运行时动态地创建代理类,并且可以实现对被代理对象的增强和修改。
    下面是一个简单的静态代理的示例:
public interface Shape {void draw();
}public class Rectangle implements Shape {@Overridepublic void draw() {System.out.println("Drawing a rectangle...");}
}public class RectangleProxy implements Shape {private Rectangle rectangle;public RectangleProxy(Rectangle rectangle) {this.rectangle = rectangle;}@Overridepublic void draw() {System.out.println("Drawing a rectangle...");rectangle.draw();System.out.println("Drawing a rectangle...done");}
}public class Main {public static void main(String[] args) {Shape rectangle = new Rectangle();Shape rectangleProxy = new RectangleProxy(rectangle);rectangleProxy.draw();}
}

在上面的示例中,Rectangle 是被代理类,RectangleProxy 是代理类。RectangleProxy 实现了与 Rectangle 相同的接口 Shape,并且在 draw() 方法中调用了 Rectangle 的 draw() 方法。这样,在调用 rectangleProxy.draw() 时,额外的操作也会被执行。

需要注意的是,静态代理需要在编译期间创建代理类和被代理类的关系,而动态代理可以在运行时动态地创建代理类和被代理类的关系。动态代理的灵活性和灵活性使其成为实际应用程序中的常用技术。

1.3、行为型模式

行为型模式关注对象之间的交互关系,主要解决如何让对象之间更好地协同工作。以下是一些常见的行为型模式:

1.3.1、策略模式:定义一系列可互换的算法,并将每个算法封装起来,使它们可以相互替换。

策略模式是定义了一系列可以互相替换的算法,使得在运行时可以根据情况选择合适的算法。

策略模式包括以下组件:

  • 策略(Strategy):定义了一系列的策略接口,每个策略接口表示一个具体的算法。策略类通常包含一些共同的行为和属性。
  • 具体策略(ConcreteStrategy):实现了策略接口,实现了具体的算法。每个具体策略类表示一个具体的算法。
  • 环境(Context):持有策略对象的引用,并且使用策略对象的方法来执行具体的操作。
    下面是一个简单的示例代码:
// 策略接口
public interface Strategy {public void execute();
}// 具体策略类1
public class ConcreteStrategy1 implements Strategy {@Overridepublic void execute() {System.out.println("ConcreteStrategy1 is executed.");}
}// 具体策略类2
public class ConcreteStrategy2 implements Strategy {@Overridepublic void execute() {System.out.println("ConcreteStrategy2 is executed.");}
}// 环境类
public class Context {private Strategy strategy;public Context(Strategy strategy) {this.strategy = strategy;}public void executeStrategy() {strategy.execute();}
}// 测试代码
public class Main {public static void main(String[] args) {Strategy strategy1 = new ConcreteStrategy1();Context context1 = new Context(strategy1);context1.executeStrategy(); // 输出:ConcreteStrategy1 is executed.Strategy strategy2 = new ConcreteStrategy2();Context context2 = new Context(strategy2);context2.executeStrategy(); // 输出:ConcreteStrategy2 is executed.}
}

在上面的示例中,Strategy 是一个策略接口,定义了一个 execute() 方法。ConcreteStrategy1 和 ConcreteStrategy2 是具体的策略类,分别实现了 Strategy 接口,并重写了 execute() 方法。Context 类持有一个策略对象的引用,并在 executeStrategy() 方法中调用了策略对象的 execute() 方法。在测试代码中,我们可以根据需要选择具体的策略对象来执行操作。

1.3.2、观察者模式:定义对象之间的依赖关系,当一个对象发生改变时,所有依赖于它的对象都会收到通知并自动更新。

观察者模式定义了一种一对多的依赖关系,使得当一个对象(主题)的状态发生改变时,所有依赖于它的对象(观察者)都能够得到通知并自动更新。

观察者模式包括以下组件:

  • 主题(Subject):维护一个观察者列表,并定义了添加和删除观察者的方法。当主题的状态发生改变时,它会自动通知所有的观察者。
  • 观察者(Observer):定义了一个更新方法,当主题的状态发生改变时,该方法将被调用。
    下面是一个简单的示例代码:
// 主题接口
public interface Subject {void registerObserver(Observer observer);void removeObserver(Observer observer);void notifyObservers();
}// 具体主题类
public class ConcreteSubject implements Subject {private ObserverList observers;private int state;public ConcreteSubject() {observers = new ObserverList();}public void setState(int state) {this.state = state;notifyObservers();}@Overridepublic void registerObserver(Observer observer) {observers.addObserver(observer);}@Overridepublic void removeObserver(Observer observer) {observers.removeObserver(observer);}@Overridepublic void notifyObservers() {for (Observer observer : observers) {observer.update();}}
}// 观察者接口
public interface Observer {void update();
}// 具体观察者类1
public class ConcreteObserver1 implements Observer {private int state;@Overridepublic void update() {state = ((ConcreteSubject) ((ObserverObject) subject).getSubject()).getState();System.out.println("ConcreteObserver1 received state: " + state);}
}// 具体观察者类2
public class ConcreteObserver2 implements Observer {private int state;@Overridepublic void update() {state = ((ConcreteSubject) ((ObserverObject) subject).getSubject()).getState();System.out.println("ConcreteObserver2 received state: " + state);}
}// 观察者列表类
public class ObserverList {private ArrayList<Observer> observers;public ObserverList() {observers = new ArrayList<Observer>();}public void addObserver(Observer observer) {observers.add(observer);}public void removeObserver(Observer observer) {observers.remove(observer);}
}
1.3.3、模板方法模式:定义一个操作算法的骨架,将某些步骤的执行延迟到子类中实现。

模板方法模式定义了一个操作的框架,将算法的步骤封装到不同的抽象方法中,从而允许子类在不改变算法结构的情况下重定义某些步骤的具体实现。

在 Java 中,模板方法模式通常使用继承来实现。一个抽象类定义了算法的框架,包括一些抽象方法和一些具体方法。子类可以继承这个抽象类,重定义抽象方法以提供自己的实现,而具体方法则保留了与框架的接口一致的实现。

以下是一个简单的示例代码,演示了模板方法模式的使用:

public abstract class Template {public void templateMethod() {step1();System.out.println("Algorithm framework");step2();System.out.println("Algorithm framework");step3();}public abstract void step1();public abstract void step2();public abstract void step3();
}public class ConcreteTemplate extends Template {@Overridepublic void step1() {System.out.println("Concrete step 1");}@Overridepublic void step2() {System.out.println("Concrete step 2");}@Overridepublic void step3() {System.out.println("Concrete step 3");}
}

在上面的代码中,Template 是一个抽象类,定义了一个模板方法 templateMethod(),它包含了算法的框架。step1()、step2() 和 step3() 是抽象方法,子类需要提供具体的实现。ConcreteTemplate 是 Template 的一个子类,重定义了所有的抽象方法,提供了自己的实现。在 templateMethod() 中,算法的框架保持不变,但是每个步骤的具体实现都由子类提供。

使用模板方法模式的好处是它可以让子类在不改变算法结构的情况下重定义某些步骤的具体实现。这使得代码更加灵活和可维护。同时,模板方法模式还可以提高代码的可重用性,因为算法的框架可以在不同的子类中重复使用。

1.3.4、状态模式:将一个对象的状态转换细化为各个状态,每个状态对应一个类,实现状态的转换和特定状态的逻辑。

状态模式允许一个对象在其内部状态改变时改变其行为。状态模式通过把状态封装到单独的状态类中来实现的,每个状态类负责处理对应的状态,并且可以有一个机会改变内部状态或者发出命令。

以下是一个简单的示例代码,演示了状态模式的使用:

public interface State {void handle(Context context);
}public class StateA implements State {@Overridepublic void handle(Context context) {System.out.println("StateA handled, context state: " + context.getState());context.setState(new StateB());}
}public class StateB implements State {@Overridepublic void handle(Context context) {System.out.println("StateB handled, context state: " + context.getState());context.setState(new StateA());}
}public class Context {private State state;private String stateData;public Context(State initialState) {this.state = initialState;}public void setState(State state) {this.state = state;}public String getStateData() {return stateData;}public void setStateData(String stateData) {this.stateData = stateData;}public void request() {state.handle(this);}
}

在上面的代码中,State 是一个接口,定义了一个 handle() 方法。StateA 和 StateB 是实现了 State 接口的两个具体状态类,每个状态类都有自己的 handle() 方法。Context 类保存了一个当前状态的引用,并有一个 request() 方法来处理请求。当 Context 的 request() 方法被调用时,当前状态会处理请求,并在处理结束后改变状态。

使用状态模式的好处是它可以让一个对象根据其内部状态来改变其行为,并且可以将状态封装到单独的状态类中,使得代码更加清晰和易于维护。同时,状态模式还可以提高代码的可重用性,因为不同的状态类可以具有相似的行为。

1.3.5、命令模式:定义一个命令接口,通过接收命令对象来实现命令的执行。

命令模式允许你将请求封装为一个对象,从而使请求可以被取消,被记录,或者被参数化。

以下是一个简单的示例代码,演示了命令模式的使用:

public interface Command {void execute();
}public class ConcreteCommand implements Command {private Receiver receiver;public ConcreteCommand(Receiver receiver) {this.receiver = receiver;}@Overridepublic void execute() {receiver.doSomething();}
}public interface Receiver {void doSomething();
}public class ConcreteReceiver implements Receiver {@Overridepublic void doSomething() {System.out.println("Receiver did something");}
}public classInvoker`public class Invoker {private Command command;public Invoker(Command command) {this.command = command;}public void setCommand(Command command) {this.command = command;}public void executeCommand() {command.execute();}
}

在上面的代码中,Command 是一个抽象命令接口,定义了一个 execute() 方法。ConcreteCommand 是一个具体命令类,实现了 Command 接口,并关联了一个 Receiver 对象。当调用 execute() 方法时,具体命令类会调用接收者的 doSomething() 方法。Receiver 是一个接收者接口,定义了一个 doSomething() 方法。ConcreteReceiver 是一个具体接收者类,实现了 Receiver 接口,并实现了 doSomething() 方法。Invoker 是调用者接口,它持有对命令的引用,并调用命令的 execute() 方法来执行请求。通过这种方式,调用者与具体命令的实现解耦,使得可以灵活地增加新的命令类,而不需要修改调用者的代码。

1.3.6、迭代器模式:提供一种方法来顺序访问一个聚合对象的元素,而不暴露其底层表示。

迭代器模式提供了一种在集合对象中遍历元素的标准方法。迭代器模式允许我们顺序访问一个聚合对象的元素,而不暴露其底层表示。

以下是一个简单的示例代码,演示了迭代器模式的使用:

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;public class IteratorExample {public static void main(String[] args) {List<String> list = new ArrayList<>();list.add("Apple");list.add("Banana");list.add("Orange");Iterator<String> iterator = list.iterator();while (iterator.hasNext()) {String item = iterator.next();System.out.println(item);}}
}

在上面的代码中,我们创建了一个 List 对象,并向其中添加了一些元素。然后,我们使用 iterator() 方法获取了一个迭代器对象,并使用 hasNext() 方法检查是否还有下一个元素。如果有,我们使用 next() 方法获取下一个元素,并输出到控制台。通过这种方式,我们可以遍历整个列表。

迭代器模式的主要优点是它提供了一种统一的访问聚合对象的方法,而不必关心底层表示。此外,迭代器模式还可以在遍历过程中删除元素,而不会导致 ConcurrentModificationException 异常。

1.3.7、访问者模式:在不改变数据结构的前提下增加新的操作,通过定义一个访问者类来实现对数据的操作。

访问者模式允许一个操作作用于一个对象结构中的各个元素,而这个对象结构可以是一个简单的数组,也可以是一个复杂的树形结构。访问者模式主要解决的是一种数据结构或者对象结构中,数据类型复杂且变化多端的问题。

访问者模式包含以下主要角色:

  • 访问者(Visitor):定义了一个访问操作的方法,该方法接收一个元素对象作为参数。
    被访问对象(Element):提供访问者所需的方法,一般该方法被称为 accept。
  • 具体元素类(ConcreteElement):实现 Element 类,并接受访问者的访问,在访问完毕后执行一些具体的操作。
    以下是一个简单的示例代码,演示了访问者模式的使用:
// 定义元素类
class ConcreteElementA {private String name;public ConcreteElementA(String name) {this.name = name;}public void accept(Visitor visitor) {visitor.visit(this);}
}class ConcreteElementB {private String name;public ConcreteElementB(String name) {this.name = name;}public void accept(Visitor visitor) {visitor.visit(this);}
}// 定义访问者接口
interface Visitor {void visit(ConcreteElementA elementA);void visit(ConcreteElementB elementB);
}// 定义具体访问者类
class ConcreteVisitor implements Visitor {public void visit(ConcreteElementA elementA) {System.out.println(elementA.name + " visited by ConcreteVisitor");}public void visit(ConcreteElementB elementB) {System.out.println(elementB.name + " visited by ConcreteVisitor");}
}

在上面的代码中,我们定义了两个元素类 ConcreteElementA 和 ConcreteElementB,它们都实现了 Element 类的 accept 方法。然后,我们定义了一个访问者接口 Visitor,其中包含了两个访问方法分别对应两个元素类。最后,我们创建了一个具体的访问者类 ConcreteVisitor,实现了 Visitor 接口中的方法。通过这种方式,我们可以对不同的元素类执行不同的操作。

1.3.8、备忘录模式:保存一个对象的内部状态,并在需要时恢复该对象的状态。

备忘录模式提供了一种保存对象内部状态的机制,并在以后需要时能够恢复这个状态。它允许在不暴露对象内部状态的情况下保存和恢复对象的状态。

在 Java 中,备忘录模式可以使用以下步骤实现:

定义一个备忘录类(Memento),该类包含了原始对象(Originator)的内部状态。

public class Memento {private String state;public Memento(String state) {this.state = state;}public String getState() {return state;}
}

定义一个原始对象(Originator),该对象允许其他对象保存和恢复其内部状态。

public class Originator {private String state;public Originator(String state) {this.state = state;}public String getState() {return state;}public Memento getMemento() {return new Memento(state);}public void restoreFromMemento(Memento memento) {this.state = memento.getState();}
}

定义一个管理者类(Caretaker),该类负责保存和恢复原始对象的备忘录。

public class Caretaker {private Memento memento;public Caretaker(Memento memento) {this.memento = memento;}public Memento getMemento() {return memento;}public void setMemento(Memento memento) {this.memento = memento;}
}

在上述代码中,Originator 类的 getMemento() 方法用于创建一个新的备忘录,restoreFromMemento() 方法用于从备忘录中恢复状态。Caretaker 类负责保存和恢复备忘录。使用备忘录模式时,需要先保存原始对象的当前状态,然后在需要时恢复该状态。

1.3.9、中介者模式:将一组对象之间的交互封装到一个中介者对象中,降低对象之间的耦合性。

中介者模式提供了一种将对象之间的交互集中管理的方法,从而降低对象之间的耦合度,增强系统的可维护性和可复用性。

在 Java 中,中介者模式可以使用以下步骤实现:

定义一个中介者类(Mediator),该类包含了与各个同事类(Colleague)交互的逻辑。

public class Mediator {public void performAction(Colleague colleague) {// 处理同事类之间的交互逻辑}
}

定义一个同事类(Colleague),该类与中介者类交互,并将自身的状态传递给中介者。

public class Colleague {private Mediator mediator;public Colleague(Mediator mediator) {this.mediator = mediator;}public void changeState() {// 修改自身的状态mediator.performAction(this); // 调用中介者的方法,处理与其它同事类的交互逻辑}
}

在客户端代码中,创建中介者和同事类的实例,并调用相应的方法。

public class Client {public static void main(String[] args) {Mediator mediator = new Mediator();Colleague colleague1 = new Colleague(mediator);Colleague colleague2 = new Colleague(mediator);// 设置同事1的状态,触发中介者处理逻辑colleague1.changeState();// 设置同事2的状态,同样会触发中介者处理逻辑colleague2.changeState();}
}

在上述代码中,中介者类 Mediator 集中处理同事类之间的交互逻辑,避免了同事类之间的直接交互,降低了系统的耦合度。同时,通过将交互逻辑集中管理,也方便了对系统行为的扩展和维护。

1.3.10、解释器模式:定义一个解释器接口,通过解释器来解释并执行特定的语法规则。

解释器模式提供了一种构建解析、解释和处理语言表达式的方式。在 Java 中,解释器模式可以使用以下步骤实现:

定义一个抽象表达式类(Expression),该类声明了解释表达式的方法。

public interface Expression {int interpret(Context context);
}

定义一个具体表达式类(ConcreteExpression),该类实现了抽象表达式类中的 interpret() 方法。

public class ConcreteExpression implements Expression {@Overridepublic int interpret(Context context) {// 实现解释表达式的逻辑}
}

定义一个上下文类(Context),该类包含了与表达式相关的数据和操作。

public class Context {private int value;public Context(int value) {this.value = value;}public int getValue() {return value;}
}

创建一个解释器类(Interpreter),该类包含了与表达式相关的解释逻辑。

public class Interpreter {public int interpret(Expression expression) {return expression.interpret(new Context(0)); // 传入默认的上下文对象}
}

在客户端代码中,创建具体表达式和上下文对象,并调用解释器类的 interpret() 方法来解释表达式。

public class Client {public static void main(String[] args) {Expression expression = new ConcreteExpression(); // 创建具体表达式对象Context context = new Context(10); // 创建上下文对象Interpreter interpreter = new Interpreter(); // 创建解释器对象int result = interpreter.interpret(expression); // 调用解释器解释表达式,并获取结果System.out.println(result); // 输出结果}
}

在上述代码中,解释器模式通过定义抽象表达式类和具体表达式类,使得不同类型的表达式可以灵活地添加到系统中。同时,通过将解释逻辑封装在解释器类中,使得系统的可维护性和可复用性得到了提高。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/57883.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

算法通关村第6关【白银】| 树的层次遍历问题

一、基本层次遍历问题 1.二叉树的层次遍历 思路&#xff1a;使用队列可以很好的保存遍历状态&#xff0c;出队将结点左右子结点入队&#xff0c;用size记录下一层的元素个数&#xff0c;这样就能区分出层了 class Solution {public List<List<Integer>> levelOr…

【数据结构】树与二叉树

文章目录 &#x1f340;树型结构&#x1f431;‍&#x1f464;什么是树型结构&#x1f431;‍&#x1f453;树型结构的概念&#x1f431;‍&#x1f3cd;树的表示形式&#x1f431;‍&#x1f409;树的应用 &#x1f333;二叉树&#x1f431;‍&#x1f464;二叉树的概念&#…

LLM-chatgpt训练过程

流程简介 主要包含模型预训练和指令微调两个阶段 模型预训练&#xff1a;搜集海量的文本数据&#xff0c;无监督的训练自回归decoder&#xff1b; O T P ( O t < T ) O_TP(O_{t<T}) OT​P(Ot<T​)&#xff0c;损失函数CE loss指令微调&#xff1a;在输入文本中加入…

java解析json

1. 解析根节点为“{}”的json {"id": 1525490,"name": "有缘网" }代码&#xff1a; String jsonString "{\"id\":1525490\",\"name\":\"有缘网\"}";JSONObject jsonObject JSONObject.…

Android获取手机已安装应用列表JAVA实现

最终效果: 设计 实现java代码: //获取包列表private List<String> getPkgList() {List<String> packages new ArrayList<String>();try {//使用命令行方式获取包列表Process p Runtime.getRuntime().exec("pm list packages");//取得命令行输出…

图数据库Neo4j学习五渲染图数据库neo4jd3

文章目录 1.现成的工具2.Neo4j JavaScript Driver3.neovis4.neo4jd34.1neo4jd3和neovis对比4.2获取neo4jd34.3neo4jd3的数据结构4.4Spring data neo4.4.1 定义返回数据格式4.4.1.1NeoResults4.4.1.2GraphVO4.4.1.3NodeVO4.4.1.4ShipVO 4.4.2 SDN查询解析4.4.2.1 Repo查询语句4.…

Java小记-矩阵转置

对于给定的一个二维矩阵&#xff0c;请转置后进行输出。 输入描述 对于一个n*m的矩阵&#xff0c;输入有n行&#xff0c;每行是m个以空格分隔的数字。 输出描述 n*m矩阵的转置矩阵。输出m行&#xff0c;每行是n个空格分隔的数据。 import java.io.*; import java.util.*;pub…

【Rust日报】2023-08-25 SDXL in Rust,AIGC狂喜!

Stable-Diffusion-XL-Burn&#xff1a;SDXL in Rust 在reddit上看到这个项目 Stable-Diffusion-XL-Burn ,它 是一个基于 Rust 的项目&#xff0c;将stable diffusion xl 移植到了 Rust 深度学习框架 burn 中。在reddit回帖上&#xff0c;已经有小伙伴在热情的尝试&#xff0c;还…

Go测试之.golden 文件

Go测试中的.golden 文件是干什么用的&#xff1f;请举例说明 在Go语言中&#xff0c;.golden文件通常用于测试中的黄金文件&#xff08;golden files&#xff09;。黄金文件是在测试期间记录预期输出结果的文件。测试用例运行时&#xff0c;黄金文件用于比较实际输出与预期输出…

【IMX6ULL驱动开发学习】11.Linux之SPI驱动

参考&#xff1a;驱动程序开发&#xff1a;SPI设备驱动_spi驱动_邓家文007的博客-CSDN博客 目录 一、SPI驱动简介 1.1 SPI架构概述 1.2 SPI适配器&#xff08;控制器&#xff09;数据结构 1.2 SPI设备数据结构 1.3 SIP设备驱动 1.4 接口函数 二、SPI驱动模板 一、SPI驱动…

718. 最长重复子数组

718. 最长重复子数组 原题链接&#xff1a;完成情况&#xff1a;题解&#xff1a;方法一&#xff1a;动态规划方法二&#xff1a;滑动窗口方法三&#xff1a;二分查找 哈希 原题链接&#xff1a; 718. 最长重复子数组 https://leetcode.cn/problems/maximum-length-of-repe…

Android App的设计规范

Android App 设计规范是为开发者和设计师提供的一系列准则和建议&#xff0c;以确保应用在 Android 设备上的外观、交互和用户体验保持一致。以下是一些常见的 Android App 设计规范要点&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专业的软件外包开…

Apifox下载安装步骤

我们先访问网址 https://apifox.com/?utm_sourcebaidu&utm_mediumsem&utm_campaign251430236&utm_content7810722111&utm_termapifox%E6%9F%A5%E7%9C%8B%E7%89%88%E6%9C%AC&bd_vid8323327349775096324 然后 这里这个免费下载已经写的这么明显了 那就直接点…

大华摄像头有问题,海康摄像头也有问题

买了个大华摄像头&#xff0c;除了抗噪方面效果不好&#xff0c;我是很满意的。前一段时间摄像头启动出了点问题&#xff08;忘记拔掉SD卡&#xff09;&#xff0c;于是买了个海康的。 大华摄像头是3寸&#xff0c;海康是2寸。视频效果差多了。看来大有大的道理。更可恨的是&a…

PMO(Project Management Office)

PMO 是项目管理办公室&#xff08;Project Management Office&#xff09;的缩写。它是组织内的一个部门或团队&#xff0c;负责支持和促进项目管理活动&#xff0c;以确保项目按时、按预算、按要求完成。 PMO 的职责和角色可以因组织的性质和需求而有所不同&#xff0c;但通常…

java中 while 和 for 两种循环写法的使用场景

目录 场景 能否将声明变量写在while的小括号里&#xff1f; 能否将声明变量写在for的小括号里&#xff1f; 省流&#xff1a; BufferedReader br new BufferedReader(null);for(String line; (line br.readLine()) ! null;){System.out.println(line);}正文&#xff1a; …

c++学习之string实现

字符串 - C引用 (cplusplus.com)这里给出标准官方的string实现&#xff0c;可以看到设计还是较为复杂的&#xff0c;有成员函数&#xff0c;迭代器&#xff0c;修饰符&#xff0c;容量&#xff0c;元素访问&#xff0c;字符串操作等&#xff0c;将字符尽可能的需求都设计出来&a…

Jmeter(三十):并发测试(设置集合点)

集合点:让所有请求在不满足条件的时候处于等待状态。 如:我集合点设置为50,那么不满足50个请求的时候,这些请求都会集合在一起,处于等待状态,当达到50的时候,就一起执行。从而达到并发的效果。 那么Jmeter中可以通过同步定时器 Synchronizing Timer 来完成。 Number …

计算机竞赛 基于机器视觉的手势检测和识别算法

0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基于深度学习的手势检测与识别算法 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非常推荐&#xff01; &#x1f9ff; 更多资料, 项目分享&#xff1a; https://gitee.com/dancheng…

医学影像PACS系统源码,患者登记、图像采集和处理、图像存储、报告产生的影像系统

PACS系统是医院影像科室中应用的一种系统&#xff0c;主要用于获取、传输、存档和处理医学影像。它通过各种接口&#xff0c;如模拟、DICOM和网络&#xff0c;以数字化的方式将各种医学影像&#xff0c;如核磁共振、CT扫描、超声波等保存起来&#xff0c;并在需要时能够快速调取…