一个项目中工厂应该住在哪里?
Where should factories live in a project?
我有一个名为 MudEngine.Core
的项目的解决方案。这个项目包含一些基本的 类 然后是我的域对象在后面抽象的所有接口。 IWorld
和 IRealm
.
等接口
域接口示例
public interface IWorld : IGameComponent, ICloneableComponent<IWorld>
{
/// <summary>
/// Gets how many hours it takes to complete one full day in this world.
/// </summary>
int HoursPerDay { get; }
/// <summary>
/// Gets or sets the game day to real hour ratio.
/// </summary>
double GameDayToRealHourRatio { get; set; }
/// <summary>
/// Adds a collection of realms to world, initializing them as they are added.
/// </summary>
/// <param name="realms">The realms.</param>
/// <returns>
/// Returns an awaitable Task
/// </returns>
IRealm[] GetRealmsInWorld();
/// <summary>
/// Initializes and then adds the given realm to this world instance.
/// </summary>
/// <param name="realm">The realm to add.</param>
/// <returns>Returns an awaitable Task</returns>
Task AddRealmToWorld(IRealm realm);
/// <summary>
/// Creates and initializes a new instance of a realm.
/// </summary>
/// <param name="name">The name of the realm.</param>
/// <param name="owner">The world that owns this realm.</param>
/// <returns>Returns an initialized instance of IRealm</returns>
Task<IRealm> CreateRealm(string name, IWorld owner);
/// <summary>
/// Adds a collection of realms to world, initializing them as they are added.
/// </summary>
/// <param name="realms">The realms.</param>
/// <returns>Returns an awaitable Task</returns>
Task AddRealmsToWorld(IEnumerable<IRealm> realms);
/// <summary>
/// Removes the given realm from this world instance, deleting the realm in the process.
/// If it must be reused, you may clone the realm and add the clone to another world.
/// </summary>
/// <param name="realm">The realm to remove.</param>
/// <returns>Returns an awaitable Task</returns>
Task RemoveRealmFromWorld(IRealm realm);
/// <summary>
/// Removes a collection of realms from this world instance.
/// If any of the realms don't exist in the world, they will be ignored.
/// The realms will be deleted during the process.
/// If they must be reused, you may clone the realm and add the clone to another world.
/// </summary>
/// <param name="realms">The realms collection.</param>
/// <returns>Returns an awaitable Task</returns>
Task RemoveRealmsFromWorld(IEnumerable<IRealm> realms);
}
我还有一个名为 MudEngine.Mud
的项目,它是所有接口的默认实现。
MudEngine.Core
项目包括我工厂的接口。 IWorldFactory
和 IRealmFactory
等工厂。 IWorld 界面有一个创建方法,它使用给定的 IRealmFactory
来创建领域和 return 它们。
示例工厂接口
/// <summary>
/// Provides methods for creating an instance of an IRealm implementation
/// </summary>
public interface IRealmFactory
{
/// <summary>
/// Creates and initializes a new instance of a realm.
/// </summary>
/// <param name="name">The name of the realm.</param>
/// <param name="owner">The world that owns this realm.</param>
/// <returns>Returns an initialized instance of IRealm</returns>
Task<IRealm> CreateRealm(string name, IWorld owner);
/// <summary>
/// Creates and initializes a new instance of a realm.
/// </summary>
/// <param name="name">The name of the realm.</param>
/// <param name="owner">The world that owns this realm.</param>
/// <param name="timeZoneOffset">The time zone offset to apply to the realm.</param>
/// <returns>Returns an initialized instance of IRealm</returns>
Task<IRealm> CreateRealm(string name, IWorld owner, ITimeOfDay timeZoneOffset);
/// <summary>
/// Creates and initializes a new instance of a realm.
/// All of the children zones will be initialized prior to being added to the realm.
/// </summary>
/// <param name="name">The name of the realm.</param>
/// <param name="owner">The world that owns this realm.</param>
/// <param name="zones">A collection of zones that will be initialized and added to the realm.</param>
/// <returns>Returns an initialized instance of IRealm</returns>
Task<IRealm> CreateRealm(string name, IWorld owner, IEnumerable<IZone> zones);
/// <summary>
/// Creates and initializes a new instance of a realm.
/// All of the children zones will be initialized prior to being added to the realm.
/// </summary>
/// <param name="name">The name of the realm.</param>
/// <param name="owner">The world that owns this realm.</param>
/// <param name="timeZoneOffset">The time zone offset to apply to the realm.</param>
/// <param name="zones">A collection of zones that will be initialized and added to the realm.</param>
/// <returns>Returns an initialized instance of IRealm</returns>
Task<IRealm> CreateRealm(string name, IWorld owner, ITimeOfDay timeZoneOffset, IEnumerable<IZone> zones);
}
当我在 IWorld 接口上调用 CreateRealm
时,实现使用 IRealmFactory 来创建它。工厂通过 IWorld 实现的构造函数传入。
我现在的问题是,工厂实现应该存在于何处?包含域接口实现的同一个项目为工厂提供实现是否很常见,还是应该由消费层(例如 presentation/unit 测试项目)负责实现工厂并使用它们?
目的是可以根据您正在构建的基于文本的游戏类型来替换这些组件。所以我倾向于每个实现接口的包都有自己的工厂。不过,我关心的是 DI 设置。 IoC 容器进一步位于分层(server/client 应用程序)需要知道它应该使用每个包中的哪个工厂,而不是仅仅使用 IoC 容器所属的消费层中定义的工厂.
有这方面的行业标准指南吗?
您似乎正在尝试构建具有抽象子域的域模型(我想您可以称它们为子域,因为它们是成熟的游戏系统),即用户可以在运行时动态选择一个子域,从而触发一些子域-不可知论行为,然后在一段时间内用它做特定于子域的事情,并可能在完成后跳转到另一个。
尽管根据我的经验,您很少会在 "normal" 业务线项目中遇到这种情况,但我会这样做:
为通用的世界和领域管理创建一个包罗万象的限界上下文。将 IWorld
和 IRealmFactory
放在那里。
为每个游戏系统子域创建一个单独的限界上下文。它们可以是整个项目或只是命名空间。在这些写IRealmFactory
的实现。
这是我认为使用 Container.Resolve(...)
可以合法的少数实例之一。在IoC配置中创建命名注册并随时解析其中之一,以连接一个游戏系统对应的对象子图。
我有一个名为 MudEngine.Core
的项目的解决方案。这个项目包含一些基本的 类 然后是我的域对象在后面抽象的所有接口。 IWorld
和 IRealm
.
域接口示例
public interface IWorld : IGameComponent, ICloneableComponent<IWorld>
{
/// <summary>
/// Gets how many hours it takes to complete one full day in this world.
/// </summary>
int HoursPerDay { get; }
/// <summary>
/// Gets or sets the game day to real hour ratio.
/// </summary>
double GameDayToRealHourRatio { get; set; }
/// <summary>
/// Adds a collection of realms to world, initializing them as they are added.
/// </summary>
/// <param name="realms">The realms.</param>
/// <returns>
/// Returns an awaitable Task
/// </returns>
IRealm[] GetRealmsInWorld();
/// <summary>
/// Initializes and then adds the given realm to this world instance.
/// </summary>
/// <param name="realm">The realm to add.</param>
/// <returns>Returns an awaitable Task</returns>
Task AddRealmToWorld(IRealm realm);
/// <summary>
/// Creates and initializes a new instance of a realm.
/// </summary>
/// <param name="name">The name of the realm.</param>
/// <param name="owner">The world that owns this realm.</param>
/// <returns>Returns an initialized instance of IRealm</returns>
Task<IRealm> CreateRealm(string name, IWorld owner);
/// <summary>
/// Adds a collection of realms to world, initializing them as they are added.
/// </summary>
/// <param name="realms">The realms.</param>
/// <returns>Returns an awaitable Task</returns>
Task AddRealmsToWorld(IEnumerable<IRealm> realms);
/// <summary>
/// Removes the given realm from this world instance, deleting the realm in the process.
/// If it must be reused, you may clone the realm and add the clone to another world.
/// </summary>
/// <param name="realm">The realm to remove.</param>
/// <returns>Returns an awaitable Task</returns>
Task RemoveRealmFromWorld(IRealm realm);
/// <summary>
/// Removes a collection of realms from this world instance.
/// If any of the realms don't exist in the world, they will be ignored.
/// The realms will be deleted during the process.
/// If they must be reused, you may clone the realm and add the clone to another world.
/// </summary>
/// <param name="realms">The realms collection.</param>
/// <returns>Returns an awaitable Task</returns>
Task RemoveRealmsFromWorld(IEnumerable<IRealm> realms);
}
我还有一个名为 MudEngine.Mud
的项目,它是所有接口的默认实现。
MudEngine.Core
项目包括我工厂的接口。 IWorldFactory
和 IRealmFactory
等工厂。 IWorld 界面有一个创建方法,它使用给定的 IRealmFactory
来创建领域和 return 它们。
示例工厂接口
/// <summary>
/// Provides methods for creating an instance of an IRealm implementation
/// </summary>
public interface IRealmFactory
{
/// <summary>
/// Creates and initializes a new instance of a realm.
/// </summary>
/// <param name="name">The name of the realm.</param>
/// <param name="owner">The world that owns this realm.</param>
/// <returns>Returns an initialized instance of IRealm</returns>
Task<IRealm> CreateRealm(string name, IWorld owner);
/// <summary>
/// Creates and initializes a new instance of a realm.
/// </summary>
/// <param name="name">The name of the realm.</param>
/// <param name="owner">The world that owns this realm.</param>
/// <param name="timeZoneOffset">The time zone offset to apply to the realm.</param>
/// <returns>Returns an initialized instance of IRealm</returns>
Task<IRealm> CreateRealm(string name, IWorld owner, ITimeOfDay timeZoneOffset);
/// <summary>
/// Creates and initializes a new instance of a realm.
/// All of the children zones will be initialized prior to being added to the realm.
/// </summary>
/// <param name="name">The name of the realm.</param>
/// <param name="owner">The world that owns this realm.</param>
/// <param name="zones">A collection of zones that will be initialized and added to the realm.</param>
/// <returns>Returns an initialized instance of IRealm</returns>
Task<IRealm> CreateRealm(string name, IWorld owner, IEnumerable<IZone> zones);
/// <summary>
/// Creates and initializes a new instance of a realm.
/// All of the children zones will be initialized prior to being added to the realm.
/// </summary>
/// <param name="name">The name of the realm.</param>
/// <param name="owner">The world that owns this realm.</param>
/// <param name="timeZoneOffset">The time zone offset to apply to the realm.</param>
/// <param name="zones">A collection of zones that will be initialized and added to the realm.</param>
/// <returns>Returns an initialized instance of IRealm</returns>
Task<IRealm> CreateRealm(string name, IWorld owner, ITimeOfDay timeZoneOffset, IEnumerable<IZone> zones);
}
当我在 IWorld 接口上调用 CreateRealm
时,实现使用 IRealmFactory 来创建它。工厂通过 IWorld 实现的构造函数传入。
我现在的问题是,工厂实现应该存在于何处?包含域接口实现的同一个项目为工厂提供实现是否很常见,还是应该由消费层(例如 presentation/unit 测试项目)负责实现工厂并使用它们?
目的是可以根据您正在构建的基于文本的游戏类型来替换这些组件。所以我倾向于每个实现接口的包都有自己的工厂。不过,我关心的是 DI 设置。 IoC 容器进一步位于分层(server/client 应用程序)需要知道它应该使用每个包中的哪个工厂,而不是仅仅使用 IoC 容器所属的消费层中定义的工厂.
有这方面的行业标准指南吗?
您似乎正在尝试构建具有抽象子域的域模型(我想您可以称它们为子域,因为它们是成熟的游戏系统),即用户可以在运行时动态选择一个子域,从而触发一些子域-不可知论行为,然后在一段时间内用它做特定于子域的事情,并可能在完成后跳转到另一个。
尽管根据我的经验,您很少会在 "normal" 业务线项目中遇到这种情况,但我会这样做:
为通用的世界和领域管理创建一个包罗万象的限界上下文。将
IWorld
和IRealmFactory
放在那里。为每个游戏系统子域创建一个单独的限界上下文。它们可以是整个项目或只是命名空间。在这些写
IRealmFactory
的实现。这是我认为使用
Container.Resolve(...)
可以合法的少数实例之一。在IoC配置中创建命名注册并随时解析其中之一,以连接一个游戏系统对应的对象子图。