【面试】Java中的多种设计模式(十种主要设计模式)

Java中的多种设计模式(十种主要设计模式)

在这里插入图片描述

文章概述

设计模式是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。它是软件工程中常见问题的解决方案的一种描述或模板。设计模式可以提供一种通用的、可重用的解决方案,帮助开发人员解决某类问题时采用一种结构良好且经过验证的方法。

设计模式并不是可以直接转化为代码的东西,而是一种解决问题的思路、一种经验的总结。它在面向对象软件设计中起到指导作用,通过设计模式,开发人员能够更容易地理解代码结构,更快地定位和解决问题。

设计模式的主要目标包括:

  1. 提高代码的可重用性: 设计模式可以使开发人员将经过验证的设计思想和方法应用于新的问题,从而提高代码的可重用性。

  2. 提高代码的可维护性: 设计模式能够提供一种清晰的结构,使得代码更易于理解和维护。

  3. 降低代码的耦合性: 设计模式通过定义良好的接口和抽象类,有助于降低代码组件之间的依赖关系,减少耦合性。

  4. 提高代码的可扩展性: 设计模式通过强调松散耦合,使得系统更容易扩展和修改。

一些常见的设计模式包括单例模式、工厂模式、观察者模式、策略模式等。这些模式都有明确定义的结构和角色,开发人员可以根据问题的性质选择适当的设计模式来解决。设计模式是一种在软件开发中非常有用的实践,但也需要根据具体情况慎重选择和应用。

设计模式列举

Java 中常用的设计模式有很多,其中一些主要的设计模式包括:

  1. 单例模式(Singleton Pattern):

    • 确保一个类只有一个实例,并提供一个全局访问点。
    • 示例:java.lang.Runtimejava.awt.Desktop
  2. 工厂模式(Factory Pattern):

    • 定义一个创建对象的接口,但由子类决定实例化哪个类。
    • 示例:java.util.Calendarjava.text.NumberFormat
  3. 抽象工厂模式(Abstract Factory Pattern):

    • 提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
    • 示例:javax.xml.parsers.DocumentBuilderFactory
  4. 建造者模式(Builder Pattern):

    • 将一个复杂对象的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。
    • 示例:java.lang.StringBuilderjava.nio.ByteBuffer
  5. 原型模式(Prototype Pattern):

    • 通过复制现有对象来创建新对象,而不是通过实例化。
    • 示例:java.lang.Object#clone()
  6. 适配器模式(Adapter Pattern):

    • 将一个类的接口转换成客户希望的另一个接口。
    • 示例:java.util.Arrays#asList()java.io.InputStreamReader
  7. 装饰器模式(Decorator Pattern):

    • 动态地给一个对象添加一些额外的职责,就扩展功能而言,装饰模式比生成子类更灵活。
    • 示例:java.io中的诸多类,如BufferedReader
  8. 观察者模式(Observer Pattern):

    • 定义对象间的一对多依赖,当一个对象状态改变时,所有依赖它的对象都得到通知并被自动更新。
    • 示例:java.util.Observerjava.util.Observable
  9. 策略模式(Strategy Pattern):

    • 定义一系列算法,将每个算法封装起来,并使它们可以互换。
    • 示例:java.util.Comparatorjava.util.Collections#sort()
  10. 责任链模式(Chain of Responsibility Pattern):

    • 使多个对象都有机会处理请求,从而避免了请求的发送者和接收者之间的耦合关系。
    • 示例:java.util.logging.Logger#log()

这些设计模式提供了在不同场景下解决问题的通用解决方案,有助于提高代码的可维护性、可扩展性和重用性。在实际开发中,根据具体的问题和需求选择合适的设计模式是很重要的。

单例模式详解

单例模式(Singleton Pattern) 是一种创建型设计模式,其主要目的是确保一个类只有一个实例,并提供一个全局访问点。这种模式适用于需要全局访问点且只能有一个实例的场景,例如线程池、缓存、日志对象等。

单例模式的特点:

  1. 单一实例: 单例模式确保类只有一个实例,提供一个全局访问点。
  2. 延迟实例化(Lazy Initialization): 实例只有在第一次被请求时才被创建,避免了不必要的资源开销。
  3. 全局访问点: 提供一个全局的访问点,使得其他类可以轻松地访问该实例。

单例模式的实现方式:

1. 饿汉式(Eager Initialization):
public class Singleton {private static final Singleton instance = new Singleton();// 私有构造方法,防止外部实例化private Singleton() {}// 全局访问点public static Singleton getInstance() {return instance;}
}

在饿汉式中,实例在类加载时就被创建,因此保证了线程安全。但是可能会导致不必要的资源浪费,因为实例在整个生命周期中都会存在,即使没有被用到。

2. 懒汉式(Lazy Initialization):
public class Singleton {private static Singleton instance;// 私有构造方法,防止外部实例化private Singleton() {}// 全局访问点,使用双重检查锁定来保证线程安全public static Singleton getInstance() {if (instance == null) {synchronized (Singleton.class) {if (instance == null) {instance = new Singleton();}}}return instance;}
}

懒汉式在第一次被请求时才创建实例,避免了不必要的资源浪费。但是需要注意,需要使用双重检查锁定(Double-Checked Locking)来保证在多线程环境中的线程安全性。

