C# 在描述和实现之间抽象对象
我觉得我知道设计模式,但这让我难以理解。我有两个独立的项目,一个作为另一个的库。该库读取XML文件并将其解析为数据结构。它只负责XML之间的转换。我的另一个项目是一个引擎,它作用于库集合的数据。它可能包含镜像库中的类,但带有行为方法。如果您想知道,将引擎和库分离的原因是有第三个项目,一个编辑器,它修改XML数据。这是一场游戏 我现在在库中创建一个类,它表示可以在引擎中执行的一组命令,以及包含许多不同命令的对象。我的问题是,我无法找到一个好的模式来定义这些命令和抽象它们的容器,这样引擎就不必打开类型。如果这里没有两个项目,只有一个,那就容易了。不同的命令将实现一个包含C# 在描述和实现之间抽象对象,c#,design-patterns,interface,C#,Design Patterns,Interface,我觉得我知道设计模式,但这让我难以理解。我有两个独立的项目,一个作为另一个的库。该库读取XML文件并将其解析为数据结构。它只负责XML之间的转换。我的另一个项目是一个引擎,它作用于库集合的数据。它可能包含镜像库中的类,但带有行为方法。如果您想知道,将引擎和库分离的原因是有第三个项目,一个编辑器,它修改XML数据。这是一场游戏 我现在在库中创建一个类,它表示可以在引擎中执行的一组命令,以及包含许多不同命令的对象。我的问题是,我无法找到一个好的模式来定义这些命令和抽象它们的容器,这样引擎就不必打开类
Execute()
方法或类似方法的接口。但这在这里行不通。该库不能实现命令的行为,只能实现从XML中提取的属性
我可以在库中创建容器可以使用的接口,其中包含加载和保存XML数据的方法。但发动机仍必须打开命令,以便:
在引擎的容器类中运行方法以相应地修改其自身状态,或
创建控制命令行为的正确类型的引擎对象,然后通过其接口执行
所讨论的容器是我的游戏和其中关键帧的剪切场景。这些命令将控制其行为,如音乐、图像、文本等
以下是库中的一些示例代码:
public class SceneInfo
{
/* Other stuff... */
public List<KeyFrameInfo> KeyFrames { get; private set; }
}
public class KeyFrameInfo
{
public List<IKeyFrameCommandInfo> Commands { get; private set; }
}
public class KeyFramePlayCommandInfo : IKeyFrameCommandInfo
{
public int Track { get; set; }
public static KeyFramePlayCommandInfo FromXml(XElement node)
{
var info = new KeyFramePlayCommandInfo();
info.Track = node.GetInteger("track");
return info;
}
public void Save(XmlTextWriter writer)
{
writer.WriteStartElement("PlayMusic");
writer.WriteAttributeString("track", Track.ToString());
writer.WriteEndElement();
}
}
公共类场景信息
{
/*其他东西*/
公共列表关键帧{get;private set;}
}
公共类电子信息
{
公共列表命令{get;private set;}
}
公共类KeyFramePlayCommandInfo:IKeyFrameCommandInfo
{
公共int轨道{get;set;}
公共静态KeyFramePlayCommandInfo FromXml(XElement节点)
{
var info=新的KeyFramePlayCommandInfo();
info.Track=node.GetInteger(“Track”);
退货信息;
}
公共作废保存(XmlTextWriter)
{
编剧。编剧开始语(“播放音乐”);
WriteAttributeString(“track”,track.ToString());
writer.writeedelement();
}
}
我还没有编写引擎的一半,但它将访问关键帧。命令
,对其进行迭代,然后执行。。。某物我试图避免类型转换,而不过度设计这个问题。它可能有类似于KeyFramePlayCommand
(与KeyFramePlayCommandInfo
)的类
有没有解决这个问题的好模式?您可以为工厂创建一个接口,为给定的命令类型创建一个、命令执行器。还添加一个函数,将工厂注册到库中。当您需要执行一个命令时,您可以向注册工厂发出该命令,并获取一个executor对象。如果我正确理解了您的问题,那么此代码位于库端
现在,从应用程序端,您可以创建工厂实现,将所有已知命令类型注册到可以执行它们的类中,最后将此工厂注册到库中
有很多不同的方法可以做到这一点。我将添加假设您不想将void Execute()
添加到SceneInfo
、KeyFrameInfo
或IKeyFrameCommandInfo
——它们毕竟是信息类。那么,让我们创建一个SceneRunner
类:
公共类场景浏览器
{
公共执行工程师(场景信息场景){
//在场景上循环。关键帧和关键帧。命令,然后执行
}
}
因为我们不知道如何执行这些命令,所以让我们创建一个工厂,让我们获得一个知道如何执行这些命令的类:
public interface IKeyFrameCommandFactory
{
IKeyFrameCommand GetCommand(IKeyFrameCommandInfo info);
}
并向runner添加factory接口和注册机制。由于应用程序端可能不想处理这种情况,因此让我们将这两种情况都设置为静态:
public class SceneRunner
{
static public RegisterFactory(IKeyFrameCommandFactory factory)
{
this.factory = factory;
}
static private IKeyFrameCommandFactory factory = null;
}
到目前为止,一切顺利。现在,工厂代码的时间到了(在库客户端)。这非常类似于(您可以通过添加接口规范,逐字使用他的代码):
public void keycomandfactory:IKeyFrameCommandFactory
{
私有静态映射;
公共IKeyCommand GetKeyCommand(IKeyCommandInfo信息)
{
类型infoType=info.GetType();
返回Activator.CreateInstance(映射[infoType],info);
}
}
如果您不喜欢进入反射,可以在命令上实现,并使用IDictionary映射
作为您的映射,以及映射[infoType].Clone()
以获取命令的新实例
现在,剩下两件事:将信息类与命令类关联,以及注册工厂。两者都应该相当明显
对于关联,您可以将RegisterCommand(Type infoType,IPrototype命令)
添加到工厂,并将关联添加到程序入口点,或者在静态方法中丢弃关联,然后从程序入口点调用该方法。您甚至可以设计一个属性来指定命令类的info类,解析程序集,并自动添加关联
最后,通过调用SceneRunner向您的SceneRunner注册工厂。RegisterFactory(new keycomandfactory())
处理实例化问题(特别是从逻辑中抽象实例化)工厂通常起作用
public void KeyCommandFactory{
public static GetKeyCommand(IKeyCommandInfo info){
/* ? */
}
}
因为你是唯一的一个
public void KeyCommandFactory{
public static GetKeyCommand(IKeyCommandInfo info){
/* ? */
}
}
public void KeyCommandFactory{
private static Map<string,Type> Mappings;
private static KeyCommandFactory(){
/* Example */
Mappings.Add("PlayMusic",typeof(PlayMusicCommand));
Mappings.Add("AnimateFrame",typeof(AnimateFrameCommand));
Mappings.Add("StopFrame",typeof(StopFrameCommand));
}
public static GetKeyCommand(IKeyCommandInfo info){
return (IKeyCommandInfo)Activator.CreateInstance(Mappings[info.CommandName]); // Add this property
}
}
public void KeyCommandFactory{
public static GetKeyCommand(IKeyCommandInfo info){
Type type = Type.GetType("Namespace.To." + info.CommandName + "Command");
return (IKeyCommandInfo)Activator.CreateInstance(type, info); // Add this property
}
}