惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

GbyAI
GbyAI
Y
Y Combinator Blog
Recent Announcements
Recent Announcements
D
Docker
Blog — PlanetScale
Blog — PlanetScale
罗磊的独立博客
美团技术团队
V
V2EX
Last Week in AI
Last Week in AI
D
DataBreaches.Net
T
The Blog of Author Tim Ferriss
宝玉的分享
宝玉的分享
Microsoft Security Blog
Microsoft Security Blog
Microsoft Azure Blog
Microsoft Azure Blog
人人都是产品经理
人人都是产品经理
M
MIT News - Artificial intelligence
P
Proofpoint News Feed
B
Blog RSS Feed
博客园_首页
B
Blog
博客园 - 叶小钗
I
InfoQ
WordPress大学
WordPress大学
L
LangChain Blog
Apple Machine Learning Research
Apple Machine Learning Research
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
A
About on SuperTechFans
The GitHub Blog
The GitHub Blog
The Register - Security
The Register - Security
MyScale Blog
MyScale Blog
云风的 BLOG
云风的 BLOG
博客园 - 司徒正美
Latest news
Latest news
W
WeLiveSecurity
T
The Exploit Database - CXSecurity.com
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
aimingoo的专栏
aimingoo的专栏
小众软件
小众软件
Cyberwarzone
Cyberwarzone
Scott Helme
Scott Helme
D
Darknet – Hacking Tools, Hacker News & Cyber Security
C
CERT Recently Published Vulnerability Notes
C
CXSECURITY Database RSS Feed - CXSecurity.com
Recent Commits to openclaw:main
Recent Commits to openclaw:main
N
News and Events Feed by Topic
S
Secure Thoughts
The Hacker News
The Hacker News
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Google DeepMind News
Google DeepMind News

Bright LGM's Blog

LLM 时代的集成意图识别实践 每日一思 每日一思 每日一思 每日一思 每日一思 每日一思 每日一思 成长 平淡与不平淡 每日一思 每日一思 每日一思 每日一思 每日一思 每日一思 每日一思 每日一思 每日一思
外部性与程序设计
Bright LGM · 2024-11-23 · via Bright LGM's Blog

经济学里面有一个名为“外部性“的概念。外部性是指一个人或企业的行为对其他人或企业产生的影响。

外部性可以是正的,也可以是负的。比如,一个企业的生产活动可能会产生污染,这就是一种负的外部性,对周围的环境和居民造成了伤害。相反,一个企业的生产活动也可能带来正面的外部性,比如提高周围地区的就业机会或改善周围地区的基础设施。

外部性对于组织职责划分的启示

利用这个概念,我们可以用来分析组织的职责划分。

什么样的职责应该由下级组织负责?我们说,当某一件事没有产生外部性或产生了正的外部性,那么下级组织应该负责。此时如果上级组织非得介入,则反而会增加下级组织的沟通成本,降低效率。因为上级组织往往由于掌握不了足够的细节,而要求下级组织频繁的汇报信息。

但是,当某一件事产生了负的外部性,那么上级组织应该负责。此时,下级组织往往没有驱动力去解决这个问题,因为这会增加下级组织的成本,但是却不会增加下级组织的收益。

举个例子。比如,企业在生产过程中赚到了钱,顺便改善了周边的经济环境,创造了正的外部性,很高兴。但是对于产生污染这样的负外部性,企业就不太会在意,且没有驱动力去解决,因为这会增加企业的成本。这时,作为上级组织的政府就应该介入,协调企业和周围居民的矛盾,并负责督促企业处理污染,避免对周围环境和居民造成伤害。

外部性对于程序设计的启示

在了解了外部性及其应用之后,我发现它对程序设计也有很大的启发。

无外部性或负外部性与高内聚

其一是在类或模块的职责划分上。如果一个类或模块的行为不会对其他类或模块产生影响,那这个行为就应该让这个类或模块自己处理。我们常常说的内聚性就是这个道理。如果一个类可以基于自己管理的数据独立完成某个功能,那么这个功能就应该由这个类自己实现,而不是由调用它的类来插手。