单例模式的应用场景:

  1. 资源共享: 单例模式可以确保多个模块使用同一个实例,共享资源。
  2. 控制实例数量: 有些情况下,系统只需要一个实例,通过单例模式可以控制实例的数量。
  3. 全局访问点: 提供一个全局的访问点,方便其他类调用。

两个经典例子分别是:

  1. Java中的Runtime类: Runtime类的实例是单例的,可以通过Runtime.getRuntime()方法获取,用于与运行时环境交互。

    Runtime runtime = Runtime.getRuntime();
    
  2. 日志系统中的Logger类: 许多日志框架使用单例模式来管理日志实例,以确保所有的日志输出都通过同一个日志对象进行管理。

    Logger logger = Logger.getLogger("MyLogger");
    

这两个例子都体现了单例模式在实际应用中的常见场景和优势。

工厂模式详解

工厂模式(Factory Pattern) 是一种创建型设计模式,其主要目的是提供一个接口来创建对象,但允许子类在运行时更改创建的类。工厂模式解决了直接在代码中使用构造函数创建对象的问题,使得代码更具灵活性和可维护性。

工厂模式的主要组成部分:

  1. 抽象产品(Abstract Product): 定义了产品的接口,声明了产品的方法。
  2. 具体产品(Concrete Product): 实现了抽象产品接口的具体类,是工厂模式创建的对象。
  3. 抽象工厂(Abstract Factory): 声明了创建抽象产品的方法,是工厂模式的核心接口。
  4. 具体工厂(Concrete Factory): 实现了抽象工厂接口,负责创建具体产品的对象。

工厂模式的实现方式:

1. 简单工厂模式(Simple Factory Pattern):
public interface Product {void produce();
}public class ConcreteProductA implements Product {@Overridepublic void produce() {System.out.println("Product A is produced.");}
}public class ConcreteProductB implements Product {@Overridepublic void produce() {System.out.println("Product B is produced.");}
}public class SimpleFactory {public static Product createProduct(String type) {if ("A".equals(type)) {return new ConcreteProductA();} else if ("B".equals(type)) {return new ConcreteProductB();}throw new IllegalArgumentException("Invalid product type");}
}

在简单工厂模式中,通过一个工厂类的静态方法来创建产品对象。这种方式简单易懂,但不符合开闭原则,如果需要新增产品类型,需要修改工厂类的代码。

2. 工厂方法模式(Factory Method Pattern):
public interface Product {void produce();
}public class ConcreteProductA implements Product {@Overridepublic void produce() {System.out.println("Product A is produced.");}
}public class ConcreteProductB implements Product {@Overridepublic void produce() {System.out.println("Product B is produced.");}
}public interface Factory {Product createProduct();
}public class ConcreteFactoryA implements Factory {@Overridepublic Product createProduct() {return new ConcreteProductA();}
}public class ConcreteFactoryB implements Factory {@Overridepublic Product createProduct() {return new ConcreteProductB();}
}

在工厂方法模式中,定义一个抽象的工厂接口,由具体的工厂类实现,每个工厂类负责创建特定类型的产品。这样可以满足开闭原则,添加新的产品类型时只需新增相应的工厂类。

工厂模式的应用场景:

  1. 对象的创建复杂: 当一个对象的创建过程比较复杂,包括多个步骤或者涉及到一些条件判断时,可以使用工厂模式封装这些复杂的创建过程。
  2. 类不希望直接暴露在外部: 当类的实现细节发生变化时,不希望直接修改客户端的代码,可以通过工厂模式来隐藏具体的实现。

两个经典的例子是:

  1. Java中的Calendar类: Calendar类是一个抽象工厂类,提供了静态方法getInstance(),根据不同的实现子类(如GregorianCalendar)来创建不同的日历对象。

    Calendar calendar = Calendar.getInstance();
    
  2. Java中的Executor框架: Executor框架提供了一系列工厂方法,用于创建不同类型的线程池,如Executors.newFixedThreadPool()Executors.newCachedThreadPool()等。

    ExecutorService executorService = Executors.newFixedThreadPool(5);
    

这两个例子展示了工厂模式在实际应用中的灵活性和可扩展性,通过工厂模式可以更好地组织和管理对象的创建过程。

抽象工厂模式详解

抽象工厂模式(Abstract Factory Pattern) 是一种创建型设计模式,与工厂方法模式类似,它也是用于创建一系列相关或相互依赖的对象,但抽象工厂模式更强调一系列相关的产品对象的创建,形成一个产品族,而不仅仅是一个单一的产品。

抽象工厂模式的主要组成部分:

  1. 抽象产品(Abstract Product): 定义了产品的接口,声明了产品的方法。
  2. 具体产品(Concrete Product): 实现了抽象产品接口的具体类,是工厂模式创建的对象。
  3. 抽象工厂(Abstract Factory): 声明了一系列创建抽象产品的方法,形成一个产品族的接口。
  4. 具体工厂(Concrete Factory): 实现了抽象工厂接口,负责创建一系列相关的具体产品。

抽象工厂模式的实现方式:

// 抽象产品A
public interface ProductA {void useA();
}// 具体产品A1
public class ConcreteProductA1 implements ProductA {@Overridepublic void useA() {System.out.println("Product A1 is used.");}
}// 具体产品A2
public class ConcreteProductA2 implements ProductA {@Overridepublic void useA() {System.out.println("Product A2 is used.");}
}// 抽象产品B
public interface ProductB {void useB();
}// 具体产品B1
public class ConcreteProductB1 implements ProductB {@Overridepublic void useB() {System.out.println("Product B1 is used.");}
}// 具体产品B2
public class ConcreteProductB2 implements ProductB {@Overridepublic void useB() {System.out.println("Product B2 is used.");}
}// 抽象工厂
public interface AbstractFactory {ProductA createProductA();ProductB createProductB();
}// 具体工厂1
public class ConcreteFactory1 implements AbstractFactory {@Overridepublic ProductA createProductA() {return new ConcreteProductA1();}@Overridepublic ProductB createProductB() {return new ConcreteProductB1();}
}// 具体工厂2
public class ConcreteFactory2 implements AbstractFactory {@Overridepublic ProductA createProductA() {return new ConcreteProductA2();}@Overridepublic ProductB createProductB() {return new ConcreteProductB2();}

在抽象工厂模式中,抽象工厂接口定义了一系列创建相关产品的方法,每个具体工厂类实现了这些方法,分别创建一系列具体的产品对象。这样,客户端可以通过选择具体的工厂来创建一整套相关的产品。

抽象工厂模式的应用场景:

  1. 一系列相关产品的创建: 当需要创建一系列相关或相互依赖的产品对象时,使用抽象工厂模式可以确保这些产品对象之间的兼容性。
  2. 系统独立于其产品的创建、组合和表示: 客户端通过抽象接口与产品的系列进行交互,使得客户端与具体实现解耦。

两个经典的例子是:

  1. 图形界面库中的抽象工厂: 在图形界面库中,抽象工厂可以定义创建按钮、文本框等界面元素的方法,而具体工厂可以分别实现这些方法以创建特定风格(如Windows风格、Mac风格)的界面元素。

  2. 数据库访问库中的抽象工厂: 在数据库访问库中,抽象工厂可以定义创建连接、命令等数据库访问对象的方法,而具体工厂可以实现这些方法以创建特定类型(如MySQL、Oracle)的数据库访问对象。

这两个例子展示了抽象工厂模式在实际应用中的灵活性和可维护性,通过抽象工厂模式可以方便地扩展产品系列,而不影响客户端代码。

建造者模式详解

建造者模式(Builder Pattern) 是一种创建型设计模式,其主要目的是将一个复杂对象的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。建造者模式适用于需要一步一步构建复杂对象的场景,以及在构建过程中需要更改对象的属性。

建造者模式的主要角色:

  1. 产品(Product): 表示被构建的复杂对象,包含多个组成部分。
  2. 抽象建造者(Builder): 声明了构建产品各个部分的抽象方法。
  3. 具体建造者(Concrete Builder): 实现了抽象建造者接口,负责构建产品的各个部分,同时提供一个获取构建结果的方法。
  4. 指挥者(Director): 调用建造者的构建方法,按照一定的顺序和逻辑来构建产品。

建造者模式的实现方式:

// 产品
public class Product {private String partA;private String partB;private String partC;// 省略构造函数、getter和setter方法@Overridepublic String toString() {return "Product{partA='" + partA + "', partB='" + partB + "', partC='" + partC + "'}";}
}// 抽象建造者
public interface Builder {void buildPartA();void buildPartB();void buildPartC();Product getResult();
}// 具体建造者A
public class ConcreteBuilderA implements Builder {private Product product = new Product();@Overridepublic void buildPartA() {product.setPartA("PartA for BuilderA");}@Overridepublic void buildPartB() {product.setPartB("PartB for BuilderA");}@Overridepublic void buildPartC() {product.setPartC("PartC for BuilderA");}@Overridepublic Product getResult() {return product;}
}// 具体建造者B
public class ConcreteBuilderB implements Builder {private Product product = new Product();@Overridepublic void buildPartA() {product.setPartA("PartA for BuilderB");}@Overridepublic void buildPartB() {product.setPartB("PartB for BuilderB");}@Overridepublic void buildPartC() {product.setPartC("PartC for BuilderB");}@Overridepublic Product getResult() {return product;}
}// 指挥者
public class Director {public void construct(Builder builder) {builder.buildPartA();builder.buildPartB();builder.buildPartC();}
}

建造者模式的应用场景:

  1. 构建一个复杂对象: 当一个对象的构建过程比较复杂,有很多步骤和参数需要设置时,使用建造者模式可以将构建过程封装起来,使得代码更清晰。
  2. 创建的对象需要多个表示: 当一个对象有多个表示形式(不同的属性),而且需要在构建过程中灵活改变时,可以使用建造者模式。

两个经典的例子就是:

  1. StringBuilder类: StringBuilder类允许动态地构建字符串对象,通过链式调用append方法可以方便地添加字符、字符串等内容。最后通过toString方法获取最终的字符串对象。

    StringBuilder stringBuilder = new StringBuilder();
    stringBuilder.append("Hello");
    stringBuilder.append(" ");
    stringBuilder.append("Builder");
    String result = stringBuilder.toString();
    
