我有一个名为UpdateUserDevices (UserModel)
的方法。 UserModel
包含List<DeviceModel>
,它将设备列表与特定用户相关联。 (1对多)。
当我调用该方法时,一切都按照要求进行操作,但是对于嵌套循环和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 );
}
}
}
答案 0 :(得分:1)
可能我不明白究竟发生了什么,但在我看来,你可以通过删除所有最外面的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可以分成三个独立的,没有嵌套的ifs。
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 );
}
答案 1 :(得分:0)
继续状态是导致高圈复杂度的原因
替换
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 );
}
然后你也可以删除继续,这有点代码味道。
使用else - 如果可能的组合较少(路径较少)通过您的方法,至少从圈复杂度分析器sw的视图
更简单的例子:
if (debugLevel.equals("1") {
debugDetail = 1;
}
if (debugLevel.equals("2") {
debugDetail = 2;
}
此代码有4个路径,如果将复杂度乘以2,则每个路径都增加一个。 从人类智能来看,这个例子看起来很简单。
对于复杂性分析器,这更好:
if (debugLevel.equals("1") {
debugDetail = 1;
} else if (debugLevel.equals("2") {
debugDetail = 2;
}
现在只有2条可能的路径! 你使用人类智能看到,这两个条件是相互排斥的,但是 复杂性分析器只能在第二个例子中看到使用else-if子句。
答案 2 :(得分:0)
我会做什么涉及方法的细分。你正在尝试做一些事情,所以我们可以将它们分开。将循环条件移动到变量(可读性)。检查边缘情况,首先是空的。将删除移动到自己的方法(可读性/可维护性)。将main(复杂逻辑)移动到其自己的方法(可读性/可维护性)。 UpdateUserDevices现在看起来很干净。
主要方法:
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;
}
Merge Repos 方法:原始方法的肉和土豆现在有自己的方法。发生在这里的事情。已移除workingDevice
以添加devicesToUpdate
(直接与间接逻辑=&gt;取联合然后对所有元素执行包含与执行交叉一次相同)。现在我们可以有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,但这既不在这里也不在那里)