mercredi 14 décembre 2016

How do I use Interfaces, Generics and design patterns to implement this solution

I haven't posted very many questions. This question is pretty open ended, and it's pretty long so I'm guessing that some folks may be put off. I've searched for a couple of days now, but I think that I just don't know what I'm looking for... If anyone has thoughts and can share them, it would be much appreciated.

I’ve been developing “Object Oriented” code for about 15 years. I usually have to perform the same types of tasks on each of my projects, so I’ve re-used the same schemes. It’s gotten me by for this long…

Recently, I took over a project that made heavy use of design patterns, and generics. I was pleasantly surprised at how much more simple and compact the code could be.

So, as I began working on my current project, I decided to try and implement some of those techniques. I’m familiar with Design Patterns (Fowler and GoF), but have never really implemented them.

The application renames mp4 files using a list of movies or Tv shows (only one type at a time)

  1. Files for renaming are selected via the UI (select a directory and read them in)
  2. Movies or Tv Shows are associated with each file
  3. The file(s) is/are renamed according to rules specific to movies or Tv shows.

The program will “File.Move()” a file from SourceFullName to OutputFullName, where OutputFullName = OutputDirectoryPath + OutputFileName.

Output Directory is specified via the UI and Output file name is constructed using a desired pattern and collected information.

For example, I may have a file named STALAG17_D1.Title1.mp4 and I’d like to rename it “Stalag 17 (1951).mp4

or

SIMPS_D1.Title9.mp4 => “The Simpson Season 1 Episode 9.mp4”

The application builds the OutputFielName using rules for either a movie or a tv show.

I have the following files in my project:

ProgramFormat.cs - Enum for Movie or TvShow FileManagerFactory.cs - Creates an instance of the desired Manager (for movies or for tv shows)

IMediaFileManager.cs - An interface intended to abstract away the implementation MovieFileManager.cs - Manages the list of MovieFileObjects TvEpisodeFileManager.cs -

Movie.cs - Contains details about the user selected movie TvEpisode.cs - Contains details about the user selected tv show

MediaFileInfoBase.cs - Base class that contains an instance of a FileInfo object, and properties that help with the renaming MovieFileInfo.cs - One property that sets the OutputFileName TvSeriesFileInfo.cs - TvSeries Specific Properties that are used to construct the "Read Only" OutputFileName property

