跳转到内容

移除设值函数

移除设值函数(Remove Setting Method)去掉一个字段的 setter,让它只能在构造时赋值一次。 它解决的痛是:一个本该创建后就不变的字段(比如 ID)留着修改的口子,读代码的人不得不提防它在某处被改掉。

人员档案里的 Person 有一个 id。现在的写法是先创建一个空对象,再分别设名字和 ID。ID 是这个人在系统里的身份,创建之后改它,就等于把档案挂到了另一个人名下。

class Person:
def __init__(self) -> None:
self.name = ""
self._id = ""
@property
def id(self) -> str:
return self._id
@id.setter
def id(self, value: str) -> None:
self._id = value
martin = Person()
martin.name, martin.id = "马丁", "1234"
print(martin.name, martin.id)
# 输出:
# 马丁 1234
export class Person {
name = "";
private _id = "";
get id(): string {
return this._id;
}
set id(value: string) {
this._id = value;
}
}
const martin = new Person();
martin.name = "马丁";
martin.id = "1234";
console.log(martin.name, martin.id);
// 输出:
// 马丁 1234
class Person:
def __init__(self, id: str, name: str) -> None:
self.name = name
self._id = id
@property
def id(self) -> str:
return self._id
martin = Person("1234", "马丁")
print(martin.name, martin.id)
# 输出:
# 马丁 1234
export class Person {
constructor(
readonly id: string,
public name: string,
) {}
}
const martin = new Person("1234", "马丁");
console.log(martin.name, martin.id);
// 输出:
// 马丁 1234

ID 挪进了构造函数,setter 删掉了。Python 里只读属性被赋值会在运行时抛 AttributeError;TS 的 readonly 让 martin.id = "5678" 在编译时就报错。名字还能改,因为人确实会改名。现在读代码的人一看类的定义就知道:ID 一旦创建就不会变,不必去搜整个项目里有没有人改它。

移除设值函数:如果某个字段不应在对象创建后被修改,就去掉它的设值函数,改为在构造函数里赋值。

适合身份类字段(ID、订单号、创建时间)以及值对象的所有字段,第 33 章的值对象正是靠没有设值函数才能被安全共享。另一个常见场合是“先 new 一个空对象、再逐个 set”的创建方式,这种对象在设完之前处于半成品状态,把必填字段挪进构造函数,半成品就不会存在。

字段确实需要在生命周期里改变时,别删。订单状态、库存数量、用户昵称都会变,强行做成只读,只会逼着调用方“复制一个新对象再替换旧的”,代码更绕。有些框架也要求无参构造加 setter,比如部分序列化库和 ORM 需要先创建空对象再逐个填字段,这时可以保留 setter,但在命名或注释里写明只给框架用。