Java常用设计模式

1.代理模式

代理模式:
  当我们要去访问一个目标类的时候,不是直接去访问这个目标类,而是去访问这个目标类的代理类,由这个代理类去调用目标类。
  也就是我们间接的去访问目标类。   

代理模式的作用

代理模式的作用:
  我们可以在代理类调用目标类之前或之后去添加一些预处理和后处理的操作,来扩展一些不属于目标类的功能,也就是不侵入目标类。
  比如说:
  1.在方法开始和结束前记录日志,
  2.在方法执行前进行额外的参数校验。
  3.进行事务管理,
  4.权限校验等
  总的来说,代理模式就是实现了"功能增强""控制访问"

代理模式是一种设计思想,在实际实现上又分为"静态代理""动态代理"

静态代理

静态代理:
  1.代理类需要自己手动实现,
  2.同时需要代理的目标类是确定明确的。
  
优点:静态代理实现起来比较简单,更容易让人理解。
缺点:当确定的目标类发现改变时,所有的代理类都得改变,会牵一发而动全身,不利于功能的增强和拓展。

动态代理

动态代理      基于反射机制
  在程序执行过程中,使用jdk的反射机制,创建代理类对象,并动态得指定要代理的目标类。
  也就是说:动态代理是一种创建java对象的能力,让你不用new对象。

什么是动态代理?
  1.使用jdk的反射机制,创建对象的能力,创建的是代理类的对象。而不用你创建类文件,不用写Java文件。
  2.动态是指在程序执行时,调用jdk提供的方法才能创建代理类的对象。

特点: 无侵入式的给代码增加额外的功能
  动态代理代理的目标类是活动的,是可设置的
  1.不用创建代理类
  2.可以给不同的目标随时创建代理

动态代理的两种方式

1.JDK动态代理:
  使用java反射包中的类和接口实现动态代理的功能。
  反射包java.lang.reflect里的三个类:InvocationHandler,Method,Proxy.

2.cgli动态代理:cglib是第三方的工具库创建代理对象。
  cglib的原理是继承,cglib通过继承目标类,创建他的子类,在子类中重写父类中同名的方法,实现功能的修改。

1.JDK动态代理

JDK动态代理案例
  我想看鸡哥表演,听鸡你太美和看打篮球,于是鸡哥的经纪公司开始筹划演唱会,开始定价门票.
public class Test {
    public static void main(String[] args) {
        //1.获取代理的对象,找到鸡哥的经纪公司,我大喊鸡哥
        BigStar bigStar = new BigStar("鸡哥");
        Star proxy = ProxyUtil.createProxy(bigStar);

        //2.经纪公司请出鸡哥唱歌,我要点歌,我要听鸡你太美
        String resurl = proxy.sing("鸡你太美");
        System.out.println(resurl);

        //3.经纪公司请出鸡哥跳舞
        proxy.dance();
    }
}

在这里插入图片描述

总结:    整个演唱会中体现了"功能解耦","功能增强","权限控制".
  1.JAVA提供了什么API帮我们创建代理?
  2.newProxyInstance()方法在创建代理时,需要接几个参数,每个参数的含义是什么?
  3.通过InvocationHandler的invoke方法指定代理干的事时,这个invoke会被谁调用?要接几个参数?

  在我听鸡哥唱歌看表演的整个过程中
  经纪公司做了增强功能,准备了演出的各个环节,搭建舞台,收门票等工作;
  鸡哥直接上台表演即可,不用操心舞台怎么搭建,门票收多少钱,
  我也提出了我要听鸡你太美的诉求.

