DDD 领域驱动设计落地实践系列:战略设计和战术设计_ddd战略设计-程序员宅基地

技术标签: 云原生  DDD  分布式  

一、引言

相信大家对于什么是 DDD 有了初步的了解,知道它是一种微服务的架构设计方法论,为我们解决如何建立领域模型,如何实现微服务划分等提供了方向和指导。但是对于如何具体落地使用 DDD,可能大家还是一脸懵 B 的状态,因此从本文开始以及后面的文章将对如何进行 DDD 落地进行详细的阐述。在这其中还是会涉及到 DDD 中的一些重要概念,原本想着在一篇文章中介绍所有的概念,但是我觉得,概念总是在它该出现的时候出现才会让大家印象深刻,否则这些概念只是死板的概念,我们不清楚他为什么出现以及可以解决什么问题。

DDD为什么能火起来?

我们都知道这些年随着设备以及技术的发展,软件架构发生了很多变化,从最初的单机(BS/CS)架构到后面的集中式架构,再到如今的微服务架构, 现在基本可以说是微服务架构盛行的时代, DDD早在2004年就由埃里克·埃文斯提出, 但一直处于一个不愠不火的状态,直到Martin Fowler的《Microservices》引起大家注意, 也就是微服务盛行之后(这儿需要说明的是,微服务最早的提出者不是Martin Fowler,而是Fred George), DDD再次回到人们视野中间,为什么呢 ?

我们先看一下三种技术架构的演进以及主要区别: 

第一阶段是单机架构特征是整个开发围绕着数据库进行设计和开发。

第二个阶段是三层式的集中式架构采用面向对象的设计方法,业务逻辑分业务层、逻辑层、数据访问层,这种架构很容易某一层或者几层变得臃肿,扩展性较差, 另外摩尔定律失效, 单台机器性能有限。

第三层阶段是微服务架构在集中式架构中, 系统分析、设计和开发往往是独立进行的,而且各个阶段负责人可能不一样,那么就涉及到交流信息丢失的问题另外项目从分析到开发经历的流程很长,很容易最终开发设计与需求实现的不一样微服务主要就是解决第二阶段的这些痛点,实现应用之间的解耦,解决单体应用扩展性的问题。

微服务存在的问题

进入微服务之后 , 解决了集中式架构的单体应用很多问题, 但是新的问题应运而生 , 微服务的粒度应该多大 ?微服务如何设计呢?微服务如何拆分 ?微服务边界在哪里 ?

很长时间人们都没有解决这一问题,就连Martin Fowler在提出微服务架构的时候也没有告诉我们这该如何拆分微服务。

甚至在很长的时间里人们对微服务拆分产生了一些误解, 有人认为:"微服务很简单,就是将之前的单体应用拆分成多个部署包, 或者将原来的单体应用架构替换为一套支持微服务的技术架构,就算是微服务了。" 还有人认为微服务应该拆分得越小越好。

鉴于上述情形, 很多项目因为前期拆分过度, 导致复杂度过高, 导致后期难以运维甚至难以上线。

可以得出一个结论:微服务拆分困境产生的根本原因就是不知道业务或者微服务的边界到底在什么地方。换句话说,确定了业务边界和应用边界,这个困境也就迎刃而解了。

而DDD就是解决了这个确定业务边界的问题,可见DDD并不是一种技术架构,而是一种划分业务领域范围的方法论。DDD的兴起是由于很多熟悉领域驱动建模(DDD)的工程师在进行微服务设计时, 发现用DDD的思路进行业务梳理可以很好规划服务边界, 可以很好实现微服务内部和外部的"高内聚、低耦合"。于是越来越多的人将DDD作为业务划分的指导思想。

那么,什么是DDD呢?

DDD概述

DDD是一种拆解业务、划分业务、确定业务边界的方法, 是一种高度复杂的领域设计思想,将我们的问题拆分成一个个的域, 试图分离技术实现的复杂性主要解决的是软件难以理解难以演进的问题,DDD不是一种架构, 而是一种架构方法论, 目的就是将复杂问题领域简单化, 帮助我们设计出清晰的领域和边界, 可以很好的实现技术架构的演进。DDD包括两部分,战略设计部分战术设计部分

