当前位置: 首页 > news >正文

【设计模式】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); // 输出结果}
}

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

相关文章:

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

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

【MySQL】表的增删查改(进阶)

目录 1.数据库约束 1.1NOT NULL&#xff1a;非空约束 1.2UNIQUE&#xff1a;唯一值约束 1.3DEFAULT&#xff1a;默认值约束 1.4PRIMARY KEY&#xff1a;主键约束 1.5FOREIGN KEY&#xff1a;外键约束 1.6CHECK约束 2.表的设计 2.1一对一 2.2一对多 2.3多对多 3.新增…...

Vim几种跳转方式

ps: 以下时我常用的一些跳转指令&#xff0c;用于参考和复习记忆。还有一些后续会更新。 文件内跳转 移动光标 普通模式下左h&#xff0c;右l&#xff0c;上k&#xff0c;下j。&#xff08;可以使用数字hlkj&#xff0c;实现跳跃式移动&#xff09;。 字符间跳转 …...

element-ui 弹窗里面嵌套弹窗,解决第二个弹窗被遮罩层掩盖无法显示的问题

当我们在 element-ui 中使用弹窗嵌套弹窗时&#xff0c;会出现第二个弹窗打开时被一个遮罩层挡着&#xff0c;就像下面这样&#xff1a; 下面提供两种解决方案 &#xff1a; 一、第一种方案 我们查询element-ui 官网可以发现 el-dialog 有这样几个属性&#xff1a; 具体使用就…...

【业务功能篇76】微服务网关路由predicates断言条件-filters路由转换地址-跨域问题-多级目录树化层级设计-mybatisPlus逻辑删除

业务开发-基础业务-分类管理 启动renren-fast如果出现如下错误 -Djps.track.ap.dependenciesfalse 添加相关配置即可 分类管理 1.后端分类接口 JDK8特性&#xff1a;https://blog.csdn.net/qq_38526573/category_11113126.html 在后端服务中我们需要查询出所有的三级分类信…...

apache的ab工具测试网页优化效果速度以及服务器承载

今天为大家介绍一款apache自带的一种的测试网页优化效果速度以及服务器承载的工具——ab.exe。 大家在工作中或者开发中可以使用apache的ab工具来测试自己的网站并发量大小&#xff0c;和某个页面的访问时间。 一、基本用法 如果你是用的是apache的话&#xff0c;那么只要进…...

【进阶篇】MySQL 存储引擎详解

文章目录 0.前言1.基础介绍2.1. InnoDB存储引擎底层原理InnoDB记录存储结构和索引页结构InnoDB记录存储结构&#xff1a;InnoDB索引页结构&#xff1a; 3. MVCC 详解3.1. 版本号分配&#xff1a;3.2. 数据读取&#xff1a;3.3. 数据写入&#xff1a;3.4. 事务隔离级别&#xff…...

Spring集成【MyBatis】和【PageHelper分页插件】整合---详细介绍

一&#xff0c;spring集成Mybatis的概念 Spring 整合 MyBatis 是将 MyBatis 数据访问框架与 Spring 框架进行集成&#xff0c;以实现更便捷的开发和管理。在集成过程中&#xff0c;Spring 提供了许多特性和功能&#xff0c;如依赖注入、声明式事务管理、AOP 等 它所带来给我们的…...

PyCharm下安装配置PySide6开发环境(Qt Designer(打开,编辑)、PyUIC和PyRCC)

一.准备工作 1.安装python和pycharm并配置好环境变量 python安装路径 pycharm安装路径&#xff1a; python系统变量&#xff1a; pycharm环境变量&#xff1a; 注意&#xff1a;正常安装&#xff0c;并勾选ADD PATH一般会自动配好 2.在pycharm创建一个新的python的虚拟环境 …...

pytest fixture 创建一个 requests.session() 对象

当你运行这段代码时&#xff0c;它会执行以下操作&#xff1a; 1. 导入必要的库&#xff1a;pytest 和 requests。 2. 定义一个夹具&#xff08;fixture&#xff09;函数 session&#xff0c;使用 pytest.fixture(scopesession) 装饰器进行标记。这个夹具函数在整个测试会话期…...

深入分析负载均衡情景

