精华内容
下载资源
问答
  • 这是一篇介绍指南针先进方法的博客,但是事实是不能用,笔者试过了~有兴趣的可以看一下,笔者不再赘言说一下不能用的原因,是因为这个onSensorChanged(SensorEvent event)方法调用这里出了问题,他不是像想像中的...

    这是一篇介绍指南针先进方法的博客,但是事实是不能用,笔者试过了~有兴趣的可以看一下,笔者不再赘言

    说一下不能用的原因,是因为这个onSensorChanged(SensorEvent event)方法调用这里出了问题,他不是像想像中的那样,根据传感器的类型的不同用if语句就可以控制,(其他的我试过,比如说线性加速度传感器和即将要说的已经过时的方向传感器是可以根据类型用if语句控制的)在磁力传感器和加速度传感器一起使用时,onSensorChanged(SensorEvent event)方法对磁力传感器的监听频率远远大于加速度传感器就导致后面 SensorManager.getRotationMatrix(R, null, accelerometerValues, magneticFieldValues) 方法中的accelerometerValues一直处于未接收数据状态,这样整个过程就走不了了~
    所以还是使用最原始的方向传感器吧,

    sm = (SensorManager) getSystemService(SENSOR_SERVICE);
    
    sm.registerListener(this, SensorManager.SENSOR_ORIENTATION,SensorManager.SENSOR_DELAY_GAME);

    至于方向传感器的缺点,也很明显,就是一开始的传输的数据不准确,将近持续一秒,所以如果是需要比较准确的数据,最好一开始就将方向传感器打开,不用后注销。

    展开全文
  • java面试时经常会问到关于单例设计模式,因为它能考察的知识点较多且在开发中经常用到。...A:把构造方法私有化; B:在成员变量中自己new一个类实例,并用private将其私有化且用static将其静态修饰; C:对

    java面试时经常会问到关于单例设计模式,因为它能考察的知识点较多且在开发中经常用到。

    那我就来说一说我对于单例设计模式的一些浅见。

    首先,在Java中,什么是单例呢?

    就是保证类在内存中只有一个对象。

    那么问题来了,如何才能保证类在内存中只有一个对象呢?

    A:把构造方法私有化;

    B:在成员变量中自己new一个类实例,并用private将其私有化且用static将其静态修饰;

    C:对外提供一个静态方法返回这个对象。

    依据以上的思路,就可以写出如下的代码:

    public class Student{
        private Student(){}
        private static Student s=new Student();
        public static Student getStudent(){
            return s;
        }
    }

    以上的实现方法在业内称为”饿汉式”,可以这样理解:这个单例对象随着类的加载就被创建出来了,就像饿狼扑食一样,这里的”狼”就是单例对象,而“食”就是类加载。
    还有另一种实现方法称为“懒汉式”。先看代码实现:

    public class Student{
        private Student(){}
        private static Student s=null;
        public static Student getStudent()
        {
            if(s==null)
            {
                s=new Student();
            }
            return s;
        }
    }

    从代码中可以看出:这个单例对象是随着调用类的静态方法才被被创建的,并不是随着类的加载而被创建,所以它要滞后于类加载,故称为“懒汉式”。
    但以上代码会出现线程安全问题。
    那就让我来分析一下为什么会出现线程安全问题
    判断是否会出现线程安全问题必须满足一下三个条件,缺一不可:
    A:是否为多线程环境;
    B:是否有共享数据;
    C:是否有多条语句操作共享数据

    那我就按这三个条件分析以上代码:
    A是有可能的
    B共享数据就是 Student对象
    C
    if(t==null){
        t=new Student();
    }
    这里有两条语句在操作共享数据
    那么问题又来了,如何解决线程安全问题呢?
    加锁
    而加锁又有两种实现方法:1.同步代码块;2.同步方法
    显然对以上代码而言,同步方法是个简洁明了的。所以修改后的代码如下:

    public class Student{
        private Student(){}
        private static Student s=null;
        public static synchronized Student getStudent(){
            if(s==null)
            {
                s=new Student();
            }
            return s;
        }
    ]

    那么,这两种单例实现代码的应用场景是什么呢?

    答案:开发一般用“饿汉式”,因为它不会出现问题且代码简单,面试时用“懒汉式”,因为它不仅考察了单例模式实现,还考察了线程安全。


    展开全文
  • 领域驱动实践总结二:架构分析与代码设计 领域驱动设计DDD是一设计思想,它可以同时指导中台业务建模和微服务设计(中台本质是业务模型,微服务是业务模型的系统落地),领域驱动设计强调领域模型和微服务设计的一体...

    目录

    领域驱动实践总结二:架构分析与代码设计

    一、微服务架构模型的对比与选择

    (一)整洁架构

    (二)六边形架构

    (三)DDD 分层架构

    1.用户接口层

    2.应用层

    3.领域层

    4.基础层

    5.从三层架构向 DDD 分层架构演进

    (四)三种微服务架构模型的对比和分析

    二、领域驱动设计分层架构与微服务代码模型

    (一)代码模型总目录结构

    1.微服务一级目录结构

    2.用户接口层目录结构、职能和代码形态

    3.应用层目录结构、职能和代码形态

    4.领域层目录结构、职能和代码形态

    5.基础层层目录结构、职能和代码形态

    (二)应用层的领域对象分析

    1.实体方法的封装

    2.领域服务的组合和封装

    3.应用服务的组合和编排

    (三)领域层的领域对象分析

    1.设计实体

    2.找出聚合根

    3.设计值对象

    4.设计领域事件

    5.设计领域服务

    6.设计仓储

    (四)代码模型强调内容

    第一点:聚合之间的代码边界一定要清晰。

    第二点:你一定要有代码分层的概念。

    三、正确理解微服务的边界

    (一)逻辑边界

    (二)物理边界

    (三)代码边界

    四、正确认识服务和数据在微服务各层的协作

    (一)正确认识服务的协作

    1. 服务的类型

    2. 服务的调用(三类主要场景)

    微服务内跨层服务调用

    微服务之间的服务调用

    领域事件驱动

    3. 服务的封装与组合

    (二)正确认识服务数据的协作

    1.基础层数据协作

    2.领域层数据协作

    3.应用层数据协作

    4.用户接口层数据协作

    5.前端应用数据协作

    参考书籍、文献和资料


    领域驱动实践总结二:架构分析与代码设计

    领域驱动设计DDD是一种设计思想,它可以同时指导中台业务建模和微服务设计(中台本质是业务模型,微服务是业务模型的系统落地),领域驱动设计强调领域模型和微服务设计的一体性,先有领域模型然后才有微服务,而不是脱离领域模型来谈微服务设计。

    微服务拆分困境产生的根本原因:不知道业务或者微服务的边界到底在什么地方。

    DDD 核心思想:通过领域驱动设计方法定义领域模型,从而确定业务和应用边界,保证业务模型与代码模型的一致性。

    对于领域驱动设计的学习做的总结主要写三篇博客,主要包括三部分:基本理论总结与分析、架构分析与代码设计、具体应用设计分析,主要参考的资料为极客时间的欧创新架构师的《DDD》实战,其他参考书籍在文章下方的参考书籍中。

    本次主要总结DDD架构分析与代码设计:

    一、微服务架构模型的对比与选择

    微服务架构模型现有的选择模型包括:整洁架构、CQRS 和六边形架构、DDD 分层架构等。

    (注:CQRS架构之前博客中有讲,本次不做分析)

    每种架构模式虽然提出的时代和背景不同,但其核心理念都是为了设计出“高内聚低耦合”的架构,轻松实现架构演进。

    DDD 分层架构的思想使架构边界变得越来越清晰,它在微服务架构模型中,占有非常重要的位置。建议选择DDD 分层架构

    (一)整洁架构

    在整洁架构里,同心圆代表应用软件的不同部分,从里到外依次是领域模型、领域服务、应用服务和最外围的容易变化的内容,比如用户界面和基础设施。

    整洁架构最主要的原则是依赖原则,它定义了各层的依赖关系,越往里依赖越低,代码级别越高,越是核心能力。外圆代码依赖只能指向内圆,内圆不需要知道外圆的任何情况。

    (二)六边形架构

    六边形架构的核心理念是:应用是通过端口与外部进行交互的。

    也就是说,在下图的六边形架构中,红圈内的核心业务逻辑(应用程序和领域模型)与外部资源(包括 APP、Web 应用以及数据库资源等)完全隔离,仅通过适配器进行交互。它解决了业务逻辑与用户界面的代码交错问题,很好地实现了前后端分离。

    六边形架构各层的依赖关系与整洁架构一样,都是由外向内依赖。

    六边形架构的一个端口可能对应多个外部系统,不同的外部系统也可能会使用不同的适配器,由适配器负责协议转换。这就使得应用程序能够以一致的方式被用户、程序、自动化测试和批处理脚本使用。

    (三)DDD 分层架构

    从上到下依次是:用户接口层、应用层、领域层和基础层。

    1.用户接口层

    用户接口层负责向用户显示信息和解释用户指令。

    这里的用户可能是:用户、程序、自动化测试和批处理脚本等等。

    2.应用层

    应用层是很薄的一层,理论上不应该有业务规则或逻辑,主要面向用例和流程相关的操作

    位于领域层之上,领域层包含多个聚合,所以它可以协调多个聚合的服务和领域对象完成服务编排和组合,协作完成业务操作。

    应用层也是微服务之间交互的通道,它可以调用其它微服务的应用服务,完成微服务之间的服务组合和编排。

    注意

    • 在设计和开发时,不要将本该放在领域层的业务逻辑放到应用层中实现。因为庞大的应用层会使领域模型失焦,时间一长你的微服务就会演化为传统的三层架构,业务逻辑会变得混乱。
    • 应用服务是在应用层的,它负责服务的组合、编排和转发,负责处理业务用例的执行顺序以及结果的拼装,以粗粒度的服务通过 API 网关向前端发布。
    • 应用服务还可以进行安全认证、权限校验、事务控制、发送或订阅领域事件等。

    3.领域层

    领域层的作用是实现企业核心业务逻辑,通过各种校验手段保证业务的正确性

    领域层主要体现领域模型的业务能力,它用来表达业务概念、业务状态和业务规则。

    领域层包含聚合根、实体、值对象、领域服务等领域模型中的领域对象。

    注意:

    • 领域模型的业务逻辑主要是由实体和领域服务来实现的,其中实体会采用充血模型来实现所有与之相关的业务功能。
    • 实体和领域服务在实现业务逻辑上不是同级的,当领域中的某些功能,单一实体(或者值对象)不能实现时,领域服务就会出马,它可以组合聚合内的多个实体(或者值对象),实现复杂的业务逻辑。

    4.基础层

    基础层是贯穿所有层的,它的作用就是为其它各层提供通用的技术和基础服务,包括第三方工具、驱动、消息中间件、网关、文件、缓存以及数据库等。比较常见的功能还是提供数据库持久化。

    基础层包含基础服务,它采用依赖倒置设计,封装基础资源服务,实现应用层、领域层与基础层的解耦,降低外部资源变化对应用的影响。

    5.从三层架构向 DDD 分层架构演进

    DDD 分层架构中的要素其实和三层架构类似,只是在 DDD 分层架构中,这些要素被重新归类,重新划分了层,确定了层与层之间的交互规则和职责边界。

    • 三层架构向 DDD 分层架构演进,主要发生在业务逻辑层和数据访问层
    • DDD 分层架构在用户接口层引入了 DTO,给前端提供了更多的可使用数据和更高的展示灵活性。
    • DDD 分层架构对三层架构的业务逻辑层进行了更清晰的划分,改善了三层架构核心业务逻辑混乱,代码改动相互影响大的情况。
    • DDD 分层架构将业务逻辑层的服务拆分到了应用层和领域层应用层快速响应前端的变化领域层实现领域模型的能力
    • 数据访问层和基础层之间三层架构数据访问采用 DAO 方式DDD 分层架构的数据库等基础资源访问,采用了仓储(Repository)设计模式,通过依赖倒置实现各层对基础资源的解耦。仓储又分为两部分:仓储接口和仓储实现仓储接口放在领域层中,仓储实现放在基础层。原来三层架构通用的第三方工具包、驱动、Common、Utility、Config 等通用的公共的资源类统一放到了基础层。

    (四)三种微服务架构模型的对比和分析

    • 重点关注图中的红色线框,它们是非常重要的分界线,这三种架构里面都有,它的作用就是将核心业务逻辑与外部应用、基础资源进行隔离。
    • 红色框内部主要实现核心业务逻辑,划分了应用层和领域层,来承担不同的业务逻辑。
    • 领域层实现面向领域模型,实现领域模型的核心业务逻辑,属于原子模型,它需要保持领域模型和业务逻辑的稳定,对外提供稳定的细粒度的领域服务,所以它处于架构的核心位置。
    • 应用层实现面向用户操作相关的用例和流程,对外提供粗粒度的 API 服务。它就像一个齿轮一样进行前台应用和领域层的适配,接收前台需求,随时做出响应和调整,尽量避免将前台需求传导到领域层。应用层作为配速齿轮则位于前台应用和领域层之间。

    二、领域驱动设计分层架构与微服务代码模型

    DDD 并没有给出标准的代码模型,不同的人可能会有不同理解。这里我们在使用的时候还是建议按照欧创新架构师总结的来进行适用,具体如下:

    (一)代码模型总目录结构

    根据 DDD 分层架构模型建立了标准的微服务代码模型,在代码模型里面,各代码对象各据其位、各司其职,共同协作完成微服务的业务逻辑。它包括用户接口层、应用层、领域层和基础层,分层架构各层的职责边界非常清晰,又能有条不紊地分层协作。

    • 用户接口层:面向前端提供服务适配,面向资源层提供资源适配。这一层聚集了接口适配相关的功能。
    • 应用层职责:实现服务组合和编排,适应业务流程快速变化的需求。这一层聚集了应用服务和事件相关的功能。
    • 领域层:实现领域的核心业务逻辑。这一层聚集了领域模型的聚合、聚合根、实体、值对象、领域服务和事件等领域对象,以及它们组合所形成的业务能力。
    • 基础层:贯穿所有层,为各层提供基础资源服务。这一层聚集了各种底层资源相关的服务和能力。

    业务逻辑从领域层、应用层到用户接口层逐层封装和协作,对外提供灵活的服务,既实现了各层的分工,又实现了各层的协作。

    1.微服务一级目录结构

    微服务一级目录是按照 DDD 分层架构的分层职责来定义的。从下面这张图中,我们可以看到,在代码模型里分别为用户接口层、应用层、领域层和基础层,建立了 interfaces、application、domain 和 infrastructure 四个一级代码目录。

    2.用户接口层目录结构、职能和代码形态

    主要存放用户接口层与前端交互、展现数据相关的代码。前端应用通过这一层的接口,向应用服务获取展现所需的数据。这一层主要用来处理用户发送的 Restful 请求,解析用户输入的配置文件,并将数据传递给 Application 层。数据的组装、数据传输格式以及 Facade 接口等代码都会放在这一层目录里。

    具体如下:

    • Assembler:实现 DTO 与领域对象之间的相互转换和数据交换。一般来说 Assembler 与 DTO 总是一同出现。
    • Dto:它是数据传输的载体,内部不存在任何业务逻辑,我们可以通过 DTO 把内部的领域对象与外界隔离。
    • Facade:提供较粗粒度的调用接口,将用户请求委派给一个或多个应用服务进行处理。

    3.应用层目录结构、职能和代码形态

    主要存放应用层服务组合和编排相关的代码。应用服务向下基于微服务内的领域服务或外部微服务的应用服务完成服务的编排和组合向上为用户接口层提供各种应用数据展现支持服务应用服务和事件等代码会放在这一层目录里

    具体如下:

    • Event(事件):这层目录主要存放事件相关的代码。它包括两个子目录:publish 和 subscribe。前者主要存放事件发布相关代码,后者主要存放事件订阅相关代码(事件处理相关的核心业务逻辑在领域层实现)。为了实现事件的统一管理,建议你将微服务内所有事件的发布和订阅的处理都统一放到应用层,事件相关的核心业务逻辑实现放在领域层。通过应用层调用领域层服务,来实现完整的事件发布和订阅处理流程。
    • Service(应用服务):这层的服务是应用服务。应用服务会对多个领域服务或外部应用服务进行封装、编排和组合,对外提供粗粒度的服务。应用服务主要实现服务组合和编排,是一段独立的业务逻辑。你可以将所有应用服务放在一个应用服务类里,也可以把一个应用服务设计为一个应用服务类,以防应用服务类代码量过大。

    4.领域层目录结构、职能和代码形态

    主要存放领域层核心业务逻辑相关的代码。领域层可以包含多个聚合代码包,它们共同实现领域模型的核心业务逻辑。聚合以及聚合内的实体、方法、领域服务和事件等代码会放在这一层目录里。

    具体如下:

    • Aggregate(聚合):它是聚合软件包的根目录,可以根据实际项目的聚合名称命名,比如权限聚合。在聚合内定义聚合根、实体和值对象以及领域服务之间的关系和边界。聚合内实现高内聚的业务逻辑,它的代码可以独立拆分为微服务。以聚合为单位的代码放在一个包里的主要目的是为了业务内聚,而更大的目的是为了以后微服务之间聚合的重组。聚合之间清晰的代码边界,可以让你轻松地实现以聚合为单位的微服务重组,在微服务架构演进中有着很重要的作用。
    • Entity(实体):它存放聚合根、实体、值对象以及工厂模式(Factory)相关代码。实体类采用充血模型,同一实体相关的业务逻辑都在实体类代码中实现。跨实体的业务逻辑代码在领域服务中实现
    • Event(事件):它存放事件实体以及与事件活动相关的业务逻辑代码
    • Service(领域服务):它存放领域服务代码一个领域服务是多个实体组合出来的一段业务逻辑。你可以将聚合内所有领域服务都放在一个领域服务类中,你也可以把每一个领域服务设计为一个类。如果领域服务内的业务逻辑相对复杂,建议将一个领域服务设计为一个领域服务类,避免由于所有领域服务代码都放在一个领域服务类中,而出现代码臃肿的问题。领域服务封装多个实体或方法后向上层提供应用服务调用
    • Repository(仓储):它存放所在聚合的查询或持久化领域对象的代码,通常包括仓储接口和仓储实现方法。为了方便聚合的拆分和组合,我们设定了一个原则:一个聚合对应一个仓储。(特别说明:按照 DDD 分层架构,仓储实现本应该属于基础层代码,但为了在微服务架构演进时,保证代码拆分和重组的便利性,把聚合仓储实现的代码放到了聚合包内。)

    5.基础层层目录结构、职能和代码形态

    主要存放基础资源服务相关的代码,为其它各层提供的通用技术能力、三方软件包、数据库服务、配置和基础资源服务的代码都会放在这一层目录里。

    具体如下:

    • Config:主要存放配置相关代码。
    • Util:主要存放平台、开发框架、消息、数据库、缓存、文件、总线、网关、第三方类库、通用算法等基础代码,你可以为不同的资源类别建立不同的子目录。

    (二)应用层的领域对象分析

    应用层的主要领域对象是应用服务和事件的发布以及订阅。

    在事件风暴或领域故事分析时,我们往往会根据用户或系统发起的命令,来设计服务或实体方法。为了响应这个命令,我们需要分析和记录:

    • 在应用层和领域层分别会发生哪些业务行为;
    • 各层分别需要设计哪些服务或者方法;
    • 这些方法和服务的分层以及领域类型(比如实体方法、领域服务和应用服务等),它们之间的调用和组合的依赖关系。

    在严格分层架构模式下,不允许服务的跨层调用,每个服务只能调用它的下一层服务。服务从下到上依次为:实体方法、领域服务和应用服务。建议采用服务逐层封装的方式,服务的封装和调用主要有以下几种方式:

    1.实体方法的封装

    实体方法是最底层的原子业务逻辑。

    • 如果单一实体的方法需要被跨层调用,你可以将它封装成领域服务,这样封装的领域服务就可以被应用服务调用和编排了。
    • 如果它还需要被用户接口层调用,需要将这个领域服务封装成应用服务

    经过逐层服务封装,实体方法就可以暴露给上面不同的层,实现跨层调用。

    封装时服务前面的名字可以保持一致,你可以用 *DomainService 或 *AppService 后缀来区分领域服务或应用服务

    2.领域服务的组合和封装

    领域服务会对多个实体和实体方法进行组合和编排,供应用服务调用。

    如果它需要暴露给用户接口层,领域服务就需要封装成应用服务

    3.应用服务的组合和编排

    应用服务会对多个领域服务进行组合和编排,暴露给用户接口层,供前端应用调用。

    多个应用服务可能会对多个同样的领域服务重复进行同样业务逻辑的组合和编排。当出现这种情况时,就需要分析是不是领域服务可以整合了。可以将这几个不断重复组合的领域服务,合并到一个领域服务中实现,这样领域模型将会越来越精炼,更能适应业务的要求。

    应用服务类放在应用层 Service 目录结构下。领域事件的发布和订阅类放在应用层 Event 目录结构下。

    (三)领域层的领域对象分析

    事件风暴结束时,领域模型聚合内一般会有:聚合、实体、命令和领域事件等领域对象

    在完成故事分析和微服务设计后,微服务的聚合内一般会有:聚合、聚合根、实体、值对象、领域事件、领域服务和仓储等领域对象。

    1.设计实体

    大多数情况下,领域模型的业务实体与微服务的数据库实体是一一对应的。

    但某些领域模型的实体在微服务设计时,可能会被设计为多个数据实体,或者实体的某些属性被设计为值对象。

    在分层架构里,实体采用充血模型,在实体类内实现实体的全部业务逻辑。这些不同的实体都有自己的方法和业务行为,比如地址实体有新增和修改地址的方法,银行账号实体有新增和修改银行账号的方法。

    实体类放在领域层的 Entity 目录结构下。

    2.找出聚合根

    聚合根来源于领域模型,聚合根是一种特殊的实体,它有自己的属性和方法聚合根可以实现聚合之间的对象引用,还可以引用聚合内的所有实体。

    • 在个人客户聚合里,个人客户这个实体是聚合根,它负责管理地址、电话以及银行账号的生命周期。
    • 个人客户聚合根通过工厂和仓储模式,实现聚合内地址、银行账号等实体和值对象数据的初始化和持久化。

    聚合根类放在代码模型的 Entity 目录结构下。聚合根有自己的实现方法,比如生成客户编码,新增和修改客户信息等方法。

    3.设计值对象

    根据需要将某些实体的某些属性或属性集设计为值对象。值对象类放在代码模型的 Entity 目录结构下。

    在个人客户聚合中,客户拥有客户证件类型,它是以枚举值的形式存在,所以将它设计为值对象。

    有些领域对象可以设计为值对象,也可以设计为实体,我们需要根据具体情况来分析:

    • 如果这个领域对象在其它聚合内维护生命周期,且在它依附的实体对象中只允许整体替换,我们就可以将它设计为值对象。
    • 如果这个对象是多条且需要基于它做查询统计,建议将它设计为实体。

    4.设计领域事件

    如果领域模型中领域事件会触发下一步的业务操作,我们就需要设计领域事件

    • 首先确定领域事件发生在微服务内还是微服务之间。
    • 然后设计事件实体对象,事件的发布和订阅机制,以及事件的处理机制。
    • 判断是否需要引入事件总线或消息中间件

    领域事件实体和处理类放在领域层的 Event 目录结构下。领域事件的发布和订阅类建议放在应用层的 Event 目录结构下。

    5.设计领域服务

    如果一个业务动作或行为跨多个实体,我们就需要设计领域服务。

    领域服务通过对多个实体和实体方法进行组合,完成核心业务逻辑。可以认为领域服务是位于实体方法之上和应用服务之下的一层业务逻辑。

    按照严格分层架构层的依赖关系

    • 如果实体的方法需要暴露给应用层,它需要封装成领域服务后才可以被应用服务调用。
    • 如果有的实体方法需要被前端应用调用,我们会将它封装成领域服务,然后再封装为应用服务。

    领域服务类放在领域层的 Service 目录结构下。

    6.设计仓储

    每一个聚合都有一个仓储,仓储主要用来完成数据查询和持久化操作。

    仓储包括仓储的接口和仓储实现,通过依赖倒置实现应用业务逻辑与数据库资源逻辑的解耦。

    仓储代码放在领域层的 Repository 目录结构下。

    (四)代码模型强调内容

    第一点:聚合之间的代码边界一定要清晰

    聚合之间的服务调用和数据关联应该是尽可能的松耦合和低关联,聚合之间的服务调用应该通过上层的应用层组合实现调用,原则上不允许聚合之间直接调用领域服务。

    这种松耦合的代码关联,在以后业务发展和需求变更时,可以很方便地实现业务功能和聚合代码的重组,在微服务架构演进中将会起到非常重要的作用。

    第二点:你一定要有代码分层的概念

    写代码时一定要搞清楚代码的职责,将它放在职责对应的代码目录内。

    应用层代码主要完成服务组合和编排,以及聚合之间的协作,它是很薄的一层,不应该有核心领域逻辑代码。

    领域层是业务的核心,领域模型的核心逻辑代码一定要在领域层实现。

    如果将核心领域逻辑代码放到应用层,你的基于 DDD 分层架构模型的微服务慢慢就会演变成传统的三层架构模型了。

    三、正确理解微服务的边界

    微服务设计的重点,就是看微服务设计是否能够支持架构长期、轻松的演进。

    在事件风暴中,我们会梳理出业务过程中的用户操作、事件以及外部依赖关系等,根据这些要素梳理出实体等领域对象。

    • 根据实体对象之间的业务关联性,将业务紧密相关的多个实体进行组合形成聚合,聚合之间是第一层边界。
    • 根据业务及语义边界等因素将一个或者多个聚合划定在一个限界上下文内,形成领域模型,限界上下文之间的边界是第二层边界。

    为了方便理解,我们将这些边界细分为:逻辑边界、物理边界和代码边界。

    (一)逻辑边界

    逻辑边界主要定义同一业务领域或应用内紧密关联的对象所组成的不同聚类的组合之间的边界

    微服务内聚合之间的边界就是逻辑边界。一般来说微服务会有一个以上的聚合,在开发过程中不同聚合的代码隔离在不同的聚合代码目录中。它是一个虚拟的边界,强调业务的内聚,可根据需要变成物理边界,也就是说聚合也可以独立为微服务。

    微服务的架构演进并不是随心所欲的,需要遵循一定的规则,这个规则就是逻辑边界。微服务架构演进时,在业务端以聚合为单位进行业务能力的重组,在微服务端以聚合的代码目录为单位进行微服务代码的重组。

    来看一个微服务实例,在下面这张图中,我们可以看到微服务里包含了两个聚合的业务逻辑,两个聚合分别内聚了各自不同的业务能力,聚合内的代码分别归到了不同的聚合目录下。

    那随着业务的快速发展,如果某一个微服务遇到了高性能挑战,需要将部分业务能力独立出去,我们就可以以聚合为单位,将聚合代码拆分独立为一个新的微服务,这样就可以很容易地实现微服务的拆分。

    另外,我们也可以对多个微服务内有相似功能的聚合进行功能和代码重组,组合为新的聚合和微服务,独立为通用微服务,有点做中台的感觉!

    (二)物理边界

    微服务之间的边界是物理边界。它强调微服务部署和运行的隔离,关注微服务的服务调用、容错和运行等。

    物理边界主要从部署和运行的视角来定义微服务之间的边界。不同微服务部署位置和运行环境是相互物理隔离的,分别运行在不同的进程中。这种边界就是微服务之间的物理边界。

    举例:

    有些项目团队在将集中式单体应用拆分为微服务时,首先进行的往往不是建立领域模型,而只是按照业务功能将原来单体应用的一个软件包拆分成多个所谓的“微服务”软件包,而这些“微服务”内的代码仍然是集中式三层架构的模式,“微服务”内的代码高度耦合,逻辑边界不清晰,这里我们暂且称它为“小单体微服务”

    而随着新需求的提出和业务的发展,这些小单体微服务会慢慢膨胀起来。当有一天你发现这些膨胀了的微服务,有一部分业务功能需要拆分出去,或者部分功能需要与其它微服务进行重组时,你会发现原来这些看似清晰的微服务,不知不觉已经摇身一变,变成了臃肿油腻的大单体了,而这个大单体内的代码依然是高度耦合且边界不清的。

    这种单体式微服务只定义了一个维度的边界,也就是微服务之间的物理边界,本质上还是单体架构模式。微服务设计时要考虑的不仅仅只有这一个边界,别忘了还要定义好微服务内的逻辑边界和代码边界,这样才能得到你想要的结果。

    (三)代码边界

    不同层或者聚合之间代码目录的边界是代码边界。它强调的是代码之间的隔离,方便架构演进时代码的重组。

    代码边界主要用于微服务内的不同职能代码之间的隔离

    微服务开发过程中会根据代码模型建立相应的代码目录,实现不同功能代码的隔离。由于领域模型与代码模型的映射关系,代码边界直接体现出业务边界。

    代码边界可以控制代码重组的影响范围,避免业务和服务之间的相互影响。

    微服务如果需要进行功能重组,只需要以聚合代码为单位进行重组就可以了。

    四、正确认识服务和数据在微服务各层的协作

    (一)正确认识服务的协作

    1. 服务的类型

    按照分层架构设计出来的微服务,其内部有 Facade 服务、应用服务、领域服务和基础服务。之前都有提到,这里可以回忆一下!

    2. 服务的调用(三类主要场景)

    微服务的服务调用包括三类主要场景:微服务内跨层服务调用,微服务之间服务调用和领域事件驱动。

    • 微服务内跨层服务调用

    前端应用调用发布在 API 网关上的 Facade 服务,Facade 定向到应用服务应用服务作为服务组织和编排者,它的服务调用有这样两种路径:

    • 第一种是应用服务调用并组装领域服务。此时领域服务会组装实体和实体方法,实现核心领域逻辑。领域服务通过仓储服务获取持久化数据对象,完成实体数据初始化。
    • 第二种是应用服务直接调用仓储服务。这种方式主要针对像缓存、文件等类型的基础层数据访问。这类数据主要是查询操作,没有太多的领域逻辑,不经过领域层,不涉及数据库持久化对象。

     

    • 微服务之间的服务调用

    微服务之间的应用服务可以直接访问,也可以通过 API 网关访问

    由于跨微服务操作,在进行数据新增和修改操作时,你需关注分布式事务,保证数据的一致性。

    • 领域事件驱动

    领域事件驱动包括微服务内和微服务之间的事件。

    • 微服务内通过事件总线(EventBus)完成聚合之间的异步处理。
    • 微服务之间通过消息中间件完成。异步化的领域事件驱动机制是一种间接的服务访问方式。

    当应用服务业务逻辑处理完成后,如果发生领域事件,可调用事件发布服务,完成事件发布。

    当接收到订阅的主题数据时,事件订阅服务会调用事件处理领域服务,完成进一步的业务操作。

    3. 服务的封装与组合

    微服务的服务是从领域层逐级向上封装、组合和暴露的。基本如下图体现:

    (二)正确认识服务数据的协作

    在 DDD 中有很多的数据对象,这些对象分布在不同的层里。它们在不同的阶段有不同的形态:

    • 数据持久化对象 PO(Persistent Object),与数据库结构一一映射,是数据持久化过程中的数据载体。
    • 领域对象 DO(Domain Object),微服务运行时的实体,是核心业务的载体。
    • 数据传输对象 DTO(Data Transfer Object),用于前端与应用层或者微服务之间的数据组装和传输,是应用之间数据传输的载体。
    • 视图对象 VO(View Object),用于封装展示层指定页面或组件的数据。

    微服务各层数据对象的职责和转换过程如下:

    1.基础层数据协作

    基础层的主要对象是 PO 对象。

    我们需要先建立 DO 和 PO 的映射关系

    • 当 DO 数据需要持久化时,仓储服务会将 DO 转换为 PO 对象,完成数据库持久化操作。
    • 当 DO 数据需要初始化时,仓储服务从数据库获取数据形成 PO 对象,并将 PO 转换为 DO,完成数据初始化。

    大多数情况下 PO 和 DO 是一一对应的。但也有 DO 和 PO 多对多的情况,在 DO 和 PO 数据转换时,需要进行数据重组。

    2.领域层数据协作

    领域层的主要对象是 DO 对象。

    DO 是实体和值对象的数据和业务行为载体,承载着基础的核心业务逻辑

    通过 DO 和 PO 转换,我们可以完成数据持久化和初始化。

    3.应用层数据协作

    应用层的主要对象是 DO 对象。

    如果需要调用其它微服务的应用服务,DO 会转换为 DTO,完成跨微服务的数据组装和传输。

    用户接口层先完成 DTO 到 DO 的转换,然后应用服务接收 DO 进行业务处理。如果 DTO 与 DO 是一对多的关系,这时就需要进行 DO 数据重组

    4.用户接口层数据协作

    用户接口层会完成 DO 和 DTO 的互转,完成微服务与前端应用数据交互及转换。

    Facade 服务会对多个 DO 对象进行组装,转换为 DTO 对象,向前端应用完成数据转换和传输。

    5.前端应用数据协作

    前端应用主要是 VO 对象

    展现层使用 VO 进行界面展示,通过用户接口层与应用层采用 DTO 对象进行数据交互。

     

    参考书籍、文献和资料

    1.极客时间课程《DDD实战》,欧创新,2019.

    2.郑天民. 微服务设计原理与架构. 北京:人民邮电出版社,2018.

    3.陈超、秦金卫、张逸等. 高可用可伸缩微服务架构. 电子工业出版社. 2019.

    4.Eric Evans. 领域驱动设计-软件核心复杂性应对之道。 人民邮电出版社. 2018.

    展开全文
  • 领域驱动设计DDD是一种设计思想,它可以同时指导中台业务建模和微服务设计(中台本质是业务模型,微服务是业务模型的系统落地),领域驱动设计强调领域模型和微服务设计的一体性,先有领域模型然后才有微服务,而不是...

    目录

    领域驱动实践总结三:具体应用设计分析

    一、应用项目的基本背景

    二、针对项目进行领域驱动的战略设计阶段

    (一)事件风暴确定产品愿景

    (二)事件风暴进行业务场景分析

    场景分析一:请假       用户:请假人

    场景分析二:审批       用户:审批人

    场景分析三:人员组织关系     详细的分析过程以及考勤的场景分析就不描述了

    (三)事件风暴进行领域建模

    第一步:找出领域实体和值对象等领域对象

    第二步:找出聚合根,根据实体、值对象与聚合根的依赖关系,建立聚合

    第三步:根据业务及语义边界等因素,定义限界上下文

    (四)事件风暴进行微服务的拆分

    三、针对项目进行领域驱动的战术设计阶段

    (一)分析微服务领域对象

    1.具体服务的识别和设计

    2.聚合中的对象分析

    3.微服务内的对象清单

    (二)设计微服务代码结构

    1.应用层代码结构

    2.领域层代码结构

    (三)后续的工作:详细设计 + 代码开发和测试

    1. 详细设计

    2. 代码开发和测试

    四、具体代码参考

    参考书籍、文献和资料


    领域驱动实践总结三:具体应用设计分析

    领域驱动设计DDD是一种设计思想,它可以同时指导中台业务建模和微服务设计(中台本质是业务模型,微服务是业务模型的系统落地),领域驱动设计强调领域模型和微服务设计的一体性,先有领域模型然后才有微服务,而不是脱离领域模型来谈微服务设计。

    微服务拆分困境产生的根本原因:不知道业务或者微服务的边界到底在什么地方。

    DDD 核心思想:通过领域驱动设计方法定义领域模型,从而确定业务和应用边界,保证业务模型与代码模型的一致性。

    对于领域驱动设计的学习做的总结主要写三篇博客,主要包括三部分:基本理论总结与分析、架构分析与代码设计、具体应用设计分析,主要参考的资料为极客时间的欧创新架构师的《DDD》实战,其他参考书籍在文章下方的参考书籍中。

    本次主要总结DDD具体应用设计分析:

    一、应用项目的基本背景

    项目的目标是实现在线请假和考勤管理功能描述如下

    • 请假人填写请假单提交审批,根据请假人身份、请假类型和请假天数进行校验,根据审批规则逐级递交上级审批,逐级核批通过则完成审批,否则审批不通过退回申请人。
    • 根据考勤规则,核销请假数据后,对考勤数据进行校验,输出考勤统计。

    备注:本想以大视频业务系统为背景自己弄一个的,但是时间太紧,暂时还是以欧创新架构师提供的案例做总结和分析理解,后期有时间会自己出一个关于视频系统的。

    二、针对项目进行领域驱动的战略设计阶段

    战略设计采用的方法是事件风暴,包括:产品愿景、场景分析、领域建模和微服务拆分等几个主要过程。

    战略设计是根据用户旅程分析,找出领域对象和聚合根,对实体和值对象进行聚类组成聚合,划分限界上下文,建立领域模型的过程。

    战略设计阶段建议参与人员:领域专家、业务需求方、产品经理、架构师、项目经理、开发经理和测试经理。

    (一)事件风暴确定产品愿景

    产品愿景是对产品顶层价值设计,对产品目标用户、核心价值、差异化竞争点等信息达成一致,避免产品偏离方向

    事件风暴时,所有参与者针对每一个要点,在贴纸上写出自己的意见,贴到白板上。

    事件风暴主持者会对每个贴纸,讨论并对发散的意见进行收敛和统一,形成下面的产品愿景图。其实,也就是让所有人确定统一的大方向统一语言,我们究竟在干什么,目的和意义是什么等的基本问题。

    产品愿景分析对于初创系统明确系统建设重点,统一团队建设目标和建立通用语言是很有价值的。

    针对本项目,最后确定的产品愿景图整理成一段文字就是

    为了满足内外部人员,他们的在线请假、自动考勤统计和外部人员管理的需求,我们建设这个在线请假考勤系统,它是一个在线请假平台,可以自动考勤统计。它可以同时支持内外网请假,同时管理内外部人员请假和定期考勤分析,而不像 HR 系统,只管理内部人员,且只能内网使用。我们的产品内外网皆可使用,可实现内外部人员无差异管理。

    达到的目的与意义

    通过产品愿景分析,项目团队统一了系统名称——在线请假考勤系统,明确了项目目标和关键功能,与竞品(HR)的关键差异以及自己的优势和核心竞争力等。

    (二)事件风暴进行业务场景分析

    场景分析是从用户视角出发,探索业务领域中的典型场景,产出领域中需要支撑的场景分类、用例操作以及不同子域之间的依赖关系,用以支撑领域建模。

    项目团队成员一起用事件风暴分析请假和考勤的用户旅程。

    根据不同角色的旅程和场景分析,尽可能全面地梳理从前端操作到后端业务逻辑发生的所有操作、命令、领域事件以及外部依赖关系等信息

    以请假和人员场景作为示例------------

    场景分析一:请假       用户:请假人

    • 请假人登录系统:从权限微服务获取请假人信息和权限数据,完成登录认证。
    • 创建请假单:打开请假页面,选择请假类型和起始时间,录入请假信息。保存并创建请假单,提交请假审批。
    • 修改请假单:查询请假单,打开请假页面,修改请假单,提交请假审批。
    • 提交审批:获取审批规则,根据审批规则,从人员组织关系中获取审批人,给请假单分配审批人。

    场景分析二:审批       用户:审批人

    • 审批人登录系统:从权限微服务获取审批人信息和权限数据,完成登录认证。
    • 获取请假单:获取审批人名下请假单,选择请假单。
    • 审批:填写审批意见。
    • 逐级审批:如果还需要上级审批,根据审批规则,从人员组织关系中获取审批人,给请假单分配审批人。重复以上 4 步。最后审批人完成审批。

    备注:完成审批后,产生请假审批已通过领域事件。后续有两个进一步的业务操作:发送请假审批已通过的通知,通知邮件系统告知请假人;将请假数据发送到考勤以便核销。

    场景分析三:人员组织关系     详细的分析过程以及考勤的场景分析就不描述了

    (三)事件风暴进行领域建模

    领域建模是通过对业务和问题域进行分析,建立领域模型。

    向上通过限界上下文指导微服务边界设计,向下通过聚合指导实体对象设计

    领域建模是一个收敛的过程,分三步:

    第一步:找出领域实体和值对象等领域对象

    根据场景分析,分析并找出发起或产生这些命令或领域事件的实体和值对象,将与实体或值对象有关的命令和事件聚集到实体

    根据场景分析,分析并找出发起或产生这些命令或领域事件的实体和值对象,将与实体或值对象有关的命令和事件聚集到实体。

    注意表达中的名词和动词等,名词往往是实体、值对象等,动词往往对应着命令的相关行为

    第二步:找出聚合根,根据实体、值对象与聚合根的依赖关系,建立聚合

    定义聚合前,先找出聚合根

    从上面的实体中,我们可以找出“请假单”和“人员”两个聚合根然后找出与聚合根紧密依赖的实体和值对象。我们发现审批意见、审批规则和请假单紧密关联,组织关系和人员紧密关联。

    找出这些实体的关系后,我们发现还有刷卡明细、考勤明细和考勤统计,这几个实体没有聚合根。这种情形在领域建模时你会经常遇到,对于这类场景我们需要分情况特殊处理:

    • 刷卡明细、考勤明细和考勤统计这几个实体,它们之间相互独立,找不出聚合根,不是富领域模型,但它们一起完成考勤业务逻辑,具有很高的业务内聚性。
    • 我们将这几个业务关联紧密的实体,放在一个考勤聚合内。
    • 微服务设计时,我们依然采用 DDD 的设计和分析方法。由于没有聚合根来管理聚合内的实体,我们可以用传统的方法来管理实体

    经过分析,我们建立了请假、人员组织关系和考勤三个聚合。其中请假聚合有请假单、审批意见实体和审批规则等值对象。人员组织关系聚合有人员和组织关系等实体。考勤聚合有刷卡明细、考勤明细和考勤统计等实体。

    第三步:根据业务及语义边界等因素,定义限界上下文

    由于人员组织关系聚合与请假聚合,共同完成请假的业务功能,两者在请假的限界上下文内。

    考勤聚合则单独构成考勤统计限界上下文。

    因此我们为业务划分请假和考勤统计两个限界上下文,建立请假和考勤两个领域模型。

    (四)事件风暴进行微服务的拆分

    理论上一个限界上下文就可以设计为一个微服务,但还需要综合考虑多种外部因素,比如:职责单一性、敏态与稳态业务分离、非功能性需求(如弹性伸缩、版本发布频率和安全等要求)、软件包大小、团队沟通效率和技术异构等非业务要素。

    在这个项目,我们划分微服务主要考虑职责单一性原则

    因此根据限界上下文就可以拆分为请假和考勤两个微服务。其中请假微服务包含人员组织关系和请假两个聚合,考勤微服务包含考勤聚合。

    三、针对项目进行领域驱动的战术设计阶段

    战术设计是根据领域模型进行微服务设计的过程。这个阶段主要梳理微服务内的领域对象,梳理领域对象之间的关系,确定它们在代码模型和分层架构中的位置,建立领域模型与微服务模型的映射关系,以及服务之间的依赖关系

    战术设计阶段建议参与人员:领域专家、产品经理、架构师、项目经理、开发经理和测试经理等。战术设计包括以下阶段:

    (一)分析微服务领域对象

    事件风暴基础上,我们进一步细化领域对象以及它们的关系,补充事件风暴可能遗漏的业务和技术细节

    • 我们分析微服务内应该有哪些服务?
    • 服务的分层?
    • 应用服务由哪些服务组合和编排完成?
    • 领域服务包括哪些实体和实体方法?
    • 哪个实体是聚合根?
    • 实体有哪些属性和方法?
    • 哪些对象应该设计为值对象等。

    1.具体服务的识别和设计

    事件风暴的命令是外部的一些操作和业务行为,也是微服务对外提供的能力。它往往与微服务的应用服务或者领域服务对应。我们可以将命令作为服务识别和设计的起点

    具体步骤如下:

    • 根据命令设计应用服务,确定应用服务的功能,服务集合,组合和编排方式。服务集合中的服务包括领域服务或其它微服务的应用服务。
    • 根据应用服务功能要求设计领域服务,定义领域服务。这里需要注意:应用服务可能是由多个聚合的领域服务组合而成的。
    • 根据领域服务的功能,确定领域服务内的实体以及功能。
    • 设计实体基本属性和方法。
    • 考虑领域事件的异步化处理。

    以提交审批这个动作为例,来说明服务的识别和设计。提交审批的大体流程是:

    • 根据请假类型和时长,查询请假审批规则,获取下一步审批人的角色。
    • 根据审批角色从人员组织关系中查询下一审批人。
    • 为请假单分配审批人,并将审批规则保存至请假单。

    通过分析,我们需要在应用层和领域层设计以下服务和方法。

    • 应用层:提交审批应用服务。
    • 领域层:领域服务有查询审批规则、修改请假流程信息服务以及根据审批规则查询审批人服务,分别位于请假和人员组织关系聚合。请假单实体有修改请假流程信息方法,审批规则值对象有查询审批规则方法。人员实体有根据审批规则查询审批人方法。下图是我们分析出来的服务以及它们之间的依赖关系。

    2.聚合中的对象分析

    请假单聚合中,聚合根是请假单

    • 请假单经多级审核后,会产生多条审批意见,为了方便查询,我们可以将审批意见设计为实体。
    • 请假审批通过后,会产生请假审批通过的领域事件,因此还会有请假事件实体。

    请假聚合有以下实体审批意见(记录审批人、审批状态和审批意见)和请假事件实体

    再来分析一下请假单聚合的值对象

    • 请假人和下一审批人数据来源于人员组织关系聚合中的人员实体,可设计为值对象。
    • 人员类型、请假类型和审批状态是枚举值类型,可设计为值对象。
    • 确定请假审批规则后,审批规则也可作为请假单的值对象。

    请假单聚合将包含以下值对象请假人、人员类型、请假类型、下一审批人、审批状态和审批规则

    -----------------------------------------------------------------------------------------------------------------------------------------------------

    人员组织关系聚合中,我们可以建立人员之间的组织关系,通过组织关系类型找到上级审批领导。

    它的聚合根是人员实体有组织关系(包括组织关系类型和上级审批领导),其中组织关系类型(如项目经理、处长、总经理等)是值对象。上级审批领导来源于人员聚合根,可设计为值对象。人员组织关系聚合将包含以下值对象:组织关系类型、上级审批领导。

    3.微服务内的对象清单

    确定各领域对象的属性后,我们就可以设计各领域对象在代码模型中的代码对象(包括代码对象的包名、类名和方法名),建立领域对象与代码对象的一一映射关系了。

    根据这种映射关系,相关人员可快速定位到业务逻辑所在的代码位置。

    (二)设计微服务代码结构

    根据 DDD 的代码模型和各领域对象所在的包、类和方法,可以定义出请假微服务的代码结构,设计代码对象。

    1.应用层代码结构

    应用层包括:应用服务、DTO 以及事件发布相关代码

    在 LeaveApplicationService 类内实现与聚合相关的应用服务,在 LoginApplicationService 封装外部微服务认证和权限的应用服务。

    (提醒一下:如果应用服务逻辑复杂的话,一个应用服务就可以构建一个类,这样可以避免一个类的代码过于庞大,不利于维护。)

    2.领域层代码结构

    领域层包括一个或多个聚合的实体类、事件实体类、领域服务以及工厂、仓储相关代码

    一个聚合对应一个聚合代码目录,聚合之间在代码上完全隔离,聚合之间通过应用层协调

    请假微服务领域层包含请假和人员两个聚合。人员和请假代码都放在各自的聚合所在目录结构的代码包中。

    如果随着业务发展,人员相关功能需要从请假微服务中拆分出来,我们只需将人员聚合代码包稍加改造,独立部署,即可快速发布为人员微服务。

    (三)后续的工作:详细设计 + 代码开发和测试

    1. 详细设计

    在完成领域模型和微服务设计后,我们还需要对微服务进行详细的设计。

    主要设计以下内容:实体属性、数据库表和字段、实体与数据库表映射、服务参数规约及功能实现等。

    2. 代码开发和测试

    开发人员只需要按照详细的设计文档和功能要求,找到业务功能对应的代码位置,完成代码开发就可以了。

    代码开发完成后,开发人员要编写单元测试用例,基于挡板模拟依赖对象完成服务测试。

    四、具体代码参考

    本次的总结都是按照极客时间课程《DDD实战》欧创新架构师的举例项目来进行的,相关代码可以克隆其代码:

    https://github.com/ouchuangxin/leave-sample.git

    后期在其基础上会有补充,以及对于视频系统的项目分析,后续博客中公布。

     

    参考书籍、文献和资料

    1.极客时间课程《DDD实战》,欧创新,2019.

    2.郑天民. 微服务设计原理与架构. 北京:人民邮电出版社,2018.

    展开全文
  • 二叉树的两种创建方法和三种遍历方法 这里的两种创建方法,一种值得是 数据结构上面的创建方法方法代码如下: 二叉树的结构定义如下: typedef struct BinaryTreeNode{ char value; ...
  • 一、领域驱动设计两大设计:战略设计和战术设计 二、理解和分析领域+子域+核心域+通用域+支撑域 三、理解和分析界限上下文,定义领域边界 四、理解和分析实体和值对象 五、理解和分析聚合思想:聚合和聚合...
  • 市场上常用的仿真器有三:STLINK、ULINK及JLINK。其中官方出品的是STLINK,价格在120元左右。STLINK包含三部分:数据线、USB线及仿真器本身。  驱动程序的安装方法:在设备管理器中找到STLINK...
  • Python案例:两种方法实现词频统计

    千次阅读 2019-10-28 17:43:54
    Python案例:两种方法实现词频统计 一、利用字典实现词频统计 1、编写源代码 2、查看运行结果 二、利用collections的Counter模块实现词频统计 1、编写源代码 2、查看运行结果 ...
  • vue 中使用 rem 布局的两种方法

    万次阅读 2019-01-25 10:35:58
    在使用 vue-cli 开发 H5 项目时,需要进行 rem 适配,下面提供两种常用的方法(以 750 设计稿为例),希望对大家有所帮助。 方法一:在 index.html 或者 main.js 中添加以下代码: const setHtmlFontSize = () =&...
  • php生成空对象的两种方法

    万次阅读 2017-11-02 00:22:39
    最基本的php中生成类,首先...下面提供两种迅速生成空对象的方式,然后迅速对对象赋值。 方法1. 定义空类,空类生成空对象,然后对对象的成员变量赋值 方法2. 使用stdClass直接生成空对象 下面附代码: . //ph
  • MFC Button Control事件获取的两种方法

    千次阅读 2016-03-12 11:40:18
    MFC 响应Button事件的两种方法
  • 在Dreamweaver中换行的两种方法

    万次阅读 2018-01-24 11:17:07
    在Dreamweaver中换行有两种:一种是在设计视图中直接回车,对应的代码是标签,即新生成一个段落。第二种是在设计视图中输入shift+Enter ,即所谓的软回车,对应的标签是,这种换行后输入的文字和换行前的文字被视为...
  • Qt设计用户界面的三种方法

    千次阅读 2019-05-24 21:41:04
    Qt设计界面有三方式: (1)手工编写创建界面的代码:此方法比较复杂,不够直观; (2) 使用Qt Designer界面编辑器设计:可直接拖放控件、设置控件的属性,简单、直观、易于操作; (3)动态加载UI文件并生成界面...
  • vs2019 实现C#调用c++的dll两种方法

    万次阅读 多人点赞 2020-06-14 17:00:14
    vs2019 实现C#调用c++的dll两种方法1.托管与非托管的区别2.非托管类的实现第一步:创建C++空项目(命名Caculate)添加一个类AddOperate第二步:将C++代码编译成动态库dll第三步:将dll拷贝到c#项目输入目录,一般在...
  • C语言提高代码效率的几种方法

    万次阅读 2016-05-03 10:05:52
    一段完美的代码不仅在于找到一个给定的问题的解决方案...本文向你介绍规范你的C代码的几种方法。 1、在可能的情况下使用typedef替代macro.当然有时候你无法避免macro,但是typedef更好。 typedef int* INT_PTR; IN
  • java提供了两种排序方式,分别是Collections.sort(List)和Collections.sort(List,Commparator),下面就这两种方法的使用做详细的说明: **-Collections.sort(List);** sort的参数是一个List集合,对List集合中的数据...
  • MAT之SVM/BP:SVR(better)和BP两种方法比较且实现建筑物钢筋混凝土抗压强度预测 目录 输出结果 代码设计 输出结果 代码设计 load concrete_data.mat n = randperm(size(attributes,2)); p_...
  • 首先还是要了解框架JFrame中的层次结构。JFrame中的层次分布及相对关系是:最底层是:JRootPane;...在这里笔者提供两种方法为一个frame设置一张背景图片方法一: 原理:我们把图片放置在第二层:Jla...
  • Android之Gallery的两种使用方法

    万次阅读 2013-01-03 21:07:02
    第一种方法: 第一步:设计xml布局文件  代码如下:main.xm  android:layout_width="fill_parent"  android:layout_height="fill_parent"  android:orientation="vertical" >    android:id="@...
  • 文章目录1、在代码代码动态设计1)xml 文件2) 功能文件2、在 xml 文件 里配置 跑马灯3、实现一个 Activity 多个TextView 跑马灯效果1) 自定义的 MarqueeTextView 类2、 xml 文件3)、功能文件 MainActivity ...
  • 创建后台任务的两种代码模式

    万次阅读 2017-04-04 10:51:25
    创建后台任务的两种代码模式后台任务是每个App都需要的一些行为,毕竟主线程是大爷,拖不起,伤不起,脏活累活都只能在不见天日的后台去做。最简单的后台任务,可以说是直接开一个线程就可以了,或者说来个Service,...
  • 提高代码阅读能力的7种方法

    千次阅读 2017-10-13 14:07:44
    原文:7 Ways to Improve Your Code Reading Skills 作者: A. N. M. Bazlur Rahman ...本文提供了7提高代码阅读技巧的方法,以下是译文。在软件开发人员的职位描述中有阅读源代码。然而,这体验...
  • 我根据自己的项目从自己完成的两种方法进行讲解并有源代码和运行截图. 一.MouseDown\MouseMove事件方法实现 二.调用API函数实现,第二种方法是采用API函数ReleaseCapture和SendMessage来实现的,最后希望该文章对大家...
  • 搭建爬虫代理ip池的两种方法(含源码)前言一、ip池是什么?二、爬取原理三、使用步骤方法一爬取网站https://www.kuaidaili.com/验证存取到mysql方法二爬取网站http://www.xicidaili.com/nn/多线程爬取源码 前言 ip...
  • 从Android代码中来记忆23种设计模式

    万次阅读 多人点赞 2016-05-30 19:56:22
    我的简书同步发布:从Android代码中来记忆23种设计模式 相信大家都曾经下定决心把23种设计模式牢记于心,每次看完之后过一段时间又忘记了~,又得回去看,脑子里唯一依稀记得的是少数设计模式的大致的定义。其实,...
  • 第一章-宝箱抽奖模块与代码设计(一)

    万次阅读 多人点赞 2016-08-24 14:53:31
    宝箱抽奖模块与代码设计(一)宝箱抽奖模块与代码设计一 无聊的开场白 每篇文章的背后都有个高大上的故事 怎样的代码才算是好的代码 简单的需求 简单的代码Java实现 一些基本的类 玩家类 抽奖服务类 先大致确定抽奖...
  • DL之DNN:自定义2层神经网络TwoLayerNet模型(计算梯度两种方法)利用MNIST数据集进行训练、预测 导读 利用python的numpy计算库,进行自定义搭建2层神经网络TwoLayerNet模型。分别利用两种计算梯度两种方法,数值...
  • Java关键字Default的两种使用方法 其实之前一直都没有关注过还有default这关键字的存在,最近重新回温《Java语言程序设计》这本书时,想总结总结常用的一些修饰符。偶然发现…嗯..是这样的. 两种使用方法: 据...
  • C/C++中计算函数运行时间的两种方法

    万次阅读 多人点赞 2017-11-04 23:30:11
    方法就是在该段代码或者函数前面,记录一个时间T1,在代码段或函数后面记录时间T2,那其运行时间就是T2-T1,下面看看具体运算方法方法一:  clock()是C/C++中的计时函数,而与其相关的数据类型是clock_t; ...
  • 代码设计 六大原则

    万次阅读 2016-07-02 02:13:22
    问题由来:类T负责个不同的职责P1和P2。由于职责P1需要发生改变而需要修改T类,就有可能导致原来运行正常的职责P2功能发生故障。解决方法:遵循单一职责原则。分别建立新的类来对应相应的职责;这样就能避免修改类...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,557,507
精华内容 623,002
关键字:

代码设计的两种方法