0

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

(我的问题与 Unity 无关,纯属设计问题)

这是我目前拥有的:

  1. 一个IApp接口,带有实现者:

    1. TextViewer
    2. ImageViewer
    3. MediaPlayer
  2. 一个IFile接口,带有实现者:

    1. TextFile
    2. ImageFile
    3. MediaFile- 带孩子:
      1. VideoFile
      2. AudioFile

这是界面:

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

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

关于 my 的一句话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&MediaPlayerAudioFile&MediaPlayer现在耦合得更紧密了。
  2. 现在有一个循环依赖(MediaPlayer必须知道Audio/VideoFile,反之亦然)
  3. 我认为Audio/VideoFiles 能够自己打开自己是没有意义的(尽管他们并没有真正这样做,他们只是在告诉MediaPlayer如何打开它们。MediaPlayer应该知道如何打开它们,他不知道'不需要任何人告诉他如何做他的工作。)
  4. 感觉很多余,好比叫人指着自己的耳朵,于是右手绕着脑袋,指向了自己的左耳而不是右耳!- 正在发生的事情是我们要去:

任何一个

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

或者

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

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

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

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

如果我的判断有误,请纠正我。

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

4

1 回答 1

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 等)中定义键值对并使用其中一个System.Acticator.CreateInstance重载创建正确的实例。

interface IFileActionHandler
{
    void PlayFile(IFile file);
}

class FileActionHandlerBase : IFileActionHandler
{
    IApp _app;

    public FileActionHandlerBase(IApp app) // It may not be needed depending on what you want to do.
    {
        _app = app;
    }

    public abstract void PlayFile(IFile file);        
}

class AudioFileActionHandler : FileActionHandlerBase
{
    public AudioFileActionHandler(IApp app)
        : base(app) { }

    public override void PlayFile(IFile file)
    {
        // Your implementation...
    }
}

class VideoFileActionHandler : FileActionHandlerBase
{
    public VideoFileActionHandler(IApp app)
        : base(app) { }

    public override void PlayFile(IFile file)
    {
        // Your implementation...
    }
}



public class PlaybackControls
    {
        private MediaPlayer player;
        private int index;

        Dictionary<string, IFileActionHandler> _fileActionHandlers;            

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

            _fileActionHandlers = new Dictionary<string, IFileActionHandler>();
            _fileActionHandlers.Add(typeof(VideoFile).Name, new VideoFileActionHandler(player));
            _fileActionHandlers.Add(typeof(AudioFile).Name, new AudioFileActionHandler(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];
        }

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

            _fileActionHandlers[media.GetType().Name].PlayFile(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; }            
    }
于 2013-10-28T02:40:42.880 回答