【问题标题】:Iterating over subclasses with different properties迭代具有不同属性的子类
【发布时间】:2018-12-31 08:57:50
【问题描述】:

我对以下场景的设计模式或最佳实践方法感兴趣:

假设我们有多个从公共接口派生的子类:

public interface IPerson{
    string Name;
}

public class SomePerson : IPerson{
    string Name;
    bool hasAccess;

    bool GetAccessInfo(){
    //implementation
    }
}


public class OtherPerson : IPerson{
    string Name;
    int NumberOfPoints;

    int GetNumberOfPoints(){
    //implementation
    }
}

现在假设我们有一个包含 IPerson 对象集合的类:

public class PersonHandler{
     public List<IPerson> People;
}

所以这里的问题是,您将如何迭代 People 集合,并且仍然能够在一次迭代中使用各个子类的成员和方法,例如 GetNumberOfPoints 或 GetAccessInfo?

我不喜欢显式检查类型,也不喜欢在接口中包含子类特定的方法,因为这有点违背 OO 设计的目的。

也许理想情况下这些不应该在同一个集合中?我正在寻找与语言无关的答案。

【问题讨论】:

  • 我认为这与如何迭代无关,这是一个单独的问题,但问题是如何调用仅在具有基点的派生类中的方法?
  • @zar 您可以进行强制转换/类型检查以调用子类函数/成员。但是,我认为这通常不是好的做法。
  • 是的,它不是。我记得我在某个地方遇到了 BBC 编码标准,这是被禁止的。 OOP 的想法是让类做那件事,它知道类型,但我知道我们确实想要检索结果或属性,但问题仍然存在。
  • 我之前已经考虑过这个问题,我认为解决方案可能是应用访问者模式,但该模式本身是其中最复杂的一种,不确定它的使用频率。

标签: oop inheritance design-patterns encapsulation


【解决方案1】:

我同意将特定方法添加到接口是一个坏主意(更不用说违反Interface Segregation Principle)。

模式匹配是函数式编程中的一种常见特性/实践,近年来已进入更多面向对象的语言中(在 C# 7 中使用 is 运算符以及其他构造(如 switch)引入语句,如下所示),它指的是基于给定对象的形状有条件地分支并绑定到其成员,而不必显式且不安全地强制转换对象。

我知道您正在寻找与语言无关的答案,但您的上述问题在 C# 中可能如下所示:

public interface IPerson
{
    string Name { get; set; }
}

public class SomePerson : IPerson
{
    private readonly bool _hasAccess;

    public SomePerson(string name, bool hasAccess)
    {
        Name = name;
        _hasAccess = hasAccess;
    }

    public string Name { get; set; }

    public bool GetAccessInfo() => _hasAccess;
}

public class AnotherPerson : IPerson
{
    private readonly int _points;

    public AnotherPerson(string name, int points)
    {
        Name = name;
        _points = points;
    }

    public string Name { get; set; }

    public int GetNumberOfPoints() => _points;
}

class Program
{
    static void Main(string[] args)
    {
        var people = new List<IPerson>
        {
            new SomePerson("Angie", true),
            new AnotherPerson("Frank", 3),
            new SomePerson("Bill", false),
            new AnotherPerson("Sally", 9)
        };

        foreach (var person in people)
        {
            switch (person)
            {
                case SomePerson p:
                    Console.WriteLine(p.GetAccessInfo()
                        ? $"{p.Name} has access."
                        : $"{p.Name} does not have access.");
                    break;
                case AnotherPerson p:
                    Console.WriteLine($"{p.Name} has {p.GetNumberOfPoints()} points.");
                    break;
            }
        }
    }
}

希望这会有所帮助!

【讨论】:

  • switch (person)? person 定义在哪里?
  • personforeach 循环变量,代表people 列表中的当前项。
  • 是的,这绝对是可行的。在 C# 中,您还可以使用 isas 关键字进行类型检查。
【解决方案2】:

我讨厌将帖子标记为关闭,因为每个问题都是独一无二的并且有变化,而且每个帖子都可能吸引不同的专家,但请查看 StackOverflow 上的 post,这非常相似,答案是访问者模式。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多