C# 在描述和实现之间抽象对象

C# 在描述和实现之间抽象对象,c#,design-patterns,interface,C#,Design Patterns,Interface,我觉得我知道设计模式,但这让我难以理解。我有两个独立的项目,一个作为另一个的库。该库读取XML文件并将其解析为数据结构。它只负责XML之间的转换。我的另一个项目是一个引擎,它作用于库集合的数据。它可能包含镜像库中的类,但带有行为方法。如果您想知道,将引擎和库分离的原因是有第三个项目,一个编辑器,它修改XML数据。这是一场游戏 我现在在库中创建一个类,它表示可以在引擎中执行的一组命令,以及包含许多不同命令的对象。我的问题是,我无法找到一个好的模式来定义这些命令和抽象它们的容器,这样引擎就不必打开类

我觉得我知道设计模式,但这让我难以理解。我有两个独立的项目,一个作为另一个的库。该库读取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
       }
    }