chp2.

lambda表达式的几种形式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// () -> 表示没有参数且Runnable接口只有一个run方法
Runnable noArguments = () -> System.out.println("Hello World");

// 只有一个参数的表达式可以省略括号
ActionListener oneArgument = event -> System.out.println("button clicked");

// 表达式可以为代码块,用{}括起来
Runnable multiStatement = () -> {
System.out.print("Hello");
System.out.println(" World");
};

// 包含多个参数,返回的add变量是代码 BinaryOperator而不是相加之后的结果(有点类似于将函数作为变量)
BinaryOperator<Long> add = (x, y) -> x + y;

// 括号内的参数也可以指定类型而不是让编译器来推断
BinaryOperator<Long> addExplicit = (Long x, Long y) -> x + y;

Lambda 表达式的类型依赖于上下文环境,是由编译器推断出来的。

变量引用:

在lambda表达式中可以引用非final变量但该变量在既成事实上必须是final。也就是只能给该变量赋值一次,也就是值引用。

函数接口:

使用只有一个方法的接口来表示某特定方法并反复使用。

Java中重要的函数接口:

chp3.

外部迭代

内部迭代

lambda表达式采用惰性求值,在没有进行及早求知(reduce)之前不会进行前面的求值操作。 ex:

1
2
3
4
5
6
// 由于采用惰性求值,这个表达式并不会有输出
allArtists.stream()
.filter(artist -> {
System.out.println(artist.getName());
return artist.isFrom("London");
});

常用流操作

  • collect(toList()) : 由Stream里的值生成一个列表,是一个及早求值操作。

  • map : 将一个流中的值转换成一个新的流。

  • filter :

    1
    2
    3
    4
    // ex
    List<String> beginningWithNumbers = Stream.of("a", "1abc", "abc1")
    .filter(value -> isDigit(value.charAt(0)))
    .collect(toList());
  • flatMap : 用Stream替换值,然后将多个Stream连接成一个Stream

  • max,min

    1
    2
    3
    4
    // ex:
    Track shortestTrack = tracks.stream()
    .min(Comparator.comparing(track -> track.getLength()))
    .get();
  • reduce

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // 使用reduce求和
    int count = Stream.of(1, 2, 3)
    .reduce(0, (acc, element) -> acc + element);
    // 展开reduce
    BinaryOperator<Integer> accumulator = (acc, element) -> acc + element;
    int count = accumulator.apply(
    accumulator.apply(
    accumulator.apply(0, 1),
    2),
    3);

chp4.

labmda类型推导

默认方法

三定律
如果对默认方法的工作原理, 特别是在多重继承下的行为还没有把握, 如下三条简单的定律可以帮助大家。

  1. 类胜于接口。 如果在继承链中有方法体或抽象的方法声明, 那么就可以忽略接口中定义的方法。
  2. 子类胜于父类。 如果一个接口继承了另一个接口, 且两个接口都定义了一个默认方法,
    那么子类中定义的方法胜出。
  3. 没有规则三。 如果上面两条规则不适用, 子类要么需要实现该方法, 要么将该方法声明为抽象方法。

其中第一条规则是为了让代码向后兼容。

Optional

(这里的介绍有点简略)

chp5.

方法引用

1
2
3
4
// idea会在写出前一种形式的时候给出修改建议
artist -> artist.getName() ==> Artist::getName

(name, nationality) -> new Artist(name, nationality) ==> Artist::new

元素顺序

  • 在一个有序集合中创建一个流时, 流中的元素就按出现顺序排列
  • 如果集合本身就是无序的, 由此生成的流也是无序的

收集器

  • 使用 toCollection, 用定制的集合收集元素 -> stream.collect(toCollection(TreeSet::new));
  • 生成值
    • maxBy -> artists.collect(maxBy(comparing(getCount)));
    • averagingInt -> .collect(averagingInt(album -> album.getTrackList().size()));
  • 分块(partition)-> return artists.collect(partitioningBy(Artist::isSolo));
  • 分组(groupby)
    1
    2
    3
    4
    // 返回Map k,v value为对象的List
    public Map<Artist, List<Album>> albumsByArtist(Stream<Album> albums) {
    return albums.collect(groupingBy(album -> album.getMainMusician()));
    }
  • 字符串 -> .collect(Collectors.joining(", ", "[", "]")); joining(分割符,前缀,后缀)
  • 组合收集器(下游收集器)
    1
    2
    3
    4
    5
    6
    // counting
    albums.collect(groupingBy(album -> album.getMainMusician(),
    counting()));
    // mapping
    return albums.collect(groupingBy(Album::getMainMusician,
    mapping(Album::getName, toList())));

