Oop 迭代超类型集合时的类型转换。选择?
这是我遇到的一个很常见的问题。让我们听听你的解决方案。我将以员工管理应用程序为例:- 我们有一些实体类,其中一些实现了特定的接口Oop 迭代超类型集合时的类型转换。选择?,oop,language-agnostic,Oop,Language Agnostic,这是我遇到的一个很常见的问题。让我们听听你的解决方案。我将以员工管理应用程序为例:- 我们有一些实体类,其中一些实现了特定的接口 public interface IEmployee { ... } public interface IRecievesBonus { int Amount { get; } } public class Manager : IEmployee, IRecievesBonus { ... } public class Grunt : IEmployee /* Thi
public interface IEmployee { ... }
public interface IRecievesBonus { int Amount { get; } }
public class Manager : IEmployee, IRecievesBonus { ... }
public class Grunt : IEmployee /* This company sucks! */ { ... }
我们有一个员工集合,我们可以对其进行迭代。我们需要获取实现IRecievesBonus的所有对象并支付奖金
朴素的实现大致如下:-
foreach(Employee employee in employees)
{
IRecievesBonus bonusReciever = employee as IRecievesBonus;
if(bonusReciever != null)
{
PayBonus(bonusReciever);
}
}
或者交替使用C#:-
foreach(employees.OfType()中的IRecievesBonus bonusreceiver)
{
工资奖金(BonusReceiver);
}
- 我们不能修改IEEmployee接口以包含子类型的详细信息,因为我们不想用只有子类型关心的详细信息污染超级类型
- 我们没有仅包含子类型的现有集合
- 我们不能使用访问者模式,因为元素类型不稳定。此外,我们可能有一个同时实现IRecievesBonus和IDrinksTea的类型。它的Accept方法将包含对visitor.Visit(this)的不明确调用
public interface IEmployee
{
public IList<IEmployeeProperty> Properties { get; }
}
public interface IEmployeeProperty { ... }
public class DrinksTeaProperty : IEmployeeProperty
{
int Sugars { get; set; }
bool Milk { get; set; }
}
foreach (IEmployee employee in employees)
{
foreach (IEmployeeProperty property in employee.Propeties)
{
// Handle duplicate properties if you need to.
// Since this is just an example, we'll just
// let the greedy ones have two cups of tea.
DrinksTeaProperty tea = property as DrinksTeaProperty;
if (tea != null)
{
MakeTea(tea.Sugers, tea.Milk);
}
}
}
公共接口IEEmployee
{
公共IList属性{get;}
}
公共接口IEmployeProperty{…}
公共类DrinksTeaProperty:IEEmployeeProperty
{
int Sugars{get;set;}
bool Milk{get;set;}
}
foreach(雇员中的雇员)
{
foreach(employee.properties中的IEEmployeeProperty属性)
{
//如果需要,请处理重复的属性。
//因为这只是一个例子,我们将
//让贪婪的人喝两杯茶。
DrinksTeaProperty tea=属性作为DrinksTeaProperty;
if(tea!=null)
{
泡茶(茶、糖、茶、牛奶);
}
}
}
在这个例子中,将这些特质从员工类型中剔除无疑是值得的——特别是因为一些经理可能喝茶,而一些经理可能不喝茶——但我们仍然有相同的类型转换的潜在问题
是不是只要我们在正确的水平上做,就“好”了?还是我们只是在转移问题
圣杯将是访客模式的变体,其中:-
- 您可以添加元素成员,而无需修改所有访问者
- 访客只能访问他们感兴趣的类型
- 访问者可以根据接口类型访问成员
- 元素可能实现由不同访问者访问的多个接口
- 不涉及铸造或反射
但我明白这可能是不现实的。我肯定会尝试通过组合而不是继承来解决这个问题,将所需的属性/特征与员工关联起来,而不是将其子类化 我可以给出一个部分用Java编写的示例,我认为它与您的语言(C#)非常接近,非常有用
public enum EmployeeProperty {
RECEIVES_BONUS,
DRINKS_TEA,
...
}
public class Employee {
Set<EmployeeProperty> properties;
// methods to add/remove/query properties
...
}
此解决方案比子类化灵活得多:
- 它可以轻松处理员工属性的任何组合,而使用子类化,随着属性数量的增加,您将体验到子类的组合爆炸
- 它允许您在运行时更改Employee属性,而使用子类化则需要更改对象的具体类李>
因此,我相信在这些情况下,降级的尴尬意味着我们的设计可能存在更深层次的问题。您可以实现一个自定义迭代器,该迭代器只迭代IRecievesBonus类型。谢谢。因此,进行持续的讨论有点困难,因为评论有点狭窄。a) 为什么你认为这比类型开关好?b) 如果PayBonus需要的不仅仅是一个标志,例如,如果它需要奖金的数量(只有子类才会有)?-更新了问题。@Iain,同样,我想我在您键入时回答了您的问题-请参阅我的更新:-)接受此答案,因为它是评分最高的答案。将重新开始另一个问题:“我们的设计有一个更深层次的问题”。推测迭代器将不会
public enum EmployeeProperty {
RECEIVES_BONUS,
DRINKS_TEA,
...
}
public class Employee {
Set<EmployeeProperty> properties;
// methods to add/remove/query properties
...
}
foreach(Employee employee in employees) {
if (employee.getProperties().contains(EmployeeProperty.RECEIVES_BONUS)) {
PayBonus(employee);
}
}