0%

前言

该系列源于张逸总监的 Clean Code 培训,包涵了如何写出高质量代码的思想、代码案例以及重构代码的实际演练。本篇为该系列的第一篇,主要介绍迪米特法则信息专家模式,并结合案例实操在 IntelliJ IDEA 中如何重构代码。本文大部分内容转载自张总博客: 迪米特法则与重构

准备工作

  • IntelliJ IDEA,相比 Eclipse,IDEA 在重构方面十分优秀,结合快捷键,使用起来让人赏心悦目。
  • 示例代码地址 https://github.com/agiledon/cleancode.git ,进行实际操作有助于加深理解和记住快捷键。代码有两个分支,master 分支为重构前的代码,after-refactoring 分支为重构后代码,可以使用快捷键 Cmd + D 查看代码差异
  • 活用快捷键,本人使用的 IDEA 快捷键为 Mac OS X 10.5+ 。Windows 用户将 Cmd 替换成 Ctrl,或者在 IDEA 的 Refactor 菜单中查看快捷键。
快捷键 作用
Ctrl + T 重构菜单
Shift + F6 重命名方法、属性、文件
Cmd + Alt + M 提取方法(extract method)
Cmd + Alt + N 内联(Inline),与 extract 相反
Cmd + Shift + 上下箭头 上下移动声明体(statement)

迪米特法则

在面向对象设计的世界里,有一个寻常却又常常为人所忽略的原则——迪米特法则(Law of Demeter)。这个原则认为,任何一个对象或者方法,它应该只能调用下列对象:

  • 该对象本身
  • 作为参数传进来的对象(也可以是该对象的字段)
  • 在方法内创建的对象

这个原则用以指导正确的对象协作,分清楚哪些对象应该产生协作,哪些对象则对于该对象而言,又应该是无知的。

代码案例及分析

阅读全文 »

前言

你可能知道,JavaScript 语言的执行环境是单线程(single thread)。这种模式的好处是实现起来比较简单,执行环境相对单纯;坏处是只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行。常见的浏览器无响应(假死),往往就是因为某一段 JavaScript 代码长时间运行(比如死循环),导致整个页面卡在这个地方,其他任务无法执行。为了解决这个问题,JavaScript 语言将任务的执行模式分成两种:同步(Synchronous)和 异步(Asynchronous)。

  • 同步模式:后一个任务等待前一个任务结束,然后再执行,程序的执行顺序与任务的排列顺序是一致的、同步的;
  • 异步模式则完全不同,每一个任务有一个或多个回调函数(callback),前一个任务结束后,不是执行后一个任务,而是执行回调函数,后一个任务则是不等前一个任务结束就执行,所以程序的执行顺序与任务的排列顺序是不一致的、异步的。

异步模式非常重要。在浏览器端,耗时很长的操作都应该异步执行,避免浏览器失去响应,最好的例子就是 Ajax 操作。在服务器端,Node.js 的异步 I/O 则保证了同一时间可以响应大量的 Http 请求。

本文主要介绍 JavaScript 的异步编程发展:从回调函数到 ES6 的 Promise 对象,再到 ES7 的 async/await 关键字。示例代码已上传至GitHub

回调函数

回调(Callback)函数是异步编程最基本的方法。回调就好比你送女朋友到车站,并让她回家了给你回条短信。回调函数在完成任务后就会被调用,Node.js 就使用了大量的回调函数

  • 看看下面的示例代码,你要求女朋友回到家后发一条短信告诉你,然后她在回家的路上愉快的耍起了手机,哈哈哈
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function goHome(callback) {
setTimeout(() => {
callback()
}, 2000)
console.log('I am playing a mobile phone')
}

function sendMessage() {
console.log('I have just arrived home.')
}

goHome(sendMessage)

// result:
// I am playing a mobile phone.
// I have just arrived home. // after 2000ms

注意打印结果的顺序。先打印的是I am playing a mobile phone,而后过了 2s 打印I have just arrived home。这是因为使用了setTimeout()这个函数,表示延时 2000ms 后执行回调函数。

阅读全文 »

Preface

最近遇到需要在 Spring Cloud 框架中加入用户认证的功能,发现 GitHub 有第三方认证功能,遂先写了个 demo 实现通过 GitHub 三方认证完成用户认证,虽然不是最终解决方案,但是有助于理解用户认证的流程

  • 代码沿用之前的 Sidecar – 将Node应用引入Spring Cloud 博客中的代码,用到以下模块

    1
    2
    3
    4
    ─ sidecar-example
    ├── author-service // 需要认证才能访问的微服务
    ├── eureka-server // 注册中心
    └── api-gateway // 网关,一切访问的入口

主要修改的是api-gateway,实现效果就是:用户通过api-gateway访问任何一个微服务,需要先跳转到 GitHub 进行认证,认证通过后才获取访问权限

Concepts

在这之前,有一些概念需要先了解

SSO

SSO(or Single Sign On) 单点登录,是目前比较流行的企业业务整合的解决方案之一。SSO 的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。非常适用于微服务架构,可以实现在一个微服务应用登录后(通常有专门的认证服务器),即可访问该微服务集群中的所有其他微服务

OAuth 2.0