本文出现的内核代码来自Linux5.4.28&#xff0c;为了减少篇幅&#xff0c;我们尽量不引用代码&#xff0c;如果有兴趣&#xff0c;读者可以配合代码阅读本文。 一、有几种负载均衡的方式&#xff1f; 整个Linux的负载均衡器有下面的几个类型&#xff1a; 实际上内核的负载均衡…...

WPF基础入门-Class5-WPF命令

WPF基础入门 Class5-WPF命令 1、xaml编写一个button&#xff0c;Command绑定一个命令 <Grid><ButtonWidth"100"Height"40" Command"{Binding ShowCommand}"></Button> </Grid>2、编写一个model.cs namespace WPF_Le…...

云安全攻防(十三)之 使用minikube安装搭建 K8s 集群

使用minikube安装搭建 K8s 集群 Kubernetes 是一个可移植的、可扩展的开源平台&#xff0c;用于管理容器化的工作负载和服务&#xff0c;可促进声明式配置和自动化,一般来说K8s安装有三种方式&#xff0c;分别是Minikube装搭建 K8s 集群&#xff0c;特点是只有一个节点的集群&…...

Python数据分析 | 各种图表对比总结

本期将带领大家一起对在数据可视化的过程中常用的一些图表进行下总结&#xff1a; 条形图 【适用场景】 适用场合是二维数据集&#xff08;每个数据点包括两个值x和y&#xff09;&#xff0c;但只有一个维度需要比较&#xff0c;用于显示一段时间内的数据变化或显示各项之间的…...

linux系统(centos、ubuntu、银河麒麟服务、uos、deepin)判断程序是否已安装,通用判断方法:适用所有应用和命令的判断

前言 项目中需要判断linux服务器中是否已经安装了某个服务 方法有很多种&#xff0c;但是很多都不通用&#xff0c; 脚本代码就不容易做成统一的 解决方案 用下面的脚本代码去进行判断 用jdk测试 脚本意思如下&#xff1a; 输入java -version命令&#xff0c;将返回的字…...

Python3多线程/多进程解决方案(持续更新ing...)

诸神缄默不语-个人CSDN博文目录 文章目录 1. 多线程2. 多进程示例1&#xff1a;multiprocessing.Pool直接实现对一个列表中的每个元素的函数操作示例2&#xff1a;使用苏神写的工具函数实现对一个迭代器中每个元素的函数操作 1. 多线程 2. 多进程 示例1&#xff1a;multiproc…...

在`CentOS`中安装`Docker Engine`

本文总结如何在CentOS中安装Docker Engine 〇、Docker Engine 介绍 Docker Engine是一种开源容器化技术&#xff0c;用于构建和容器化应用程序。Docker引擎作为一个客户端-服务器应用程序: 具有长时间运行守护进程的服务器。指定接口的api&#xff0c;程序可以使用这些接口与…...

[ VMware 虚拟机 ] 启动不了图形界面,报 “The system is running in low-graphics mode” 错误

文章目录 问题现象异常原因解决方案 问题现象 在启动虚拟机的时候&#xff0c;不能正常的进入图形界面&#xff0c;报 “The system is running in low-graphics mode” 错误。 异常原因 启动界面的xorg.conf文件失败并删除。 解决方案 1、点击异常界面上的 “ok”后&…...

如何提高视频清晰度?视频调整清晰度操作方法

现在很多小伙伴通过制作短视频发布到一些短视频平台上记录生活&#xff0c;分享趣事。但制作的视频有些比较模糊&#xff0c;做视频的小伙伴应该都知道&#xff0c;视频画质模糊不清&#xff0c;会严重影响观众的观看体验。 通过研究&#xff0c;总结了以下几点严重影响的点 …...

IO进程线程,文件与目录,实现linux任意目录下ls -la

注意文件的名字、路径是如何输入的。 函数opendir打开目录&#xff0c;struct dirent&#xff0c;struct stat这些结构体的含义。 readdir()函数是一个用于读取目录内容的系统调用或库函数&#xff0c;在类Unix操作系统中&#xff08;如Linux&#xff09;广泛使用。它用于遍历…...

R语言如果列表中有列表,且每个子列表有一个向量:如何转变为仅仅一个列表里面含有向量