战略设计主要从业务视角出发,建立业务领域模型,划分领域边界,建立通用语言的限界上下文,限界上下文可以作为微服务设计的参考边界。

战术设计则从技术视角出发,侧重于领域模型的技术实现,完成软件开发和落地,包括:聚合根、实体、值对象、领域服务、应用服务和资源库等代码逻辑的设计和实现。

二、DDD 大致实现过程 

如下图所示,实现 DDD 落地大致需要经历这样三个阶段,即为业务分析 -> 战略设计 -> 战术设计,不同阶段的输出都是下一阶段的输入。业务分析阶段为战略设计输出经过统一语言描述的业务事件、业务逻辑以及业务分类,而战略设计阶段又为战术设计阶段输入领域模型以及边界上下文,方便其进行微服务拆分以及模型映射。下面我们分别看下这三个阶段到底都做了什么事情。

业务分析:在这个阶段需要集齐项目团队的成员主要包括领域专家、设计人员、开发人员等一起对业务问题域以及业务期望进行全面的梳理,厘请业务中的统一语言,在业务领域中发现领域事件、领域对象及其对应的领域行为,搞清楚他们各自的关联关系。

战略设计:通过 DDD 的理论,对业务进行领域划分构建领域模型,梳理出相应的限界上下文,通过统一的领域语言从战略层面进行领域划分以及构建领域模型。在构建领域模型的过程中需要梳理出对应的聚合、实体、以及值对象。

战术设计:以领域模型为战术设计的输入,以限界上下文作为微服务划分的边界进行微服务拆分,在每个微服务中进行领域分层,实现领域模型对于代码的映射,从而实现 DDD 的真正落地实施。

三、相关概念补充 

在介绍战略设计和战术设计之前,我们先来弄清楚一些晦涩难懂的概念性的内容,这些概念看上去总是不明觉厉。我觉得 DDD 不容易入门的一个原因就是这些概念太不好理解了,即便是《领域驱动设计:软件核心复杂性应对之道》原著中的描述更加让人难以捉摸。要想理解这些概念性的内容,我们要思考为什么有这样的概念,以及它的出现是为了解决什么问题,在实际的上下文场景下是怎么运用的,我想只有这样我们对这些不明觉厉的东东才能有更加深刻的理解。

1、值对象

值对象看上去又是比较抽象的概念,越抽象的概念我们越要抓住最主要的脉络,才能理解。就值对象来说,我们可以把他理解没有 ID 的东东,什么叫没有 ID 呢?可以这么理解,当我们在外面吃饭的时候,在餐厅里面选位子落座,我们会关心这个桌子是哪里生成的,编码是什么以及规格是怎样的吗?显然不会,有空位坐就可以了。当然我们还是需要关注值对象上下文的,如刚才的例子,我们消费者并不关心坐哪个位子,但是商家是关心的,因为他要根据桌号来进行上菜以及收账。

再来举一个例子,在我们设计用户信息的时候,这其中就会包括用户的地址,用户的地址实际是由这个用户是哪个省的,哪个市的,哪个县的以及具体地址是什么、邮编是什么等等属性信息组成,那么这个地址信息在用户信息中实际就是一个值对象,他不需要唯一的 ID 来标识。

 2、实体

和值对象不同的是,实体是有唯一标识的,这就好比我们每个人都会有身份证,身份证上面的身份证号码就是每个人的唯一标识。另外这个身份证号码会一直跟着我们不会改变,即使你改了名字户口状态变化,但是身份证号码是不会变化的,这也是实体的另一个特征就是它具有延续性。实体对应了业务对象的业务属性以及业务行为。

值对象以及实体都是领域模型中的领域对象,是组成领域模型的基础元素,一起实现领域内的最基本的核心领域逻辑。

