SRP:“它说你的类或方法应该只做一件事”
我什么时候知道我的方法正在做不止一件事?
例子:
我有一个类
Bus
,其中有一个 List<Passenger>
和一个枚举 BusState
。 Bus
的状态取决于List<Passenger>
的大小。
public void addPassenger(Passenger p){
this.passengerList.add(p);
if (passengerList.size < 10)
this.state = BusState.EMPTY;
else if (passengerList.size < 30)
this.state = BusState.HALF_FULL;
else if (passengerList.size >= 30)
this.state = BusState.FULL;
}
即使我重构这个:
public void addPassenger(Passenger p){
this.passengerList.add(p);
changeBusState();
}
private void changeBusState(){
if (passengerList.size < 10)
this.state = BusState.EMPTY;
else if (passengerList.size < 30)
this.state = BusState.HALF_FULL;
else if (passengerList.size >= 30)
this.state = BusState.FULL;
}
在我看来,方法
addPassenger()
不仅仅做一件事:如何理解SRP?这个方法不止做一件事吗?
我同意
addPassenger
正在做不止一件事。
让它只做一件事的一种方法是删除
state
字段并使用 getState
方法,根据乘客数量返回状态(假设您正在用 Java 编写并且 BusState
是一个枚举) ):
public BusState getState() {
if (passengerList.size < 10)
return BusState.EMPTY;
else if (passengerList.size < 30)
return BusState.HALF_FULL;
else if (passengerList.size >= 30)
return BusState.FULL;
else
return BusState.UNKNOWN; // somehow the no. of passengers is negative? You can consider throwing an exception here as well...
}
罗伯特·马丁将“一件事”解释为“改变的一个商业理由”。对于所有代码库来说,没有通用的“一”定义,因为我们创建的 API 在不同的抽象级别上工作。因此,这完全取决于谁是您班级的客户,以及他们可能需要进行哪些更改。
在您的情况下,可以说该方法正在做两件事:它管理总线的内容并计算状态。因此,改变可能有两个原因:
添加乘客的不同业务逻辑:例如,人们可能希望验证公交车上是否有足够的空间容纳另一名乘客,如果没有则抛出异常
关于
BusState
语义的不同业务逻辑(最简单的例子:人们可能希望满载巴士从 31 名乘客开始,而不是 30 名)在这种情况下,您可以更改
addPassenger
以仅专注于添加:
public void addPassenger(Passenger p){
this.passengerList.add(p);
}
并更改
BusState
getter 以按需执行计算(类似于 Sweeper 在 他们的答案中提出的建议):
public BusState getBusState() {
if (passengerList.size < 10)
return BusState.EMPTY;
else if (passengerList.size < 30)
return BusState.HALF_FULL;
else if (passengerList.size >= 30)
return BusState.FULL;
else
throw ...
}
之前的答案很有用,但我会为以后发现这个的人添加更多内容。
解决问题的正确方法正是提问者所做的。我要做的唯一一件事就是将函数名称从 addPassenger 更改为 addPassengerToBus (或 boardBus)。
在巴士上添加一名乘客应该会改变巴士的饱满度。您永远不会希望在不计算总线充满度变化的情况下将某人添加到总线中。从“商业”角度(公交车业务)来看,这是一回事。
不是一回事,而是精确的数值临界点,应该通过它自己的函数计算并在添加乘客时调用。
如果将人员添加到公交车上确实与计算公交车的满员率无关,那么要做的就是拆分功能并让调用 addPassenger 的方法现在直接调用这两个方法。