【问题标题】:An OS design issue: File types associated with their appropriate programs操作系统设计问题:与相应程序相关的文件类型
【发布时间】:2013-11-05 22:34:41
【问题描述】:

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

UA Crosslink

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

这是我所拥有的:

摘要FILE,有孩子:

  1. TextFile
  2. MediaFile
  3. ImageFile

还有一个摘要Application,有孩子:

  1. TextViewer
  2. MediaPlayer
  3. ImageViewer

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

现在,我想了想,应该把who-opens-whom信息放在哪里? 我应该让每个文件自己打开吗?通过在FILE 中创建抽象Open,并在子文件中覆盖它? - 我认为这没有意义,因为文件并不真正知道如何打开自己,它是知道如何打开文件的应用程序。比如一个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 这样的真实操作系统是如何做到的? - 好吧,如果你开始 |默认程序 |将文件类型或协议与程序相关联。 - 你会看到一个字典类型列表,键(文件类型)和值(关联程序) - 所以我想这样做,创建一个字典,我用它注册,一个应用程序的文件类型。这就是我被困的地方......

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

我破解了这一点,创建了一个中间类 App,然后让我的 Application&lt;T&gt; 继承自:

public abstract class App { }

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

好的,现在我可以让我的字典是&lt;Type, App&gt;

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);
   }
}

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

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

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

【问题讨论】:

  • @terrybozzio:我考虑过标记 unity3d,但后来我发现几乎没有任何相关内容 - 这是一个纯粹的设计问题。这就是为什么我认为,操作系统更好。无论如何...

标签: c# inheritance polymorphism unity3d


【解决方案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 创建每个类的实例,然后执行一个已知方法,例如返回IFile 实现的完整类型的GetSupportedFileType,即IApplication支持。

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

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);
    }
}

【讨论】:

  • 太棒了!但是,有一个问题:在您的MediaApplication 中,您有Open 使用IFile - 这意味着我必须将其转换为MediaFile(知道它实现了IFile),对吗?因为我需要MediaFile 信息...
  • 是的,假设您确实需要知道执行操作的类型,每个 OpenFile 实现都需要转换为适当的类型。如果可以的话,最好的处理方法是向 IFile 接口添加尽可能多的内容。这种方法还允许单个应用程序支持多种文件类型;只需修改 OpenFile 即可测试 IFile 的类型并做出相应的行为。
  • 好吧,我想这会行得通,但让我试试看 :D - 但是你不认为如果每个应用程序在他们的Open 方法中采用正确类型的文件会是最佳的吗? - 我的意思是,如果有人进入 mediaPlayer.Open( 并看到 IFile 作为参数,这意味着实现 IFileanything 应该适合...(这是真的,我的意思是我们会检查以确保我们得到正确的文件类型,但是,你知道......我希望你能感觉到我) - 也许有办法解决这个问题?
  • 好吧,我想这不是什么大问题。我想我会采用您的解决方案,看看总结了什么。如果您不介意,我可能稍后再联系您。不过有一件事,你为什么要为文件和应用程序使用接口?为什么不抽象类?他们说,当你有逻辑时使用类,当你有行为时使用接口!我之所以选择一个抽象类来应用,是因为我认为它是“某种东西”,它是一个对象,它是一个有生命的东西,而不是一种行为......你能说明一下吗?非常感谢。
  • 选择接口而不是抽象类作为控制元素的主要原因是易于维护、实现和单元测试。当您实现抽象类时,随着时间的推移添加特性和功能,您的实现更加僵化,因此更加脆弱。但是,接口和抽象类并不相互排斥。没有理由不能实现所有文件都继承自的基本抽象类,并且该类是实现 IFile 的类。但是,使用接口允许您为不同类型的数据(即 URL)引入第二个基类
【解决方案2】:

也许这对你有用,而不是在应用程序类中声明一个抽象方法 并创建泛型,您可以创建一个接口并实现其 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....
        }
    }

【讨论】:

  • 感谢您的回复,这绝对是有趣的! - 但这只是解决方案的一半,那么您将如何解决关联/绑定问题?
  • 您的意思是像您在问题中所说的示例?右键单击文本文件和您询问的那些选项?....我真的不知道设计,但如果您有上下文菜单,请大致了解(例如)使用 open() 命令并且如果用户单击打开菜单项,则在处理程序中您首先检查文件扩展名(这是一种快速检查方法),如果是 .txt 然后是 TextViewer.Open(TextFile路径),其中路径 TextFile 变量是根据选择并传递给 open 方法的文件创建的
猜你喜欢
  • 2022-01-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-29
  • 1970-01-01
  • 1970-01-01
  • 2011-09-11
  • 2014-05-03
相关资源
最近更新 更多