【设计模式-2】策略模式 - 避免冗余的if-else判断。数据库迁移框架、flink 类型转换框架例子对策略模式的使用

news2025/7/10 10:50:11

文章目录

  • 1. 介绍
  • 2. 策略模式结构
  • 3. 策略模式使用
    • 3.1. 场景一: 表迁移
    • 3.2. flink connector类型转换

1. 介绍

当if else过多时,可以通过策略模式来进行重构。先定义一个接口,各else处理分支实现这个接口,并定义 < 条件 , 处理类 > 的映射关系,然后根据条件找到响应的处理类执行即可。

当有新的分支时,只需要新增一个实现类,增加一个映射关系即可。

 

2. 策略模式结构

在这里插入图片描述

  • 策略接口:提供了一个函数式接口,提供了分支逻辑实现的多种方法:匿名类、普通类等
  • context类:通过组合策略接口,并执行统一方法,实现面向接口的动态方法。

先来一个官方的伪代码:

策略接口和实现类

// 一个函数式的策略接口
interface Strategy is
    method execute(a, b)

// 实现类:实现了+、-、* 算法。
class ConcreteStrategyAdd implements Strategy is
    method execute(a, b) is
        return a + b

class ConcreteStrategySubtract implements Strategy is
    method execute(a, b) is
        return a - b

class ConcreteStrategyMultiply implements Strategy is
    method execute(a, b) is
        return a * b

上下文类的逻辑

class Context is
    // 引用策略接口:面向策略
    private strategy: Strategy


    //传入策略实现类,或者维护一个map
    method setStrategy(Strategy strategy) is
        this.strategy = strategy

    //根据传入的实现类,执行不同的逻辑
    method executeStrategy(int a, int b) is
        return strategy.execute(a, b)

client

class ExampleApplication is
    method main() is
       。。。
        创建上下文对象

        if (action == addition) then
            context.setStrategy(new ConcreteStrategyAdd())

        if (action == subtraction) then
            context.setStrategy(new ConcreteStrategySubtract())

        if (action == multiplication) then
            context.setStrategy(new ConcreteStrategyMultiply())

        result = context.executeStrategy(First number, Second number)

 
 

3. 策略模式使用

3.1. 场景一: 表迁移

有一个需求:项目版本升级(不同版本之间数据表结构不同),需要迁移数据库到新的版本,要求出一个框架,适配项目中所有的数据库的迁移。

  1. 首先可以使用策略模式,一个表对应一个实现类,通过**类名子串(“t1,t2,t3”)**标识要迁移的表有哪些,然后遍历实例化这些实现类,并放到一个list中,最后遍历这个list,完成某个库的迁移;
  2. 其次数据迁移涉及到库的连接和库的关闭,所以在创建迁移逻辑之前和迁移之后要维护好这些逻辑,可以统一放到一个context类中。

大致逻辑:

1.策略接口和实现

 迁移时的sql逻辑接口
 @FunctionalInterface
 public interface JdbcDataMigrateFunction {
        /**
         * 所有迁移和被迁移的表都在一个mysql节点下
         */
        void dataMigrate(Connection dbConn, JdbcPros pros);
    }

@Log4j2
public class FlinkJobAndInstanceFunction implements JdbcDataMigrateFunction {
...
}

2.context的逻辑

//构造分支的运行逻辑:1. 根据字符串去创建分支逻辑类;2. 分支运行逻辑构建:连接数据库,遍历执行分支逻辑,关闭连接。
public class JdbcTableBuilder {

    private JdbcPros jdbcPros;
    private JdbcDataMigrateFunction[] jdbcDataMigrateFunctions;
    private Connection datalakeDbConn;

    public JdbcTableBuilder(String propertiesPath) {
        jdbcPros = getJdbcPros(propertiesPath);
        String datalakeMigrationTables = jdbcPros.getDatalakeMigrationTables();
        String[] datalakeTableNames = datalakeMigrationTables.split(",");
        jdbcDataMigrateFunctions = new JdbcDataMigrateFunction[datalakeTableNames.length];
        for (int i = 0; i < datalakeTableNames.length; i++) {
            jdbcDataMigrateFunctions[i] = createJdbcDataMigrateFunction(datalakeTableNames[i]);
        }
    }

