架构设计和概要设计

初步再来探讨下架构设计和概要设计的区别和边界问题。先谈下架构设计: 架构设计包括了功能性架构和技术架构设计两个部分的内容,功能性架构解决业务流程和功能问题,而技术架构解决非功能性需求等问题。两种架构都包括了动态和静态两个方面的内容,对于功能性架构中动态部分为业务流程驱动全局用例,用例驱动的用例实现等;对于技术架构中动态部分为架构运行机制,而静态部分为框架,分层等方面的内容。 功能性架构包括了全局用例设计,这个本身是用例分析和设计的一个延续,而全局用例分析建议的思路仍然是业务流程,业务用例建模到系统用例建模的过程。全局用例分析清楚后可以开始考虑子系统和模块的划分,形成系统的功能架构图,当然在划分过程中一定要考虑到通过CRUD矩阵等分析方法来分析模块如何划分合理,如何保证模块本身高内聚和松耦合。 在全局用例分析完成后涉及到数据模型的设计,数据建模仍然从业务驱动,从最初的业务对象和单据入手,到最终的数据概念模型和逻辑模型等。架构设计中全局数据模型不一定覆盖所有的数据对象和数据表;但是核心的主数据,核心业务单据数据一定要覆盖到,模型到的层次到逻辑模型即可。如果用面向对象的分析方法,这里需要出的是UML建模中的概念模型和逻辑模型,体现核心对象和类,核心对象和类之间的关系。 将全局用例分析和数据模型建立融合在一起,可以看到这两者结合起来会形成一个系统完成的领域模型层。一直认为领域模型思路应该引入架构设计,只有领域模型才是真正关注功能性架构,而不用马上关注到具体的技术分层和技术实现。 前面两者做完后可以看到一个大系统被分解为了多个子系统或模块,那么接着要考虑的就是模块间的集成架构,分析完集成架构模块间的接口基本就出来了。接口设计应该是架构设计的另外一个核心内容。要明白架构设计一个重要作用就是架构设计完成后各个模块可以并行开始概要设计,详细设计和开发工作。只要大家都遵循架构设计约定的接口规则即可以了。 集成架构考虑完另外一个核心内容就是公共可复用组件的抽取和识别,包括了功能组件和技术组件,需要识别出来哪些是可复用的,如何进行复用。对于复用层次本身又包括了数据层复用,逻辑层组件复用,界面层UI组件的复用等。复用是架构价值体现的的另外一个关键点。 这些都做完后,接着一个步骤应该在架构设计阶段做的就是对架构输出成功进行模拟验证,前面完成了分解动作,必须通过模拟验证来看看后续分解内容能否很好的集成和组装。很多时候我们做架构设计的时候往往不做这块内容,导致架构设计一些内容变成空中楼阁,无法落地。 再回来看技术架构设计,首先谈下静态部分的内容。这里面就包括了软件开发的分层架构,开发框架等内容,包括开发规范约定,技术平台和语言的选择,使用的规约等都需要考虑。很多时候我们看到谈架构的时候说到的三层或多层架构,仅仅是完整架构设计里面很小的一部分内容。 除了分层架构外,接着考虑的就是各种非功能性需要,我们在架构上需要如何设计。这里面包括了事务,缓存,异常,日志,安全,性能,可用性,容错能力等。这些逐个点都要在架构设计中说清楚如何考虑,由于这些本身就属于一个应用系统中技术平台要考虑的内容,因此应该设计为较为公用的技术组件供上层的业务组件使用。要明白很多时候为何谈到AOP或可插拔架构,只有这样去考虑问题,才会考虑真正的功能性架构设计和功能实现和非功能性技术架构这块充分解耦,实现进一步的灵活装配。 再回到架构设计视图层面,还需要考虑的就是整个应用系统的部署架构,部署架构本身也包括了逻辑视图和物理视图,应用最终开发出来了如何进行部署,这涉及到了IT基础架构方面的细化,也需要考虑清楚。 概要设计 概要设计首先要明白的是根据架构设计的内容进一步对某个模块的设计进一步细化。架构设计在系统级,而概要设计在子系统或模块级。拿建筑来比喻,架构设计是把一个建筑的框架结构全部定清楚,包括地基要挖多深,核心框架和承重结构如何,每一层的结构图如何,应该分为几个大套间这些内容都应该定下来。每个大套间的水,电,气等管道接入点在哪里等。而概要设计要做的是拿着一个套间,来考虑这个套间内部该如何设计,如何划分功能区域,如何将水电气接入点进一步在房间内延伸,哪些地方需要进一步增加非承重的隔断等。 基于以上思路我们看到在架构设计的时候,除了很少部分的核心用例我们会谈到具体的用例实现完,大多数功能我们都不会谈到具体的用例实现层面。而到了概要设计则需要进一步的分解我这块模块究竟需要实现哪些功能点,具体的每个功能点究竟如何实现都必须要考虑到。 严格的概要设计,我们希望是到了概要设计的某个功能模块,模块所涉及到的核心的类全部会出来,类关系图全部会出来。数据库设计也进一步细化到该模块的数据库物理模型。对于用例进行用例实现分析,在实现分析过程中可以看到每个类核心的public方法全部会分析识别出来。 拿着架构设计的接口,概要设计也需要进一步细化,细化出接口具体的输入输出和使用方法,包括模块应该使用哪些外部接口,模块本身又提供哪些接口出去都必须细化清楚。做概要设计的时候一定要清楚当前做的这个模块在整个应用系统架构中的位置,和外部的集成和交互点。 概要设计不用到详细设计这么细化,包括类里面的私有方法,public方法的具体实现步骤和逻辑,伪代码等。但是我们要看到概要设计里面对于核心的业务逻辑必须要设计清楚如何实现,实现的机制和方法。很多时候我们到了概要设计画uml的时序图,很多时候一看没有任何意义,全是跨层的简单的交互和调用。这个应该在架构设计的架构运行机制说清楚即可。设计到多个业务类间的交互调用才是重点,一个简单的功能增删改查,完全没有必要画什么时序图。 其次架构设计中给出了各种安全,性能,缓存的设计。那么在概要设计中出来另外一个问题,即架构给出的各种实现方案和技术,我们在概要设计中如何选择,如何使用。不是所有功能都需要缓存,那就要说清楚哪些功能根据分析需要缓存,需要缓存哪些对象,缓存本身的时效性如何设置等问题。 概要设计作为我们要达到一个目的,就是不论是谁拿走概要设计来做,最终实现出来的功能模块不会走样,功能模块最终实现出来可能有性能,易用性等方面的问题,但是整个功能实现的大框架一定是定了的。 另外一篇文章可参考:http://321echo.blog.163.com/blog/static/999257872010020102113705/ 转载自博客园

