小程序中的大道理之三--对称性和耦合问题

再继续扒

继续 前一篇 的话题, 在那里, 提到了抽象, 耦合及 MVC, 现在继续探讨这些, 不过在此之前先说下第一篇里提到的对称性.

注: 以下讨论建立在前面的基础之上, 为控制篇幅起见, 这里将不再重复前面说到的部分, 如果您还没看过前两篇章, 阅读起来可能会有些困难.

这是第一篇的链接小程序中的大道理

对称性(Symmetry)

这里先说下对称性的问题. 问题中的图案是左右对称的, 但到目前为此, 我们的代码却不是对称的, 输出之所以对称原因在于空格与背景之间难以区分.

让我们换个符号, 比如脱字符"^", 再输出一下, 就可以看出不对称来, 因为在右边我们输出星号后就直接换行了:

star and caret not symmetry

只要稍微调整一下程序, 在输出换行之前再输出一下空格(或者现在的脱字符"^"), 就能满足对称的输出了:

private String getLineContent(int lineCount, int lineNumber) {StringBuilder content = new StringBuilder();String firstPart = getFirstPart(lineCount, lineNumber);// 1. 空格部分content.append(firstPart);// 2. 星号部分content.append(getSecondPart(lineNumber));// 3. 空格部分content.append(firstPart);// 4. 换行部分content.append(System.lineSeparator());return content.toString();
}

这样之后, 图案是左右对称, 反映在程序上就呈现出上下对称了. 当然也可以把三个语句写在一行里, 这样它也左右对称了:

star symmetry compare to code symmetry

程序是对客观世界的问题的一种映射, 因此程序的结构反映了问题的结构.

当然了, 如果你的程序写得很松散, 结构, 层次不清晰, 就不太容易看出这种对称来.

不知道是否有人意识到了, 在前面我有意无意地忽略了另一个对称问题. 这就是星号的对称, 在上图中只是把它当成对称轴看待.

撇开什么脱字符"^"或者空格, 其实单纯由星号构成的三角形也是对称的, 这是更主要的一个对称:

star symmetry

这种对称又来自哪里呢? 让我们深入到 getSecondPart 里面去:

private String getSecondPart(int lineNumber) {int count = getElementCountOfSecondPart(lineNumber);StringBuilder part = new StringBuilder();for (int i = 0; i < count; i++) {part.append(" ");}return part.toString();
}

很遗憾, 你看不到什么对称. 继续深入到 getElementCountOfSecondPart 去:

public int getElementCountOfSecondPart(int lineNumber) {return lineNumber * 2 + 1;
}

好了, 你已经到头了, 可好像还是找不到对称的影子呀? 前面说"程序的结构反映了问题的结构", 难道这个结论并不总是成立的?

