4

我正在使用 Unity3D 和 C# 创建一个伪游戏内操作系统(有点野心)

UA交联

请注意,这是一个纯粹的设计问题,您可以在不了解 Unity 的情况下回答。

这是我所拥有的:

有孩子的摘要FILE

  1. TextFile
  2. MediaFile
  3. ImageFile

此外,还有一个带有孩子的摘要Application

  1. TextViewer
  2. MediaPlayer
  3. ImageViewer

显然,TextViewer应该打开TextFiles,MediaPlayer应该打开MediaFiles和ImageViewer应该打开ImageFiles。

现在,我想了想,我应该把谁打开谁的信息放在哪里?我应该让每个文件自己打开吗?通过在 中创建摘要并OpenFILE子文件中覆盖它?- 我认为这没有意义,因为文件并不真正知道如何打开自己,它是知道如何打开文件的应用程序。比如一个pdf文件,如果你没有pdf阅读器(Adobe之类的),是无法打开pdf的。

这意味着TextViewer应该有一个Open(TextFile text)方法,MediaPlayer应该有一个Open(MediaFile media)并且ImageViewer应该有一个Open(ImageFile image)方法。

这可以通过在 的声明中使用泛型来实现Application,例如:

public abstract class Application<T> where T : FILE
{
  public abstract void Open(T file);
}

然后:

public class TextViewer : Application<TextFile>
{
    public override void Open(TextFile file)
    {
        Console.WriteLine("TextViewer opening text file...");
    }
}

public class MediaPlayer : Application<MediaFile>
{
    public override void Open(MediaFile file)
    {
        Console.WriteLine("MediaPlayer opening media file...");
    }
}

public class ImageViewer : Application<ImageFile>
{
    public override void Open(ImageFile file)
    {
        Console.WriteLine("ImageViewer opening image file...");
    }
}

下面是 UML 图的样子:

在此处输入图像描述

看起来不错......现在有趣的部分,如何绑定/关联,每个文件类型,正确的应用程序应该打开它?即右键单击文本文件| 获取选项列表:“打开”、“重命名”、“删除”等 | 选择“打开”——接下来会发生什么?

我想出了一个想法,在FILEApplication- 如果我们想一想,像 Windows 这样的真正的操作系统是如何做到的?- 好吧,如果你开始 | 默认程序 | 将文件类型或协议与程序相关联。- 你会看到一个字典类型列表,键(文件类型)和值(关联程序) - 所以我想这样做,创建一个字典,我用它注册,一个应用程序的文件类型。这就是我被困的地方......