  2. Java中的Locale.Builder类: Locale.Builder类用于构建Locale对象,可以通过设置语言、国家、地区等属性来构建一个Locale对象。

    Locale.Builder localeBuilder = new Locale.Builder();
    Locale locale = localeBuilder.setLanguage("en").setRegion("US").build();
    

这两个例子展示了建造者模式在实际应用中的灵活性,通过建造者模式,可以更好地组织和管理对象的构建过程,使得代码更加清晰和可维护。

原型模式详解

原型模式(Prototype Pattern) 是一种创建型设计模式,其主要目的是通过复制现有对象来创建新对象,而不是通过构造函数进行创建。原型模式对于创建成本较高的对象,或者对象的创建过程比较复杂而又需要频繁创建时,能够提高性能和简化代码。

原型模式的主要角色:

  1. 原型接口(Prototype): 声明了克隆方法的接口。
  2. 具体原型类(Concrete Prototype): 实现了克隆方法,通过复制自身创建新对象。

原型模式的实现方式:

// 原型接口
public interface Prototype extends Cloneable {Prototype clone();
}// 具体原型类
public class ConcretePrototype implements Prototype {private String attribute;public ConcretePrototype(String attribute) {this.attribute = attribute;}@Overridepublic Prototype clone() {try {// 利用Java的clone()方法实现对象的浅复制return (ConcretePrototype) super.clone();} catch (CloneNotSupportedException e) {e.printStackTrace();return null;}}public String getAttribute() {return attribute;}public void setAttribute(String attribute) {this.attribute = attribute;}
}

原型模式的应用场景:

  1. 创建成本较高的对象: 当创建对象的成本较高,但又需要频繁创建新对象时,可以使用原型模式,通过复制现有对象来创建新对象,避免重复的初始化操作。
  2. 对象的构建过程复杂: 当对象的构建过程比较复杂,而且需要创建多个相似对象时,可以使用原型模式来简化对象的构建过程。

两个经典的例子是:

  1. Java中的Object.clone()方法: Object类提供了一个clone()方法,可以实现对象的浅复制。任何类只要实现Cloneable接口并覆写clone()方法,就可以通过clone()方法实现对象的复制。

    MyClass original = new MyClass();
    MyClass copy = (MyClass) original.clone();
    
  2. 图形编辑器中的复制粘贴功能: 在图形编辑器中,用户可以通过复制一个图形对象来创建一个新的相似对象,然后粘贴到画布上。这个过程可以使用原型模式来实现,复制操作就是通过原型对象创建新对象的过程。

这两个例子展示了原型模式在实际应用中的灵活性和可用性,通过原型模式可以方便地实现对象的复制,避免了重复的初始化过程。需要注意的是,对于复杂对象,可能需要考虑深复制的问题,以确保克隆对象的属性也是独立的。

适配器模式详解

适配器模式(Adapter Pattern) 是一种结构型设计模式,它允许接口不兼容的类能够一起工作。适配器模式充当两个不兼容接口之间的桥梁,使得它们能够协同工作。

适配器模式的主要角色:

  1. 目标接口(Target): 定义客户端使用的与特定领域相关的接口。
  2. 适配器(Adapter): 实现目标接口,并持有被适配者的实例,负责将客户端的请求转化为被适配者的接口。
  3. 被适配者(Adaptee): 包含需要被适配的接口,即客户端希望使用的接口。
  4. 客户端(Client): 使用目标接口与适配器进行交互,调用适配器的方法。

适配器模式的实现方式:

1. 类适配器模式:
// 目标接口
public interface Target {void request();
}// 被适配者
public class Adaptee {public void specificRequest() {System.out.println("Specific request");}
}// 适配器
public class ClassAdapter extends Adaptee implements Target {@Overridepublic void request() {specificRequest();}
}
2. 对象适配器模式:
// 目标接口
public interface Target {void request();
}// 被适配者
public class Adaptee {public void specificRequest() {System.out.println("Specific request");}
}// 适配器
public class ObjectAdapter implements Target {private Adaptee adaptee;public ObjectAdapter(Adaptee adaptee) {this.adaptee = adaptee;}@Overridepublic void request() {adaptee.specificRequest();}
}

适配器模式的应用场景:

  1. 系统需要与已有的类库、框架或模块进行集成: 当系统需要使用一些已经存在的类,但这些类的接口与系统要求的接口不一致时,可以使用适配器模式进行适配。
  2. 不同接口间的协同工作: 当系统中的一个组件需要与另一个组件协同工作,但它们的接口不兼容时,可以使用适配器模式。

两个经典的例子就是:

  1. Java中的Arrays.asList()方法: 该方法返回一个List对象,但底层实际上是基于数组的,通过适配器模式将数组适配为List

    String[] array = {"A", "B", "C"};
    List<String> list = Arrays.asList(array);
    
  2. Java中的InputStreamReader类: InputStreamReader是字节流到字符流的适配器,它将字节流适配为字符流,使得可以按字符而不是字节来读取数据。

    InputStream inputStream = new FileInputStream("file.txt");
    Reader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
    

这两个例子都展示了适配器模式在实际应用中的灵活性和可用性,通过适配器模式可以使得原本不兼容的接口能够协同工作。

装配器模式详解

装饰器模式(Decorator Pattern) 是一种结构型设计模式,它允许动态地将责任附加到对象上。装饰器模式提供了一种灵活的方式,通过扩展类的功能来创建新的对象,而不是通过创建子类。

装饰器模式的主要角色:

  1. 组件接口(Component): 定义了具体组件和装饰器的共同接口,客户端通过该接口与具体组件进行交互。
  2. 具体组件(Concrete Component): 实现了组件接口的具体类,是被装饰的原始对象。
  3. 装饰器抽象类(Decorator): 实现了组件接口,并包含一个指向具体组件的引用,通过该引用可以调用被装饰对象的方法。
  4. 具体装饰器(Concrete Decorator): 扩展了装饰器抽象类,添加了额外的责任或行为。

装饰器模式的实现方式:

// 组件接口
public interface Component {void operation();
}// 具体组件
public class ConcreteComponent implements Component {@Overridepublic void operation() {System.out.println("ConcreteComponent operation");}
}// 装饰器抽象类
public abstract class Decorator implements Component {private Component component;public Decorator(Component component) {this.component = component;}@Overridepublic void operation() {component.operation();}
}// 具体装饰器A
public class ConcreteDecoratorA extends Decorator {public ConcreteDecoratorA(Component component) {super(component);}@Overridepublic void operation() {super.operation();System.out.println("ConcreteDecoratorA operation");}
}// 具体装饰器B
public class ConcreteDecoratorB extends Decorator {public ConcreteDecoratorB(Component component) {super(component);}@Overridepublic void operation() {super.operation();System.out.println("ConcreteDecoratorB operation");}
}

装饰器模式的应用场景:

  1. 动态地给对象添加功能: 当需要动态地给对象添加额外的功能或责任时,装饰器模式可以提供一种灵活的解决方案,避免使用静态继承。
  2. 避免使用子类进行扩展: 当类的数目庞大,且每个类都需要以多种方式进行扩展时,使用装饰器模式可以避免创建大量的子类。

两个经典的例子如下:

  1. Java中的BufferedReader类: BufferedReaderReader的装饰器,它通过持有一个Reader对象,并在其基础上添加了缓冲区的功能,提供了更高效的读取方法。

    Reader reader = new FileReader("file.txt");
    BufferedReader bufferedReader = new BufferedReader(reader);
    
  2. Java中的InputStreamReader类: InputStreamReader是字节流到字符流的适配器,同时也是Reader的装饰器,它可以通过包装其他InputStream对象来提供字符流的读取能力。

    InputStream inputStream = new FileInputStream("file.txt");
    Reader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
    

这两个例子都展示了装饰器模式在实际应用中的灵活性和可用性,通过装饰器模式可以在运行时动态地给对象添加额外的功能,而无需修改其结构。

观察者模式详解

观察者模式(Observer Pattern) 是一种行为型设计模式,其主要目的是定义对象之间的一对多依赖关系,使得当一个对象状态改变时,其所有依赖对象都会得到通知并自动更新。观察者模式提供了一种简单而灵活的方法来实现发布-订阅机制。

观察者模式的主要角色:

  1. 主题(Subject): 负责维护一组观察者对象,提供方法来添加、删除和通知观察者。
  2. 具体主题(Concrete Subject): 实现了主题接口,维护自身状态,并在状态改变时通知观察者。
  3. 观察者(Observer): 定义了更新接口,以便在主题状态发生改变时得到通知。
  4. 具体观察者(Concrete Observer): 实现了观察者接口,具体的观察者对象,在接收到通知时执行特定的操作。

观察者模式的实现方式:

// 主题接口
public interface Subject {void addObserver(Observer observer);void removeObserver(Observer observer);void notifyObservers();
}// 具体主题
public class ConcreteSubject implements Subject {private List<Observer> observers = new ArrayList<>();private int state;@Overridepublic void addObserver(Observer observer) {observers.add(observer);}@Overridepublic void removeObserver(Observer observer) {observers.remove(observer);}@Overridepublic void notifyObservers() {for (Observer observer : observers) {observer.update(state);}}public void setState(int state) {this.state = state;notifyObservers();}
}// 观察者接口
public interface Observer {void update(int state);
}// 具体观察者A
public class ConcreteObserverA implements Observer {@Overridepublic void update(int state) {System.out.println("ConcreteObserverA is notified with state: " + state);}
}// 具体观察者B
public class ConcreteObserverB implements Observer {@Overridepublic void update(int state) {System.out.println("ConcreteObserverB is notified with state: " + state);}
}

观察者模式的应用场景:

  1. 对象之间存在一对多的依赖关系: 当一个对象的改变需要通知其他多个对象时,可以使用观察者模式。
  2. 抽象和实现之间存在分离: 当一个对象的改变需要通知其他多个对象时,但又希望尽量减少它们之间的耦合度,可以使用观察者模式。

两个经典的例子如下:

  1. Java中的java.util.Observablejava.util.Observer Observable是抽象主题类,Observer是抽象观察者接口。具体主题通过继承Observable,具体观察者通过实现Observer接口。

    // 具体主题
    public class ConcreteSubject extends Observable {private int state;public void setState(int state) {this.state = state;setChanged();notifyObservers(state);}
    }// 具体观察者
    public class ConcreteObserver implements Observer {@Overridepublic void update(Observable o, Object arg) {int state = (int) arg;System.out.println("ConcreteObserver is notified with state: " + state);}
    }
    
