跳到主要内容

· 阅读需 4 分钟
wen

What

本文是对谷歌Write Change-Resilient Code with Domain Objects的翻译。

原作者: Amy Fu

译文

尽管产品的需求经常变化,但其基本理念通常变化缓慢。由此得出一个有趣的结论:如果我们编写的代码符合产品的基本理念,那么它在未来产品变更中存活的可能性就会更高。

领域对象是我们代码中的基本构建块(如类和接口),它们与产品的基本概念相匹配。我们不是编写代码来匹配产品需求的期望行为(“将文本设置为白色”),而是匹配其底层概念(“文本颜色设置”)。

例如,假设你是披萨团队的一员,该团队向饥饿的谷歌员工出售美味新鲜的披萨。由于需求量大,你们团队决定增加送货服务。

如果没有域对象,最快的送比萨途径就是直接创建一个 deliverPizza 方法:

公共类 DeliveryService

公开的 void 方法 deliverPizza(List pizzas):...},

}

虽然这种做法在初期效果不错,但如果 gPizza 将其产品线扩展到其他食品上会怎样呢? 你可以添加一个新的方法:

公开的 void 方法 deliverWithDrinks(List pizzas, List drinks) ...},

但是随着你需要的功能越来越多(比如零食、甜食等),你将不得不不断添加更多的方法。你该如何修改初始实现以避免这种持续的维护负担呢?

你可以添加一个表示产品理念的领域对象,而不是其需求:

  • 用例是指帮助产品满足其业务需求的特定行为。

  • 一个域对象代表了由多个相似用例共享的通用概念。

为了确定合适的领域对象,请问自己:

  • 1.该产品支持哪些相关的使用场景,我们未来计划支持哪些方面?

A:gPizza 打算开始送披萨,将来还会送饮料和零食等其他产品。

    1. 这些用例有哪些共同点?

A:gPizza 想要把顾客订购的食物送达。

    1. 我们可以用什么域对象来表示这个通用概念呢?

A:这个领域对象是食品订单。我们可以将用例封装到一个 FoodOrder 类中。

领域对象是一种有用的抽象,但要避免选择过于通用的对象,因为在提高可维护性和更复杂、更模糊的代码之间存在权衡。通常,目标是仅支持计划中的用例,而不是所有可能的用例(参见 YAGNI 原则)。

// GOOD: It's clear what we're delivering.
public void deliver(FoodOrder order) {}

· 阅读需 4 分钟
wen

前言

最近推特上很火的 "【汉语新解】提示词" 的效果太赞了,可以说拓展了 prompt 的可能性。

无奈作者是用 lisp 写的,对于我这种没有 lisp 基础的人来说,看着实在有点累 😄

所以把它转换成 Python 版本。希望对大家有所帮助。

Python 代码

# 原Lisp版本作者: 李继刚
# 本 Python 版本作者: @wifecooky (@Twitter)
# 版本: 0.1.0
# 模型: Claude Sonnet
# 用途: 将一个汉语词汇进行全新角度的解释

class 新汉语老师:
"""你是年轻人,批判现实,思考深刻,语言风趣"""
def __init__(self):
self.风格 = ["Oscar Wilde", "鲁迅", "罗永浩"]
self.擅长 = "一针见血"
self.表达 = "隐喻"
self.批判 = "讽刺幽默"

def 汉语新解(用户输入):
"""你会用一个特殊视角来解释一个词汇"""
def 抓住本质(输入):
# 实现抓住本质的逻辑
return 输入

def 辛辣讽刺(输入):
# 实现辛辣讽刺的逻辑
return 输入

def 一针见血(输入):
# 实现一针见血的逻辑
return 输入

def 隐喻(输入):
# 实现隐喻的逻辑
return 输入

def 精练表达(输入):
# 实现精练表达的逻辑
return 输入

解释 = 精练表达(隐喻(一针见血(辛辣讽刺(抓住本质(用户输入)))))
few_shots = {"委婉": "刺向他人时, 决定在剑刃上撒上止痛药。"}

return SVG_Card(解释)

def SVG_Card(解释):
"""输出SVG 卡片"""
design_rule = "合理使用负空间,整体排版要有呼吸感"
design_principles = ["干净", "简洁", "典雅"]

def 设置画布():
return {"宽度": 400, "高度": 600, "边距": 20}

def 标题字体():
return "毛笔楷体"

def 自动缩放():
return {"最小字号": 16}

配色风格 = {
"背景色": ("蒙德里安风格", "设计感"),
"主要文字": ("汇文明朝体", "粉笔灰"),
"装饰图案": "随机几何图"
}

def 排版输出(用户输入, 英文, 日语):
# 实现排版输出的逻辑
return f"{用户输入} {英文} {日语}"

def 批判内核(输入):
# 实现批判内核的逻辑
return 输入