首先,我应该在字典中使用什么类型的键和值?- 我想了一会儿,得出的结论应该是<Type, Application> - 但问题是,我不能自己写Application,我必须指定泛型T参数:(

我通过创建一个中间类来破解它,App然后让我Application<T>继承自:

public abstract class App { }

public abstract class Application <T> : App where T : FILE { /* stuff... */ }

好的,现在我可以让我的字典成为<Type, App>

public static class Mediator
{
    static private TextViewer tv;
    static private MediaPlayer mp;
    static private ImageViewer iv;
    static private Dictionary<Type, App> dic = new Dictionary<Type, App>();

    static Mediator()
    {
        tv = new TextViewer();
        iv = new ImageViewer();
        mp = new MediaPlayer();

        // register what we have
        dic.Add(typeof(TextFile), tv);
        dic.Add(typeof(MediaFile), mp);
        dic.Add(typeof(ImageFile), iv);
    }

    static public void Open(FILE file)
    {
        App app = dic[file.GetType()];
        if (app == null)
        {
            Console.WriteLine("No application was assigned to open up " + file);
            return;
        }

        app. // here is where my hack falls short, no Open method inside App :(
    }
}

哎呀,这完全没用!- 如您所见,我正在尽力避免以下事情:

if (file is TextFile)
  // open with text viewer
else if (file is MediaFile)
  // open with media player
else if (file is ImageFile)
  // open with image viewer
else if etc

这对我来说就像一场噩梦!

通过使我的应用程序成为单例,我可以避免所有麻烦,所以现在在每个文件中:

public class TextFile : FILE
{
   public override void Open()
   {
      TextViewer.Instance.Open(this);
   }
}

其余的文件依此类推。但我不想那样做,我不想放弃使用单例!如果我希望我的MediaPlayerorTextViewer有多个实例而不是一个实例怎么办?

我已经为此苦苦挣扎了很长时间,希望我能弄清楚我的问题。我只是想知道,将文件类型与正确的应用程序相关联如何以正确的方式工作(例如在 Windows 中) - 我怎样才能以优雅、强大的方式实现我所追求的?- 有没有我可以使用的设计模式?- 我上面所有的尝试,我想对了吗?我接近解决方案了吗?

非常感谢您提前提供的任何帮助。

4

2 回答 2

1

也许这对你有用,而不是在应用程序类中声明一个抽象方法并创建泛型,你可以创建一个接口并实现它的 open 方法,如下所示:

    public interface IFile<T>
    {
        void Open(T path);
    }

    public abstract class Application
    {
        //public abstract void Open();
    }


    public class TextViewer : Application, IFile<TextFile>
    {
        public void Open(TextFile path)
        {
            //open textfile....
        }
    }

    public class MediaPlayer : Application, IFile<MediaFile>
    {     
        public void Open(MediaFile path)
        {
            //open media file...
        }
    }

    public class ImageViewer : Application, IFile<ImageFile>
    {
        public void Open(ImageFile path)
        {
            //open imagefile....
        }
    }
于 2013-10-26T23:53:43.420 回答
1

在没有对设计进行大量思考并且不知道统一性或文件如何真正表示的情况下,我将采用基于接口的方法,而不是基于泛型的方法。

首先,我将定义一个接口来定义我的文件类,即 IFile,并在我的每个文件中实现它。

public interface IFile
{

}

public class MediaFile : IFile
{
    ...
}

接下来,我将定义一个接口来定义我的应用程序类,即 IApplication,并在我的每个应用程序中实现它。

public interface IApplication
{
    Type GetSupportedApplicationType();
    void OpenFile(IFile oFile);
}


public class MediaApplication : IApplication
{

    #region IApplication Members

    public Type GetSupportedApplicationType()
    {
        return typeof(MediaFile);
    }

    public void OpenFile(IFile oFile)
    {
        // do the work
    }

    #endregion
}

那么,文件和应用程序之间的链接将是一个字典,其中包含文件的类类型作为键,应用程序的类类型作为值:

<GetType(MediaFile), GetType(MediaPlayer)>

当中介器被传递文件执行时,它可以通过使用文件的类型在字典中搜索适当的应用程序类型来找到适当的应用程序。

找到合适的应用程序类型后,您可以使用System.GetActivator. 然后,由于您知道它实现了 IApplication,因此您可以在应用程序上执行诸如此类的方法OpenFile(IFile)

唯一的技巧是创建初始字典条目。为此,我实际上会使用反射来收集实现 IApplication 的类的列表。

完成此操作后,您可以使用 为每个类创建一个实例System.GetActivator,然后执行一个已知方法,例如返回支持的实现GetSupportedFileType的完整类型。IFileIApplication

(下面的代码采用了注册应用的快捷方式,比实现反射方式更容易)

public static class Mediator
{
    static private Dictionary<Type, Type> dic = new Dictionary<Type, Type>();

    static Mediator()
    {
        RegisterApp(new MediaApplication());
    }

    static void RegisterApp(IApplication oApp)
    {
        dic.Add(oApp.GetSupportedApplicationType(), oApp.GetType());
    }

    static public void Open(IFile file)
    {
        Type appType = dic[file.GetType()];
        if (appType == null)
        {
            Console.WriteLine("No application was assigned to open up " + file);
            return;
        }

        IApplication app = (IApplication)System.Activator.CreateInstance(appType);
        app.OpenFile(file);
    }
}
于 2013-10-26T23:56:49.550 回答