3、聚合 

又是一个看上去不好理解的概念,实际上用大白话来说,如果人是一个个不同的业务实体,那么社会中的不同组织、机构就是将对应技能的人聚集在一起发挥更大的业务价值以及完成更加复杂的业务行为的的集合。这就好比我们公司里面有种各样的部门,有人力部门负责招聘和薪酬、有销售部门负责营销、有研发部门负责产品研发。不同的部门实际就是不同的聚合。 聚合就是有业务关联关系的实体以及值对象的集合,通过实体、值对象以及各自之间的业务逻辑聚合在一起完成某个业务节点,我们就可以理解为聚合。我们可以根据业务的单一职责以及高内聚的的设计原来来进行聚合的划分。 

4、聚合根 

聚合的出现实际是一种业务单元,那它必定涉及到数据的持久化,如果在聚合中的任意实体都可以被外部进行数据修改,那么我们将很难保证聚合内的数据一致性。因此我们需要有一个数据输入修改的统一入口来保证聚合内的数据修改统一的符合聚合中的业务规则,因此出现了聚合根的概念。聚合根实际是就是一种实体,具备唯一标识,有独立的生命周期。但是他是特殊的实体,他有协调实体以及值对象完成业务逻辑的功能,好比是一个部门的负责人一样。一个聚合只有一个聚合根,聚合根在聚合之内采用引用依赖的方式对实体和值对象进行组织和协调,聚合根和聚合根之间通过唯一 id 进行聚合之间的协同。 

例如,在电商中,主订单、订单明细他们的业务规则相同,而且基本上都是一同操作的,对订单进行操作的时候,基本上都会同时修改主订单和订单明细,那么主订单和订单明细就是一个聚合。在这个聚合中,操作的入口基本都是主订单,所以主订单就是这个聚合的聚合根。

5、领域

领域根据核心程度不同,分为Core Domain、Supporting Domain、Generic Domain。

Core Domain(核心领域):公司的业务核心。例如电商业务中,商品、购物车、交易、促销、优惠、支付等都属于核心领域

Generic Domain(通用领域):通用的领域,没有个性化的需求,甚至是各个公司都类似的功能或市场上可以直接购买到,可以被多个子域使用的领域,例如:用户、权限、认证、人脸识别等。

Supporting Domain(支撑领域):一般是只不是系统中的最核心模块,但是也不是通用的组件和服务,但是对核心业务起到了支撑的作用的模块。

6、Bounded Context

什么是Bounded Context?

Bounded Context定义领域边界,以确保每个上下文含义在它特定的边界内都具有唯一含义。Bounded Context定义了模型的适用范围,使团队所有成员能够明确地知道什么应该在模型中实现,不应该在模型中实现。注意:处于不同界限上下文中,领域模型一定不可以共用。例如:下面示例中的商品/货物、商品/订单明细不可以使用同一个模型,不过这对于有经验的开发者而言也是显而易见的。

如果我们要设计一个电商平台,那么我们需要就要有商品、交易、支付、物流等模块,这些模块其实就是一个个的领域。在不同领域中,同样一个事物的因为关注点不同,其含义也不同,但是在确定的上线文中,其含义就是确定的。例如:

在商品领域我们关注商品的基本信息(名字、品牌、型号、价格等),在交易领域我们关注交易流程(订单创建、支付结果、履约过程等),但同样是商品,在交易领域中我们称之为订单明细或交易明细;所以当我们在交易领域谈及商品的时候,我们指的是订单的明细(商品),而且一般只关注交易履约状态,顺其自然的不会那么在意商品属性。

同样是一个用户,在用户领域我们关注其基本信息、账号密码等但是在权限领域,我们关注的是其拥有的菜单、功能权限,关注其角色。

以上示例这直接反映在代码层面时,其差异是非常明显的,当在确定领域内讨论问题是,其指代性也很强。

在不同场景中,我们对同一个事物的称呼也有较大差异。例如:

1、商品、货物:同样一个东西,在交易领域叫做商品,在物流领域叫货物。渠道商品、后端商品:

2、在进销存管理中,在销售测叫做渠道商品(可以通过多个渠道售卖,例如在淘宝、京东上卖);在采购侧叫做后端商品。

四、战略设计 

战略设计这四个字听起来有点高大上的感觉,感觉离我等 DS 比较远。实际上无论对于公司或者个人来说,都需要有个发展的目标以及指导方针,指引我们该向何方前进,这个指导性的内容,我们就可以称之为战略。那么在 DDD 领域,战略设计主要从业务角度出发,划分业务的领域边界,建立基于通用语言和业务上下文语义边界的限界上下文,实现业务领域模型的构建。使得限界上下文可以作为微服务拆分和设计的边界。因为必须先有边界清晰的领域模型以及上下文,我们才能设计出边界清晰的微服务。因此在战略设计阶段最重要的就是限界上下文以及领域建模。 

举个例子:公园有一棵桃树, 如果我们想好好研究桃树该怎么研究 ?

桃子好吃吗?贵不贵?品种?怎么种植?种在什么地方 ?做成桃木剑?桃子树叶药用价值?

你看, 这样研究每一个问题都很有道理, 但是又很混乱,再回忆一下初中生物书上是这么研究的?

先将植物根据大家的理解分成多个器官组成, 像桃子、桃叶、桃花等等, 然后将每一个器官再根据功能细分成组织, 再根据这个组织中各个细胞的形态等作用分成不同的细胞, 你看看这是不是一种很有条理的分析方法。

 

DDD也是如此, 当我们面对桃树这种复杂的业务的时候,先根据固有的认识分成多个器官(领域),然后再在每一个领域中根据某些维度(这儿是功能)分为多个组织(聚合), 而每一个组织中由很多细胞(实体)组成,这就是一种战略, 有哪些好处呢 ?可以确保我们讨论的边界, 也就是讨论的东西是一个领域一个维度的, 对于桃树来说, 桃子 、桃花、桃叶、树干都是不同的领域, 划分不同领域的就是边界, 我们这儿叫领域边界, 当我们确定好这些领域之后, 就可以确保我们讨论的是同一个领域部分的东西,这样的好处就是我们可以规定好一些概念, 或者说术语, 以后大家讨论的时候就尽可能少的信息丢失。 

通常在业务分析阶段我们把业务域的主体流程进行了梳理以及分析,同时将业务期望进行了定义。在进行战略设计的过程中,需要对业务领域进行全面的梳理,首先对业务进行领域切分,划分出业务的上下文边界,然后建立对应的限界上文中的领域模型,上下文边界和领域模型可以做为微服务拆分拆分设计的输入,指导微服务落地时的拆分设计。其中业务中,对应的领域模型十分重要。那么我们该采用什么方法才能从复杂的业务领域中构建出高内聚低耦合的领域模型呢?我们可以通过业务分析以及领域建模两个阶段来实现领域模型的构建。

 

 1、业务分析

 A、事件风暴

通过事件风暴的方法可以实现快速分析和分解复杂的业务领域,分析并提取出对应的领域模型。事件风暴是早 2013 年提出来的,通过组织领域专家、以及项目团队成员在一起通过头脑风暴的方式,通过梳理业务领域中所有的领域事件以及领域事件的参与者以及输入。 

参与者

主要包括领域专家、DDD 专家、架构师、产品经理、项目经理、开发人员以及测试人员等。

实际活动

当把参与者集合在一起之后,大家需要通过头脑风暴的方式梳理当前的业务域问题。比如某个业务动作或者行为是否会触发下一个业务动作,这个动作(领域事件)的输入和输出是什么,是谁(实体)发出的什么动作(命令),触发了这个动作,这些我们都需要梳理清楚。

 我们以大家最熟悉的电商系统来举个栗子,在电商业务中有一个重要的环节就是物流。因此物流是电商业务的重要业务子域。当用户下完订单之后,在仓储领域就需要陈诚对应的货物拣货单,系统根据拣货单中的货物给不同分区的仓内作业人员生成对应的拣货任务,作业人员根据对应的拣货任务进行货品的拣取,并放入相应的货物容器中。最终将所有订单商品在拣货完成后进行合流,进行校验,最终形成包裹再进行后续的配送流程。