def 线条图(输入):
# 实现线条图的逻辑
return 输入

def 极简总结(输入):
# 实现极简总结的逻辑
return 输入

卡片元素 = [
排版输出("用户输入", "英文", "日语"),
解释,
线条图(批判内核(解释)),
极简总结(线条图(批判内核(解释)))
]

# 这里应该返回一个SVG字符串,但为了简化,我们只返回一个描述
return f"SVG卡片: {卡片元素}"

def start():
"""启动时运行"""
system_role = 新汉语老师()
print("说吧, 他们又用哪个词来忽悠你了?")

# 运行规则
# 1. 启动时必须运行 start() 函数
# 2. 之后调用主函数 汉语新解(用户输入)

if __name__ == "__main__":
start()
while True:
user_input = input("请输入一个词汇(输入 'quit' 退出): ")
if user_input.lower() == 'quit':
break
result = 汉语新解(user_input)
print(result)

测试

测试过程

  • 输出结果

sample

· 阅读需 5 分钟
wen

前言

估计东京有大约 8,000 家咖啡店。这些咖啡店类型多样,涵盖了从独立的小型咖啡馆到星巴克、Tully's Coffee 和 Doutor 等连锁品牌。东京的咖啡文化非常发达,许多白领、家庭主妇和学生都喜欢在这些咖啡店中放松或工作。包括笔者自己也是如此。😊

本文正是对我此刻在咖啡店中思考的这个问题进行的整理和反思 😄。

原因

结合我自身利用咖啡店的经验,可以从以下两个角度展开:心理上的放松 🧘‍♂️ 和集中注意力 🎯。

咖啡店看书

心理上的放松 🧘‍♀️

咖啡店作为一种“第三空间”(即介于家庭和工作场所之间的空间),提供了独特的环境,可以帮助人们从日常压力中解脱出来。这种放松不仅来源于环境的变化,还源于咖啡店的整体氛围:

  • 环境氛围 🏠

咖啡店通常拥有舒适的座椅 🛋️、温暖的灯光 💡 和柔和的背景音乐 🎶。这种组合创造了一种温馨的氛围,让人们可以放松身心。在这样一个环境中,人们可以远离家庭琐事或办公室压力,享受片刻的宁静 😌。

  • 社交与独处的平衡 🤝

咖啡店提供了一个既能融入社交氛围又能保持个人空间的场所。即使不直接与他人交流,看到周围人的活动也能让人感到安全和舒适 👥。这种轻微的社交联系有助于减轻孤独感,同时又不会打扰到个人的独处时光。

  • 心理仪式 🛎️

许多人将去咖啡店看书或工作视为一种心理仪式。这种仪式感让他们感觉自己在为个人时间和精神健康投入精力,从而增加了心理上的满足感 😊。

集中注意力 🎯

  • 心理上的自律感 ⏳

在咖啡店里,特别是当周围的人也在认真工作或学习时,这种环境会无形中激励个体更加自律 💪。这种自律感不仅提升了注意力,还增强了任务完成的成就感 🏆。

  • 分散与专注的微妙平衡 ⚖️

在家,人们容易受到各种干扰,如家务的打断等。而在咖啡店,虽然环境变化较多,但这些变化通常是背景化的,不会直接打扰到个体的任务。这种微妙的分散与专注的平衡反而有助于人们保持长时间的集中 🕒。这点和上面“环境氛围”中提到的原因有重合之处。

  • 背景噪音的作用 🎧

研究表明,适度的背景噪音可以提高人们的专注度和创造力 💡。咖啡店的环境噪音,如低语声、咖啡机的运作声,可以形成一种“白噪音”,有效地屏蔽掉更为突兀或分散注意力的声音 🔊。这种白噪音有助于人们进入“心流”状态,从而提高工作或学习效率 📈。实际上,如果家中没有干扰因素,适当的背景噪音在家里也同样有效 🏡。

结语 📝

咖啡店作为一个独特的空间,结合了心理放松与专注力提升的优势,成为了许多人工作、学习或阅读的理想之地 📍。在这样一个充满氛围的地方,人们不仅能找到片刻的平静,还能在温暖的咖啡香气中,找到更高效、更愉悦的工作与学习体验 📖😊。

· 阅读需 3 分钟
wen

简介

  • Chrome 浏览器内置强大的截图功能
  • 无需外部工具或插件,即可获取各种类型的截图
  • 本文将介绍适用于 Mac 和 Windows 用户的操作方法

概述

以下流程图展示了使用 Chrome DevTools 进行截图的基本步骤:

如何打开 DevTools

有下面几种方法可以打开 Chrome DevTools:(任选一种即可)

  1. Mac: Command + Option + I Windows: F12 或 Ctrl + Shift + I
  2. 在浏览器窗口中右击并选择"检查"
  3. Chrome 菜单 > 更多工具 > 开发者工具