话到嘴边又不知道该怎么描述,也算是拾起了很久没用的博客吧,这也算是自己少见的文字输出方式了所以还是来写一写.

之所以会写这周搞的一些新东西这样的博客,主要是这周摸🐟的时光似乎有些多了,也是四处搞了很多新东西和一些之前遗留的事,就来记录一下吧.

敏捷学习仓库合并

在许久之前看了基于GitHub的敏捷学习方法之道与术之后就深受影响然后搞了一个仓库Agile-Learning也坚持搞了一段时间(MySQL文档翻译就是这样促成的),但是过年回来之后惰性发作也就许久没有再去动这个仓库也就闲置了许久,最近打算重新使用之后发现private的仓库不能无偿使用wiki,也就借着public的机会把这个仓库和github.io的仓库给合并了顺便整理了一下吧.

因为写博客的分支是在backup上的,所以对博客访问也没有什么影响,也是方便自己管理,之前没有public敏捷学习的仓库也是因为打算实践一段时间找到自己的使用方式之后再public出来,现在也是想用wiki就用public的方式来督促一下自己吧算是.

用wiki整合知识

承接上文现在也是打算吧一些开发和日常过程中自己Google过的不熟悉的东西整理到wiki里面去了(毕竟记性不好,还是写下来靠谱):
个人知识点整理用wiki

对于wiki的话其实也就是一个MarkDown文件的文件夹,大致的目录如下:

主要是*_Footer.md*,_Sidebar.md,Home.md三个文件,需要注意的是没有Home.md这个文件的话进入wiki看到的就是文件列表了.还有一点需要吐槽的是wiki默认的git方式是https的,需要自己手动改成ssh方式的不然就还是需要账号密码而不能用密钥的方式了.

博客HTTPS

关于https,很早之前就已经用上了,当时使用的是cloudflare的免费HTTPS方式,但是现在发现GitHub Pages已经自己支持HTTPS了,就改用GitHub自带的了:

VSCode remote

因为现在一直在用VSCode写MarkDown而在经历了整合仓库之后在clone的时候发现网速巨慢,大概需要10-30min才能把仓库clone下来,实在是无法忍受之后想到了用阿里云的服务器来写博客,之前也是对VSCode remote略有耳闻就Google并且尝试了一下,目前来看在写Markdown这一块还是很不错的并且由于文件保存在服务器上所以哪怕没有保存的修改也不会丢失.

步骤

  1. 下载openssh:OpenSSH-Win64.zip
  2. 添加到Path
  3. PowerShell使用ssh xxx@xxx.xxx.xxx尝试连接服务器
  4. VSCode安装Remote的插件
  5. 在本地的.ssh/config文件下进行服务器的配置:
  6. 完成之后如下就已经可以了:

Windwos Terminal && WSL

先来一个效果图吧(想自定义主题的但是目前好像文章不是很多还得好好研究一下):

Win10下的安装也很简单就不在赘述了,下面就放一下配置文件好了:

