闲谈开闭原则——基于UI动画框架

2023-11-12

  本文继续聊另外一个设计原则:开闭原则。在UI动画框架中,开闭原则在“动画策略”和“移动算法”这两个类体系中均有所体现。照旧,先看一下开闭原则的定义。

1. 开闭原则

一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。

  有人说过“唯一不变的就是变化”,尤其是软件需求的变化。在不断支持新需求的时候,应尽量避免“修改”已有的代码,尽量通过“扩展”来实现变化。所谓的“修改”,指的是修改现有系统的关键抽象,比如在继承体系中修改类的设计;而“扩展”指的是在现有系统的基础(或框架)之上附加新的组件来实现新增的功能,比如新加入一个派生类,新注册一个回调函数等。

  有人可能会说,新加一个派生类,总要在一个地方创建这个派生类的实例对象吧,这不算是“修改”吗?算!确实是修改了现有代码。但是这个修改只局限在“初始化”阶段(或者说“配置”阶段),而现有系统的核心抽象层并没有感知到这个修改,核心逻辑也不用做任何改动,这就是“对修改关闭”的含义。

2. 框架中的开闭原则

  开闭原则在动画框架中有两处体现,一个是策略类体系:

在这里插入图片描述

在实际应用中,UI动画框架扩展的需求已经多达10个以上,在应用初期,也只有简单的几个动画策略,比如:帧率、移动、缩放等。某一天,客户提了一个“淡入淡出”的动画需求,框架怎么支持呢?步骤大概是这样的:

  1. 首先实现“淡入淡出”的功能类:FadeStrategy;
  2. 接着在初始化阶段,将该类纳入框架;
  3. 最后,客户在配置文件中用动画语言编写具体的动画。

第2步骤涉及到的初始化阶段的改动,在这里:

IStrategy* AnimationParser::BuildStrategy(const AttriMap& attributes) const
{
    if (attributes["id"] == "frame"):
        return new FrameStrategy(attributes["param"]);
    else if (attributes["id"] == "move"):
        return new MoveStrategy(attributes["param"]);
    else if (attributes["id"] == "scale")
        return new ScaleStrategy(attributes["param"]);
    else if (attributes["id"] == "fade")
        return new FadeStrategy(attributes["param"]);
    ...
    else
        throw ("invalid strategy type!") + attributes["id"]);
}

BuildStrategy就是初始化阶段负责将具体策略类加入进框架的地方。我们对现有代码所作的修改仅此而已,框架的核心抽象:Window类、IStrategy基类以及现有的各派生类(FrameStrategy、MoveStrategy或ScaleStrategy)都没有做任何修改,甚至是使用该新增动画策略(FadeStrategy)的地方也没有改动,在框架中,你甚至不知道在哪里使用了这个新增动画策略,一切都隐藏在IStrategy背后。我们为框架新增了功能,但没有对核心做任何修改,我们轻而易举地做到了“对扩展开放,对修改关闭”!

  另外一处体现开闭原则的地方是移动算法类体系:

在这里插入图片描述

窗口移动方式很快就由直线移动(水平、垂直、斜线平移),扩展到二次曲线移动(抛物线、反抛物线移动等)。比如又来了一个圆形转动的需求!实现的步骤如下:

  1. 首先实现“圆形”轨迹的功能类:Circle;
  2. 接着在初始化阶段,将该类纳入框架;
  3. 最后,客户在配置文件中用动画语言编写具体的动画。

第2步骤涉及到的初始化阶段的改动,在这里:

Locus* BuildLocus(const String& type, ...)
{
    if (type == "horizontal")
        return BuildHorizontal(...);
    else if (type == "parabola")
        return BuildParabola(...);
    else if (type = "circle")
        return BuildCircle(...);
    ...
    else
        return nullptr;
}

我们再一次看到了开闭原则的体现。

3. 结语

  有关设计模式的几个原则,比如“依赖倒置”原则、“接口隔离”原则、“最少知识”原则以及本文讨论的“开闭”原则,这些原则有相当程度上的相通性,虽然它们的实现方式各不相同,但它们都有一个共同点:解耦!
  就我个人的理解,还有一个“逻辑与数据分离”的原则(或者说算法与数据分离)基本上也是在为解耦而翻来覆去的捣腾!当你准备对你的业务实施“算法与数据”分离原则的时候,你得到的类图框架大抵是这样的:

在这里插入图片描述

  只有最大限度的解耦,你才有足够的自由度,以足够低的代价修改你的系统,扩展你的框架,支持多变的需求!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

闲谈开闭原则——基于UI动画框架 的相关文章