那么在这个流程当中,我们就会涉及到的实体就有订单、拣货单、拣货任务、用户、容器等对应的值对象为地址信息领域事件为拣货单生成、拣货任务生成。

2、领域建模 

 在我看来,领域建模实际上是整个 DDD 领域驱动设计中最重要的环节。对上,好的领域模型说明对业务流程的梳理以及业务领域的抽象做得好。对下,高内聚低耦合的领域模型可以直接决定微服务设计的质量和水平。

A、找出实体和值对象

经过全盘的业务梳理之后,我们可以在业务对象中找到对应的实体以及值对象,这里面就会牵扯到哪些领域对象设计为实体,哪些领域对象设计为值对象。主要还是根据实际的业务特征来决定。

B、构建聚合

通过业务梳理,我们找到了业务下所有的实体以及值对象,接下来我们就要构建聚合了。在构建聚合之前,我们需要先从实体集合中找到聚合根,这就好比打仗的时候讲究擒贼先擒王,王擒到了之后,归属于下面的小兵就会乖乖就范了。

那么我们应该怎么判断一个实体它就是聚合根呢?主要是看这个实体是不是有专门的模块来进行维护,自身是不是有完整的独立的生命周期以及是不是有全局的唯一 ID。通过这几个判断条件我们很容易找到对应的聚合根,如下图所示,在仓内进行作业的任务,其中拣货单就是一个聚合根,满足上述的几个条件,同时可以将和其有业务关联的实体例如货物、拣货容器等归并到拣货单,最终形成拣货聚合。

  

 C、划分聚合到边界上下文

首先,我们需要将之前梳理出来的领域事件,事件流转的触发命令都全部罗列出来,在这个过程中提取出产生业务行为的对象,就是前文所说的实体。如前面所说的物流域,库存、容器。在这个过程中我们需要找出对应的实体以及值对象,同时在这些实体中找出聚合根,将存在存在紧密业务逻辑关系的聚合根、实体以及值对象划分到一起形成聚合,再根据之前划分的边界上下文将多个聚合划到限界上下文中。

五、战术设计

不同于战略设计的高屋建瓴,战术设计是从实际的技术角度出发,它更加侧重于领域模型的技术实现,按照领域模型完成微服务的开发以及落地。从某种意义来说,战略设计实际就是战术设计的输入。在战术设计中会有聚合、聚合根、实体、值对象、领域服务、领域事件的代码实现,通过将这些领域对象映射到代码中实现设计以及系统的落地。在这个阶段,我们需要梳理微服务的边界以及边界内的领域对象以及它们之间的关系,并且可以实际的将这些领域模型确定在哪些代码模块中以及微服务分层中的具体位置。

通过 DDD 按照一定的规则对业务领域进行细分,是的问题范围可以限定在指定的边界中,因此我们可以在这个边界内建立领域模型,而后再通过代码实现领域模型,从而解决相应的业务问题。

因此在战术设计阶段,我们有个重要的事项需要去完成,一个是微服务的领域对象分析与边界划分,另一个是微服务的结构分层。

1、微服务领域对象分析与边界划分 

在战略设计阶段,我们已经构建的业务领域模型,领域模型中包含了实体、值对象、聚合根。在上文中我们实际已经根据一些业务域进行了聚合的划分,那么在此处我们需要根据不同的聚合以及已有的限界上下文继续划分微服务。有的微服务会包含多个聚合,有的微服务只有一个聚合。 

2、微服务结构分层 