1
2
3
4
5
6
7
8
9
{
"globals" :
{
"alwaysShowTabs" : true,
"copyOnSelect" : false,
"defaultProfile" : "{9acb9455-ca41-5af7-950f-6bca1bc9722f}",
"initialCols" : 120,
"initialRows" : 30,
"keybindings" :

这里主要是defaultProfile这个字段,对应下面每个terminal类型的guid,我改成了默认启动WSL的窗口.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
"profiles" : 
[
{
"acrylicOpacity" : 0.75,
"closeOnExit" : true,
"colorScheme" : "Vintage",
"commandline" : "wsl.exe",
"cursorColor" : "#FFFFFF",
"cursorShape" : "bar",
"fontFace" : "Fira Code",
"fontSize" : 10,
"guid" : "{9acb9455-ca41-5af7-950f-6bca1bc9722f}",
"historySize" : 9001,
"icon" : "ms-appx:///ProfileIcons/{9acb9455-ca41-5af7-950f-6bca1bc9722f}.png",
"name" : "WSL",
"padding" : "0, 0, 0, 0",
"snapOnInput" : true,
"startingDirectory" : "//wsl$/Ubuntu/home/tang",
"useAcrylic" : true
},

这是WSL的配置文件,icon这个是需要更换的,目录是在(我的文件夹权限有点问题就用命令行来查看了):

guidicon一起改掉就ok了.

我还更换了一下字体:"fontFace" : "Fira Code", 关于字体可以自行Google.

终端主题在下面的"schemes" : 里有自带默认的几个风格.修改到"colorScheme" : "Vintage",这个字段就可以应用.

可以看出这一次巨硬还是很良心的,而且配置文件也很容易修改,可以说是Win开发的一大福音了.

id_rsa

以前的我只知道莽,无脑生成密钥就完事了.

但是在经历了许多环境和服务器的摧残之后,还是认识到了使用一套自己的密钥的重要性了,于是在生成了一套之后把GitHub和服务器都替换了一下,现在就very方便了(顺便保存在了1password里).

Pi_hole

可以查看示例:Pi_hole Admin

知道这东西是在老莱的频道发现的:【官方双语】世界清净了…用树莓派屏蔽所有在线广告!#linus谈科技

感觉还挺有意思的但是介于没有树莓派然后又想到服务器不是有公网ip吗,于是就在阿里服务器上搭了一个,一开始感觉还非常不错但是在经历了更多网页的洗礼之后发现还是不太行,现在用的是adblock+Pi hole的双重过滤方式.

需要注意的是如果在服务器上部署需要在安全组里开放53的UDP端口,是DNS需要的端口.

安装的话由于有一键脚本,直接跟着做就成了所以也就不在赘述了.

1
curl -sSL https://install.pi-hole.net | bash

(后期补充:docker脚本)

1
2
3
4
5
6
7
8
9
10
11
docker run -d \
--name pihole \
-p 53:53/tcp -p 53:53/udp \
-p 8081:80 \
-p 443:443 \
-e TZ="Asia/Shanghai" \
-v "$(pwd)/etc-pihole/:/etc/pihole/" \
-v "$(pwd)/etc-dnsmasq.d/:/etc/dnsmasq.d/" \
--dns=127.0.0.1 --dns=1.1.1.1 \
--restart=unless-stopped \
pihole/pihole:latest

自建RSS

我也算是个轻度的RSS使用者了吧,之前一直在使用Inoreader但是最近发现广告变多了而且免费账户着实有些弟弟,就想着反正有个服务器就克服一下懒癌尝试一下吧.

然后Google了一顿还是发现之前看过的:如何搭建属于自己的 RSS 服务,高效精准获取信息感觉还不错就跟着做了,不得不说:真香. 在docker的加持之下安装是非常之简单了,唯一的坑是Mercury 的全文插件,现在已经需要自建了, 然后参考了:通过Docker安装Tiny RSS,mercury全文插件及mercury-parser-api;申请域名通配符证书,安装nginx并配置ssl 使用docker安装了Mercury Parser API.

直接使用

1
docker run -p 3000:3000 -d --restart=always  wangqiru/mercury-parser-api

然后配置API地址就可以了,当然之前是使用API KEY的形式.

然后把Inoreader的订阅源导入过来就成了,有一点值得吐槽的是Inoreader导出的opml文件竟然是xml格式的,然后直接导入ttrss就会失败,但是改一下后缀就ok了.

TabNine

据说是杀手级AI代码补全工具,毕竟现在是”AI大数据”的时代就下载下来蹭一下热度呗,实际的使用体验还算是不错,写Markdown的时候竟然也会提示单词的补全,好评.

在idea也装了插件,补全智能程度还算是非常的不错,在填参数的时候甚至会帮你补全后面的’)’.

介绍的文章也很多我也就不再唠叨用就完事了(毕竟我只是个记录贴 :)

Perface

TDD rules:

  1. write new code only if you first have a failing automated test.
  2. eliminate duplication. 消除重复设计

Technical implications:

  1. design organically: running code providing feedback between decisions.
  2. write your own test.
  3. development env must provide rapid response to small change.
  4. design must be highly cohesive, loosely coupled components(高内聚,低耦合) to make testing easy.

Programing order:

  1. Red-write a little that doesn’t work.
  2. Green-make the test work quickly.
  3. Refactor-eliminate all the duplication created.
  • 红:写测试
  • 绿:写代码通过测试
  • 重构:消除重复设计,优化结构

Section I: MoneyExample

  • a to-do list to remind what need to do.
  • when write a test,imagine the perfect interface for operation.
  • Dependency is the key problem in software development at all scales.
  • If you can make steps too small, you can certainly make steps the right size.(为什么测试要足够小)

TDD cycle:

  1. write a test.
  2. make it run.
  3. make it right.

the goal is clean code works.

First we can talk about whether the system should work like this or like that. Once we decide on the correct behavior, we can talk about the best way of achieving that behavior

首先需要考虑系统是怎么样的,确定实现的思路之后就找到最佳的实现办法.(TDD难点:对系统的总体认识和设计)

That is a risk you actively manage in TDD. We aren’t striving for perfection. By saying everything two ways, as both code and tests, we hope to reduce our defects enough to move forward with confidence.

TDD开发的风险:测试和编码同时进行会引入缺陷.

The different phases have different purposes. They call for different styles of solution, different aesthetic viewpoints.

不同的阶段所注重的点是不同的,所以在初期设计阶段我们可以忍受重复的设计和复制粘贴代码,一切都是为了尽快完成这个阶段(clean code是重构的任务).

消除类冗余的过程:

  1. 把子类的公共代码移到父类中.
  2. 对父类的其他子类进行简化.
  3. 合并equals()函数到父类.

对equals()和hashCode()函数的覆盖和重写过程是发生在重构中的,并且已经有许多的测试来对重构做支撑.

在制造对象的时候使用工厂方法(factory method).

使用工厂模式和合理的构造器参数将重复代码移动到父类中去.

With the tests you can decide whether an experiment would answer the question faster. Sometimes you should just ask the computer.

在TDD中,重构时由于有足够多的测试来支撑改动所以可以使用测试来迅速验证想法而如果没有测试验证想法就只能依赖思考和论证.

在重构的过程中将子类代码消除后就要将其删除.

使用多态来消除显式的类型判定.

Section II: Example: xUnit

对TDD测试的要求:

  • Easy to write for programmers. 易于编写
  • Easy to read for programmers. 易于阅读
  • Quick to execute. 快速执行
  • Order independent. 顺序无关
  • Deterministic. 确定的:执行结果不随执行次数变化
  • Piecemeal. 零碎(足够小)
  • Composable. 可组合:可以以各种组合方式运行测试
  • Versionable. 多版本
  • A priori. 先验(在代码能运行之前就写好测试)
  • Automatic. 自动化
  • Helpful when thinking about design. 对系统的设计的思考有帮助(测试先行需要对系统有良好的组织).

Lots of refactoring has this feel—separating two parts so you can work on the separately. If they go back together when you are finished, fine, if not, you can leave them separate.

Here is another general pattern of refactoring—take code that works in one instance and generalize it to work in many by replacing constants with variables.

先用常量进行测试,通过之后将常量位置改为变量就能在更多情况下使用.

测试模式:

  1. 创建对象
  2. 激活(测试)对象
  3. 检查结果

测试的矛盾:

  • Performance 性能:测试的执行要越快越好
  • Isolation 隔离:测试之间不要互相耦合并且测试执行不依赖于其执行顺序

Section III: Patterns

  • 自动化测试很重要
  • 测试之间相互独立
  • 开始编码之前列出测试清单
  • 测试优先(测试先行)
  • 使用断言
  • 使用容易理解的测试数据
  • 使测试数据的意图明显

从测试中发现问题:

  • 冗长的设置(初始化)代码 -> 对象太大
  • 冗余的设置 -> 对象间耦合
  • 测试运行时间过长 -> 测试不会被运行或运行有问题

Is TDD Dead?

在大略阅读了Google软件测试之道和TDD之后,感觉TDD最明显的特征就是快速的实现和重构,自然这其中需要很强的”clean code”的能力,例如自然的使用工厂模式和提取参数使子类的方法向上转移到父类之上.

而TDD的难点也就在于在系统开发之前,如何进行合理有效的拆分,而一旦没有清晰的思路,所谓的测试先行也就变成了冥思测试用例而不得,导致开发速度变得更慢.

TDD的难以施行在结合Google的测试历程之后会发现,精通测试确实是”太难了”,需要开发人员在编码和测试两个方向都有较好的能力才能比较顺利的施行,而开发编写测试本身就会”遭到质疑”.哪怕强如Google也花了很长时间才让开发人员能够适应自己测试的开发节奏,可见在全新的组织中尝试实行TDD会是很痛苦的过程.

当然,TDD也并非是开发方法论的全部.个人感觉关键还是需要在编程时有时刻重构的思维和编写”clean code”的能力,而TDD更像是对这两种要求的结合.当你习惯了重构和能够写出”clean code”的时候,T不TDD也就变得没有那么重要了.

0%