大家平时肯定遇到过第三方登录的情况,不知道有没有仔细想过它的流程。假设现在我们用微博账号登录简书

阅读全文 »

前言

ESLint 是一款 JavaScript 的代码规约插件,可以自定义规则,不符合规则的代码可以设置不同级别的报错。方便对于团队内成员的代码质量进行统一管理。之前在 Vue 项目中使用vue-cli时可以自动添加 ESLint 代码规约插件,所以也没去管怎么手动添加 ESLint。但是最近在Koa2项目中,需要手动的去添加并配置 ESLint。本文将介绍如何安装以及配置 ESLint 的规则。

安装

在项目目录下使用npm命令安装 ESLint,并且是开发时依赖需要加上--save-dev。不推荐全局安装,这样可以实现项目规则的自定义化以及团队成员的统一化

1
npm install --save-dev eslint

初始化,会询问一些问题用于设置 ESLint,比如回答使用 Airbnb 的风格,不同的风格会有一些预定义的规则。

1
2
3
4
5
6
➜ ./node_modules/.bin/eslint --init

? How would you like to configure ESLint? Use a popular style guide
? Which style guide do you want to follow? Airbnb
? Do you use React? No
? What format do you want your config file to be in? JavaScript

完成后会在项目目录下生成.eslintrc.js文件,除此之外还可以手动添加.eslintignore,作用和.gitignore一样用于 Eslint 忽略检查的文件。

接着需要在 WebStorm 中启用 Eslint,勾选Enable

E9C4D048-BFC5-4D13-A4E8-30CD61BEE953

阅读全文 »

Preface

Sidecar 起源于 Netflix Prana,它的目的是将 Non-JVM 语言整合到 Netflix OSS 生态系统中,如今 Spring Cloud 将 Spring Boot 与 Netflix OSS 整合成一套微服务解决框架,大大简化了程序员的开发。而 Sidecar 就是其中的一个衍生物,用于将 Non-JVM 语言,譬如 Node.js、Python 引入至 Spring Cloud 框架中。

  • 本文的示例代码已上传至 Github

  • 使用 Maven 构建的多模块项目sidecar-example,并将 Node.js 开发的book-service模块拷贝至一个项目中

    1
    2
    3
    4
    5
    6
    // 模块结构如下
    ─ sidecar-example
      ├── author-service // Java 开发的微服务,用于测试服务间通信
      ├── eureka-server // 注册中心
      ├── node-sidecar // Sidecar,用于将 Node 应用引入 Spring Cloud
    └── book-service // Node.js 开发的微服务
  • 使用的 Spring Boot 版本及 Spring Cloud 全家桶版本

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.9.RELEASE</version>
    <relativePath/>
    </parent>

    <dependencyManagement>
    <dependencies>
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-parent</artifactId>
    <version>Edgware.RELEASE</version>
    <type>pom</type>
    <scope>import</scope>
    </dependency>
    </dependencies>
    </dependencyManagement>

Sidecar

先来看看引入 Sidecar 需要做些什么

  • 添加 Maven 依赖,很简单,甚至于不需要eureka-client的依赖,因为它已经整合至 Sidecar 的依赖中

    1
    2
    3
    4
    5
    6
    <dependencies>
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-netflix-sidecar</artifactId>
    </dependency>
    </dependencies>
  • 接下来是注解,在 Sidecar 主类上添加@EnableSidecar注解,我们来看看这个注解包含些什么

    1
    2
    3
    4
    5
    6
    7
    8
    9
    @EnableCircuitBreaker
    @EnableZuulProxy
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Import(SidecarConfiguration.class)
    public @interface EnableSidecar {

    }

    包含了网关 Zuul 以及微服务结构中不可或缺的熔断器 Hystrix

  • 最后是配置文件,在application.yml中添加如下配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    server:
    port: 9091
    spring:
    application:
    name: node-sidecar
    eureka:
    instance:
    hostname: localhost
    client:
    serviceUrl:
    defaultZone: http://${eureka.instance.hostname}:9090/eureka/
    sidecar:
    port: 3000
    health-uri: http://localhost:${sidecar.port}/health

    声明服务名和注册中心地址都没什么问题,最核心的就是 sidecar 的几个配置,包括

    • sidecar.port 监听的 Node 应用的端口号,
    • sidecar.health-uri Node 应用的健康检查接口的 uri

健康检查接口

Node.js 的微服务应用book-service我采用的 Koa 框架,这里不多赘述,需要注意的是:该应用必须实现一个/health健康检查接口,Sidecar 应用会每隔几秒访问一次该接口,并将该服务的健康状态返回给 Eureka

  • 只需要返回{ status: 'UP' }这样一串 Json 即可

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    const Koa = require('koa')
    const router = require('koa-router')()

    const app = new Koa()

    // log request URL:
    app.use(async (ctx, next) => {
    console.log(`Process ${ctx.request.method} ${ctx.request.url}...`)
    await next()
    })

    // add routes:
    router.get('/health', async (ctx, next) => {
    ctx.response.body = {
    status: 'UP'
    }
    })

    // add router middleware:
    app.use(router.routes())

    app.listen(3000)
    console.log('app started at port 3000...')

服务注册

阅读全文 »