  2. Android中的事件监听机制: Android中的事件监听机制也是观察者模式的一种应用,例如,通过setOnClickListener方法设置按钮的点击事件监听器,当按钮被点击时,注册的监听器就会收到通知并执行相应的操作。

    Button button = findViewById(R.id.button);
    button.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {// 按钮被点击时执行的操作}
    });
    

这两个例子展示了观察者模式在实际应用中的灵活性和可用性,通过观察者模式可以实现对象之间的松耦合,使得它们之间的关系更加灵活和可维护。

策略模式详解

策略模式(Strategy Pattern) 是一种行为型设计模式,它定义了一系列算法,将每个算法封装起来,并使它们可以互相替换。策略模式使得算法的变化独立于使用算法的客户端,从而实现了算法的灵活性和可维护性。

策略模式的主要角色:

  1. 策略接口(Strategy): 定义了算法的接口,具体策略类要实现这个接口。
  2. 具体策略类(Concrete Strategy): 实现了策略接口,包含具体的算法实现。
  3. 上下文(Context): 维持一个对策略对象的引用,可以在运行时切换不同的策略。

策略模式的实现方式:

// 策略接口
public interface Strategy {void performAlgorithm();
}// 具体策略类A
public class ConcreteStrategyA implements Strategy {@Overridepublic void performAlgorithm() {System.out.println("ConcreteStrategyA is used.");}
}// 具体策略类B
public class ConcreteStrategyB implements Strategy {@Overridepublic void performAlgorithm() {System.out.println("ConcreteStrategyB is used.");}
}// 上下文
public class Context {private Strategy strategy;public Context(Strategy strategy) {this.strategy = strategy;}public void setStrategy(Strategy strategy) {this.strategy = strategy;}public void executeAlgorithm() {strategy.performAlgorithm();}
}

策略模式的应用场景:

  1. 存在多个相似的算法,需要在运行时动态选择其中一个: 当一个问题可以有多种解决方式,并且需要根据具体情况选择其中一种方式时,可以使用策略模式。
  2. 避免使用条件语句决定算法的选择: 当需要根据某个条件选择不同的算法时,使用策略模式可以避免使用繁琐的条件语句,提高代码的可读性和可维护性。

两个经典的例子如下:

  1. 排序算法的策略模式: 假设有一个排序类,可以根据不同的排序算法进行排序。通过策略模式,可以将各种排序算法封装成具体的策略类,然后在运行时选择不同的策略。

    // 策略接口
    public interface SortingStrategy {void sort(int[] array);
    }// 具体策略类A
    public class BubbleSort implements SortingStrategy {@Overridepublic void sort(int[] array) {// 冒泡排序算法的具体实现}
    }// 具体策略类B
    public class QuickSort implements SortingStrategy {@Overridepublic void sort(int[] array) {// 快速排序算法的具体实现}
    }// 上下文
    public class Sorter {private SortingStrategy strategy;public Sorter(SortingStrategy strategy) {this.strategy = strategy;}public void setStrategy(SortingStrategy strategy) {this.strategy = strategy;}public void performSort(int[] array) {strategy.sort(array);}
    }
    
  2. 支付方式的策略模式: 假设有一个支付类,可以根据不同的支付方式进行支付。通过策略模式,可以将各种支付方式封装成具体的策略类,然后在运行时选择不同的策略。

    // 策略接口
    public interface PaymentStrategy {void pay(double amount);
    }// 具体策略类A
    public class CreditCardPayment implements PaymentStrategy {@Overridepublic void pay(double amount) {// 信用卡支付的具体实现}
    }// 具体策略类B
    public class PayPalPayment implements PaymentStrategy {@Overridepublic void pay(double amount) {// PayPal支付的具体实现}
    }// 上下文
    public class PaymentProcessor {private PaymentStrategy strategy;public PaymentProcessor(PaymentStrategy strategy) {this.strategy = strategy;}public void setStrategy(PaymentStrategy strategy) {this.strategy = strategy;}public void processPayment(double amount) {strategy.pay(amount);}
    }
    

这两个例子展示了策略模式在实际应用中的灵活性和可用性,通过策略模式可以方便地扩展和替换算法或业务规则。

责任链模式详解

责任链模式(Chain of Responsibility Pattern) 是一种行为型设计模式,它允许多个对象都有机会处理请求,从而避免将请求的发送者与接收者直接耦合在一起。请求沿着链传递,直到有一个对象处理它为止。

责任链模式的主要角色:

  1. 处理者接口(Handler): 定义了处理请求的接口,具体处理者要实现这个接口。
  2. 具体处理者(Concrete Handler): 实现了处理者接口,处理它所负责的请求,可以访问其后继者。
  3. 客户端(Client): 创建一个请求并将它发送给处理者。

责任链模式的实现方式:

// 处理者接口
public interface Handler {void handleRequest(Request request);
}// 具体处理者A
public class ConcreteHandlerA implements Handler {private Handler successor;@Overridepublic void handleRequest(Request request) {if (request.getType() == RequestType.TYPE_A) {// 处理请求的逻辑} else if (successor != null) {successor.handleRequest(request);}}public void setSuccessor(Handler successor) {this.successor = successor;}
}// 具体处理者B
public class ConcreteHandlerB implements Handler {private Handler successor;@Overridepublic void handleRequest(Request request) {if (request.getType() == RequestType.TYPE_B) {// 处理请求的逻辑} else if (successor != null) {successor.handleRequest(request);}}public void setSuccessor(Handler successor) {this.successor = successor;}
}// 请求类
public class Request {private RequestType type;public Request(RequestType type) {this.type = type;}public RequestType getType() {return type;}
}// 请求类型枚举
public enum RequestType {TYPE_A, TYPE_B
}

责任链模式的应用场景:

