需要帮助降低圈复杂度
本文关键字:复杂度 帮助 | 更新日期: 2023-09-27 18:35:15
我有一个名为UpdateUserDevices (UserModel)
的方法。UserModel
包含一个List<DeviceModel>
,该将设备列表与特定用户相关联。(一对多)。
当我调用该方法时,一切都按 excpected 工作,但是嵌套循环和 if 语句非常复杂。
降低圈复杂度的好模式是什么?我想到了"CoR",但这可能有点矫枉过正。
private void UpdateUserDevices( UserModel model )
{
// Get users current devices from database
var currentDevicesFromDatabase = _deviceRepository.FindByUserId( model.Id ).ToList();
// if both the model devices and the datbase devices have records
// compare them and run creates, deletes, and updates
if( model.Devices.Any() && currentDevicesFromDatabase.Any() )
{
var devicesToAdd = model.Devices.Exclude( currentDevicesFromDatabase, d => d.Id ).ToList();
var devicesToDelete = currentDevicesFromDatabase.Exclude( model.Devices, d => d.Id ).ToList();
var workingDevices = model.Devices.Union( currentDevicesFromDatabase );
foreach( var device in workingDevices )
{
// Add devices
if( devicesToAdd.Contains( device ) )
{
_deviceRepository.Create( device );
continue;
}
// delete devices
if( devicesToDelete.Contains( device ) )
{
_deviceRepository.Delete( device );
continue;
}
// update the rest
_deviceRepository.Update( device );
}
return;
}
// model.Devices doesn't have any records in it.
// delete all records from the database
if( !model.Devices.Any() )
{
foreach( var device in currentDevicesFromDatabase )
{
_deviceRepository.Delete( device );
}
}
// database doesn't have any records in it
// create all new records
if( !currentDevicesFromDatabase.Any() )
{
foreach( var device in currentDevicesFromDatabase )
{
_deviceRepository.Create( device );
}
}
}
可能是我不明白到底发生了什么,但在我看来,您可以通过删除所有最外层的 if 语句并只执行最顶层的代码块来简化它。
private void UpdateUserDevices ( UserModel model )
{
// Get users current devices from database
var currentDevicesFromDatabase = _deviceRepository.FindByUserId( model.Id );
var devicesToAdd = model.Devices.Exclude( currentDevicesFromDatabase, d => d.Id ).ToList();
var devicesToDelete = currentDevicesFromDatabase.Exclude( model.Devices, d => d.Id ).ToList();
var workingDevices = model.Devices.Union( currentDevicesFromDatabase ).ToList();
foreach ( var device in workingDevices )
{
if ( devicesToAdd.Contains( device ) )
{
// Add devices
_deviceRepository.Create( device );
}
else if ( devicesToDelete.Contains( device ) )
{
// Delete devices
_deviceRepository.Delete( device );
}
else
{
// Update the rest
_deviceRepository.Update( device );
}
}
}
实际上,foreach 可以分成三个独立的,没有嵌套的 if。
private void UpdateUserDevices ( UserModel model )
{
var currentDevicesFromDatabase = _deviceRepository.FindByUserId( model.Id );
// Take the current model and remove all items from the database... This leaves us with only records that need to be added.
var devicesToAdd = model.Devices.Exclude( currentDevicesFromDatabase, d => d.Id ).ToList();
// Take the database and remove all items from the model... this leaves us with only records that need to be deleted
var devicesToDelete = currentDevicesFromDatabase.Exclude( model.Devices, d => d.Id ).ToList();
// Take the current model and remove all of the items that needed to be added... this leaves us with only updateable recoreds
var devicesToUpdate = model.Devices.Exclude(devicesToAdd, d => d.Id).ToList();
foreach ( var device in devicesToAdd )
_deviceRepository.Create( device );
foreach ( var device in devicesToDelete )
_deviceRepository.Delete( device );
foreach ( var device in devicesToUpdate )
_deviceRepository.Update( device );
}
继续状态是高圈复杂度的原因
取代
if( devicesToAdd.Contains( device ) )
{
_deviceRepository.Create( device );
continue;
}
// delete devices
if( devicesToDelete.Contains( device ) )
{
_deviceRepository.Delete( device );
continue;
}
与类似的东西
if( devicesToAdd.Contains( device ) ) {
_deviceRepository.Create( device );
} else if( devicesToDelete.Contains( device ) ) {
// delete devices
_deviceRepository.Delete( device );
}
然后你也可以删除继续,这有点代码气味。
否则,如果通过您的方法的组合可能较少(路径较少),至少从圈复杂性分析仪 sw 的角度来看是这样。
一个更简单的例子:
if (debugLevel.equals("1") {
debugDetail = 1;
}
if (debugLevel.equals("2") {
debugDetail = 2;
}
此代码有 4 条路径,每条路径都是 if 将复杂度乘以 2。从人类智能来看,这个例子看起来很简单。
对于复杂性分析器来说,这更好:
if (debugLevel.equals("1") {
debugDetail = 1;
} else if (debugLevel.equals("2") {
debugDetail = 2;
}
现在只有 2 条可能的路径!你,用人类的智慧看到,这两个条件是相互排斥的,但是复杂性分析器仅在第二个示例中使用 else-if 子句看到这一点。
我要做的是分解方法。您正在尝试做一些事情,以便我们可以将它们分开。将循环条件移动到变量中(可读性)。已检查边缘情况,要么为空,首先。将删除移动到其自己的方法(可读性/可维护性)。将主要(复杂逻辑)移至自己的方法(可读性/可维护性)。更新用户设备现在看起来很干净。
主要方法:
private void UpdateUserDevices( UserModel model )
{
// Get users current devices from database
var currentDevicesFromDatabase = _deviceRepository.FindByUserId( model.Id ).ToList();
$isUserDevicesEmpty = !model.Devices.Any();
$isRepositoryEmpty = !currentDevicesFromDatabase.Any();
if($isRepositoryEmpty || $isUserEmpty){
// Missing One
if($isRepositoryEmpty){
this.deleteEmpty(currentDevicesFromDatabase);
}
if($isUserDevicesEmpty){
this.deleteEmpty(model.Devices);
}
}
else{
this.mergeRepositories(currentDevicesFromDatabase, model);
}
return;
}
合并存储库方法:原始方法的肉和土豆现在有自己的方法。这里发生的事情。删除了要添加devicesToUpdate
workingDevice
(直接与间接逻辑 => 采取联合然后对所有元素执行包含与执行一次相交相同)。现在我们可以有 3 个单独的 foreach 循环来处理更改。(您避免为每个设备做contains
,也许是其他效率提升)。
private void mergeRepositories(UserModel model, List currentDevicesFromDatabase)
{
// if both the model devices and the datbase devices have records
// compare them and run creates, deletes, and updates
var devicesToAdd = model.Devices.Exclude( currentDevicesFromDatabase, d => d.Id ).ToList();
var devicesToDelete = currentDevicesFromDatabase.Exclude( model.Devices, d => d.Id ).ToList();
var devicesToUpdate = model.Devices.Intersect( currentDevicesFromDatabase, d => d.Id ).ToList();
foreach( device in devicesToAdd ){
_deviceRepository.Create( device );
}
foreach( device in devicesToDelete ){
_deviceRepository.Delete( device );
}
foreach( device in devicesToUpdate){
_deviceRepository.Update( device );
}
return;
}
删除空方法:此处看不到任何内容
private void deleteEmpty(List devices){
foreach( var device in devices )
{
_deviceRepository.Delete( device );
}
return
}
现在它很好,很简单。或者无论如何,我认为是这样。(我不得不做类似的事情来列出Ebay上的物品。我实际写的是一个略有不同的版本,没有在整个集合上使用 Intersect,但这既不在这里也不在那里)