April 9, 2024 · 1 min · Egbert Ke

ARTS打卡 第四周

1、Algorithm 无重复字符的最长子串 给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。 示例 1: 输入: s = “abcabcbb” 输出: 3 解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。 示例 2: 输入: s = “bbbbb” 输出: 1 解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。 示例 3: 输入: s = “pwwkew” 输出: 3 解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。 请注意,你的答案必须是 子串 的长度,“pwke” 是一个子序列,不是子串。 提示: 0 <= s.length <= 5 * 104 s 由英文字母、数字、符号和空格组成 思路 这道题主要用到思路是:滑动窗口 什么是滑动窗口? 其实就是一个队列,比如例题中的 abcabcbb,进入这个队列(窗口)为 abc 满足题目要求,当再进入 a,队列变成了 abca,这时候不满足要求。所以,我们要移动这个队列! 如何移动? 我们只要把队列的左边的元素移出就行了,直到满足题目要求! 一直维持这样的队列,找出队列出现最长的长度时候,求出解! 时间复杂度:O(n)...

September 11, 2023 · 2 min · Egbert Ke

ARTS打卡 - 第三周

1、Algorithm 和为 K 的子数组 给你一个整数数组 nums 和一个整数 k ,请你统计并返回 该数组中和为 k 的连续子数组的个数 。 示例 1: 输入:nums = [1,1,1], k = 2 输出:2 示例 2: 输入:nums = [1,2,3], k = 3 输出:2 提示: 1 <= nums.length <= 2 * 104 -1000 <= nums[i] <= 1000 -107 <= k <= 107 思路 我们可以基于方法一利用数据结构进行进一步的优化,我们知道方法一的瓶颈在于对每个 iii,我们需要枚举所有的 jjj 来判断是否符合条件,这一步是否可以优化呢?答案是可以的。 我们定义 pre[i]为[0..i]里所有数的和,则 pre[i]可以由 pre[i−1]递推而来,即: pre[i]=pre[i−1]+nums[i] 那么[j..i]这个子数组和为k这个条件我们可以转化为 pre[i]−pre[j−1]==k 简单移项可得符合条件的下标j需要满足: pre[j−1]==pre[i]−k 所以我们考虑以i结尾的和为k的连续子数组个数时只要统计有多少个前缀和为pre[i]−k的pre[j]即可。 代码 public class Solution { public int subarraySum(int[] nums, int k) { int count = 0, pre = 0; HashMap < Integer, Integer > mp = new HashMap < > (); mp....

September 4, 2023 · 2 min · Egbert Ke

ARTS打卡 - 第二周

1、Algorithm 换水问题 超市正在促销,你可以用 numExchange 个空水瓶从超市兑换一瓶水。最开始,你一共购入了 numBottles 瓶水。 如果喝掉了水瓶中的水,那么水瓶就会变成空的。 给你两个整数 numBottles 和 numExchange ,返回你 最多 可以喝到多少瓶水。 示例 示例 1: 输入:numBottles = 9, numExchange = 3 输出:13 解释:你可以用 3 个空瓶兑换 1 瓶水。 所以最多能喝到 9 + 3 + 1 = 13 瓶水。 示例2 输入:numBottles = 15, numExchange = 4 输出:19 解释:你可以用 4 个空瓶兑换 1 瓶水。 所以最多能喝到 15 + 3 + 1 = 19 瓶水。 提示: 1 <= numBottles <= 100 2 <= numExchange <= 100 思路 首先我们一定可以喝到 bbb 瓶酒,剩下 bbb 个空瓶。接下来我们可以拿瓶子换酒,每次拿出 eee 个瓶子换一瓶酒,然后再喝完这瓶酒,得到一个空瓶。以此类推,我们可以统计得到答案。...

August 28, 2023 · 1 min · Egbert Ke

ARTS打卡 - 第一周

前言 我不是一个喜欢凑热闹的人, 我不玩抖音、不玩微博这些东西。 但是这次不一样,陈皓老师在我前进的道路上给了我精神上很大的鼓舞,我还记得大约三年前,刚转正正式参加工作的时候,看着陈皓博客的文章,让我热血沸腾的感觉,心想有一天我也要成为这么牛逼的程序员。虽然我现在还远远没有达到,但是这种信念似乎还一直在我心中,这个更加重要。 多说几句吧 刚开始工作第一年,业余时间基本都在按照陈皓老师的专栏中介绍的路径在补充学习,汇编,unp,apue等,虽然现在几乎全忘光了,但是我怀念那种感觉。 陈皓老师去世了,世事无常阿卧槽! 1、Algorithm 颜色分类(荷兰国旗问题) 给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。 此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。 注意: 不能使用代码库中的排序函数来解决这道题。 进阶: 一个直观的解决方案是使用计数排序的两趟扫描算法。 首先,迭代计算出0、1 和 2 元素的个数,然后按照0、1、2的排序,重写当前数组。 你能想出一个仅使用常数空间的一趟扫描算法吗? 示例 输入: [2,0,2,1,1,0] 输出: [0,0,1,1,2,2] 思路 双指针, 开始位置分别位于数组两端, 分别分割0和2的区域, 从左向右遍历,将0交换到左边, 将2交换到右边,然后处理一下终止和边界情况。 代码 class Solution: def sortcolors(self, nums: List[int]) -> None: n = len(nums) p0, p2 = 0, n - 1 i = 0 while i <= p2: while i <= p2 and nums[i] == 2: nums[i], nums[p2] = nums[p2], nums[i] p2 -= 1 if nums[i] == 0: nums[i], nums[p0] = nums[p0], nums[i] p0 += 1 i += 1 2、Review 分享一下medium中一篇关于规则引擎介绍的文章...

August 21, 2023 · 2 min · Egbert Ke

博客再再开张

大概在两年前,我写了一篇《博客再开张》的文章,记录了我的博客迁移之旅。 最近,我的博客再次迁移。 迁移到了github,使用hugo + papermod 强力驱动☺️ 为什么要迁移 说到底,就是维护成本 之前的维护成本为什么高 1,服务器 需要自己购买服务器,自运维,运维对象包括服务器续费,web服务维护(运行在docker中) 2,数据 博客数据需要定期备份, 写脚本定时dump下来,推送到github中存储起来 3,备案 之前有个备案站点,每年需要重新审核一次,而且我不太喜欢备案,现在已将备案信息删除,域名和服务器不再具有备案信息。 4,证书 免费证书每3个月需要维护一次,自动续期的话需要单独跑个进程。使用cloudflare服务的话,国内响应速度太慢。 所以,维护成本相对还是比较高的,加上平时业余时间不是很多,时间长了人就乏了,迫切需要维护成本低的博客方案。 为什么要写博客 有一天,我看到了别人的博客,看到他们写的文字,记录自己的想记录的东西。 我知道,那是我一直想做的事情,我必须继续做下去。 不写博客,当我回过头来,我会失落,会后悔,就这么感性的原因。 博客能记录自己文字,让自己的思绪有个干净的地方停留,以后能够回顾自己的心路历程。如果能给别人带来一些思考或者帮助,那也是极好的。 迁移之后维护成本为什么低 1,serverless 都是静态文件,no backend, no fucking logic. 2, continus deploy I just need write a fucking markdown and git push. 为什么一开始没有使用这个方案 一开始是在CSDN上写,写了110多篇,记录自己日常学习时候的小收获,包括刷的算法题,解决过的case等,那是最初纯粹的初衷。 然后,想自己徒手写博客,锻炼自己的工程能力,项目经验等,于是自己徒手撸了一个简单博客,前后端都自己写。 然后,感觉功能比较弱,想要一个类似wordpress的博客,于是选了typecho建立博客 然后,i have no much time. 想简单点,just write something. 但是又不想用CSDN这样的平台,因为感觉平台不纯粹, 我自己写的东西,被平台免费拿走去赚广告费,还因为用了他们免费的博客服务沾沾自喜,no. 于是到了现在这一步,serverless,纯静态站. 后续计划 1,博客迁移,将之前写的博客迁移到这个站点(不包括CSDN) 2,完善站点功能,评论,站点统计,可能还会加个adsence😋 朋友,你也开始写博客吧,You need it.

July 15, 2023 · 1 min · Egbert Ke

Hello World

Hello World

July 9, 2023 · 1 min · Egbert Ke

汉字在屏幕上的显示过程以及乱码的原因

一、计算机中的显示原理 要想在计算机的显示器上显示文字,首先你得写一个程序,这个程序的任务就是就是把文字的显示信息发给显卡,显示信息包括在这个屏幕上的输出位置、字的大小等等。然后显卡就知道怎么显示这个字符了。 屏幕上是如何显示文字的原理是什么呢? 屏幕上其实有很多个小灯,小到肉眼看不见,当他们不亮时,屏幕就是黑色的,当他们亮了一部分,如果那一部分刚好是个文字的形状,那么屏幕上就显示文字了。这个原理就跟军训时人摆文字显示字符一样。如下图,通过led灯的开和关显示出了123。放到显示器上,小灯会变得特变小,肉眼很难看到,当一部分红色的小灯亮了,那一部分刚好摆成123的形状,那么红色的123这三个字符就在屏幕上显示出来了。 如何让显示器得知道是那个灯亮那个灯灭,这就是显卡的作用了,操作系统会根据文字的编码,去字库中找到要显示的字符的点阵数据,点阵数据指明了哪个灯应该亮起,亮起的颜色是什么颜色。显卡会结合点阵数据和其他显示信息,进行计算(比如按照一定比例扩大等),然后发给显示器控制显示器的显示! 注意:这一部分的具体细节我不确定,但是大概的思路应该是没错的。 通过以上得知,关键点在于文字的编码,只要知道了文字的编码,就能找到字库的点阵数据。众所周知,文字的编码有很多种,而乱码的根本原因是文件保存时采用的编码和打开文件时用于解码的编码不一致,从而找到了错误的点阵数据,显示了错误的输出!。 二、从键盘输入开始理解编码的存在形式 以window系统为例,假设你刚刚打开了记事本。 1.你在键盘上按下了’a‘。 2.你的按下触发了电路,键盘扫描到你a被按下,于是键盘形成了’a’的扫描码,发送到了存在于键盘上的寄存器,同时给CPU发送了一个中断信号,告诉CPU我这有活动了! 3.CPU根据键盘的中断线路号检测到是键盘发出的中断信号,于是根据中断号计算出键盘的中断处理程序在内存中的地址,转到键盘的中断处理程序去执行。 4.键盘的中断处理程序找到键盘的驱动程序代码,转到键盘的驱动程序执行。 5.键盘的驱动程序去读取键盘上的保存扫描码的寄存器,把‘a’的扫描码读到内存中。 6.驱动程序把扫描码转换成虚拟码。为什么要转换呢,因为不同的键盘由于厂家不同,型号不同、设计不同的原因,‘a’这个按键产生的扫描码在不同的键盘上是不一样的,为了统一管理,驱动程序得把不同键盘按下的‘a’转换成统一的表示。比如把不同键盘按下的‘a’产生的扫描码统一转换成一个字节的0x41。驱动程序要进行转换,那么驱动程序得知道这是哪种类型的键盘,不然没有转换的依据,原理是键盘的相关信息比如生产厂家、键盘型号等会保存在键盘上的一些只读寄存器中,计算机通过这些只读寄存器就知道这是哪种键盘。从而就知道该键盘的扫描码对应的虚拟码。 7.驱动程序把0x41交给操作系统上自带的且在后台默默运行的的IMM进程。 8.IMM进程把0x41交给系统当前使用的的输入法编辑器。比如搜狗输入法或者百度输入法。系统上所有的输入法,都由IMM管理。 9.输入法收到了0x41,对0x41进行处理,霹雳巴拉一顿操作,首先查到0x41这个值对应的可能的文字,比如可能是‘啊’、‘阿’、‘吖‘…等。首先查询系统当前的代码页是哪一个,也就是系统默认编码,若你没有修改系统的默认编码,则查找的结果为GBK(相当于GB2312)编码的。于是通过GBK的代码页这些可能的文字的GBK编码找出来,通过操作系统从字库中寻找这些可能文字的字库数据,交给显卡,显卡把他们显示出来。 10.显卡把他们显示出来后,屏幕上显示了好多个文字让你选择,那么你通过键盘的左移右移回车等操作选中了一个字,这个键盘操作又产生扫描码,最后还是输入法接收到了你的键盘按键输入情况,然后,输入法就可以根据你的键盘输入情况确定你选中了哪个文字,假设你选中了’啊’,于是输入法把‘啊’这个字的GBK码交给操作系统,操作系统把这个GBK码放进指定内存中,这指定内存被称为输入缓冲区。 11.记事本可以扫描缓冲区有没有内容,当检测到了缓冲区有了内容后,记事本至少需要两个操作,一是把‘啊’这个文字显示到记事本的窗口里面,二是把"啊"的编码放到自己的内存空间。 12.记事本接收到的是GBK编码,这个编码保存在了记事本的内存空间,并把“啊”输出到了记事本的窗口中。这时你的输入操作已经结束了(如果你不再进行输入),接下来就是保存这个记事本的内容了,如果想要保存这个“啊”,你的应用程序就得向操作系统申请一个文件,把“啊”的编码写进文件中。如果你不作任何操作,硬盘上就会默认保存的是‘啊’的GBK编码,如果你想保存的是’啊’的其他编码,那也可以,转换一下编码格式,然后放进文件中保存。放进文件中保存,那c语言来说,有二进制方式的写和文本文件方式的写,该用什么方式呢?首先说明什么是文本文件,文本文件就是保存文本的文件,里边都是一些字符(也就是文本)的编码,解析出来后都是文字,你用utf-8格式保存的,里边就是utf-8格式的字符的编码,你用GBK格式保存的,里边就是GBK的编码,总之里边保存的是文字信息。另一个就是二进制文件,一般来说我们编程很少用到。二进制文件里边保存到不是文本,比如视频文件、图片文件、3D模型等。其实二进制文件和文本文件在文件中的保存形式都是0和1的二进制流,既然都是0和1的二进制流,为什么要区分他们呢,因为他们有点区别,比如文本文件以EOF(值为-1)作为文件结束标志,因为不管是什么编码,都没有哪个字符的编码值是-1。而二进制流就不一样了,里边完全有可能有一段字节代表着-1,因此不能以-1作为文件的结束标志,一般来说二进制文件应该是通过比较文件长度来判断结束标志的。在c语言中,我们通常是使用fwrite()和fread()函数来读写文件,那么我们并没有指明以什么编码方式来读出或写进文件啊,别忘了,两个函数会是系统调用相关的,而系统默认的编码格式就是GBK,因此这两个函数都是按GBK来进行读或取的。如果你想使用其他编码比如UNICODE,就得使用其他读写的函数了,比如fgetwc()、fwscanf();这些函数会把GBK编码转换成UNICODE编码再进行读和写。 13.记事本默认保存的编码是ANSI,ANSI也叫多字节字符集,ANSI其实不是一种编码方式,是所有使用不定长字节来表示字符的编码格式的统称,在简体中文Windows操作系统上,ANSI指的是GBK编码,在繁体中文Windows操作系统中,ANSI编码代表Big5;在日文Windows操作系统中,ANSI 编码代表 JIS 编码。当然你用可以更改记事本的保存格式。Unicode同理,Unicode是一个字符集,不是一个编码方式,在windows这边,Unicode指的是UTF-16,在其他环境下,可能指的是UTF-8或UTF-32,比如linux上指的是utf-8. | | | | | 三.缕一缕编程的过程 1.首先,现在我们简化一下VS2013这个软件,把VS2013看成是记事本(编辑器)+编译器的结合体。它只有编写文本进行保存和对文本文件进行编译的功能。如果你使用的是中文操作系统的Windowsd的VS2013编写源代码,在你编写完成后,运行之前或者按下CTRL+S,那么你的源代码就会保存起来。跟记事本的保存一样,那么它默认应该是使用GBK编码格式来保存你的代码源文件,那我不想按GBK来保存怎么办呢?可以在文件->高级配置选项里修改源代码的保存格式。 2.假设你的源代码里有一个字符"你好",在你把你的源代码保存了之后,硬盘上你的源代码文件中存在着"你好,世界!“的GBK编码:C4E3(你) BAC3(好) 。 3.你的打印文件里面有打印"你好"这个中文字符的语句,你想在屏幕上显示"你好” 4.你点击了运行按钮,首先,编译器的做的工作就是启动它的编译器对你的源代码进行编译,要进行编译,首先得解析源代码文件,要解析一个文件,得先知道它是什么编码,否则解析要出错啊,那么编译器是按什么编码格式来解析你的源文件呢,不用想就知道,那肯定是GBK编码嘛,毕竟编辑器的默认编码就是GBK的。那我要是把编辑器的编码格式换了怎么办呢,没事,编译器改成一样的不就完事了。修改的方式为项目 -> 属性 -> 配置属性 -> c/c++ -> 命令行 -> 其他选项。在其他选项里输入你所需要的编码,比如utf-8。 一般这一行是空的,我们只需把“从父级或项目默认设置继承”选上就好了。这样它就会根据你项目的编码格式主动更改相同的编码进行解析。 4.等等,你好像记得有个地方也能修改项目的编码属性?就在项目 -> 属性 -> 配置属性 -> 常规 ->字符集那,有个使用多字节字符集和使用Unicode字符集?它默认也不是GBK啊,它默认是Unicode呢。这又是什么玩意?首先,在这里,多字节字符集=ANSI=GBK,Unicode=utf-16。这玩意是这样的,它不是设置你写的代码的编码格式,但是他能控制你使用的API的版本。什么意思呢?你写的程序肯定有#include<“xxx”>的代码,#号说明这是一个预编译命令,c语言里可没有#这个操作符。预编译命令是给编译器看的,编译器检测到了预编译命令后,在链接的时候就会把#include<“xxx”>删掉,把#include<“xxx”>原本的代码复制过来放到这个地方。预编译指令还有一个较为常见的就是#ifndef。完整意思就是if not define,字面上来理解就是如果没有定义。而在vs上编程你经常#incluide<“xxx”>里边的代码里经常有和以下类似的代码: ifndef Unicode typedef MessageBox MessageBoxA #endif typedef MessageBox MessageBoxW 翻译如下: 如果没有定义 Unicode MessageBox 就是MessageBoxA 结束 MessageBox 就是 MessageBoxW...

July 20, 2022 · 1 min · Egbert Ke

分布式理论的简单理解

一个分布式系统里面,节点组成的网络本来应该是连通的。然而可能因为一些故障,使得有些节点之间不连通了,整个网络就分成了几块区域。数据就散布在了这些不连通的区域中。这就叫分区。 当你一个数据项只在一个节点中保存,那么分区出现后,和这个节点不连通的部分就访问不到这个数据了。这时分区就是无法容忍的。 提高分区容忍性的办法就是一个数据项复制到多个节点上,那么出现分区之后,这一数据项就可能分布到各个区里。容忍性就提高了。 然而,要把数据复制到多个节点,就会带来一致性的问题,就是多个节点上面的数据可能是不一致的。要保证一致,每次写操作就都要等待全部节点写成功,而这等待又会带来可用性的问题。 总的来说就是,数据存在的节点越多,分区容忍性越高,但要复制更新的数据就越多,一致性就越难保证。为了保证一致性,更新所有节点数据所需要的时间就越长,可用性就会降低。 转载自知乎

January 7, 2022 · 1 min · Egbert Ke

生命的意义

生命在于运动,及早睡早起 运动代表生命的活力和激情 早睡代表自律 早起代表积极的生活态度,精进

October 20, 2021 · 1 min · Egbert Ke