    JdbcDataMigrateFunction createJdbcDataMigrateFunction(String dataflowTableName) {
        switch (dataflowTableName) {
            case "table1":
                return new FlinkJobFunction();
            // todo:添加其他表的迁移逻辑
//            case ""
//                ;
            default:
                throw new RuntimeException("No logical instance of the table migration could be found");
        }
    }


    //CONTEXT的逻辑
    public void runDataMigrate() {
        open(jdbcPros);
        //run
        for (JdbcDataMigrateFunction function : jdbcDataMigrateFunctions) {
            function.dataMigrate(datalakeDbConn, dodpDbConn);
        }
        //close
        closeConn();
    }


    /**
     * 1、jdbc链接
     */
    public void open(JdbcPros pros) {
        try {
            DriverManager.getDrivers();
            Class.forName(pros.getDrivername());

            dodpDbConn = DriverManager.getConnection(
                    pros.getDodpDbURL(),
                    pros.getDodpDbUsername(),
                    pros.getDodpDbPassword());
        } catch (SQLException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }


    /**
     * 关闭Connection
     */
    public void closeConn() {
        if (dodpDbConn != null) {
            try {
                dodpDbConn.close();
            } catch (SQLException se) {
            } finally {
                dodpDbConn = null;
            }
        }
    }

 

3.2. flink connector类型转换

/** Base class for all converters that convert between JDBC object and Flink internal object. */

public class JdbcRowConverter
        extends AbstractRowConverter<
                ResultSet, JsonArray, FieldNamedPreparedStatement, LogicalType> {

    private static final long serialVersionUID = 1L;

    //context逻辑:根据flink sql中字段类型来构建类型转换规则
    public JdbcRowConverter(RowType rowType) {
        super(rowType);
        for (int i = 0; i < rowType.getFieldCount(); i++) {
            toInternalConverters.add(
                    wrapIntoNullableInternalConverter(
                            createInternalConverter(rowType.getTypeAt(i))));
            toExternalConverters.add(
                    wrapIntoNullableExternalConverter(
                            createExternalConverter(fieldTypes[i]), fieldTypes[i]));
        }
    }

    @Override
    protected ISerializationConverter<FieldNamedPreparedStatement>
            wrapIntoNullableExternalConverter(
                    ISerializationConverter<FieldNamedPreparedStatement> serializationConverter,
                    LogicalType type) {
        final int sqlType =
                JdbcTypeUtil.typeInformationToSqlType(
                        TypeConversions.fromDataTypeToLegacyInfo(
                                TypeConversions.fromLogicalToDataType(type)));
        return (val, index, statement) -> {
            if (val == null
                    || val.isNullAt(index)
                    || LogicalTypeRoot.NULL.equals(type.getTypeRoot())) {
                statement.setNull(index, sqlType);
            } else {
                serializationConverter.serialize(val, index, statement);
            }
        };
    }

    //内部转换:将数据转换为flink能进行处理的数据
    @Override
    public RowData toInternal(ResultSet resultSet) throws Exception {
        GenericRowData genericRowData = new GenericRowData(rowType.getFieldCount());
        for (int pos = 0; pos < rowType.getFieldCount(); pos++) {
            Object field = resultSet.getObject(pos + 1);
            genericRowData.setField(pos, toInternalConverters.get(pos).deserialize(field));
        }
        return genericRowData;
    }

    @Override
    public RowData toInternalLookup(JsonArray jsonArray) throws Exception {
        GenericRowData genericRowData = new GenericRowData(rowType.getFieldCount());
        for (int pos = 0; pos < rowType.getFieldCount(); pos++) {
            Object field = jsonArray.getValue(pos);
            genericRowData.setField(pos, toInternalConverters.get(pos).deserialize(field));
        }
        return genericRowData;
    }

    //外部数据转换
    @Override
    public FieldNamedPreparedStatement toExternal(
            RowData rowData, FieldNamedPreparedStatement statement) throws Exception {
        for (int index = 0; index < rowData.getArity(); index++) {
            toExternalConverters.get(index).serialize(rowData, index, statement);
        }
        return statement;
    }

    //具体策略的实现类:由flink sql字段名匹配类型转换规则实例
    @Override
    protected IDeserializationConverter createInternalConverter(LogicalType type) {
        switch (type.getTypeRoot()) {
            case NULL:
                return val -> null;
            case BOOLEAN:
            case FLOAT:
            case DOUBLE:
            case INTERVAL_YEAR_MONTH:
            case INTERVAL_DAY_TIME:
            case INTEGER:
            case BIGINT:
                return val -> val;
            case TINYINT:
                return val -> ((Integer) val).byteValue();
            case SMALLINT:
                // Converter for small type that casts value to int and then return short value,
                // since
                // JDBC 1.0 use int type for small values.
                return val -> val instanceof Integer ? ((Integer) val).shortValue() : val;
            case DECIMAL:
                final int precision = ((DecimalType) type).getPrecision();
                final int scale = ((DecimalType) type).getScale();
                // using decimal(20, 0) to support db type bigint unsigned, user should define
                // decimal(20, 0) in SQL,
                // but other precision like decimal(30, 0) can work too from lenient consideration.
                return val ->
                        val instanceof BigInteger
                                ? DecimalData.fromBigDecimal(
                                        new BigDecimal((BigInteger) val, 0), precision, scale)
                                : DecimalData.fromBigDecimal((BigDecimal) val, precision, scale);
            case DATE:
                return val ->
                        (int) ((Date.valueOf(String.valueOf(val))).toLocalDate().toEpochDay());
            case TIME_WITHOUT_TIME_ZONE:
                return val ->
                        (int)
                                ((Time.valueOf(String.valueOf(val))).toLocalTime().toNanoOfDay()
                                        / 1_000_000L);
            case TIMESTAMP_WITH_TIME_ZONE:
            case TIMESTAMP_WITHOUT_TIME_ZONE:
                return val -> TimestampData.fromTimestamp((Timestamp) val);
            case CHAR:
            case VARCHAR:
                return val -> StringData.fromString(val.toString());
            case BINARY:
            case VARBINARY:
                return val -> (byte[]) val;
            case ARRAY:
            case ROW:
            case MAP:
            case MULTISET:
            case RAW:
            default:
                throw new UnsupportedOperationException("Unsupported type:" + type);
        }
    }

    @Override
    protected ISerializationConverter<FieldNamedPreparedStatement> createExternalConverter(
            LogicalType type) {
        switch (type.getTypeRoot()) {
            case BOOLEAN:
                return (val, index, statement) ->
                        statement.setBoolean(index, val.getBoolean(index));
            case TINYINT:
                return (val, index, statement) -> statement.setByte(index, val.getByte(index));
            case SMALLINT:
                return (val, index, statement) -> statement.setShort(index, val.getShort(index));
            case INTEGER:
            case INTERVAL_YEAR_MONTH:
                return (val, index, statement) -> statement.setInt(index, val.getInt(index));
            case BIGINT:
            case INTERVAL_DAY_TIME:
                return (val, index, statement) -> statement.setLong(index, val.getLong(index));
            case FLOAT:
                return (val, index, statement) -> statement.setFloat(index, val.getFloat(index));
            case DOUBLE:
                return (val, index, statement) -> statement.setDouble(index, val.getDouble(index));
            case CHAR:
            case VARCHAR:
                // value is BinaryString
                return (val, index, statement) ->
                        statement.setString(index, val.getString(index).toString());
            case BINARY:
            case VARBINARY:
                return (val, index, statement) -> statement.setBytes(index, val.getBinary(index));
            case DATE:
                return (val, index, statement) ->
                        statement.setDate(
                                index, Date.valueOf(LocalDate.ofEpochDay(val.getInt(index))));
            case TIME_WITHOUT_TIME_ZONE:
                return (val, index, statement) ->
                        statement.setTime(
                                index,
                                Time.valueOf(
                                        LocalTime.ofNanoOfDay(val.getInt(index) * 1_000_000L)));
            case TIMESTAMP_WITH_TIME_ZONE:
            case TIMESTAMP_WITHOUT_TIME_ZONE:
                final int timestampPrecision = ((TimestampType) type).getPrecision();
                return (val, index, statement) ->
                        statement.setTimestamp(
                                index, val.getTimestamp(index, timestampPrecision).toTimestamp());
            case DECIMAL:
                final int decimalPrecision = ((DecimalType) type).getPrecision();
                final int decimalScale = ((DecimalType) type).getScale();
                return (val, index, statement) ->
                        statement.setBigDecimal(
                                index,
                                val.getDecimal(index, decimalPrecision, decimalScale)
                                        .toBigDecimal());
            case ARRAY:
            case MAP:
            case MULTISET:
            case ROW:
            case RAW:
            default:
                throw new UnsupportedOperationException("Unsupported type:" + type);
        }
    }
}

 
 

参考:
策略模式

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

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

相关文章

NNG 通信模式

NNG 是 nanomsg 的继任版本,纯c语言开发&#xff0c;工作模式分为几种&#xff1a; 1&#xff0c;Pipeline (A One-Way Pipe) 单向通信&#xff0c;类似与生产者消费者模型的消息队列&#xff0c;消息从推方流向拉方。 #include <stdlib.h> #include <stdio.h> #…

[附源码]java毕业设计基于的图书馆管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

RabbitMQ(四):RabbitMQ高级特性

消息队列在使用过程中&#xff0c;面临着很多实际问题需要思考&#xff1a; 消息可靠性问题&#xff1a;如何确保发送的消息至少被消费—次延迟消息问题&#xff1a;如何实现消息的延迟投递消息堆积问题&#xff1a;如何解决数百万消息堆积&#xff0c;无法及时消费的问题高可用…

面试官:MySQL 上亿大表如何优化?

背景 XX 实例&#xff08;一主一从&#xff09;xxx 告警中每天凌晨在报 SLA 报警&#xff0c;该报警的意思是存在一定的主从延迟。&#xff08;若在此时发生主从切换&#xff0c;需要长时间才可以完成切换&#xff0c;要追延迟来保证主从数据的一致性&#xff09; XX 实例的慢…

Oracle LiveLabs实验:Manage Database Instance and Memory for Oracle Database 21c

概述 此实验申请地址在这里。 实验帮助在这里。 此实验预估完成时间100分钟。 该研讨会介绍了 Oracle 数据库实例的基本知识&#xff0c;并指导您管理 Oracle 数据库的初始化参数和内存结构。 管理初始化参数以在 Oracle 数据库上执行关键任务&#xff0c;例如管理数据库实…

【教学类-08-01】20221010《门牌号(6层*3间 黑色版)》(大班主题《我们的城市》)

效果展示 背景需求&#xff1a; 我的小课题《运用Python设计大班层次性纸类学具的案例研究》获得2022年MHQ小课题立项&#xff0c;在前期的《学号名字描字帖》《身份证》《数字分合》《破译电话号码》的基础上&#xff0c;需要设计更多与大班主题活动书上的主题相关的学习材料。…

利用css 动画实现节流

节流指的避免过于频繁的执行一个函数&#xff0c;例如&#xff1a;一个保存按钮&#xff0c;为了避免重复提交或者服务器考虑&#xff0c;往往需要对点击行为做一定的限制&#xff0c;不然会频繁的请求接口&#xff0c;之前基本上是通过js去控制节流问题&#xff0c;其实css也能…

k-form-design 改成自己组件步骤

1&#xff09;修改package.json {"name": "kk-form-design","version": "1.0.2","private": false,"description": "基于vue、ant-design-vue的表单设计器,可视化开发表单","license": &quo…

Linux下的截图工具 —— Flameshot

一、简介 Flameshot是一款功能强大但易于使用的屏幕截图软件&#xff0c;中文名称火焰截图。Flameshot 简单易用并有一个CLI版本&#xff0c;所以你也可以从命令行来进行截图。Flameshot 是一个Linux发行版中完全免费且开源的截图工具。 特性&#xff1a; 外观可定制化。易于…

数据结构-红黑树

红黑树 二分查找 二叉树 二叉平衡树 平衡因子不超1 查找和二叉查找一样的 删除和插入比较复杂 四种失去平衡的方法 LR 两步 RL 两步 不断旋转比较耗时 进一步改进&#xff1a; 红黑树RBT 调整的次数少 平衡性不如二叉平衡树 &#xff0c; 插入删除频繁的使用红黑树&…

redis的主从复制,哨兵和cluster集群

一、redis性能管理 &#xff08;1&#xff09; redis-cli 127.0.0.1:6379> info memory ​ &#xff08;2&#xff09; redis-cli info memory used_memory_rss&#xff1a;是Redis向操作系统申请的内存。used_memory&#xff1a;是Redis中的数据占用的内存。used_memo…

新手零基础自学Python,安装并配置环境+教程

第一步&#xff1a;搭建python运行环境 在 Windows 上安装 Python 和安装普通软件一样简单&#xff0c;下载安装包以后猛击“下一步”即可。 Python 安装包下载地址&#xff1a;https://www.python.org/downloads/ 打开该链接&#xff0c;可以看到有两个版本的 Python&#…

java框架 —— Spring

Spring[TOC](Spring)1、概述1.1、优点1.2、组成2. IOC概述2.1 什么是IOC2.1.1 推导过程2.1.2 IOC本质2.2 HelloSpring2.2.1 导入Jar包2.2.2 编写代码2.2.2 思考2.3 IOC过程2.4 IOC 接口3. Bean 管理3.1 基于xml方式——set方法注入3.2 FactoryBean3.3 bean 作用域3.4 bean 生命…

Java——面向对象进阶(封装、继承、多态)

Java面向对象三大特性——封装、继承、多态一、封装1.封装基本概念2.访问修饰符3.Java中封装的理解4.封装的优点二、继承1.为什么需要继承2.继承层次结构3.super和this关键字4.继承语法与设计一个继承体系三、多态1.多态的概念2.多态的实现条件3.多态的优缺点一、封装 1.封装基…

【微服务架构组件】Nacos

初识nacos 最近在整合nacos做配置的热下发&#xff0c;总结下。 Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service的首字母简称&#xff0c;一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。 阿里开源产品&#xff1a;什么是 Nacos 如 Na…

[Leetcode]138. 复制带随机指针的链表

目录 1.题目链接 2.1解法①(暴力) 2.1.1解法思路&#xff1a; 2.1.2代码实现&#xff1a; 2.2解法②(进阶) 2.1.1解法思路&#xff1a; 2.2.2代码实现&#xff1a; 1.题目链接 138. 复制带随机指针的链表 - 力扣&#xff08;LeetCode&#xff09; 2.1解法①(暴力) 2.1.…

软考 - 操作系统

操作系统概述 bit和byte区别 bit 位 说白了就是0或者1&#xff1b;计算机内存中的存储都是01这两个东西。 byte(B) 字节 1byte8bit&#xff08;一字节 8比特&#xff09; 1byte就是1B 1byte 存1个英文字母&#xff0c;2个byte存一个汉字。 了解 操作系统的作用&#xff1…

SpringBoot使用EasyExcel类一键导出数据库数据生成Excel,导入Excle生成List<>数据(作者直接给demo项目)

文章目录一、简单一键导出Excel直接给出生成效果Empty&#xff0c;这个很关键controller层EasyExcel类的多种使用方式二、导入Excel生成List<>数据controller层&#xff0c;简单写法监听器写法&#xff08;观察者模式&#xff09;&#xff0c;稍微麻烦其他如果要使类中的…

动态拼接 merge 语句

【问题】 Hello everyone, I have one query, would be great if anyone can help me out on this. In SQL, I have two tables with same column names. Want to query if there is any difference in the column values and if yes will update the values(in the first ta…

LEADTOOLS 入门教程: 使用 AWS Lambda 转换文档 - C# .NET Core

LEADTOOLS 是一个综合工具包的集合&#xff0c;用于将识别、文档、医疗、成像和多媒体技术整合到桌面、服务器、平板电脑、网络和移动解决方案中&#xff0c;是一项企业级文档自动化解决方案&#xff0c;有捕捉&#xff0c;OCR&#xff0c;OMR&#xff0c;表单识别和处理&#…