  1. 有多个对象可以处理同一请求,但具体哪个对象处理由运行时决定: 当系统中的处理者可能根据请求的不同而变化,并且客户端不知道具体哪个处理者能够处理请求时,可以使用责任链模式。
  2. 需要动态指定处理流程时: 当系统中的处理流程可能根据不同的条件而变化,需要在运行时动态地指定处理流程时,可以使用责任链模式。

两个经典的例子:

  1. Java中的异常处理: Java中的异常处理机制就是一个责任链模式的实现。当一个方法抛出异常时,系统会从当前方法开始,按照调用层次逐级向上查找能够处理该异常的catch块,直到找到合适的处理者或者到达顶层方法。

    try {// 可能抛出异常的代码
    } catch (ExceptionType1 e) {// 处理ExceptionType1的逻辑
    } catch (ExceptionType2 e) {// 处理ExceptionType2的逻辑
    } catch (Exception e) {// 处理其他异常的逻辑
    }
    
  2. Android中的事件分发机制: Android中的事件分发机制也是一个责任链模式的实现。事件从顶层的View开始传递,依次经过父容器和子View,最终到达目标View。每个View都有机会处理事件,如果处理不了就交给下一个处理者。

    public boolean dispatchTouchEvent(MotionEvent event) {// 一系列的处理逻辑if (onInterceptTouchEvent(event)) {// 如果拦截了事件,直接返回,不再往下传递return true;} else {// 没有拦截,继续往下传递return super.dispatchTouchEvent(event);}
    }
    

这两个例子都展示了责任链模式在实际应用中的灵活性和可用性,通过责任链模式可以实现一种松散耦合的处理机制,使得系统更易于扩展和维护。

十种主要模式大概如此,但是日常项目中并非经常涉及,但是,可以不用,我们却不能不了解!说不定哪一天的面试就会用到呢!

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

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

相关文章

你好!Apache Seata

北京时间 2023 年 10 月 29 日&#xff0c;分布式事务开源项目 Seata 正式通过 Apache 基金会的投票决议&#xff0c;以全票通过的优秀表现正式成为 Apache 孵化器项目&#xff01; 根据 Apache 基金会邮件列表显示&#xff0c;在包含 13 个约束性投票 (binding votes) 和 6 个…

生存分析序章2——生存分析之Python篇:lifelines库入门

目录 写在开头1. 介绍 lifelines 库1.1 lifelines库简介1.2 安装与环境配置 2. 数据准备2.1 数据格式与结构2.2 处理缺失数据2.3 对异常值的处理 3. Kaplan-Meier 曲线3.1 使用 lifelines 绘制生存曲线3.2 曲线解读3.3 额外补充 4. Cox 比例风险模型4.1 lifelines 中的 Cox 模型…

使用python netmiko模块批量配置Cisco、华为、H3C路由器交换机(支持 telnet 和 ssh 方式)

0. 当前环境 外网电脑Python版本&#xff1a;3.8.5&#xff08;安装后不要删除安装包&#xff0c;以后卸载的时候用这个&#xff09;外网电脑安装netmiko第三方库&#xff1a;cmd中输入pip install netmiko内网电脑环境&#xff1a;无法搭建python环境&#xff0c;需外网电脑完…

Yolov5水果分类识别+pyqt交互式界面

Yolov5 Fruits Detector Yolov5 是一种先进的目标检测算法&#xff0c;可以应用于水果分类识别任务。结合 PyQT 框架&#xff0c;可以创建一个交互式界面&#xff0c;使用户能够方便地上传图片并获取水果分类结果。以下将详细阐述 Yolov5 水果分类识别和 PyQT 交互式界面的实现…

C/C++常见面试题(四)

C/C面试题集合四 目录 1、什么是C中的类&#xff1f;如何定义和实例化一个类&#xff1f; 2、请解释C中的继承和多态性。 3、什么是虚函数&#xff1f;为什么在基类中使用虚函数&#xff1f; 4、解释封装、继承和多态的概念&#xff0c;并提供相应的代码示例 5、如何处理内…

【Azure 架构师学习笔记】- Power Platform(1) - 简介

本文属于【Azure 架构师学习笔记】系列。 本文属于【Power Platform】系列。 Power Platform 它是一个SaaS平台&#xff0c;支持和延伸M365&#xff0c; Dynamics 365和Azure甚至其他第三方服务。主要提供低代码&#xff0c;自动化&#xff0c;数据驱动和定制化业务逻辑的服务…

【开源】基于Vue+SpringBoot的新能源电池回收系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 用户档案模块2.2 电池品类模块2.3 回收机构模块2.4 电池订单模块2.5 客服咨询模块 三、系统设计3.1 用例设计3.2 业务流程设计3.3 E-R 图设计 四、系统展示五、核心代码5.1 增改电池类型5.2 查询电池品类5.3 查询电池回…

65内网安全-域环境工作组局域网探针

这篇分为三个部分&#xff0c;基本认知&#xff0c;信息收集&#xff0c;后续探针&#xff0c; 基本认知 分为&#xff0c;名词&#xff0c;域&#xff0c;认知&#xff1b; 完整架构图 名词 dwz称之为军事区&#xff0c;两个防火墙之间的区域称之为dwz&#xff0c;但安全性…

STM32逆变器方案

输入电压&#xff1a; 额定输入电压&#xff1a;DC110V 输入电压范围&#xff1a;DC77-137.5V 额定输出参数 电压&#xff1a;200V5%&#xff08;200VAC~240VAC 可调&#xff09; 频率&#xff1a; 42Hz0.5Hz&#xff08;35-50 可调&#xff09; 额定输出容量&#xff1a;1…

mvtec3d

以bagel为例&#xff0c;其中有calibration、 bagel # 百吉圈(硬面包)calibrationcamera_parameters.jsontestcombinedgt # 缺陷部位的分割剪影pngrgb # 原图pngxyz # tiffcontamination # 污染物同上crack同上good同上 hole同上 traingoodrgbxyzvalidationgood同traincla…

【Gitlab】CICD流水线自动化部署教程

第一步&#xff0c;准备 GitLab 仓库 这个不用多说&#xff0c;得先保证你的项目已经托管在一个 GitLab 仓库中。 第二步&#xff0c;定义 .gitlab-ci.yml 文件 在你的项目根目录中创建一个 .gitlab-ci.yml 文件。这个文件将定义所有 CI/CD 的工作流程&#xff0c;包括构建、测…

QT 输入框输入限制 正则表达式限制 整理

在使用 输入数值时&#xff0c;经常遇到限制其范围的需要&#xff0c;比如角太阳高度角范围为[-90,90]&#xff0c;经度值范围[-180,180]&#xff0c;方位角范围[0,360]。Qt提供了QIntValidator和QDoubleValidator可以限定数值输入范围&#xff0c;如使用QIntValidator限制整数…

数模学习day01-层次分析法模型

已经一个多月没有更新过文章了&#xff0c;为了保住那绩点的意思微弱的优势&#xff0c;直接开摆&#xff0c;开始复习专业课和公共课考试了&#xff0c;结果虽然有遗憾但是还是算不错&#xff0c;至少没有掉到3.xx嘿嘿。 然后现在就要开始学习数学建模和算法同步了。接下来的文…

GPU性能实时监测的实用工具

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

python作业题百度网盘,python作业答案怎么查

大家好&#xff0c;小编来为大家解答以下问题&#xff0c;python作业题百度网盘&#xff0c;python作业答案怎么查&#xff0c;今天让我们一起来看看吧&#xff01; 1 以下代码的输出结果为&#xff1a; alist [1, 2, 3, 4] print(alist.reverse()) print(alist) A.[4, 3, 2, …

苏州科技大学计算机817程序设计(java) 学习笔记

之前备考苏州科技大学计算机&#xff08;专业课&#xff1a;817程序设计&#xff08;java&#xff09;&#xff09;。 学习Java和算法相关内容&#xff0c;现将笔记及资料统一整理归纳移至这里。 部分内容不太完善&#xff0c;欢迎提议。 目录 考情分析 考卷题型 刷题攻略…

7.2 uvm_resource_db in UVM

uvm_resource_db是一个类型参数化 type-parameterized的类&#xff0c;它是资源数据库顶部的一个方便层(convenience layer)。这个便利层简化了对低级数据库的访问&#xff0c;并且没有添加新功能。因此&#xff0c;uvm_resource_db不是从uvm_resource类派生的。以下uvm_resour…

怎么下载landsat 8影像并在ArcGIS Pro中进行波段组合

Landsat 8&#xff08;前身为Landsat数据连续性任务&#xff0c;或 LDCM&#xff09;于2013年2月11日由 Atlas-V火箭从加利福尼亚州范登堡空军基地发射升空&#xff0c;这里为大家介绍一下该数据的下载的方法&#xff0c;希望能对你有所帮助。 注册账号 如果之前已经注册过的…

如何通过内网穿透实现远程访问本地Linux SVN服务

文章目录 前言1. Ubuntu安装SVN服务2. 修改配置文件2.1 修改svnserve.conf文件2.2 修改passwd文件2.3 修改authz文件 3. 启动svn服务4. 内网穿透4.1 安装cpolar内网穿透4.2 创建隧道映射本地端口 5. 测试公网访问6. 配置固定公网TCP端口地址6.1 保留一个固定的公网TCP端口地址6…

深信服技术认证“SCCA-C”划重点:云计算基础

为帮助大家更加系统化地学习云计算知识&#xff0c;高效通过云计算工程师认证&#xff0c;深信服特推出“SCCA-C认证备考秘笈”&#xff0c;共十期内容。“考试重点”内容框架&#xff0c;帮助大家快速get重点知识。 划重点来啦 *点击图片放大展示 深信服云计算认证&#xff08…