在传统的工程代码结构中,大都采用 MVC 的工程结构。但是随着微服务时代的来临,传统的工程结构不能满足快速变化的业务需求。另外随着 DDD 领域驱动设计的落地,需要对于微服务的工程结构有更进一步的进化和升级。

 Eric Evans 在《领域驱动设计:软件核心复杂性应对之道》文中提出了传统的四层结构,但是实际上存在一定的问题。在 DDD 领域驱动设计中,领域层应该是核心层,但是传统的四层结构中基础层却处在核心位置,有点本末倒置的感觉。所以应该采用以领域层为核心的新的四层结构的方式优化原有的结构,实现对于基础层的解耦。

基础层:实际就是未微服务的其它层提供基础的通用技术支撑,如 Redis、MQ 以及数据库等,常见的就是数据库持久化可以放在这层当中。

用户接口层:负责向用户展示信息以及转化用户请求意图,在前后端分离的当下,可以实现在后端服务不变的情况下适配不同的用户前端的作用。

应用层:可以理解为实现领域对象以及多个聚合的服务编排以及组合,不应该有过多的业务逻辑。

领域层:领域层实际就是整个微服务的核心,在这其中涉及到领域对象的㛑状态变化以及领域规则。领域层主要包括实体、值对象、聚合根以及领域服务等。

当然实际上给还有其他的分层结构比如五层分层结构、六边形分层结构。

六、总结 

本文主要围绕 DDD 领域驱动设计落地时间的三大过程进行了阐述,详细说明了战略设计阶段以及战术设计阶段的输入和输出。 

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/summer_fish/article/details/127900779

智能推荐

[INS-40401] The Installer has detected-程序员宅基地

文章浏览阅读1.6k次。[INS-40401] The Installer has detected a configured Oracle Clusterware home on the system.如果卸载rac不干净,再安装时有可能..._ins40401

芯片验证周期归纳-程序员宅基地

文章浏览阅读591次。芯片验证周期_芯片验证周期

智能合约头文件-程序员宅基地

文章浏览阅读326次。智能合约头文件介绍这里主要是简单介绍一下该合约头文件的定义头部文件的引入这里引用了eosio的库文件和其他的C++文件,合约得其他地方就可以使用这些文件中对应的类和方法。#pragma once#include <eosiolib/serialize.hpp>#include <eosiolib/eosio.hpp>#include <eosioli..._合约头文件

一步实现多个同级div,只改变点击的当前div样式_多个div点击一个修改样式-程序员宅基地

文章浏览阅读4.3k次,点赞3次,收藏9次。记录一点小技巧,直接上代码。&lt;!DOCTYPE html&gt;&lt;html&gt; &lt;head&gt; &lt;meta charset="UTF-8"&gt; &lt;title&gt;一步实现多个同级div,只改变点击的当前div样式&lt;/title&gt; &lt;/head&gt; &lt;script type_多个div点击一个修改样式

苹果手机相机九宫格怎么设置_瞧不起iPhone相机?苹果手机5个逆天拍照功能,效果惊艳堪比单反...-程序员宅基地

文章浏览阅读612次。经常有人说苹果手机拍照效果差?要么是被美颜相机惯坏了,要么根本就不知道苹果拍照隐藏功能!国外的大神都是用苹果手机拍vlog,为什么别人就能拍出 “大片”既视感?而我只能拍出乡村爱情故事?1、 自带滤镜很多小伙伴经常为了个滤镜,用其它美颜软件拍照,但是照片总是很模糊的感觉~其实苹果手机自带拍照滤镜,拍照的时候点击右上角的三个圈,就有非常多滤镜可以选择,清晰度那叫一个高!2、竖幅全景大家都知道手机可以..._苹果相机三个圈选哪个好看

javax.net.ssl.SSLHandshakeException: PKIX path building failed-程序员宅基地

文章浏览阅读9.1k次,点赞4次,收藏5次。错误信息本地环境缺少ssl证书一、下载证书可以通过浏览器下载;也可以使用命令行1、浏览器查看证书,直接导出即可2、命令行openssl s_client -connect <目标网址:端口号> < /dev/null | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > 证书名.crt二、导入证书1. 将证书移动到$JAVA_HOME/lib/security/c..._javax.net.ssl.sslhandshakeexception: pkix path building failed: sun.security