引言 有些时候&#xff0c;比如批量读取表格中的某一列的时候&#xff0c;最终你会得到列表里面装列表&#xff0c;且每个列表里面只有一个向量的情况。我们的目标是不要中间这一层列表&#xff0c;而是直接变成列表-向量这种简单的结构&#xff0c;如何完成呢。我觉得有很多方…...

nrm管理源仓库及发布私人npm包

使用nrm管理源及切换源仓库 1.安装nrm源管理器 npm install nrm -g2.查看目前现有的源仓库 通过 nrm ls 查看现有的源 nrm ls 输出&#xff1a;这是目前现有的源 3.切换不同的源 可以通过 nrm use xxx&#xff08;源仓库名&#xff09;来切换不同的源地址 nrm use taobao…...

云计算——虚拟化中的网络架构与虚拟网络(文末送书)

作者简介&#xff1a;一名云计算网络运维人员、每天分享网络与运维的技术与干货。 公众号&#xff1a;网络豆 座右铭&#xff1a;低头赶路&#xff0c;敬事如仪 个人主页&#xff1a; 网络豆的主页​​​​​ 目录 前期回顾 前言 一.网卡虚拟化 1.网卡虚拟化方法&…...

B - 负环

题目描述 给定一个 n 个点的有向图&#xff0c;请求出图中是否存在从顶点 11 出发能到达的负环。 负环的定义是&#xff1a;一条边权之和为负数的回路。 输入格式 本题单测试点有多组测试数据。 输入的第一行是一个整数 T&#xff0c;表示测试数据的组数。对于每组数据的格…...

居中一个元素(水平+垂直居中)

我们的示例代码全在此基础上修改&#xff1a; ...... <style>* {margin: 0;padding: 0;}.par {width: 600px;height: 400px;background-color: antiquewhite;display: flex;justify-content: center;align-items: center;}.chi1 {width: 60px;height: 40px;backgrou…...

React笔记(二)JSX

一、JSX JSX是javascript XML的简写&#xff0c;实际上是javascript的扩展&#xff0c;既有javascript的语法结构&#xff0c;又有XML的结构 1、JSX的规则要求 jsx必须要有一个根节点 如果不想产生无用的根标签&#xff0c;但是还要遵守JSX的语法的要求&#xff0c;可以使用…...

[多标签分类]MultiLabelBinarizer: 从one-hot 到multi-hot

]MultiLabelBinarizer: 从one-hot 到multi-hot 背景知识One hot encoderLabelEncoderMultiLabelBinarizer总结 背景知识 多类别分类: label space至少有3个label, 且默认每个sample有一个label, 与之相对应的是二元分类Binary classification, 多标签分类: 每个sample有1至多…...

【校招VIP】前端算法考察之排序

考点介绍&#xff1a; 不同的场景中&#xff0c;不同的排序算法执行效率不同。 稳定&#xff1a;冒泡、插入、归并 不稳定&#xff1a;选择、快速、堆排序、希尔排序 『前端算法考察之排序』相关题目及解析内容可点击文章末尾链接查看&#xff01; 一、考点题目 1、使用js实…...

集创北方ICN6211 是一款MIPIDSI转RGB视频桥接IC

ICN6211 1.描述&#xff1a; ICN6211是一个桥接芯片&#xff0c;它接收MIPIDSI输入并发送RGB输出。MIPIDSI最多支持4个车道&#xff0c; 每个车道的最大运行频率为1Gbps&#xff1b;总最大输入带宽为4Gbps&#xff1b;并且还支持MIPI定义的ULPS&#xff08;超 低功耗状态&a…...

SMT制造中的产品质量检验和管理

SMT制造中的质量检验和产品物料管理都是实现高质量、低成本、高效益的重要方法。在SMT加工的过程中&#xff0c;产品质量的检验和质量把控都是重中之重&#xff0c;可以有效的降低产品不良率及返修等造成制造成本升高的风险问题&#xff0c;今天就来跟大家讨论一下SMT制造中我们…...

对接webservice接口时报错:发送方和接收方 Action 不匹配