当我们看到在某一个类的方法中直接修改另一个对象的内部状态时,就应该警惕,因为维护这个状态可能是另一个对象自己的职责。越俎代庖很可能破坏了另一个对象的内聚性,并增加了系统的耦合度。

而当一个类或者一个模块的行为对其他类或者模块产生了影响,就产生了外部性。

如果这种外部性是正的,那么我们可以说这个函数或者模块是“无害的”,无需处理,并应极力鼓励。比如,某一个类优化了内部的算法,使得整个系统的性能提高了,这就是一种正的外部性。我们应该经常鼓励这样的优化。

但是,如果这种外部性是负的,那么我们就需要特别警惕,并考虑如何处理这种负外部性。这样的负外部性常常隐藏较深难以发现。

负外部性与上层协调

举一个大家经常碰到的例子。在基于数据库的后端程序开发中,我们常常需要从数据库中读取数据构建领域对象。JPA 可以帮我们自动完成这个过程。但是,如果我们不注意,JPA 可能会产生一种负的外部性,即 N+1 问题。

N+1 问题是指,当我们从数据库中读取一个领域对象时,JPA 会自动为这个领域对象的每一个关联对象发送一个额外的查询。如果这个领域对象有 N 个关联对象,那么就会发送 N+1 个查询。这将导致性能问题。

JPA 默认在非所有者端默认使用一种叫做“惰性加载”的模式来处理关联对象。惰性加载是指,当我们从数据库中读取一个领域对象时,不会立即查询关联对象,而是等到我们真正需要使用关联对象时,再发送查询。大多时候,这种模式是有效的,因为我们可能并不需要使用所有的关联对象。但是,如果我们需要使用所有的关联对象,那么就会产生 N+1 问题。

这种负外部性很难在领域对象内部自己解决,因为它不知道调用者何时会访问到哪些关联对象。

为了解决这个问题,我们可以在对应的 repository 中显示地定义一个方法,用于一次性加载所有关联对象(或通过参数指定需要加载哪些关联对象)。这样,我们就可以在需要的时候,在调用方(上级组织)显式地加载所有关联对象。在实现时,我们可以使用 JPA 的 fetch join 特性,在查询领域对象时,同时查询关联对象。这样,就可以避免 N+1 问题。

下面是一个示例。

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
@Entity
public class Order {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private Date orderDate;

@OneToMany(mappedBy = "order", fetch = FetchType.LAZY)
private List<OrderItem> items;

}

@Entity
public class OrderItem {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String productName;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "order_id")
private Order order;

}

@Repository
public class OrderRepository {

@PersistenceContext
private EntityManager entityManager;

public Optional<Order> findOrderByIDWithItems(Long id) {
TypedQuery<Order> query = entityManager.createQuery(
"SELECT o FROM Order o LEFT JOIN FETCH o.items WHERE o.id = :id",
Order.class)
.setParameter("id", id);

List<Order> resultList = query.getResultList();
return resultList.isEmpty() ? Optional.empty() : Optional.of(resultList.get(0));
}

}

上述代码 findOrderByIDWithItems 被设计为供上层调用,其实现过程中:

  • SELECT o FROM Order o: 指定了我们要查询的主实体是 Order,并将其别名命名为 o
  • LEFT JOIN FETCH o.items: 这是 fetch join 的关键部分。这里我们执行了一次 左连接 (LEFT JOIN) 来包括所有订单。这意味着当你遍历结果列表中的每个订单并访问其 items 集合时,不会触发额外的数据库查询,因为所有必要的数据都已经在初始查询中被加载了。
    在发现 N+1 问题时,如果我们想在领域对象内部解决这个问题就很困难。此时应该改变思路,通过提供接口让上层组织中显式地调用,这个问题就迎刃而解了。

总结

外部性这一经济学概念在软件设计中也有鲜活的体现。通过识别和分析系统中的外部性,我们有以下启示:

  • 无外部性或正外部性:类或者模块的行为对其他部分没有影响(无外部性)或产生积极影响(正外部性),则其职责应尽量内聚,由自己处理,并鼓励正外部性优化。
  • 负外部性:行为对其他部分产生了负面影响,需要通过上层组织进行协调、解决。通过显式地提供清晰的接口,上层组织就可以灵活处理问题。