代理类实现代理的过程代码

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class ProxyUtil {

    /**
     * 方法的作用:
     *    给BigStar对象创建一个代理
     *    返回值就是创建的代理对象
     *
     * 形参: 被代理的Bigstar对象
     * 返回值: 给Bigstar创建的代理对象
     * @param bigStar
     */
    public static Star createProxy(BigStar bigStar){
        /**
         * newProxyInstance()参数说明
         * 参数一: 用于指定用哪个类加载器去加载生成的代理类;
         * 参数二: 指定接口,这些接口用于指定生成的代理长什么样,也就是有哪些方法
         * 参数三: 用来指定生成的代理对象要干什么事情
         */
       Star star = (Star) Proxy.newProxyInstance(
               ProxyUtil.class.getClassLoader(),
               new Class[]{Star.class},
               new InvocationHandler() {
                   @Override
                   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                       /**
                        * 参数一: "proxy"是代理的对象
                        * 参数二: "method"是要运行的方法sing
                        * 参数三: "args"是调用sing方法时,传递的实参
                        */
                       if("sing".equals(method.getName())){
                           System.out.println("经纪公司准备话筒,收钱");
                       }else if("dance".equals(method.getName())){
                           System.out.println("经纪公司准备舞台,收钱");
                       }
                       //一切准备就绪后,开始请出大明星了
                       return method.invoke(bigStar,args);
                   }
               }
       );
       return star;
    }
}

匿名内部类的lambda写法

//lambda表达式不关心对象名也不关心方法名
Star star = (Star) Proxy.newProxyInstance(
               ProxyUtil.class.getClassLoader(),
               new Class[]{Star.class},
               (proxy, method, args) -> {
                   /**
                    * 参数一: 代理的对象
                    * 参数二: 要运行的方法sing
                    * 参数三: 调用sing方法时,传递的实参
                    */
                   if("sing".equals(method.getName())){
                       System.out.println("经纪公司准备话筒,收钱");
                   }else if("dance".equals(method.getName())){
                       System.out.println("经纪公司准备舞台,收钱");
                   }
                   //一切准备就绪后,开始请出大明星了
                   return method.invoke(bigStar,args);
               }
       );
       return star;

反射机制

分析:调用一个方法时,当中包含有几个要素?
  第一要素:调用哪个对象
  第二要素:调用哪个方法
  第三要素:调用方法的时候传递什么参数
  第四要素:方法执行结束之后的返回结果
  
  调用哪个对象的哪个方法,传什么参数,返回什么值。
Java反射机制
   在程序运行时通过Class类的API动态地获取一个类的信息(属性和方法)并且能够操作该类的属性和方法的能力.
   1.获取class对象:Class
   2.构造方法:Constructor
   3.字段(成员变量):Field
   4.成员方法:Method
通过反射实例化对象

User对象

package com.lei.reflect;

public class User {

    public User() {
        System.out.println("空参构造被调用了");
    }
}

测试方法

