gpt4 book ai didi

oop - 迭代父类(super class)型集合时的类型转换。备择方案?

转载 作者:行者123 更新时间:2023-12-04 21:58:27 24 4
gpt4 key购买 nike

这是我遇到的一个很常见的问题。让我们听听您的解决方案。我将以员工管理应用程序为例:-

我们有一些实体类,其中一些实现了特定的接口(interface)。

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(IRecievesBonus bonusReciever in employees.OfType<IRecievesBonus>())
{
PayBonus(bonusReciever);
}
  • 我们不能修改 IEmployee 接口(interface)以包含子类型的详细信息,因为我们不想用只有子类型关心的细节污染父类(super class)型。
  • 我们没有仅包含子类型的现有集合。
  • 我们不能使用 访客模式因为元素类型不稳定。此外,我们可能有一个同时实现 IRecievesBonus 和 IDrinksTea 的类型。它的 Accept 方法将包含对 visitor.Visit(this) 的模棱两可的调用。

  • 我们经常被迫走这条路,因为我们不能修改父类(super class)型,也不能修改集合,例如在 .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);
    }
    }
    }

    在这个例子中,将这些特征从 Employee 类型中剔除是绝对值得的——特别是因为一些经理可能会喝茶,而另一些可能不会——但我们仍然存在类型转换的相同潜在问题。

    只要我们在正确的水平上做,它是“好的”吗?还是我们只是在转移问题?

    chalice 将是访客模式的变体,其中:-
  • 您可以在不修改所有访问者的情况下添加元素成员
  • 访问者应该只访问他们有兴趣访问的类型
  • 访问者可以根据接口(interface)类型访问成员(member)
  • 元素可能实现不同访问者访问的多个接口(interface)
  • 不涉及转换或反射

  • 但我很欣赏这可能是不现实的。

    最佳答案

    我肯定会尝试通过组合而不是继承来解决这个问题,方法是将所需的属性/特征与 Employee 相关联,而不是对其进行子类化。
    我可以部分用 Java 举个例子,我认为它与您的语言(C#)足够接近,很有用。

    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);
    }
    }
    这个解决方案比子类化要灵活得多:
  • 它可以轻松处理员工属性的任何组合,而通过子类化,随着属性数量的增长,您会体验到子类的组合爆炸式增长,
  • 它允许您轻松更改 Employee 属性运行时,而使用子类化这将需要更改对象的具体类!

  • 在 Java 中,枚举本身可以具有属性或(甚至是虚拟)方法——我不知道这在 C# 中是否可行,但在最坏的情况下,如果您需要更复杂的属性,您可以使用类层次结构来实现它们。 (即使在这种情况下,您也不会回到原点,因为您有一个额外的间接级别,这为您提供了上述灵 active 。)
    更新
    你是对的,在最一般的情况下(在上面的最后一句中讨论过)类型转换问题没有解决,只是在对象图上向下推了一层。
    一般来说,我不知道这个问题的真正令人满意的解决方案。处理它的典型方法是使用多态性:拉起公共(public)接口(interface)并通过它操作对象,从而消除了向下转换的需要。但是,如果所讨论的对象没有通用接口(interface),该怎么办?意识到在这些情况下设计并不能很好地反射(reflect)现实可能会有所帮助:实际上,我们创建了一个标记接口(interface)只是为了使我们能够将一堆不同的对象放入一个公共(public)集合中,但对象之间没有语义关系。
    所以我相信在这些情况下,向下转换的尴尬是一个信号,表明我们的设计可能存在更深层次的问题。

    关于oop - 迭代父类(super class)型集合时的类型转换。备择方案?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4022904/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com