截图类型和方法

1. 全页面截图

  • Mac: Command + Shift + P Windows: Ctrl + Shift + P
  • 输入"Capture full size screenshot"并按 Enter

2. 可视区域截图

  • 打开上述命令菜单,选择"Capture screenshot"

3. 特定元素截图

  • 点击 DevTools 左上角的元素选择工具(光标图标)
  • 在页面上点击目标元素
  • 右击元素并选择"Node screenshot"

4. 响应式设计测试

  • 点击 DevTools 顶部的设备工具栏图标(或 Mac: Command + Shift + M / Windows: Ctrl + Shift + M
  • 选择设备类型或屏幕尺寸
  • 使用上述方法进行截图

键盘快捷键(Mac / Windows)

  • 打开 DevTools: Command + Option + I / F12Ctrl + Shift + I
  • 打开命令菜单: Command + Shift + P / Ctrl + Shift + P
  • 切换设备模式: Command + Shift + M / Ctrl + Shift + M

总结

  • Mac 和 Windows 用户都能轻松使用, 无需额外软件
  • 不仅适用于 Web 开发者,对普通用户也很有用

· 阅读需 1 分钟
wen

电源按钮图标的由来

  • 以前的电源开关

img

图标中的 1 和 0 分别代表了开和关,这个图标的由来是因为二进制的 1 和 0 分别代表了开和关。

  • 现在的电源开关

img

两者一比较,就会发现,现在的电源按钮图标是一个圆形(0),中间有一个竖线(1), 也就是代表了开和关。

NOTE

图片是用 ChatGPT 生成的。

· 阅读需 2 分钟
wen

货币供应量有 3 个定义,分别是 M0、M1 及 M2,但部分地区会定义为 M1、M2 及 M3。

  • M0 = 流通中的现金
  • M1 = M0 + 商业银行活期存款,称为狭义货币供应量,又称狭义货币。
  • M2 = M1 + 商业银行定期存款,称为广义货币供应量,又称广义货币。

· 阅读需 4 分钟
wen

1. Slice 是什么?

Slice 是 Go 语言中的一种类似数组的数据结构,是对数组的一个封装。

和数组相比,slice 的长度是可以动态变化的,可以通过内置函数 append 来动态增加切片的长度。

img

2. Slice 的注意事项和常见错误以及陷阱

2.1. 未初始化的切片

未初始化的切片是 nil,对其进行操作会导致运行时错误。

package main

import "fmt"

func main() {
var fruits []string
// fruits[0] = "🍎" // 这会导致运行时错误:panic: runtime error: index out of range [0] with length 0

// 正确做法1:初始化切片
fruits := make([]string, 1)
fruits[0] = "🍎"

// 正确做法2:使用字面量初始化切片
fruits := []string{"🍎"}

// 正确做法3:使用 append 函数初始化切片。(如果不是特别注重性能,这种方式是最简单的。)
fruits = append(fruits, "🍎")
}

2.2. 使用 append 函数时,注意重新赋值

append 函数会在容量不足时重新分配一个更大的底层数组,并将原来的数据复制到新的数组中,然后返回一个新的切片,

所以注意在使用 append 函数时,如果需要使用原来的切片变量,就需要重新赋值。

    fruits := []string{"🍎"}
fruits = append(fruits, "🍌")

2.3. 切片作为函数参数时的引用问题

切片是引用类型,所以在函数参数中传递切片时,实际上是传递了切片的引用。

func modifyFruits(fruits []string) {
fruits[0] = "🍌"
}

func main() {
fruits := []string{"🍎", "🍌"}
fmt.Println("Before modification:", fruits) // 输出: Before modification: [🍎 🍌]

modifyFruits(fruits)
fmt.Println("After modification:", fruits) // 输出: After modification: [🍌 🍌]
}

2.4. 切片的截取

切片的截取操作是左闭右开的,即 a[1:3] 表示从下标 1 开始到下标 3 结束,但不包括下标 3。

    fruits := []string{"🍎", "🍌", "🍇", "🍉"}
fmt.Println(fruits[1:3]) // 输出: [🍌 🍇]

2.5. 切片的长度和容量混淆

切片的长度是指切片中元素的个数,容量是指切片底层数组的长度。

    fruits := make([]string, 2, 5)
fmt.Println("Length:", len(fruits), "Capacity:", cap(fruits)) // 输出: Length: 2 Capacity: 5

// fruits[3] = "🍇" // 这会导致运行时错误:panic: runtime error: index out of range [3] with length 2

// 正确做法:使用 append 来添加元素
fruits = append(fruits, "🍇")

2.6. 切片的复制

切片的复制是浅拷贝,即复制的是切片的引用,而不是切片的底层数组。

这个和函数参数传递的引用是类似的。

    fruits := []string{"🍎", "🍌"}
fruitsCopy := fruits
fruitsCopy[0] = "🍌"
fmt.Println("Original slice:", fruits) // 输出: Original slice: [🍌 🍌]

· 阅读需 3 分钟
wen

在 Go (Golang) 编程中,map 是一种强大且灵活的数据结构,用于存储键值对。

然而,为了确保高效和正确的使用,有几个重要的点需要注意。

本文将详细介绍这些关键考虑因素,并通过示例代码展示如何在 Go 中有效地使用 map。

1. 初始化

在使用 map 之前,必须对其进行初始化。可以使用 make 函数或使用 map 字面量来初始化。

// 使用 make
m := make(map[string]int)

// 使用 map 字面量
m := map[string]int{"🍎": 1, "🍌": 2}

2. Nil Map

nil map空 map 不同。

nil map 不能写入,尝试写入会导致运行时恐慌(panic)。

因此,始终要初始化你的 map。

var m map[string]int // m 是 nil
m["🍎"] = 1 // 这会导致恐慌

3. 从 Map 中读取

从 map 中读取时,如果键不存在,会返回值类型的零值。为了区分缺失的键和实际的零值, 可以使用多返回值的方式。

value, ok := m["🍊"]
if ok {
fmt.Println("找到了:", value)
} else {
fmt.Println("未找到")
}

4. 从 Map 中删除

使用 delete 函数从 map 中移除键值对。在键不存在的情况下调用 delete 是安全的。

delete(m, "🍎")

5. 并发访问

Map 不是并发安全的。如果多个 goroutine 同时访问一个 map,并且至少有一个修改了 map,你必须使用互斥锁或通道来同步访问。

var mu sync.Mutex

mu.Lock()
m["🍎"] = 1
mu.Unlock()

6. 迭代顺序

map 的迭代顺序不能保证在程序的不同运行中保持一致。如果你需要稳定的迭代顺序,必须显式地对键进行排序。

keys := make([]string, 0, len(m))
for k := range m {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
fmt.Println(k, m[k])
}

7. 作为 set 使用

Golang 中没有 set 类型,但可以使用 map 来模拟 set 。只需将值类型设置为 struct{} 即可。

struct{} 是一个空结构,不占用任何内存空间。这样可以节省内存,因为 map 的值是空结构,而不是实际的值。

s := make(map[string]struct{})
s["🍎"] = struct{}{}

// 检查键是否存在
_, ok := s["🍎"]

如果觉得麻烦,可以直接使用第三方库 golang-set

· 阅读需 1 分钟
wen

需求

把花纹填充到图片中可以让您的图片更具艺术感和创意。

在这篇文章中,我们将介绍如何使用 ChatGPT 来为图片添加花纹和纹理。

比如把下面的花纹填充到扇子图片中:

img

步骤

1. 打开 ChatGPT, 在输入框中上传图片。并输入 @DALL・E (一个 ChatGPT 的用于生成图片的插件)选中后,输入以下文本:

把图 1 的花纹图片填入图 2 中的扇子中。

img

2. 点击“生成”按钮,等待 ChatGPT 生成结果。

img

补充

如果不选择 @DALL・E 直接使用 ChatGPT 也可以,但效果好像不如 @DALL・E

下面是不选择 @DALL・E 的效果:

img

· 阅读需 2 分钟
wen

背景

使用 ChatGPT 生成带有中文的图片的图片时,中文会出现乱码的问题。本教程将介绍如何解决这个问题。

比如,我让 ChatGPT 生成一个带有中文的图片,输入如下:

请生成一张猫的照片,并在照片上加上中文字 “猫咪”

生成的图片如下:

img

步骤

1. 下载中文字体,比如有名的 Noto Serif SC

下载地址:Noto Serif SC

如果你想显示日语韩语等其他语言,可以下载 Noto Serif CJK 字体。

下载解压后会得到以下几个像 NotoSerifSC-xxx.otf 这样的字体文件。

xxx 部分表示的是字体的粗细,可以根据自己的需要选择。

这次我们用的是 NotoSerifSC-Regular.otf

2. 打开 ChatGPT,上传字体文件,输入你的 prompt

请生成一张猫的图片
在图片上加入“我的猫咪” 字样
请使用附件中的字体。

img

点击 3️⃣ 下载图片。

3. 生成图片

img

4. 扩展

上面生成的图片,没有指定文字的位置和颜色。现在默认好像是用黑色并显示在图片的底部中间。

如果你想指定文字的位置和颜色,可以在 prompt 中加入更多的信息。

比如:

请生成一张小猫的图片,
在图片上的左上角加入“我的猫咪” 文字,文字颜色用白色
文字请使用附件中的字体

生成的图片就会是这样:

img