public class TestReflect {
    public static void main(String[] args)  {
        try {
            Class<?> clazz = Class.forName("com.lei.reflect.User");
            //clazz通过无参构造方法new对象,所以需要保证对象的无参构造是存在的,
            //有参构造会覆盖无参构造,所以会报错
            Object object = clazz.newInstance();
            System.out.println(object);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

在这里插入图片描述

获取Class对象的三种方式
public class ReflectTest {
    public static void main(String[] args) throws ClassNotFoundException {
        /**
         * 获取class对象的三种方式
         *    1.Class.forName("全类名");
         *    2.类名.class
         *    3.对象.getClass();
         */
        //1.第一种方式
        //全类名: 包名 + 类名
        //最为常用
        Class clazz1 = Class.forName("com.lei.proxy.reflect.Student");
        System.out.println("方式一获取:"+clazz1);

        //2.第二种方式
        //一般更多的是当做参数进行传递
        Class clazz2 = Student.class;
        System.out.println("方式二获取:"+clazz2);

        //3.第三种方式
        Student student = new Student();
        Class clazz3 =  student.getClass();
        System.out.println("方式三获取:"+clazz3);

        System.out.println(clazz1 == clazz2);
        System.out.println(clazz2 == clazz3);
    }
}

在这里插入图片描述

获取对象中的方法

SomeService类中有参数列表不同的同名方法

public class SomeService {

    public void doSome(){
        System.out.println("public void doSome()执行了");
    }

    public void doSome(String str){
        System.out.println("public void doSome(String str)执行了");
    }

    public String doSome(String str,int age){
        System.out.println("public void doSome(String str,int age)执行了");
        return str + age;
    }
}

Test测试类
获取对象,并根据参数列表获取具体对应的方法

public class Test {
    public static void main(String[] args) throws Exception {
        // 使用反射机制怎么调用方法
        // 1.获取类
        Class<?> clazz = Class.forName("com.lei.reflect.SomeService");

        Object obj = clazz.newInstance();
        //获取方法
        Method doSomeMethod = clazz.getDeclaredMethod("doSome", String.class, int.class);
        //调用方法的返回值
        Object object = doSomeMethod.invoke(obj, "小磊", 18);
        System.out.println(object);
    }
}

在这里插入图片描述

获取构造方法

反射总结

反射作用:
  1.获取一个类里面所有的信息,获取到了之后,再执行其他的业务逻辑
  2.结合配置文件,动态地创建对象并调用方法
  3.反射机制很灵活,可以在不改变java源代码的基础上实例化不同对象

获取class字节码文件对象的三种方式
  1.Class.forName("全类名")
  2.类名.class
  3.对象,getClass();

如何获取构造方法,成员变量,成员变量
  get: 获取                       set: 设置
  Constructor: 构造方法            parameter: 参数
  Field: 成员变量                  Modifiers: 修饰符
  Method: 方法                    Declared: 私有的

代理模式的实际应用

AOP

代理模式(Proxy Pattern) - AOP (面向切面编程)

    模式:代理模式
    解释:Spring 的 AOP 功能通过代理模式实现,允许在不修改代码的情况下动态添加功能(如日志记录、事务管理)。

示例

@Aspect
@Component
public class LoggingAspect {
    @Before("execution(* com.example.service.*.*(..))")
    public void logBefore(JoinPoint joinPoint) {
        System.out.println("Logging before method: " + joinPoint.getSignature().getName());
    }
}

这里,LoggingAspect 类通过代理模式在执行目标方法之前插入日志逻辑。

MyBatis

MyBatis 是一个半自动化的持久层框架,主要用于 SQL 映射。

代理模式(Proxy Pattern)
    模式:代理模式
    解释:MyBatis 中的 Mapper 接口通过动态代理生成实现,用户可以直接调用接口方法,代理对象负责执行对应的 SQL 语句。

示例

public interface UserMapper {
    User selectUser(int id);
}

UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.selectUser(1);

UserMapper 接口没有实现类,但通过动态代理,MyBatis 会自动生成代理对象并执行 selectUser 方法对应的 SQL。

Dubbo

Dubbo 是一个高性能的分布式服务框架,主要用于微服务架构。

代理模式(Proxy Pattern)
    模式:代理模式
    解释:在 Dubbo 中,服务的消费者通过代理对象调用远程服务,代理对象负责处理远程调用的细节(如网络通信、序列化等)。

示例

// 接口
public interface GreetingService {
    String sayHello(String name);
}

// 服务提供者
public class GreetingServiceImpl implements GreetingService {
    public String sayHello(String name) {
        return "Hello, " + name;
    }
}

// 客户端通过代理调用远程服务
GreetingService service = ReferenceConfigCache.getCache().get(referenceConfig);
service.sayHello("world");

这里的 service 是代理对象,客户端调用时实际上是调用了代理对象。

2.单例模式

概念

在 Java 开发中,单例模式非常常见,用于确保某个类在 JVM 中只有一个实例,并提供一个全局访问点。单例模式可以避免多次创建相同对象的开销。

单例模式:
  在程序运行过程中,一个类只有一个实例对象,也就是只会new一次。确保某个类只有一个实例,并提供一个全局访问点。

为什么需要单例?
   程序过程中,有的实例,会被反复的调用,如果这个实例反复的被创建,反复的被销毁是一种性能的消耗.
   
   适用场景:比如日志类、数据库连接类等,系统中需要唯一的实例。

单例模式的类型

单例模式实现方式:
    懒汉式:指全局的单例实例在第一次使用时才被构建。
    饿汉式:指全局的单例在类加载(ClassLoader)时就会被构建。
    
    饿汉性能优于懒汉式单例

单例模式示例

懒汉式,线程不安全版本
public class Singleton {
    private static Singleton instance;

    private Singleton() {
        // 私有化构造函数,防止外部实例化
    }

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

饿汉式,线程安全版本
public class Singleton {
    private static final Singleton instance = new Singleton();

    private Singleton() {
        // 私有化构造函数
    }

    public static Singleton getInstance() {
        return instance;
    }
}

单例模式的实际应用

配置类

单例模式通常用于管理全局的配置类,这些类通常包含应用程序级别的配置信息,如数据库连接参数、文件路径等。由于这些配置信息在整个应用中是共享的,使用单例模式可以确保配置对象的唯一性。

示例

public class Configuration {
    private static Configuration instance;

    private String databaseUrl;

    private Configuration() {
        // 私有构造函数,避免外部实例化
    }

    public static Configuration getInstance() {
        if (instance == null) {
            instance = new Configuration();
        }
        return instance;
    }

    public String getDatabaseUrl() {
        return databaseUrl;
    }

    public void setDatabaseUrl(String databaseUrl) {
        this.databaseUrl = databaseUrl;
    }
}

在整个应用中,配置类的实例始终是唯一的。

日志管理器

在 Java 开发中,日志记录系统通常使用单例模式来管理。常见的日志库如 Log4j、SLF4J 都使用单例模式来确保日志管理器的唯一性。这样,应用程序的每个部分都能通过同一个日志对象进行记录。

示例

public class Logger {
    private static Logger instance;

    private Logger() {
        // 私有构造函数
    }

    public static Logger getInstance() {
        if (instance == null) {
            instance = new Logger();
        }
        return instance;
    }

    public void log(String message) {
        System.out.println("Log: " + message);
    }
}

通过 Logger.getInstance() 获取全局唯一的日志实例。

数据库连接池

为了管理数据库连接,通常会使用单例模式来创建和维护数据库连接池。连接池通过单例模式管理连接资源的分配和回收,避免频繁地打开和关闭数据库连接。

示例

public class DatabaseConnectionPool {
    private static DatabaseConnectionPool instance;

    private DatabaseConnectionPool() {
        // 私有构造函数,用于初始化连接池
    }

    public static DatabaseConnectionPool getInstance() {
        if (instance == null) {
            instance = new DatabaseConnectionPool();
        }
        return instance;
    }

    public Connection getConnection() {
        // 获取数据库连接
        return null;
    }

    public void releaseConnection(Connection connection) {
        // 释放数据库连接
    }
}

数据库连接池只会存在一个实例,管理所有的数据库连接。

线程池

线程池是用于并发编程中管理多个线程的资源,通常通过单例模式实现。线程池可以重用线程,避免频繁地创建和销毁线程,节省系统资源。

示例

public class ThreadPool {
    private static ThreadPool instance;
    private ExecutorService executorService;

    private ThreadPool() {
        executorService = Executors.newFixedThreadPool(10);
    }

    public static ThreadPool getInstance() {
        if (instance == null) {
            instance = new ThreadPool();
        }
        return instance;
    }

    public void submitTask(Runnable task) {
        executorService.submit(task);
    }
}

ThreadPool 类通过单例模式管理线程资源,确保系统中只有一个线程池。

Spring Bean

在 Spring 框架中,所有的 Bean 默认是单例模式(通过 @Component 或 XML 配置),Spring 容器会确保每个 Bean 在容器中只有一个实例。

示例

@Component
public class MyService {
    public void doSomething() {
        System.out.println("Doing something...");
    }
}

public static void main(String[] args) {
    ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
    MyService myService1 = context.getBean(MyService.class);
    MyService myService2 = context.getBean(MyService.class);
    
    System.out.println(myService1 == myService2);  // 输出: true
}

Spring 容器确保 MyService 的实例在容器中只有一个,因此 myService1 和 myService2 是同一个对象。

3.工厂方法模式

概念

工厂方法模式(Factory Method Pattern)

  定义:
    工厂方法模式是一种创建型设计模式,它定义了一个用于创建对象的接口,但由子类决定实例化哪个类。
    工厂方法使一个类的实例化延迟到其子类。

  关键点:
    工厂方法模式通过让子类决定创建哪种产品,来实现“将对象的创建与使用相分离”的目标。
    与简单工厂不同,工厂方法模式不会将所有的产品创建逻辑放在一个工厂类中,而是将创建的职责下放到具体工厂类中。

  优点:
    提高了系统的可扩展性:当有新的产品时,只需要增加具体工厂类,而不需要修改已有的工厂类或客户端代码。
    遵循了开闭原则:对扩展开放,对修改关闭。

举例说明

假设我们要设计一个日志记录系统,它可以生成不同类型的日志,比如文件日志和数据库日志。我们可以使用工厂方法模式来为不同的日志生成提供工厂类。

工厂方法模式实现

// 产品接口
interface Product {
    void use();
}

// 具体产品A
class ConcreteProductA implements Product {
    public void use() {
        System.out.println("使用产品A");
    }
}

// 具体产品B
class ConcreteProductB implements Product {
    public void use() {
        System.out.println("使用产品B");
    }
}

// 工厂接口
interface Factory {
    Product createProduct();
}

// 具体工厂A
class ConcreteFactoryA implements Factory {
    public Product createProduct() {
        return new ConcreteProductA();
    }
}

// 具体工厂B
class ConcreteFactoryB implements Factory {
    public Product createProduct() {
        return new ConcreteProductB();
    }
}

// 客户端代码
public class Client {
    public static void main(String[] args) {
        Factory factoryA = new ConcreteFactoryA();
        Product productA = factoryA.createProduct();
        productA.use();

        Factory factoryB = new ConcreteFactoryB();
        Product productB = factoryB.createProduct();
        productB.use();
    }
}

工厂模式的实际应用

Spring IOC

Spring 中的 BeanFactory 和 ApplicationContext 使用工厂模式来管理 Bean 的创建和依赖注入。
Spring 通过配置文件或注解自动创建和管理对象,而无需手动实例化。

依赖注入(Dependency Injection) - 使用 工厂模式 和 控制反转(IoC)

    模式:工厂模式 + 控制反转
    解释:Spring 的核心是依赖注入(DI)。它通过工厂模式创建对象,将对象的创建和依赖关系管理交由 Spring 容器控制。
         控制反转就是将对象的控制权从应用程序转移到容器。

示例

@Component
public class Car {
    private Engine engine;

    @Autowired
    public Car(Engine engine) {
        this.engine = engine;
    }

    public void start() {
        engine.start();
    }
}

@Component
public class Engine {
    public void start() {
        System.out.println("Engine started.");
    }
}

// Spring 配置文件或注解驱动
public static void main(String[] args) {
    ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
    Car car = context.getBean(Car.class);
    car.start(); // 输出: Engine started.
}

这里,Car 类不直接创建 Engine 对象,而是由 Spring 容器通过依赖注入管理。
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
MyService myService = context.getBean(MyService.class);

这里的 getBean() 方法是工厂模式的应用,Spring 容器负责创建和管理 MyService 的实例。

FactoryBean 接口

在 Spring 中,FactoryBean 接口允许开发者定制 Bean 的创建逻辑。实现 FactoryBean 接口的类可以用作 Bean 工厂。

public class MyFactoryBean implements FactoryBean<MyObject> {
    @Override
    public MyObject getObject() throws Exception {
        return new MyObject(); // 自定义对象创建逻辑
    }

    @Override
    public Class<?> getObjectType() {
        return MyObject.class;
    }
}

通过FactoryBean,可以灵活地控制对象的创建过程。

4.抽象工厂模式

概念

抽象工厂模式(Abstract Factory Pattern)

  定义:抽象工厂模式提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们的具体类。

  关键点:
    抽象工厂模式是工厂模式的扩展,用于创建一族对象,而不仅仅是单个对象。
    它允许客户端通过同一个接口创建不同的产品组,而不关心产品的具体实现。
    这种模式可以保证客户端一次性获取一组相关的产品,而不是从多个工厂中获取产品。

  适用场景:
    系统需要独立于产品的创建和组织。
    系统有多个产品族,并且希望客户端使用产品族中的产品,而不关心具体产品的实现。
    需要一系列相关的对象(如 GUI 工具中的按钮、文本框、窗口等)一起使用时。

举例说明

假设我们要设计一个操作系统的图形界面,可能有两个系列:Windows风格和Mac风格,每种风格下都有按钮和文本框。抽象工厂模式可以帮助我们在同一个系统中根据不同的环境生成不同风格的UI组件。

抽象工厂模式实现

// 抽象产品接口
interface Logger {
    void log(String message);
}

// 具体产品A: 文件日志
class FileLogger implements Logger {
    public void log(String message) {
        System.out.println("文件日志: " + message);
    }
}

// 具体产品B: 数据库日志
class DatabaseLogger implements Logger {
    public void log(String message) {
        System.out.println("数据库日志: " + message);
    }
}

// 抽象工厂类
abstract class LoggerFactory {
    // 工厂方法
    public abstract Logger createLogger();

    // 使用工厂创建的产品
    public void logMessage(String message) {
        Logger logger = createLogger();
        logger.log(message);
    }
}

// 具体工厂A: 文件日志工厂
class FileLoggerFactory extends LoggerFactory {
    public Logger createLogger() {
        return new FileLogger();
    }
}

// 具体工厂B: 数据库日志工厂
class DatabaseLoggerFactory extends LoggerFactory {
    public Logger createLogger() {
        return new DatabaseLogger();
    }
}

// 客户端代码
public class Client {
    public static void main(String[] args) {
        // 使用文件日志工厂
        LoggerFactory fileLoggerFactory = new FileLoggerFactory();
        fileLoggerFactory.logMessage("这是文件日志消息"); // 输出: 文件日志: 这是文件日志消息

        // 使用数据库日志工厂
        LoggerFactory databaseLoggerFactory = new DatabaseLoggerFactory();
        databaseLoggerFactory.logMessage("这是数据库日志消息"); // 输出: 数据库日志: 这是数据库日志消息
    }
}


运行结果
  文件日志: 这是文件日志消息
  数据库日志: 这是数据库日志消息
代码解析:
    抽象产品:Logger 接口定义了日志记录的方法 log(),是所有日志产品的共同行为。
    具体产品:FileLogger 和 DatabaseLogger 是具体的日志产品,分别实现了日志记录的功能。
    抽象工厂:LoggerFactory 抽象工厂类定义了创建 Logger 对象的工厂方法 createLogger()。
    具体工厂:FileLoggerFactory 和 DatabaseLoggerFactory 是具体工厂类,它们负责创建具体的日志产品。
    客户端代码:客户端通过 LoggerFactory 来调用 logMessage() 方法,而不需要知道具体的产品类是哪一个。

5.模板方法模式

概念

定义
  模板方法模式(Template Method Pattern)是一种行为型设计模式,它定义了一个操作的算法骨架,而将某些步骤的具体实现延迟到子类中。
  通过这种方式,模板方法允许子类在不改变算法结构的情况下重新定义算法的某些步骤。
模板方法模式的结构可以分为以下几部分:

    抽象类(Abstract Class):定义模板方法,模板方法包含算法的骨架,也就是多个步骤的组合。部分步骤的实现可以是抽象的,由子类去实现。
    具体子类(Concrete Class):继承自抽象类,实现其中的抽象步骤,从而定义具体的算法步骤。
 优点
    代码复用:可以将多个子类中相同的部分提取到抽象类中,减少代码重复。
    扩展性强:算法骨架不变,具体步骤可以由子类灵活实现。
    封装不变部分,扩展可变部分:将不变的部分固定在父类中,而将可变部分留给子类去实现。

 缺点
    代码维护复杂:随着抽象类的增加,可能导致类层次结构的复杂化。
    子类的实现受到限制:子类必须遵循父类定义好的框架,灵活性受到一定限制。

举例说明

假设我们有一个制作咖啡和茶的过程,制作饮品的整体流程是固定的:烧水、加入主要成分(咖啡或茶)、倒入杯子、加入调料。然而,加入主要成分和调料的具体细节是不同的。

抽象类:Beverage

abstract class Beverage {
    // 模板方法,定义了算法的骨架
    public final void prepareRecipe() {
        boilWater();
        brew();
        pourInCup();
        addCondiments();
    }

    // 烧水,这是公共的步骤,所有子类都一样
    public void boilWater() {
        System.out.println("Boiling water");
    }

    // 倒入杯子,这是公共的步骤
    public void pourInCup() {
        System.out.println("Pouring into cup");
    }

    // 抽象方法,由子类实现
    public abstract void brew();

    // 抽象方法,由子类实现
    public abstract void addCondiments();
}

解析
   Beverage 类定义了一个 prepareRecipe() 模板方法,该方法中定义了制作饮品的固定流程,
      但某些步骤如 brew() 和 addCondiments() 留给子类 Tea 和 Coffee 去实现。
   
   Tea 和 Coffee 具体实现了如何冲泡饮料和加入调料,但整个流程仍然是 Beverage 类控制的,保证了算法骨架不变。

具体类:Tea(茶)

class Tea extends Beverage {
    @Override
    public void brew() {
        System.out.println("Steeping the tea");
    }

    @Override
    public void addCondiments() {
        System.out.println("Adding lemon");
    }
}

具体类:Coffee(咖啡)

class Coffee extends Beverage {
    @Override
    public void brew() {
        System.out.println("Dripping coffee through filter");
    }

    @Override
    public void addCondiments() {
        System.out.println("Adding sugar and milk");
    }
}

客户端代码

public class TemplateMethodPatternDemo {
    public static void main(String[] args) {
        Beverage tea = new Tea();
        tea.prepareRecipe();  // 制作茶的过程

        System.out.println();

        Beverage coffee = new Coffee();
        coffee.prepareRecipe();  // 制作咖啡的过程
    }
}

输出结果
Boiling water
Steeping the tea
Pouring into cup
Adding lemon

Boiling water
Dripping coffee through filter
Pouring into cup
Adding sugar and milk
  

模板方法模式的实际应用

JDBC

JDBC 是 Java 数据库连接的标准 API。

模板方法模式(Template Method Pattern)
    模式:模板方法模式
    解释:JDBC 提供了类似 Connection, Statement 等抽象类或接口,
         用户只需要关注具体的 SQL 执行,而不需要关心底层数据库连接的细节。
Connection conn = DriverManager.getConnection(url, username, password);
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM users");

while (rs.next()) {
    System.out.println(rs.getString("name"));
}

JDBC 通过模板方法模式将数据库连接和 SQL 执行的复杂性封装在框架内。

Java Servlet API

Java 中的 HttpServlet 类是一个模板方法模式的经典例子。HttpServlet 类定义了处理 HTTP 请求的框架,通过 doGet()、doPost() 等抽象方法来让子类实现具体的行为。

public abstract class HttpServlet implements Servlet {
    // 模板方法,处理请求
    protected void service(HttpServletRequest req, HttpServletResponse resp) {
        if (req.getMethod().equals("GET")) {
            doGet(req, resp);
        } else if (req.getMethod().equals("POST")) {
            doPost(req, resp);
        }
        // 其他 HTTP 方法
    }

    protected abstract void doGet(HttpServletRequest req, HttpServletResponse resp);
    protected abstract void doPost(HttpServletRequest req, HttpServletResponse resp);
}

子类只需要实现 doGet() 或 doPost() 方法,而无需关心请求的调度逻辑。

总结:模板方法模式通过定义算法的固定流程并将具体步骤交给子类实现,有助于复用代码和灵活扩展。它适合应用于多个子类存在类似操作的场景,可以提高代码的复用性,简化子类的开发工作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值