随便推点

Maven下的JWT配置_com.auth0.jwt maven-程序员宅基地

文章浏览阅读9.5k次,点赞4次,收藏2次。SSM下的JWT配置1.简介随着互联网的不断发展,技术的迭代也非常之快。我们的用户认证也从刚开始的用户名密码转变到基于cookie的session认证,然而到了今天,这种认证已经不能满足与我们的业务需求了(分布式,微服务)。我们采用了另外一种认证方式:基于token的认证。一、与cookie相比较的优势:1、支持跨域访问,将token置于请求头中,而cookie是不支持跨域访问的;2、..._com.auth0.jwt maven

如何查看OpenCV自带函数的源代码_opencv api的源码在哪里找-程序员宅基地

文章浏览阅读3.5k次,点赞2次,收藏6次。OpenCV提供的内部函数能实现好多图像处理功能,有时我们需要改进函数或者想看一下函数的具体实现,一般有以下两种方法来查看其内部函数代码: 方法一:在opencv的安装文件夹中找到 与头文件名字对应的.C 或 .CPP 文件,然后在对函数进行查找。但不是所用函数都是这样的。例如cvSmooth函数,单击“转到定义”后,其跳转到imgproc_c.h中。通过找到im..._opencv api的源码在哪里找

地图瓦片编号与经纬度的换算关系及不同源坐标之间的相互转换_瓦片编号转经纬度-程序员宅基地

文章浏览阅读2.2k次。前言地图瓦片编号与与经纬度坐标之间的转换与简单理解。相关资料看了好多次,每次看完就忘,这里做一个简单的学习笔记。Web墨卡托投影通常提到Web墨卡托投影,我最先想到的关键词是: “3857”、“谷歌地图”。再往深了想就是“正轴等角圆柱投影”、“越靠近两极变形越大”等特性。以前对“越靠近两极变形越大”的理解是:越靠近两极,地图横向拉伸越严重。今天查资料时突然意识到一点:越靠近两级纵向拉伸同样越严重。也就是说纬度分布是不均匀的。墨卡托投影示意图地图瓦片分割目前接触的绝大多数地图瓦片是以._瓦片编号转经纬度

Windows系统Git安装教程(详解Git安装过程)_windows安装git-程序员宅基地

文章浏览阅读1.4w次,点赞13次,收藏39次。windows的git安装_windows安装git

最新支持java8的Java反编译工具汇总(大全)!!_java8 反编译工具-程序员宅基地

文章浏览阅读4k次。大多商业软件,会对程序进行加密、加壳等安全措施以防范软件被破解,从而使得反编译越来越难。反编译是一个对目标可执行程序进行逆向分析,从而得到源代码的过程。尤其是像Java这样的运行在虚拟机上的编程语言,更容易进行反编译得到源代码。 我们知道,在代码支撑方面,JDK 1.7引入了字符串Switch、泛型接口改进等新功能,1.8增加了lambda表达式、方法传递、多重注解等新特性,这使得反..._java8 反编译工具

python下列数据中不属于字符串的是_下列数据中,不属于字符串的是( )。 (6.0分)_学小易找答案...-程序员宅基地

文章浏览阅读1.6k次。【单选题】黄酮苷和黄酮苷元一般均能溶解的溶剂是【其它】跨境物流与海外仓操作【单选题】在python中,下列流程控制语句没有的是【单选题】采用碱溶解酸沉淀法提取芦丁,用石灰乳调PH时,应调至【多选题】网络拍卖网站的形式主要有( )【单选题】设有字符串str1='Hello',str2='world',则str1*2的结果是【单选题】四氢硼钠反应可用于鉴别【单选题】阅读下面一段程序: words ..._"阅读下面一段示例程序: words = \"hello,python\" result = words.capitalize() res"

推荐文章

热门文章

相关标签