媒体播放器应用程序设计问题 - 实现打开媒体文件的最佳方法

时间:2013-10-27 12:34:06

标签: c# inheritance polymorphism media-player

我正在使用Unity3D和C#创建一个媒体播放器应用程序。

(我的问题与Unity无关,这是一个纯粹的设计问题)

以下是我目前的情况:

  1. IApp界面,包含实施者:

    1. TextViewer
    2. ImageViewer
    3. MediaPlayer
  2. IFile界面,包含实施者:

    1. TextFile
    2. ImageFile
    3. MediaFile - 有孩子:
      1. VideoFile
      2. AudioFile
  3. 这是界面:

    public interface IApp
    {
      void Open(IFile file);
      Type SupportedType { get; }
    }
    

    每个应用都有一个可以打开的特定支持文件类型。

    关于我MediaPlayer的一句话是,它打开/播放音频和视频文件。但是我打开视频的方式与我打开音频的方式不同,所以每种方式都有一个独特的逻辑。

    现在这里是代码 - 非常简单(但尚未完全实现):

    public class MediaPlayer : IApp
    {
        public Type SupportedType { get { return typeof(MediaFile); } }
        public void Open(IFile file)
        {
            if (file is MediaFile)
                Console.WriteLine("MediaPlayer opening media file...");
        }
    
        List<MediaFile> Medias = new List<MediaFile>();
        public MediaFile Current { private set; get; }
        public PlaybackControls Controls { private set; get; }
        public PlaybackSettings Settings { private set; get; }
    
        public MediaPlayer()
        {
           Controls = new PlaybackControls(this);
           Settings = new PlaybackSettings(this);
        }
    
        public class PlaybackControls
        {
            private MediaPlayer player;
            private int index;
            public PlaybackControls(MediaPlayer player)
            {
                this.player = player;
            }
            public void Seek(float pos) { }
            public void Next()
            {
                index = (index + 1) % player.Medias.Count;
                player.Current = player.Medias[index];
            }
            public void Previous()
            {
                index--;
                if (index < 0)
                    index = player.Medias.Count - 1;
                player.Current = player.Medias[index];
            }
            private void PlayVideo(VideoFile video)
            {
                // video logic
            }
            private void PlayAudio(AudioFile audio)
            {
                // audio logic
            }
            public void Play(MediaFile media)
            {
                IsPlaying = true;
                if (media is AudioFile)
                    PlayAudio(media as AudioFile);
                else if (media is VideoFile)
                    PlayVideo(media as VideoFile);
            }
            public void Play()
            {
                Play(player.Current);
            }
            public void Pause()
            {
                IsPlaying = false;
            }
            public void Stop()
            {
                IsPlaying = false;
                Seek(0);
            }
            public bool IsPlaying { get; private set; }
        }
    
        public class PlaybackSettings
        {
            // Volume, shuffling, looping, etc
        }
    }
    

    我不太喜欢的是Play(Media)方法。在内部,我正在检查媒体类型,并且根据媒体是视频还是音频,我正在调用正确的方法。我不喜欢那样,我觉得这不对。如果我有其他类型的媒体,如图片怎么办?如果我想在ImageFile下移动MediaFile该怎么办? 然后我必须添加另一个else-if语句,它根本不是多态的。

    我可以做的是让媒体文件选择要调用的方法,例如:

    public abstract class MediaFile : IFile
    {
       //...
       public abstract void Open(MediaPlayer from);
       //...
    }
    
    public class AudioFile : MediaFile
    {
       public override void Open(MediaPlayer from)
       {
          from.PlayAudio(this);
       }
    }
    
    public class VideoFile : MediaFile
    {
       public override void Open(MediaPlayer from)
       {
          from.PlayVideo(this);
       }
    }
    

    现在在MediaPlayer

    public void Open(MediaFile media)
    {
      media.Open(this); // polymorphically open it
    }
    

    没有别的 - 如果,好的!但这引入了我不喜欢的其他不便之处:

    1. VideoFile&amp; MediaPlayerAudioFile&amp; MediaPlayer现在更加紧密耦合。
    2. 现在有一个循环依赖(MediaPlayer必须知道Audio/VideoFile,反之亦然)
    3. 我认为Audio/VideoFile能够自己打开自己是不合理的(尽管他们并没有真正这样做,他们只是告诉MediaPlayer打开它们。MediaPlayer应该知道怎么做,他不需要任何人告诉他如何做他的工作。)
    4. 感觉非常多余,就像是要求某人指着他的耳朵,所以他用右手环绕着他的头,指向他的左耳而不是右手! - 发生的事情是我们要去的地方:
    5. 或者

      MediaPlayer.Open(Media) -> AudioFile.Open(AudioFile) -> MediaPlayer.OpenAudio(AudioFile)
      

      MediaPlayer.Open(Media) -> VideoFile.Open(VideoFile) -> MediaPlayer.OpenVideo(VideoFile)
      

      我们以多态的名义绕着自己盘旋,我们可以直接找到正确的方法。

      我认为上述两种方法都不是最好的,但如果我选择一种,我会选择第一种。

      你怎么看?有没有更好的办法? - 一种漂亮,优雅,强大的多态方式,一举一动地射击所有鸟类?我该怎么办呢?也许我可以在这里使用一种设计模式?

      如果我的判断错了,请纠正我。

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

1 个答案:

答案 0 :(得分:1)

你有几个选择。

1)使用委托字典并根据委托运行的文件类型进行选择:

public class PlaybackControls
    {
        private MediaPlayer player;
        private int index;
        Dictionary<string, Action<MediaFile>> _fileActionMethods;

        public PlaybackControls(MediaPlayer player)
        {
            this.player = player;

            _fileActionMethods = new Dictionary<string, Action<MediaFile>>();
            _fileActionMethods.Add(typeof(VideoFile).Name, x => PlayVideoFile(x));
            _fileActionMethods.Add(typeof(AudioFile).Name, x => PlayAudioFile(x));
        }

        public void Seek(float pos) { }
        public void Next()
        {
            index = (index + 1) % player.Medias.Count;
            player.Current = player.Medias[index];
        }
        public void Previous()
        {
            index--;
            if (index < 0)
                index = player.Medias.Count - 1;
            player.Current = player.Medias[index];
        }

        public void Play(MediaFile media)
        {
            IsPlaying = true;

            _fileActionMethods[media.GetType().Name](media);
        }

        public void Play()
        {
            Play(player.Current);
        }
        public void Pause()
        {
            IsPlaying = false;
        }
        public void Stop()
        {
            IsPlaying = false;
            Seek(0);
        }
        public bool IsPlaying { get; private set; }

        private void PlayVideoFile(MediaFile file) { }

        private void PlayAudioFile(MediaFile file) { }
    }

2)第二个选项基于类似的动态选择概念,但使用另一个抽象层,使您能够使用单独的类处理每个文件。由于缺乏想象力,我将其命名为IFileActionHandler.它现在只有一种方法,但如果需要,可以添加更多方法。下面的示例显示了如何根据文件类型动态选择正确的实现。我在构造函数中创建了这些实现的字典。根据实现的内存占用量有多大,您可能需要考虑另一种方法 - 在静态文件(XML,config,txt等)中定义键值对,并使用{{1}之一创建正确的实例重载。

System.Acticator.CreateInstance