0%

原文链接

Fluent Interface,Matrin Fowler 博客,发表于 2005 年 12 月 20 日。

译文

几个月前我和 Eric Evans 进行了一次讨论会,他谈到一种接口的设计风格,我们称之为流畅接口(Fluent Interface)。它不是一种常见的风格,但我们觉得应该广为人知。描述它的最直观的方式就是例子。

最简单的例子可能就来自 Eric 编写的 TimeAndMoney Library。为了指定一段时间间隔,我们通常这么做:

1
2
3
TimePoint fiveOClock, sixOClock;
...
TimeInterval meetingTime = new TimeInterval(fiveOClock, sixOClock);

但是 TimeAndMoney 库的使用者会这样做:

1
TimeInterval meetingTime = fiveOClock.until(sixOClock);

下面我继续演示“客户下订单”这个例子。一个订单包含多个订单项,每个订单项包含商品项和购买的数量。在提交订单时一个订单项应当是可跳过的,这意味着我更希望在没有此订单项(缺货)的情况下提交订单,而不是推迟提交整个订单。所以这里可以给整个订单一个“急促(rush)”的状态标识。

要实现上述功能,最常见的编码如下:

阅读全文 »

前言

Java 拥有功能和性能都非常强大的日志库,但另一方面,Java 日志库依赖看起来丰富的让人眼花缭乱。相信大家或多或少都有这样的疑问,Log4j,SLF4J,Logback,Log4j2 这些日志框架我该如何选择?它们彼此间又有什么关系?本篇文章将介绍这些日志库的历史演进和之间的关系,方便你选择最适合的日志库。文章最后还有日志库使用的最佳实践。

历史

Log4j (Log For Java) 可以当之无愧地说是 Java 日志框架的元老,1999 年发布首个版本,2012 年发布最后一个版本,2015 年正式宣布终止,至今还有无数的系统在使用 Log4j,甚至很多新系统的日志框架选型仍在选择 Log4j。

然而老的不等于好的,在 IT 技术层面更是如此。尽管 Log4j 有着出色的历史战绩,但早已不是 Java 日志框架的最优选择。

在 Log4j 被 Apache Foundation 收入门下之后,由于理念不合, Log4j 的作者 Ceki Gülcü 离开并开发了 SLF4J 和 Logback。

SLF4J (Simple Log Facade For Java) 因其优秀的性能和理念很快受到了广泛欢迎,2016 年的统计显示,GitHub 上的热门 Java 项目中,SLF4J 是使用率第二名的类库(第一名是 Junit)。

Logback 则吸取了 Log4j 的经验,实现了很多强大的新功能,再加上它和 SLF4J 能够无缝集成,也受到了欢迎。

在这期间,Apache Logging 则一直在关门憋大招,Log4j2 在 beta 版鼓捣了几年,终于在 2014 年发布了 GA 版,不仅吸收了 Logback 的先进功能,更通过优秀的锁机制、LMAX Disruptor、”无垃圾”机制等先进特性,在性能上全面超越了 Log4j 和 Logback。

Log4j 1.x

阅读全文 »

为什么迁移

Ghost

优点:

  1. 提供后台管理界面,支持在线编辑文章,随时更新随时发布。
  2. 图片上传方便,直接拷贝到正文即可,会自动上传到服务器并生成 Markdown 链接。
  3. 支持多人同时使用,可用作团队博客。

缺点:

  1. 需要在服务器上部署 Ghost。
  2. 本地和服务器端文章同步麻烦(这也是我换用 Hexo 的主要原因)。
  3. 不支持离线编辑文章。虽说这个可以本地写完了再一次性拷贝到服务器上,但这又回到了第 2 点。
  4. Themes 配置麻烦,不够统一。Kaldorei 主题是我废了很大劲才搜罗到的,还得每次修改完源码后压缩上传(调样式的时候调的我头疼)。

dbdd8ad99f065192aa340cf474709b2d.png
fa0a3c8ae59030f8e5ef80a9b04d8c7d.png

Hexo

优点:

  1. 文档本地化。在本地编写博客,一条指令即可生成静态网页(具体体现就是你在 source/_posts 下编写 Markdown,写完了执行 hexo g 就可以生成样式美观的 index.html)。
  2. 一条指令即可部署到 GitHub Pages,不需要额外的服务器(配合第 1 条超级好用好嘛!)。
  3. 丰富的插件和主题,配置也统一化了。比如图中我使用的 Next 主题,主题的配置修改 theme/next/_config.yml;Hexo 的配置修改主目录的 _config.yml 即可。
  4. 图片上传还算方便。在与文章同名的文件夹下保存图片即可。这个下面会细说。
阅读全文 »

前言

得益于 Python 丰富的库,我们可以不用重复造轮子而是直接拿人家现成的库用,比如爬虫所需的解析 Html 功能都不用自己亲自写。所以,我在用 Python 改写之前的 Java 爬虫时,只用了 50 行代码就实现了原有功能。本文主要介绍编码时用到的库,以及总结了一些 Python 编码的知识点。