趁着早上有时间&#xff0c;赶紧记录一下&#xff0c;哈哈。 错误提示如下&#xff1a; 1、英文版&#xff1a; <s:Envelope xmlns:s“http://schemas.xmlsoap.org/soap/envelope/”><s:Body><s:Fault>a:ActionNotSupportedThe message with Action ‘’ ca…...

python实现/直播服务器/聊天服务器/的多种解决方案

python有哪些技术栈 实现直播服务器 在Python中&#xff0c;您可以使用以下技术栈来实现直播服务器&#xff1a; Flask&#xff1a;Flask是一个轻量级的Web框架&#xff0c;可用于构建直播服务器的后端。您可以使用Flask编写API端点来处理直播流的控制和管理。 Django&#xf…...

PbootCMS 3.0.4 SQL注入

1.漏洞复现 PbootCMS 3.0.4&#xff0c;下载仓库 星梦/PbootCMS - Gitee.com 复现 漏洞页面&#xff1a;http://127.0.0.1/?search 或 http://127.0.0.1/?keyword POST请求&#xff1a;1select 1 2.正向分析 从可见功能点正向分析 index.php ... // 引用内核启动文件…...

SpringBoot异步方法支持注解@Async应用

SpringBoot异步方法支持注解Async应用 1.为什么需要异步方法&#xff1f; 合理使用异步方法可以有效的提高执行效率 同步执行(同在一个线程中): 异步执行(开启额外线程来执行): 2.SpringBoot中的异步方法支持 在SpringBoot中并不需要我们自己去创建维护线程或者线程池来…...

UI/UX设计与前端开发:从零到一打造完美用户体验

引言 在当今的软件开发领域&#xff0c;UI/UX设计和前端开发是两个密不可分的环节。UI/UX设计师负责创造出直观、美观、用户友好的界面&#xff0c;而前端开发者则将这些设计转化为实际的、可交互的网页或应用。本文将深入探讨这两个领域的交集&#xff0c;并通过代码示例来展…...

Hadoop Hdfs基本命令

0目录 1.hadoop安装问题处理 2.hdfs基本命令 3.上传/下载文件和文件夹 1.hadoop安装问题处理 如果安装有进程无法启动&#xff0c;如下图 重新检查6个配置文件 Core-site.xml \ hdfs-site.xml \ hadoop-env.sh \ yarn-site.xml \ workers \ yarn-site.xml 来到hadoop313目录…...

Spring Boot 整合MyBatis(超详细)

&#x1f600;前言 本篇博文关于Spring Boot 整合MyBatis&#xff0c;希望你能够喜欢 &#x1f3e0;个人主页&#xff1a;晨犀主页 &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是晨犀&#xff0c;希望我的文章可以帮助到大家&#xff0c;您的满意是我的动力&#x…...

【管理运筹学】第 6 章 | 运输问题(4,表上作业法 |闭回路调整法以及特殊情况 | 产销不平衡的运输问题)

文章目录 引言二、表上作业法2.3 改进的方法 —— 闭回路调整法2.4 表上作业法中的特殊情况&#xff08;一&#xff09;无穷多最优解&#xff08;二&#xff09;退化 三、产销不平衡的运输问题3.1 产量大于销量3.2 销量大于产量 写在最后 引言 接下来我们学习表上作业法的最后…...

Greenplum实用技巧

一、通过gp_segment_id查看数据倾斜 gp_segment_id是表中的隐藏列&#xff0c;用来标记该行属于哪个segment节点。因此可以基于该隐藏列进行分组查询&#xff0c;获取每个segment的记录数&#xff0c;从而判断表数据的分布是否均匀或有倾斜。 qb#select gp_segment_id, count…...

以物联网为核心的智慧工地云平台:聚集智能技术,实现建筑工地智慧管理

智慧工地云平台源码&#xff0c;智慧工地项目监管平台源码&#xff0c;智慧工地可视化数据大屏源码 智慧工地云平台是将云计算、大数据、物联网、移动技术和智能设备等信息化技术手段&#xff0c;聚集在建筑工地施工管理现场&#xff0c;围绕人员、机械、物料、环境等关键要素&…...

Java项目-苍穹外卖-Day05-Redis技术应用

