对象集合的接口隔离原则
Interface Segregation Principle on a collection of objects
我正在学习 SOLID 原则,接口隔离原则让我在这种情况下很难理解。
基本上,我有一组对象...用户...使用接口。
public interface IUserInfo
{
string Name { get; }
string Extension { get; }
}
问题是在某些情况下,继承此接口的 class 不会使用扩展名。所以为了解决这个问题,它只是 returns 一个空字符串。
现在,从技术上讲这没问题,因为该字符串已绑定到 UI。所以它只会为该字段显示一个空字符串。
但是,这违反了接口隔离原则。建议拆分这些接口。
但后来我 运行 遇到了我想要在集合中使用此接口的问题,考虑一下:
public interface IUserExtension : IUserInfo
{
string Extension { get; }
}
public ObservableCollection<IUserInfo> StoredUserInfos { get; set; } = new ObservableCollection<IUserInfo>()
{
new User1(),
new User2(),
};
public class User1 : IUserExtension
{
public string Name { get; } = "Alex";
public string Extension { get; } = "(715) 925";
public override string ToString()
{
return Name;
}
}
public class User2 : IUserInfo
{
public string Name { get; } = "Daniel";
public override string ToString()
{
return Name;
}
}
我无法绑定到此集合的索引并获取扩展,因为它是一个 IUserInfo 接口。巧合的是,我也不能将其作为 IUserExtension 的集合,因为 User2 class 没有实现它。
据我所知,唯一可行的方法是使用模式匹配
if (UserIndex != -1)
{
var userInfo = StoredUserInfos[UserIndex];
Extension = userInfo is IUserExtension info ? info.Extension : "No Extensions";
Name = userInfo.Name;
}
但这会增加代码的复杂性,并且可能会违反其他规则,因为它正在检查类型。
在这种情况下,最好的解决方案是实现一个实现接口的抽象 class 并且默认实现是一个空字符串吗?
这是一个困难的问题,但我会伸出我的脖子并尝试回答。
在IUserInfo 接口中包含Extension 是否违反了ISP?我认为是和否。
假设您想在 IUserInfo 界面中包含一个 MiddleName 属性。中间名不是每个人都有的,但您可能不想将其添加到单独的界面中。与名称 属性 一起,它们是一个有凝聚力的单元,即它们属于一起。
在这种情况下,您可能希望 return 一个空字符串(如上所示)或类似内容。 (对于具有行为的更复杂的对象,Null Object 模式是更好的解决方案。)
因此,如果我们 return 解决您上面的问题,您是否将 Extension 视为属于接口本身的东西?可能不会。同样,让我们想象一下将 EmailAddress 添加到 IUserInfo 接口。与 Extension 一起,它们构成了一个有凝聚力的单元,并在概念上表示联系方式。如果你要将它们提取到一个单独的界面中,你仍然会有一些没有扩展名的用户,在这种情况下你会想要 return 一个空字符串(或类似的)。
因此,在回答您的问题时,如果您不太可能使用更多属性扩展接口,我认为您可以将 Extension 保留在 IUserInfo 接口中。否则,创建一个表示联系人详细信息概念的界面,但如果用户没有分机,则仍然 return 是一个空字符串。
我正在学习 SOLID 原则,接口隔离原则让我在这种情况下很难理解。
基本上,我有一组对象...用户...使用接口。
public interface IUserInfo
{
string Name { get; }
string Extension { get; }
}
问题是在某些情况下,继承此接口的 class 不会使用扩展名。所以为了解决这个问题,它只是 returns 一个空字符串。
现在,从技术上讲这没问题,因为该字符串已绑定到 UI。所以它只会为该字段显示一个空字符串。
但是,这违反了接口隔离原则。建议拆分这些接口。
但后来我 运行 遇到了我想要在集合中使用此接口的问题,考虑一下:
public interface IUserExtension : IUserInfo
{
string Extension { get; }
}
public ObservableCollection<IUserInfo> StoredUserInfos { get; set; } = new ObservableCollection<IUserInfo>()
{
new User1(),
new User2(),
};
public class User1 : IUserExtension
{
public string Name { get; } = "Alex";
public string Extension { get; } = "(715) 925";
public override string ToString()
{
return Name;
}
}
public class User2 : IUserInfo
{
public string Name { get; } = "Daniel";
public override string ToString()
{
return Name;
}
}
我无法绑定到此集合的索引并获取扩展,因为它是一个 IUserInfo 接口。巧合的是,我也不能将其作为 IUserExtension 的集合,因为 User2 class 没有实现它。
据我所知,唯一可行的方法是使用模式匹配
if (UserIndex != -1)
{
var userInfo = StoredUserInfos[UserIndex];
Extension = userInfo is IUserExtension info ? info.Extension : "No Extensions";
Name = userInfo.Name;
}
但这会增加代码的复杂性,并且可能会违反其他规则,因为它正在检查类型。
在这种情况下,最好的解决方案是实现一个实现接口的抽象 class 并且默认实现是一个空字符串吗?
这是一个困难的问题,但我会伸出我的脖子并尝试回答。
在IUserInfo 接口中包含Extension 是否违反了ISP?我认为是和否。
假设您想在 IUserInfo 界面中包含一个 MiddleName 属性。中间名不是每个人都有的,但您可能不想将其添加到单独的界面中。与名称 属性 一起,它们是一个有凝聚力的单元,即它们属于一起。
在这种情况下,您可能希望 return 一个空字符串(如上所示)或类似内容。 (对于具有行为的更复杂的对象,Null Object 模式是更好的解决方案。)
因此,如果我们 return 解决您上面的问题,您是否将 Extension 视为属于接口本身的东西?可能不会。同样,让我们想象一下将 EmailAddress 添加到 IUserInfo 接口。与 Extension 一起,它们构成了一个有凝聚力的单元,并在概念上表示联系方式。如果你要将它们提取到一个单独的界面中,你仍然会有一些没有扩展名的用户,在这种情况下你会想要 return 一个空字符串(或类似的)。
因此,在回答您的问题时,如果您不太可能使用更多属性扩展接口,我认为您可以将 Extension 保留在 IUserInfo 接口中。否则,创建一个表示联系人详细信息概念的界面,但如果用户没有分机,则仍然 return 是一个空字符串。