Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Oop 迭代超类型集合时的类型转换。选择?_Oop_Language Agnostic - Fatal编程技术网

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)的不明确调用
我们经常被迫这样做,因为我们不能修改超级类型,也不能修改集合,例如在.NET中,我们可能需要通过子控件集合找到此表单上的所有按钮。我们可能需要对子类型做一些事情,这取决于子类型的某些方面(例如,上面示例中的奖金金额)

让我感到奇怪的是,考虑到它出现的频率,没有一种“被接受”的方法来做到这一点

1) 类型转换值得避免吗

2) 有没有其他我没有想到的选择

编辑 péter Török建议组合Employee并将类型转换进一步推到对象树下:-

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属性,而使用子类化则需要更改对象的具体类
在Java中,枚举可以有属性或(甚至是虚拟)方法本身——我不知道这在C#中是否可行,但在最坏的情况下,如果需要更复杂的属性,可以用类层次结构实现它们。(即使在这种情况下,您也没有回到原点,因为您有一个额外的间接级别,它为您提供了上述的灵活性。)

更新 您是对的,在最一般的情况下(在上面的最后一句中讨论),类型转换问题没有得到解决,只是在对象图上向下推了一层

总的来说,我不知道这个问题的真正令人满意的解决方案。处理它的典型方法是使用多态性:调出公共接口并通过该接口操作对象,从而消除了降级的需要。但是,如果所讨论的对象没有公共接口,该怎么办?这可能有助于认识到,在这些情况下,设计不能很好地反映现实:实际上,我们创建了一个标记接口,仅仅是为了使我们能够将一组不同的对象放入一个公共集合中,但对象之间没有语义关系


因此,我相信在这些情况下,降级的尴尬意味着我们的设计可能存在更深层次的问题。

您可以实现一个自定义迭代器,该迭代器只迭代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);
  }
}