随机推荐

  • JAVA循环使每次循环出来的都是四位数例如0001,0002,0003 三种实现方式

    问题描述 循环使每次循环出来的都是四位数例如0001 0002 0003 现在给出三种实现方式 第一种实现方式 public class temp1 public static void main String args for int i
  • enum枚举变量的用法

    Class Piece public typedef enum colorless white black pieceColor enum pieceColor colorless white black 首先声明一种枚举型变量 该变量的类
  • GB/T28181-2022针对H.265、AAC的说明和技术实现

    GB T28181 2022规范说明 GB T28181 2022相对来GB T28181 2016针对H 265 AAC的更新如下 更改了 联网系统通信协议结构图 媒体流通道增加了 H 265 G 722 1 AAC 见 4 3 1 20
  • 为Linux安装软件包时后面标注的arm,aarch到底是什么玩意儿以二进制安装docker-compose为例

    引言 装docker compose装不上 去github找二进制 看到这些版本人傻了 记录一下 如何确定自己的服务器应该安装哪个 Arch命令 arch 这个最简单 直接输出架构方式 aarch64 就是 ARM 架构 x86 64 就是
  • Spring注解家族介绍:@RestController

    前言 Spring Boot可以说是当前JAVA最为重要的一个框架 而Spring Boot的基石Spring中有着丰富的注解 因此我们会利用几篇文章来讲解我目前学到的各种注解 因此本类型文章的篇幅会比较短 主要着重于介绍各个注解 目录 前
  • GitHub十大Python项目推荐,Star最高26.4k

    编写 Python 代码的乐趣应该是看到简短 简洁 易读的类 这些类用少量清晰的代码来执行大量的操作 而不是那些让读者厌烦的大量琐碎代码 在相当长的一段时间内 世界各地的开发人员在他们的大多数项目中都倾向于使用 Python 编程语言的易用
  • C语言习题

    1 输入一个正整数 输出其逆数 例如输入12345 输出应为54321 2 计算1 2 3 n的值 n是从键盘输入的自然数 3 从终端 键盘 读入20个数据到数组中 统计其中正数的个数 并计算这些正数之和 4 从终端 键盘 将5个整数输入到
  • vue 看sass版本_看看不同的Sass架构

    vue 看sass版本 As the size and complexity of a project grows you require some sort of logic to structure your Sass files It
  • cloudfoundry简介

    cloudfoundry 负载均衡 对Router做负载均衡不属于Cloud Foundry的实现范围 Cloud Foundry只须保证所有Router都可以处理任何请求 而管理员可用DNS实现负载均衡 也可部署专用硬件来实现 或者简单点
  • Shell脚本中$0、$?、$!、$$、$*、$#、$@等符号的意义

    0 Shell本身的文件名 Shell最后运行的后台进程PID 当前脚本运行的进程PID 执行上一个指令的返回值 显示最后命令的退出状态 0表示没有错误 其他任何值表明有错误 所有参数列表 如 用双引号引起来的情况 以 1 2 n 的形式输
  • Flutter中使用sqlite

    在本教程中 我们将介绍如何在flutter中使用sqlite SQLite是用于移动设备和某些计算机SQL引擎 我们可以使用它来保存应用程序的数据 持久性存储对于在加载新数据时缓存网络呼叫和脱机应用程序很有用 引入依赖 查询sqflite最
  • git快速创建多个文件夹和文件

    小目标 创建多个文件夹 1 首先切换到想要创建文件夹的位置 点击右键 git Bash Here打开终端 输入下面一串 mkdir departments employees setting salarys social attendanc
  • C++ this指针详解

    this指针 this指针介绍 成员函数的参数和成员变量重名 this区分 在类的普通成员函数中返回对象本身 this指针介绍 1 this 指针是 C 实现封装的一 种机制 它将对象和该对象调用的成员函数连接在一起 在外部看来 每一个对象
  • jar包classes文件进阶版替换

    我相信大家在做项目的时候有时候看源码 或者想改源码代码的时候 一定是修改不了滴 今天教大家一个进阶版的修改jar源码包的方式 80 的人不知道怎么处理 因为通过解压方式 java zcvf 方式会导致清单文件不存在 从而缺少信息而不能打包
  • 全概率公式习题

    简单的记录下关于全概率公式的习题 我们可以先看下抓阄不分先后的一个例子 设袋中装有10个阄 其中8个是白阄 2个是有物之阄 甲 乙二人依次抓取一个 求没人抓得有物之阄的概率 设A B分别为甲 乙抓得有物之阄的事件 显然P A 2 10 下面
  • 【Git】OpenSSL SSL_read: Connection was aborted, errno 10053

    OpenSSL SSL read Connection was aborted errno 10053报错 解决办法 1 Git默认限制推送的大小 运行命令更改限制大小即可 增加缓冲 git config global http postB
  • 用axure给按钮图片等添加点击事件跳转页面

    由于公司产品离职 只能自己做了 汗 整体过程挺好理解的 拖拽一个图片 双击能设置背景图片 然后选中按钮 并双击OnClick 2 在弹出的视图中可以修改点击事件的名字 3 由于我这个是在当前页面弹出二级页面 所以点击Current Wind
  • python tk库

    tk库是 Python 的一个图形用户界面 GUI 库 可以用来创建窗口 菜单 按钮 文本输入框等 GUI 元素 可以通过它在 Python 中创建复杂的图形界面 使用 tk库时 需要先导入 tkinter 模块 然后使用 tkinter
  • python与vb可以互换吗_vb和python混合编程

    展开全部 解题思路 把 Python 设计成带命令行参数调用的形式 在 VB6 中借助 IWshRuntimeLibrary WshShell Exec 方法进行命令行参数调用 通过 IWshRuntimeLibrary WshExec S
  • 闲谈开闭原则——基于UI动画框架

    本文继续聊另外一个设计原则 开闭原则 在UI动画框架中 开闭原则在 动画策略 和 移动算法 这两个类体系中均有所体现 照旧 先看一下开闭原则的定义 1 开闭原则 一个软件实体如类 模块和函数应该对扩展开放 对修改关闭 有人说过 唯一不变的就