1.店铺营业状态设置 需求分析和设计 左上角要求是有回显的 所以至少两个接口 1.查询营业状态接口&#xff08;分为了管理端和用户端&#xff09; 2.修改营业状态接口 因为管理端和用户端路径不同&#xff0c;所以现在是至少三个接口的 可以发现如果存到表里除了id只有一个…...

linux安装jmeter

linux安装jmeter 部署java1.8 下载jmeter安装包&#xff1a;官网、网盘5.6.2版本 # 解压 rootiZbp1at7nu2rpq4xn4zaf1Z:/opt/jmeter# sudo tar -xzf apache-jmeter-5.6.2.tgz # 加入环境变量 rootiZbp1at7nu2rpq4xn4zaf1Z:/opt/jmeter/apache-jmeter-5.6.2# export JMETER/op…...

【笔记】泛型以及如何绕过泛型定义

泛型定义以及其带来的好处 泛型使类型&#xff08;类和接口&#xff09;能够在定义类、接口和方法时成为参数。与方法声明中使用的更熟悉的形式参数非常相似&#xff0c;类型参数为您提供了一种通过不同输入重复使用相同代码的方法。区别在于形式参数的输入是值&#xff0c;而…...

JAVA JNA 调用C接口的三种方式

文章目录 1. 准备一个共享库文件2. JNA姿势1—继承Library接口3. JNA姿势2—直接NativeLibrary.getInstance3. JNA姿势3—Native方法 1. 准备一个共享库文件 test.c #include <stdio.h> int test(char *input){printf("input:%s\n",input);return 0; }libtes…...

StarRocks入门到熟悉

1、部署 1.1、注意事项 需要根据业务需求设计严谨的集群架构&#xff0c;一般来说&#xff0c;需要注意以下几项&#xff1a; 1.1.1、FE数量及高可用 FE的Follower要求为奇数个&#xff0c;且并不建议部署太多&#xff0c;通常我们推荐部署1个或3个Follower。在三个Followe…...

华为AR路由器 典型配置案例——以太网交换

目录 Eth-Trunk 例&#xff1a;配置三层链路聚合 组网需求 操作步骤 检查配置结果 配置脚本 VLAN 举例&#xff1a;配置基于接口划分VLAN&#xff0c;实现同一VLAN内的互通&#xff08;同设备&#xff09; 组网需求 操作步骤 检查配置结果 配置脚本 举例&#xff…...

DP读书:鲲鹏处理器 架构与编程(十三)操作系统内核与云基础软件

操作系统内核与云基础软件 鲲鹏软件构成硬件特定软件 鲲鹏软件构成硬件特定软件1. Boot Loader2. SBSA 与 SBBR3. UEFI4. ACPI 操作系统内核Linux系统调用Linux进程调度Linux内存管理Linux虚拟文件系统Linux网络子系统Linux进程间通信Linux可加载内核模块Linux设备驱动程序Linu…...

Vue2项目练手——通用后台管理项目第一节

Vue2项目练手——通用后台管理项目 知识补充yarn和npm区别npm的缺点&#xff1a;yarn的优点 npm查看镜像和设置镜像 项目介绍项目的技术栈 项目搭建文件目录 创建路由&#xff0c;引入element-uirouter/index.jsmain.jspages/Users.vuepages/Main.vuepages/Home.vuepages/Login…...

「Vue|网页开发|前端开发」02 从单页面到多页面网站:使用路由实现网站多个页面的展示和跳转

本文主要介绍如何使用路由控制来实现将一个单页面网站扩展成多页面网站&#xff0c;包括页面扩展的逻辑&#xff0c;vue的官方路由vue-router的基本用法以及扩展用法 文章目录 本系列前文传送门一、场景说明二、基本的页面扩展页面扩展是在扩什么创建新页面的代码&#xff0c;…...

【Nginx20】Nginx学习:FastCGI模块(二)缓存配置

Nginx学习&#xff1a;FastCGI模块&#xff08;二&#xff09;缓存配置 通过上篇文章的学习&#xff0c;普通的 PHP 与 Nginx 的连接就已经没啥大问题了。一般的网站直接那套配置就够了&#xff0c;这也是 Nginx 非常友好的一面。很多在默认的配置文件中注释掉的内容&#xff0…...