使用策略从超类创建子类

Using strategy to create subclasses from superclasses

我有一个 C#/OO/Design 相关问题。

我正在重新设计一个移植到 C# 的旧系统。我们正在重构系统,使其更简单、更面向对象。

在这个系统中,我们有各种与我们的业务领域相关的对象——文件夹、文档、用户、约会、任务等。所有这些对象都继承自一个名为 BaseObject 的基础对象,信不信由你:

public class BaseObject {
    public int Id { get; set; }
    public string Name { get; set; }
}

这个对象不是抽象的,因为我们实际上为一些简单的列表实例化了它。

我们也有自己的通用列表:

public class CustomList<T> : IENumerable<T> where T : BaseObject {
    //... several properties and methods

    }
}

我的问题是:我有许多继承自 BaseObject 的对象。例如,我可以创建一个 CustomList<User> 因为 User : BaseObject.

然而,我的许多用户控件 return CustomList<BaseObject>,仅仅是因为这是大多数基于列表的用户控件 CAN return:他们知道每个对象的名称,因为这就是他们的名字显示,他们使用它的 Id 作为键。

因此,我希望能够将 CustomList<BaseObject> 中的项目添加到任何 CustomList<T> 中。我希望能够添加对象,而不仅仅是构造一个新列表。

但是因为我不能只是从 superclass (BaseObject) 转换为 subclass (例如 User),所以我考虑在 [=17= 中实现以下方法]:

public void AddRangeOfBaseObjects(IEnumerable<BaseObject> items, Func<BaseObject, T> constructor)
    {
        foreach (var item in items)
        {
            var newObject = constructor(item);
            Add(newObject);
        }
    }

或者换句话说,如果 class 需要从 CustomList 创建一个 CustomList,它需要同时提供 CustomList 和一个方法,说明如何从 BaseObject 构造 T 的每个新实例,以及如何初始化 BaseObject 不知道的其他成员。

我认为这类似于策略模式。当然方法本身几乎没有什么代码,但是由于这个动作的普遍性,这样设计还是比较短的。

问题是 - 这是好的设计吗?是否有更好的模式来处理将对象的简化版本从 UI 移动到实际对象的情况?

基本上您已经实现了某种类似于 Linq 的 Select 函数的 map 函数。现在很多人更喜欢函数式风格而不是普通的老式 OOP。您的 AddRangeOfBaseObjects(IEnumerable<BaseObject> items, Func<BaseObject, T> constructor) 是您想要做的事情的简单明了的方法。

另一种使用 Linq 的方法可能是

public static CustomList<T> ToCustomList<T>(this IEnumerable<T> items)
    where T : BaseObject
{
    var customList = new CustomList<T>();
    foreach(var item in items)
        customList.Add(item);
}

然后像

一样使用它
IEnumerable<BaseObject> items = ...;
Func<BaseObject, T> constructor = ...;

// Usage would be
CustomList<T> customItems = 
    (from i in items
     select constructor).ToCustomList();

// or
CustomList<T> customItems = items.Select(constructor).ToCustomList();

不创建新列表

public void AddRangeOfBaseObjects(IEnumerable<BaseObject> items, Func<BaseObject, T> constructor)
{
    // AddRange from List<T>
    AddRange(items.Select(constructor);
}