案例还是用的之前 Java 网络爬虫 的案例。所需 Python Version >= 3.6,用到的库有:

  • beautifulsoup4 三方库用于解析 Html,执行 pip install beautifulsoup4 安装
  • 内置的 urllib 用于发起网络请求获取响应内容
  • 内置的 concurrent.futures 并发库中的 ProcessPoolExecutor 用于创建进程池

源码

源码已经上传到 GitHub

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import os
import re
import time
from urllib import request
from concurrent.futures import ProcessPoolExecutor
from bs4 import BeautifulSoup

WWW_BIQUGE_CM = 'http://www.biquge.cm'


def __fetch_html(url, decode='UTF-8'):
req = request.Request(url)
req.add_header('User-Agent', 'Mozilla/5.0')
with request.urlopen(req) as res:
return res.read().decode(decode)


def __parse_title_and_hrefs(index):
html = __fetch_html(f'{WWW_BIQUGE_CM}/{index}/', 'gbk')
soup = BeautifulSoup(html, 'html.parser')
links = soup.find_all('a', href=re.compile(rf'/{index}/'))
hrefs = list(map(lambda x: x['href'], links))
title = soup.h1.string
print(f'title: {title}\nhrefs: size={len(hrefs)}')
return title, hrefs


def __fetch_content(href):
print(f'parsing {href}')
html = __fetch_html(f'{WWW_BIQUGE_CM}/{href}', 'gbk')
soup = BeautifulSoup(html, 'html.parser')
return re.sub(r'<div id="content">|</div>|<br/>', '', str(soup.find('div', id='content')))


def __append_contents_to_file(title, hrefs):
with ProcessPoolExecutor(16) as executor:
contents = executor.map(__fetch_content, hrefs)
os.makedirs('downloads', exist_ok=True)
with open(f'./downloads/{title}.txt', 'wt+') as f:
for content in contents:
f.write(content)


def run(index='12/12481'):
start = time.time()
title, hrefs = __parse_title_and_hrefs(index)
__append_contents_to_file(title, hrefs)
print(f'spend time: {time.time() - start}s')


if __name__ == '__main__':
run('9/9422')

urllib

模拟浏览器发起一个 HTTP 请求,我们需要用到 urllib.request 模块。其 urlopen() 方法用于发起请求并获取响应结果,该方法可单独传入一个 urllib.request.Request 对象,并返回一个 http.client.HTTPResponse 对象。

1
2
3
4
5
def __fetch_html(url, decode='UTF-8'):
req = request.Request(url)
req.add_header('User-Agent', 'Mozilla/5.0')
with request.urlopen(req) as res:
return res.read().decode(decode)
阅读全文 »

前言

在项目开发时遇到这样一个场景:从上游传过来一个实体类对象 newEntity,但它只有部分字段,需要去库中查出对应的旧对象 oldEntity 做一次补全(相当于一次部分更新)。

一开始我们这样编码的:

1
2
3
4
5
6
7
8
9
10
11
12
13
public FlightBasic merge(FlightBasic newEntity){
if (newEntity.getAirportCode() != null){
this.setAirportCode(newEntity.getAirportCode());
}
if (newEntity.getCraftNo() != null){
this.setCraftNo(newEntity.getCraftNo());
}
if (newEntity.getCraftType() != null){
this.setCraftType(newEntity.getCraftType());
}
// too long ...
return this;
}

很快我们发现了问题:不仅类的字段很多,这样的 Entity 也有很多,所以到处都是又臭又长的 merge 方法。

其实不难总结这些代码的共性:

  • 一个 Entity 只能和本身的类型 Merge
  • 代码都是判空后对属性的 getter/setter 方法的重复调用

为了简化代码(偷懒),就在想能不能通过统一的处理完成这些 Merge 逻辑。首先想到的是代码生成,类似 MyBatis Generator,重复的工作交给脚本或者工具多好。但是很快就否定了这种方案,配置这些类的工作量也不小,而且很多类中有自己的业务逻辑,一不小心覆盖了也不行。那注解行不行呢?Spring Boot 就大量使用注解替换了之前配置繁杂的 XML 文件。实际编码后验证是可行的!本文将介绍如何配合使用 Java 的注解和反射实现上述问题中的 Merge 操作。示例代码已上传到 GitHub 上。

注解

每当你创建描述符性质的类或者接口时,一旦其中包含重复性的工作,就可以考虑使用注解来简化与自动化该过程。一个注解准确意义上来说,只不过是一种特殊的注释而已,如果没有解析它的代码,它可能连注释都不如。而解析注解往往有两种方式,一种是编译期扫描,典型的例如 @Override,这种只适用于编译器已经认识的注解,一般都是 JDK 内置注解;另一种则是通过运行期反射,也是在我们自定义注解后需要自己编码的。

阅读全文 »