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)
- Files for renaming are selected via the UI (select a directory and read them in)
- Movies or Tv Shows are associated with each file
- 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