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

各アプリには、開くことができる特定のサポートされているファイルの種類があります。

私の についての言葉は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
}

他に何もありません-if、いいね!しかし、これは私が好きではない他の不都合をもたらします:

  1. VideoFile&MediaPlayerAudioFile&MediaPlayerはより密結合になりました。
  2. 現在、循環依存関係があります(MediaPlayer知っておく必要がAudio/VideoFileあり、その逆も同様です)
  3. Audio/VideoFilesが自分で自分で開くことができるのは意味がないと思います(実際にはそうしていませんが、MediaPlayer開く方法を教えているだけです。sは方法MediaPlayerを知っているはずです、彼は知りません.彼の仕事のやり方を誰かに教えてもらう必要はない.)
  4. それは非常に冗長に感じます。誰かに耳を指すように頼むようなものです。そのため、彼は右手を頭に巻き付け、右耳ではなく左耳を指します。-何が起こっているかというと、

また

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

また

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

私たちは、ポリモーフィズムの名の下に、正しいメソッドに直接行くことができたはずの自分自身の周りをぐるぐる回っています。

上記の 2 つのアプローチはどちらも最適ではないと思いますが、どちらかを選択する場合は、最初のアプローチを選択します。

どう思いますか?より良い方法はありますか?- すべての鳥を 1 つの石で撃つ、素晴らしく、エレガントで堅牢なポリモーフィックな方法は? これについてどうすればよかったですか?ここで使用できるデザインパターンがあるかもしれません。

また、私の判断が間違っていた場合は、訂正してください。

事前に助けてくれてありがとう。

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) 2 番目のオプションは、動的選択の同様の概念に基づいていますが、個別のクラスを使用して各ファイルを処理できる別の抽象化レイヤーを使用します。想像力が足りないので、名前を付けてIFileActionHandler.います。現在、メソッドは 1 つしかありませんが、必要に応じてさらに追加できます。以下のサンプルは、ファイルの種類に基づいて正しい実装を動的に選択する方法を示しています。コンストラクターでこれらの実装の辞書を作成しました。実装のメモリ フットプリントの大きさによっては、静的ファイル (XML、config、txt など) でキーと値のペアを定義し、System.Acticator.CreateInstanceオーバーロードの 1 つを使用して正しいインスタンスを作成するという別のアプローチを検討することをお勧めします。

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 に答える