The Redesign/Refactoring challenge is to:

  • Keep as much knowledge from the client as Possible!!
  • Factor out the common code from both of the File Managers (Ideally there would only need to be one manager.
  • Modify the common code to work for either object (Movie.cs or TvShow.cs) (However each manager creates and maintains a list of MovieFileInfo objects or TvShowFileInfoObjects. How would I write code to be generic?? If I use the base class, then I don’t have access to the derived class attributes, and I would have to cast which is expensive…
  • Eliminate the overloadeded Rename methods because implementing both doesn’t make sense.
  • RenameSelectedFiles(Movie[] movies)
  • RenameSelectedFiles(TvEpisode[] episodes)

Code:

Client and Factory

       public void Main()
    {
        // In this example, we are renaming movies.  The other would be renaming TV shows

        //Figure out if Movie or Series
        var programFormat = ProgramFormat.Movie;
        IMediaFileManager fileManagerClient = FileManagerFactory.GetFileManager(programFormat);

        fileManagerClient.LoadMediaFiles(@"C:\CopyFrom", @"C:\CopyTo");

        var fileIds = new string[2];  //Simulating a UI interaction
        fileManagerClient.SelectFiles(fileIds);

        var movies = new List<Movie>(); //Simulating a UI interaction
        fileManagerClient.RenameSelectedFiles(movies.ToArray());

        fileManagerClient.SaveRenamedFiles();
    }

public static class FileManagerFactory
{
    public static IMediaFileManager GetFileManager(ProgramFormat format)
    {
        switch (format)
        {
            case ProgramFormat.Movie:
                return new MovieFileManager();

            case ProgramFormat.TvEpisode:
                return new TvEpisodeFileManager();

            default:
                throw new ArgumentOutOfRangeException(nameof(format), format, null);
        }

    }
}

MediaFileManager

public interface IMediaFileManager
{
    void LoadMediaFiles(string sourcePath, string destinationPath);
    void SelectFiles(string[] fileIds);
    void RenameSelectedFiles(Movie[] movies);
    void RenameSelectedFiles(TvEpisode[] episodes);
    void SaveRenamedFiles();
    IList<MediaFileInfoBase> LoadedFileList { get; }
}

public class MovieFileManager : IMediaFileManager
{

    private MovieFileInfo[] _selectedFiles;
    private IList<MovieFileInfo> _loadedFileInfos;

    IList<MediaFileInfoBase> IMediaFileManager.LoadedFileList => _loadedFileInfos.Cast<MediaFileInfoBase>().ToList();



    public void LoadMediaFiles(string sourcePath, string destinationPath)
    {
        _loadedFileInfos = new List<MovieFileInfo>();

        var dir = new DirectoryInfo(sourcePath);
        var fileInfos = dir.GetFiles().ToList();
        foreach (var fi in fileInfos)
        {
            var mediaFile = new MovieFileInfo(fi) {OutputDirectoryName = destinationPath};
            _loadedFileInfos.Add(mediaFile);
        }
    }

    public void SelectFiles(string[] fileIds)
    {
        _selectedFiles = new MovieFileInfo[fileIds.Length];

        for (var i = 0; i < fileIds.Length; i++)
        {
            var fileId = fileIds[i];
            var file = _loadedFileInfos.First(fi => fi.FileId == fileId);

            _selectedFiles[i] = file;

        }
    }
    public void RenameSelectedFiles(Movie[] movies)
    {
        for (var i = 0; i < movies.Length; i++)
        {

            var title = movies[i].Title;
            _selectedFiles[i].OutputFileName = $"{title}";

        }

    }

    public void RenameSelectedFiles(TvEpisode[] episodes)
    {
        throw new NotImplementedException();
    }


    public void SaveRenamedFiles()
    {
        foreach (var file in _selectedFiles)
        {
            File.Move(file.SourceFullName, file.OutputFullName);
        }
    }
}



public class TvEpisodeFileManager : IMediaFileManager
{

    private TvSeriesFileInfo[] _selectedFiles;
    private IList<TvSeriesFileInfo> _loadedFileInfos;

    IList<MediaFileInfoBase> IMediaFileManager.LoadedFileList => _loadedFileInfos.Cast<MediaFileInfoBase>().ToList();

    public void LoadMediaFiles(string sourcePath, string destinationPath)
    {
        _loadedFileInfos = new List<TvSeriesFileInfo>();

        var dir = new DirectoryInfo(sourcePath);
        var fileInfos = dir.GetFiles().ToList();
        foreach (var fi in fileInfos)
        {
            var mediaFile = new TvSeriesFileInfo(fi) { OutputDirectoryName = destinationPath };
            _loadedFileInfos.Add(mediaFile);
        }
    }

    public void SelectFiles(string[] fileIds)
    {
        _selectedFiles = new TvSeriesFileInfo[fileIds.Length];

        for (var i = 0; i < fileIds.Length; i++)
        {
            var fileId = fileIds[i];
            var file = _loadedFileInfos.First(fi => fi.FileId == fileId);

            _selectedFiles[i] = file;

        }
    }
    public void RenameSelectedFiles(Movie[] movies)
    {
        throw new NotImplementedException();

    }

    public void RenameSelectedFiles(TvEpisode[] episodes)
    {
        for (var i = 0; i < episodes.Length; i++)
        {
            _selectedFiles[i].SeriesName = episodes[i].SeriesName;
            _selectedFiles[i].EpisodeTitle = episodes[i].EpisodeName;

        }
    }


    public void SaveRenamedFiles()
    {
        foreach (var file in _selectedFiles)
        {
            File.Move(file.SourceFullName, file.OutputFullName);
        }
    }
}

MediFileBase and derived objects

public abstract class MediaFileInfoBase
{

    private readonly FileInfo _fileInfo;

    protected MediaFileInfoBase(FileInfo fileInfo)
    {
        _fileInfo = fileInfo;
        FileId = Guid.NewGuid().ToString();
    }
    //This is our Primary key to the file
    public string FileId { get; }

    public string FileExtension => _fileInfo.Extension;


    public string SourceFilename => _fileInfo.Name;
    public string SourceFullName => _fileInfo.FullName;
    public DateTime SourceFileCreationTime => _fileInfo.CreationTime;
    public string SourceDirectoryName => _fileInfo.DirectoryName;

    public abstract string OutputFileName { get; set; }

    public string OutputDirectoryName { get; set; }
    public string OutputFullName => $"{OutputDirectoryName}\\{OutputFileName}";
}
public class MovieFileInfo : MediaFileInfoBase
{
    public override string OutputFileName { get; set; }

    public MovieFileInfo(FileInfo fileInfo) : base(fileInfo)
    {

    }
}

public class TvSeriesFileInfo : MediaFileInfoBase
{
    public TvSeriesFileInfo(FileInfo originalFileInfo) : base(originalFileInfo)
    {

    }
    private string _filenameTemplate = "{0} - {1}.{2}";
    public string SeriesName { get; set; }
    public string EpisodeTitle { get; set; }
    public override string OutputFileName
    {
        get
        {
            if (string.IsNullOrEmpty(SeriesName) || string.IsNullOrEmpty(EpisodeTitle)) return string.Empty;

            return string.Format(_filenameTemplate,
                SeriesName,
                EpisodeTitle,
                FileExtension
            );
        }
        set { }
    }
}

Aucun commentaire:

Enregistrer un commentaire