有这么一个故事, 我估计很多人都听说过(文字摘自以下网址 http://www.niwota.com/submsg/5682754/):

有一位牧师在某个星期六的早晨正在为明天的讲道稿大伤脑筋. 他的太太外出买东西了, 外面正下着雨, 小儿子失去了户外活动的机会, 在屋里折腾. 牧师的思路一再被儿子打断.

牧师手边正好有本旧杂志, 为了把儿子从身边支开, 他撕下一张彩色世界地图, 再撕成碎片, 丢到客厅地板上对儿子说: "Johnny, 你把它拼成原样, 我就给你一个 Nickle(25 美分镍币). "

儿子有事可做, 又有报酬可得, 积极性很高, 立刻拼了起来. 牧师想, 这下子我可以安静一个上午, 构思自己的讲道稿了. 谁知道只隔了十分钟, 儿子就来敲书房的门了, 说已经拼好. 牧师不相信, 跑到客厅一看, 果然整幅地图完整无缺. 牧师又懊恼又惊奇地问儿子: "你怎么那么快就拼好了呢? "

儿子得意地说, 这再简单不过了, 这张地图的背后印着一幅人物肖像, 我想, 如果这个人拼对了, 世界地图也就拼对了.

牧师忍不住笑了起来, 很高兴地给了儿子一个镍币, 说, 你替我把明天讲道的题目也准备好了: 如果一个人是对的, 那么这个人的世界也是对的.

现在我们的程序能够输出一个对称的图案来, 肯定不是巧合, 而且我也可以肯定地告诉你, 这里面是有对称的. 你可以再仔细找找看, 如果你已经找到或者实在找不出来, 那么可以往下看了:

line num symmetry

现在看来是不是很明显呢? 也许你早也看出来了. 我们能得到有什么启示呢?

运用**直觉(Intuition)**去思考!

如果你的代码中怎么变换也找不出对称来, 当你的人都是错的时候, 你的世界还可能正确吗? 所以你甚至不用费心去上机验证了.

发明了**差分机(difference engine)的计算机先驱查尔斯·巴贝奇(Charles Babbage)**说:

我曾两次被(议员)问到, "巴贝奇先生, 请问假如您往机器里输入了错误的数据, 还会出来正确的答案吗? "我实在无法恰当地理解是怎样的逻辑混乱才会(使他们)提出这样的一个问题.

On two occasions I have been asked [by members of Parliament], ‘Pray, Mr. Babbage, if you put into the machine wrong figures, will the right answers come out?’ I am not able rightly to apprehend the kind of confusion of ideas that could provoke such a question.

看得出来巴贝奇的评论还是很客气的, 他当时似乎在寻求议员拨款来支持他的差分机研究, 我估计他当时内心其实想说: “你丫脑子进水了, 问这种问题!”

巴贝奇谈的是往正确的机器里输入错误的数据的情况, 在这里, 如果连机器里面(即代码)都已经是错的, 那么即便给出了正确输入, 也不可能有正确的输出了.

在某些特殊情况下, 你也许会碰到"负负得正"的情况, 但这不过是巧合而已.

你可能遇到过这样的 bug:

你加了一个新特性, 系统出错了, 你找到一处 bug, 然后你很奇怪, 为何之前居然没问题? 而在你改正这个 bug 后, 之前的一些功能反而不正常了!你可能打死也想不到系统中还有另一个你没发现的 bug 在默默地抵消了它!

诚然, 这样的"负负得正"情况即便能工作也是非常脆弱的.

当事实与直觉不符时, 那么一定有什么地方出错了.

回到我们的问题, 再单独地拿输出空格部分来看:

public int getElementCountOfFirstPart(int lineCount, int lineNumber) {return lineCount - lineNumber - 1;
}

有对称吗? 它有两个不同变量, 单独的一份随便你怎么去变换, 你都找不到对称. 这也是为何你需要二份一左一右围绕在星号旁边才能形成对称.

关于直觉, 如果你还有兴趣, 可以见我之前写的另一篇文章(有些读者可能已经看过), 深入图解字符集与字符集编码–定长与变长, 也有谈到用天平之类的模型来直观地思考问题.

本质, 证明以及直觉

按前面那图:

line num symmetry part

如果有人试图从输出的 1, 3, 5, 7 的展开式的对称性来向你证明表达式"lineNumber*2+1"的对称性, 那么呢? 这不过是本末倒置. 正如前面所说, 你把表达式做个变换就可以看出表达式是对称的, 对称是该表达式的本质属性, 所以输出的1, 3, 5, 7 的对称性恰恰是表达式所决定的.

相信很多人都跟作者类似, 在大学的数学课上, 被那些形形色色的恐怖的证明弄得苦不堪言:

这里是在对""弹琴, 数学大""们请走开或请无视(要鄙视, 俺也认了), 这里不是在说你们!你们不会懂的!

如果你的老师只能通过证明来告诉你一件事情为什么是这样, 通常你还是很难明白它为什么是这样, 你甚至可以怀疑老师是否真的深刻理解了这件事情, 要么他就是不打算告诉你真正的原因:

好好反思一下你是否在什么事情上得罪了他? 你写作业是不是全靠"Ctrl+C, Ctrl+V"?

又或者他想让你自己去领悟:

真是用心良苦!你体会到了没有? 你领悟了吗? 要是没有请继续, 学费是不会退滴, 你别领悟到其它地方去了~

而如果你的老师可以通过直觉告诉你事情为什么是这样, 你也许就会说: "啊哈, 原来如此(Aha moment)!"从此你就记住了事情为什么是这样了, 那些冗长的证明你都可以丢到一边去了.

独立的数据模型(Isolated Data Model)

前面说到 getElementCountOfFirstPartgetElementCountOfSecondPart 两个方法达到了抽象的极致, 从而与具体的表现形式解耦. 在有了对称性之后, 我们甚至可以反转两个表现形式, 依然可以呈现出所谓的"三角形"出来:

star triangle compare to space triangle

对比两种情况, 不变的是三个部分中的那些数字:

star triangle number compare to space triangle number

所以这才是图案的"魂", 或者说是它的"意", 抽象出意, 我们就能"得意而忘形".

前面说过也许有一天, 客户的需求可能扩展到 web service 上, 对于一个较大的行数, 需要传输较大的数据, 但有了这种解耦, 我们可以把一个纯粹的数组传递过去:

[3][1][3]
[2][3][2]
[1][5][1]
[0][7][0]

另一方可以在收到这个数据模型后, 再把图形还原.

因为存在对称性, 第三列甚至也可以不传.

灵活性(Flexibility)与松耦合(Loose Couple)

而这个还原过程则可以很灵活地处理, 下图演示了在本地直接利用上述两方法获取图案模式并用 icon 图片展示的效果:

红薯三角形

代码如下(我对 swing 之类的编程也不是很熟, 随便在网上搜来的代码改了下):

public class PatternPic extends JPanel {private static final long serialVersionUID = 1L;private Image image;public PatternPic(Image image) {this.image = image;};@Overridepublic void paintComponent(Graphics g) {super.paintComponent(g);int width = image.getWidth(this);int height = image.getHeight(this);int lineCount = 4;Pattern pattern = new Pattern();for (int lineNumber = 0, y = 0; lineNumber < lineCount; lineNumber++, y += height) {// 直接获取各部分的数目int elementCountOfFirst = pattern.getElementCountOfFirstPart(lineCount, lineNumber);int elementCountOfSecond = pattern.getElementCountOfSecondPart(lineNumber);for (int i = 0, x = elementCountOfFirst * width; i < elementCountOfSecond; i++, x += width) {g.drawImage(image, x, y, width, height, this);}}}public static void main(String[] args) throws IOException {Image image = ImageIO.read(PatternPic.class.getResource("/logo-git-oschina.png"));JFrame frame = new JFrame();frame.add(new PatternPic(image));frame.setSize(800, 600);frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);frame.setVisible(true);}
}

所以这样的抽象是有很大好处的, 我们说高层的一些抽象如果觉得啰嗦可以去掉一些(如上述就没作过多的抽象), 但这一层的抽象反而要保留. 我们把它作为公共的 API 提供出去, 调用者就不再受限于那些写死的"空格与星号", 这种灵活性与前面说到的可重用性及可扩展性都是紧密相关的.

MVC

MVC 中重要的一点即是强调模型(M, Model)与视图(V, View)的分离. 所谓的模型即是一种独立于视图的数据, 这与我们抽象出来的数据模型是很相似的.

真正的 MVC 与我们这里的例子间或许还有很大差异, 但在解耦合这一点, 两者是一致.

可以对比下最初的玩具式的代码, 那种情况就是一种 紧耦合(tight couple), 而这里则达到了 松耦合(loose couple) 的目的.

在当下, 常常一方面要处理传统的 web 界面, 另一方面又要处理移动端的界面, 抽取出纯粹的数据模型无疑将带给我们很大方便.

在下一篇将谈谈单元测试的问题.

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

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

相关文章

壳牌——利用人工智能应对新能源转型

荷兰皇家壳牌(Shell)最初是一家卖贝壳的商店&#xff0c;截至 2018 年&#xff0c;它是全球收入排名第五的公司。它的业务范围涵盖从勘探和钻探到提炼和零售的整个燃料供应链。壳牌在石油、天然气、生物燃料、风能和太阳能等端到端燃料生产领域处于世界领先地位。 当前&#x…

【21年扬大真题】编写程序,去除掉字符串中所有的星号。

【21年扬大真题】 编写程序&#xff0c;去除掉字符串中所有的星号。 int main() {int i 0;int j 0;char arr[30] {0};char brr[30] {0};printf("请输入一个字符串:");gets(arr);for (i 0;i < 30;i){if (arr[i] ! *) {brr[j] arr[i];j;}}int tmp j;for (i …

【Amazon】安装卸载AWS CLI操作流程(Windows 、Linux系统)

AWS 命令行界面&#xff08;AWS CLI&#xff09;是用于管理 AWS 产品的统一工具。只需要下载和配置一个工具&#xff0c;您就可以使用命令行控制多个 AWS 产品并利用脚本来自动执行这些服务。 AWS CLI v2 提供了多项新功能&#xff0c;包括改进的安装程序、新的配置选项&#…

高清动态壁纸软件Live Wallpaper Themes 4K mac中文版功能

Live Wallpaper & Themes 4K mac是一款提供各种高清动态壁纸和主题的应用程序。该应用程序提供了大量的动态壁纸和主题&#xff0c;包括自然、动物、城市、抽象等各种类别&#xff0c;可以满足用户不同的需求。除了壁纸和主题之外&#xff0c;该应用程序还提供了许多其他功…

判断序列Series中的值是否都不一样 PandasSeries中的方法:is_unique()

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 判断序列Series中的值是否都不一样 PandasSeries中的方法&#xff1a; is_unique() 选择题 请问下列程序运行的的结果是&#xff1a; import pandas as pd s1 pd.Series([1,2,3]) print("…

Odoo16系统忘记Master密码的解决方法

1 打开项目配置文件../Odoo 16.0.20231119/server/odoo.conf 2 找到admin_passwd 开头的行&#xff0c;删除该行&#xff0c;或者在该行前面添加英文半角分号;注释掉本行 3 重启odoo服务&#xff0c;然后访问页面如&#xff1a;http://localhost:8069/web 4 选择数据库是&am…

Windows核心编程 跨进程操作

目录 进程A拿到进程B句柄是否能用 句柄的权限 关于句柄表 跨进程使用句柄-继承 CreateProcess&#xff1a;bInheritHandles OpenProcess FindWinodw GetCurrentProcess 跨进程使用句柄-拷贝 跨进程操作内存 WriteProcessMemory VirtualProtectEx ReadProcessMemo…

浏览器缓存控制讲解

缓存的作用 在你访问互联网中的任何资源其所产生的任何链路中的每一个节点几乎都会进行缓存&#xff0c;整个缓存体系和细节十分复杂。比如浏览器缓存&#xff0c;服务器缓存&#xff0c;代理服务器缓存&#xff0c;CDN缓存等。 但是缓存又十分重要&#xff0c;不可缺少&…

NX二次开发UF_CURVE_ask_curve_struct_data 函数介绍

文章作者&#xff1a;里海 来源网站&#xff1a;https://blog.csdn.net/WangPaiFeiXingYuan UF_CURVE_ask_curve_struct_data Defined in: uf_curve.h int UF_CURVE_ask_curve_struct_data(UF_CURVE_struct_p_t curve_struct, int * type, double * * curve_data ) overview…

情感对话机器人的任务体系

人类在处理对话中的情感时&#xff0c;需要先根据对话场景中的蛛丝马迹判断出对方的情感&#xff0c;继而根据对话的主题等信息思考自身用什么情感进行回复&#xff0c;最后结合推理出的情感形成恰当的回复。受人类处理情感对话的启发&#xff0c;情感对话机器人需要完成以下几…

从0开始学习JavaScript--深入了解JavaScript框架

JavaScript框架在现代Web开发中扮演着关键角色&#xff0c;为开发者提供了丰富的工具和抽象层&#xff0c;使得构建复杂的、高性能的Web应用变得更加容易。本文将深入探讨JavaScript框架的核心概念、常见框架的特点以及它们在实际应用中的使用。 JavaScript框架的作用 JavaSc…

STM32 寄存器配置笔记——USART配置中断接收乒乓缓存处理

一、概述 本文主要介绍如何配置USART接收中断&#xff0c;使用乒乓缓存的设计接收数据并将其回显在PC 串口工具上。以stm32f10为例&#xff0c;配置USART1 9600波特率。具体配置参考上一章节STM32 寄存器配置笔记——USART配置 打印。 乒乓缓存的设计应用场景&#xff1a;当后面…

【ceph】如何打印一个osd的op流程,排查osd在干什么

本站以分享各种运维经验和运维所需要的技能为主 《python零基础入门》&#xff1a;python零基础入门学习 《python运维脚本》&#xff1a; python运维脚本实践 《shell》&#xff1a;shell学习 《terraform》持续更新中&#xff1a;terraform_Aws学习零基础入门到最佳实战 《k8…

canvas高级动画001:文字瀑布流

canvas实例应用100 专栏提供canvas的基础知识&#xff0c;高级动画&#xff0c;相关应用扩展等信息。 canvas作为html的一部分&#xff0c;是图像图标地图可视化的一个重要的基础&#xff0c;学好了canvas&#xff0c;在其他的一些应用上将会起到非常重要的帮助。 文章目录 示例…

elk 简单操作手册

1.1. 基础概念 EFK不是一个软件,而是一套解决方案,开源软件之间的互相配合使用,高效的满足了很多场合的应用,是目前主流的一种日志系统。 EFK是三个开源软件的缩写,分别表示:Elasticsearch , Filebeat, Kibana , 其中Elasticsearch负责日志保存和搜索,Filebeat负责收集日志,Ki…

EI期刊完整程序:MEA-BP思维进化法优化BP神经网络的回归预测算法,可作为对比预测模型,丰富内容,直接运行,免费

适用平台&#xff1a;Matlab 2020及以上 本程序参考中文EI期刊《基于MEA⁃BP神经网络的建筑能耗预测模型》&#xff0c;程序注释清晰&#xff0c;干货满满&#xff0c;下面对文章和程序做简要介绍。 适用领域&#xff1a;风速预测、光伏功率预测、发电功率预测、碳价预测等多…

eclipse项目移到idea上部署运行

1.配置web模块 另外&#xff0c;模块这里&#xff0c;也要加上Spring 2.配置Artifact &#xff08;用于tomcat&#xff09; 就是从上面配置的web模块&#xff0c;产生的工件 3.添加lib 一般是在web-inf/lib &#xff0c; 遇到的坑&#xff1a; jdk版本问题&#xff0c;这里…

使用STM32+SPI Flash模拟U盘

试验目的&#xff1a;使用STM32F103C8T6 SPI Flash&#xff08;WSQ16&#xff09;实现模拟U盘的功能 SPI Flash读写说明&#xff1a; Step1 设置SPI1 用于读取SPI Flash&#xff1b; Step2&#xff1a;设置SPI Flash 的使能信号 Step3&#xff1a;使能USB通信 Step4&#xf…

人机交互2——任务型多轮对话的控制和生成

1.自然语言理解模块 2.对话管理模块 3.自然语言生成模块

C++模拟如何实现vector的方法

任意位置插入&#xff0c;insert的返回值为新插入的第一个元素位置的迭代器&#xff1b;因为插入可能会进行扩容&#xff0c;导致start的值改变&#xff0c;所以先定义一个变量保存pos与start的相对位置&#xff1b;判断是否需要扩容&#xff1b;从插入位置开始&#xff0c;将所…