2

我正在用 C# 设计一个简单的城市模拟,其中城市是一个类。这座城市有许多系统,例如 RoadSystem 和 TrafficSystem,它们也是类。City 类聚合了上述类的实例。

class City: ICity
{
    RoadSystem.IRoadSystem       m_roadSystem;
    TrafficSystem.ITrafficSystem m_trafficSystem;
}

示例“系统”代码:

class SimpleRoadSystem : RoadSystem.IRoadSystem
{
    public SimpleRoadSystem(ICity owner)
    {
    }           
}

系统以及城市本身都有接口,可以像往常一样允许替代实施。

我遇到的经典问题是在实例化系统时。由于系统归城市类所有,我可以将 ICity 实例传递给它们。系统类可以使用 ICity 接口从拥有的城市类中提取任何所需的数据。然而,这将 SimpleRoadSystem 与城市联系在一起。但实际上道路系统是一个通用的概念,它可以用于实现一个区域或一个城镇的道路,可能有不同的接口。基本上将 RoadSystem 的所有者绑定为仅是一个城市,感觉有限制。

所以另一个想法是在city和RoadSystem之间建立一个IRoadSystemOwner接口,由City实现。

class City: ICity, IRoadSystemOwner, ITrafficSystemOwner,......
{
    RoadSystem.IRoadSystem       m_roadSystem;
    TrafficSystem.ITrafficSystem m_trafficSystem;
}

class SimpleRoadSystem : RoadSystem.IRoadSystem
{
    public SimpleRoadSystem(IRoadSystemOwner owner)
    {
    }           
}

但这并不能扩展。城市可以有任意数量的系统。它实际上是一个在运行时加载的列表。因此,如果城市有它需要的 10 种不同的系统,则需要事先预测并实施 10 种不同的所有者界面!

基本上我想弄清楚如何让 RoadSystem 拥有的类拥有一个拥有已知所有者接口的所有者!但不要通过不可扩展的继承来实现这个想法。这应该允许拥有的类从其所有者那里提取所需的信息。它应该可以很好地扩展,因为一个城市可以拥有在运行时根据数据决定的任意数量的系统。

我认为动态转换或通用属性系统的方式似乎有效。RoadSystem 类可以尝试将所有者实例转换为 ICity、ITown 或 IRegion 并查看哪个成功。或者所有者有一个 getProperty(key) 函数,它返回一个通用对象引用,可以将其转换为具体类以获取实际数据,例如:

object obj = owner.getProperty("Map")
IMap map = obj as IMap;
if (map == null)
{                
    throw new System.ArgumentException("The map could not be retrieved from owner", "original");
}

除了使用动态转换之外,还有其他方法吗?

4

3 回答 3

1

正如我所见,“City 类聚合了上述类的实例”是这里问题的根源。然后你想要一个区域类来聚合相同的,然后你重新考虑系统,当两个城市应该有相同的子系统时。这将在以后导致许多复杂性问题。

当城市对子系统一无所知时,尝试以不同的方式重新考虑这一点,因此它们是独立的,并且有一个类 World/Execution 上下文(这是所有这些的容器,并且知道关于每个人的一切)。

我看不出您想如何使用它的任何线索,因此我不提供任何“真实”方法。

using System;
using System.Collections.Generic;

namespace ConsoleApp1
{
    public class World
    {
        public List<ICity> Cities { get; set; }
        public List<IRegion> Regions { get; set; }

        public List<IRoadSystem> RoadSystems { get; set; }
        public List<ITrafficSystem> TrafficSystems { get; set; }
        //any other systems you like

        public void AddRoadSystemToCity(IRoadSystem system, ICity city)
        {
        }

        public IRoadSystem GetRoadSystem(ICity city) //or any other rule we would like to use later
        {
            throw new NotImplementedException();
        }
    }

    public interface IRegion
    {
    }

    public interface ITrafficSystem
    {
    }

    public interface IRoadSystem
    {
    }

    public interface ICity
    {
    }
}
于 2018-08-11T06:58:32.783 回答
1

然而,这将 SimpleRoadSystem 与城市联系在一起。但实际上道路系统是一个通用的概念,它可以用于实现一个区域或一个城镇的道路,可能有不同的接口。

从您的解释看来,您似乎正在寻求某种神奇的解决方案,该解决方案可以让具体类与任何协作者类型一起工作,而无需为这些协作者类型制作通用抽象。

  1. 如果SimpleRoadSystem需要与它不知道的不同“所有者”实现协作,那么除了使用接口约束关系之外,我看不到任何其他方式。

  2. 如果您想IRoadSystem为各种不同的“所有者”接口实现,其中每个实现仅与此类“所有者”接口的特定子集相关联,那么public class SimpleCityRoadSystem implements IRoadSystem<ICity>.

  3. 您当前的设计假定双向关系,通常应该避免这种关系。您还没有解释为什么SimpleRoadSystem需要知道它的所有者,但是您不能重新设计系统以使“所有者”告诉IRoadSystem做什么并提供必要的数据而不是需要SimpleRoadSystem知道它的“所有者”吗?

  4. 如果系统组件之间的通信和关系非常复杂并且需要非常灵活,那么消息传递设计可能最适合,其中所有组件都使用消息总线发送和侦听消息。

    例如,aRandomTrafficSystem可以发送(TrafficJamStarted congestionLevel=high)可以由任何相关方处理的消息。

我们根本不了解您的问题域,这无济于事。而不是解释你当前的模型和它的缺陷,你应该专注于解释你试图解决的问题的细节。也许完全不同的模型会更合适(例如,类似于游戏模型的东西,带有世界、项目(路段、汽车等)、坐标等)。

于 2018-08-11T13:29:14.477 回答
0

也许您需要的是进一步发展您的领域并拥有一些可以更好地共享的通用概念。例如,不是IRoadSystemOwner真的感觉不像是域概念的 an ,而是尝试思考 aRoadSystem实际服务的内容,您可能会想出类似 anIAdministrativeDivision或其他更好地捕捉设计意图的东西。

这是接口隔离原则在领域驱动设计中的一个应用,关键是一个城市或一个地区应该是一个行政区划,一个道路系统与之合作,并让你谈论它应该是自然的。这些概念靠自己。

于 2018-08-11T13:43:41.567 回答