Matthieu Mezil
L’objet, c’est beau
Le développement orienté objet peut être apparenté, osons le mot, à une forme d’art. Cependant, avant de devenir un « artiste » de l’objet, il y a plusieurs notions à maîtriser. A travers cet article, nous allons en aborder quelques-unes.
Par Matthieu Mezil publié le 27/04/2009 à 07:10, lu 2235 fois, 10 pages
 9 | La généricité
Il reste encore un dernier point à traiter : la généricité. En effet, il est dommage de s’être ainsi concentré sur le traitement de chaîne de caractères alors que plusieurs classes pourraient être utilisées dans des contextes bien différents afin d’améliorer la réutilisabilité du code et sa maintenance.
Pour cela, nous allons rajouter un nouvel assembly : TransformBLL.
Dans cet assembly, nous allons trouver une interface ITransform. Cette interface a pour vocation de remplacer de IStringsTransform.
Mais pour la méthode Do, comment s’abstraire du fait que c’est une collection de string qu’on manipule ?
C’est justement à ça que sert la généricité. Nous allons rendre l’interface ITransform générique de façon à ce que le type réel soit défini à postériori, lors de l’utilisation.

public interface ITransform<TSource, TDestination>

{

    IEnumerable<TDestination> Do(IEnumerable<TSource> lines);

    void Cancel();

    bool Cancelled { get; }

}

Nous allons procéder de même avec StringsTransformBase, StringsNoTransform et IStringsTransformInstanciator :

public abstract class TransformBase<TSource, TDestination> : ITransform<TSource, TDestination>

{

    public abstract IEnumerable<TDestination> Do(IEnumerable<TSource> lines);

 

    public void Cancel()

    {

        Cancelled = true;

    }

 

    public bool Cancelled { get; private set; }

}

 

public class NoTransform<T> : TransformBase<T, T>

{

    public override IEnumerable<T> Do(IEnumerable<T> lines)

    {

        return lines;

    }

 

    public override string ToString()

    {

        return "";

    }

 

}

 

public interface ITransformInstanciator<TSource, TDestination>

{

    ITransform<TSource, TDestination> CreateInstance();

}

Ensuite, il faut supprimer les classes StringsTransformBase et StringsNoTransform ainsi que les interfaces IStringsTransform et IStringsTransformInstanciator et remplacer leur utilisation par TransformBase<string, string>, NoTransform<string, string> ITransform<string, string> et ITransformInstanciator<string, string>.

public abstract class PerStringTransformBase : TransformBase<string, string>

{

    public override IEnumerable<string> Do(IEnumerable<string> lines)

    {

        Init();

        foreach (string line in lines)

        {

            if (Cancelled)

                yield break;

            yield return DoAction(line);

        }

    }

 

    protected abstract string DoAction(string line);

 

    protected virtual void Init()

    {

    }

}

 

[Export(typeof(ITransformInstanciator<string, string>))]

public class InvertCharsPerStringTransformInstanciator : ITransformInstanciator<string, string>

{

    internal InvertCharsPerStringTransformInstanciator()

    {

    }

 

    public ITransform<string, string> CreateInstance()

    {

        return new InvertCharsPerStringTransform();

    }

 

    public override string ToString()

    {

        return TransformResources.InvertCharsPerStringTransform;

    }

}

 

[Export(typeof(ITransformInstanciator<string, string>))]

public class NumberPerStringTransformInstanciator : ITransformInstanciator<string, string>

{

    internal NumberPerStringTransformInstanciator()

    {

    }

 

    public ITransform<string, string> CreateInstance()

    {

        return new NumberPerStringTransform();

    }

 

    public override string ToString()

    {

        return TransformResources.NumberPerStringTransform;

    }

}

 

public class CompositeStringsTransform : TransformBase<string, string>

{

    private List<ITransform<string, string>> _transforms = new List<ITransform<string, string>>();

 

    public CompositeStringsTransform(IEnumerable<ITransformInstanciator<string, string>> transforms)

    {

        _transforms.AddRange(transforms.Select(s => s.CreateInstance()));

    }

    public CompositeStringsTransform(params ITransformInstanciator<string, string>[] transforms)

        : this((IEnumerable<ITransformInstanciator<string, string>>)transforms)

    {

    }

 

    public List<ITransform<string, string>> Transforms

    {

        get { return _transforms; }

    }

 

    public override IEnumerable<string> Do(IEnumerable<string> lines)

    {

        foreach (var transform in Transforms)

        {

            if (Cancelled)

                break;

            lines = transform.Do(lines);

        }

        return lines;

    }

 

    public override string ToString()

    {

        var sb = new StringBuilder();

        foreach (var transform in Transforms)

        {

            sb.Append(" & ");

            sb.Append(transform);

        }

        return sb.ToString().Substring(3);

    }

}

 

public static class FileTransform

{

    private static Dictionary<string, BackgroundWorker> _transformsRunning = new Dictionary<string, BackgroundWorker>();

    private static object _lockObject = new object();

 

    public static FileTransformInfo Transform(string inFilePath, string outFilePath,

        IEnumerable<ITransformInstanciator<string, string>> transforms, Action<string> showError,

        Func<string, bool> showErrorWithRetry, Action<bool, FileTransformInfo> transformDone)

    {

        ITransform<string, string> transform;

        if (transforms.Skip(1).Any())

            transform = new CompositeStringsTransform(transforms);

        else

        {

            var transformInstanciator = transforms.FirstOrDefault();

            transform = transformInstanciator == null ? new NoTransform<string>() :

                transformInstanciator.CreateInstance();

        }

        var result = new FileTransformInfo

        {

            Transform = transform,

            InFilePath = inFilePath,

            OutFilePath = outFilePath

        };

        if (!(File.Exists(inFilePath) || _transformsRunning.ContainsKey(inFilePath)))

            FileNotFoundShowError(inFilePath, showError);

        else if (!Directory.Exists(Path.GetDirectoryName(outFilePath)))

            DirectoryNotFoundShowError(outFilePath, showError);

        else

        {

            var bg = new BackgroundWorker();

            bg.DoWork += (s, e) =>

            {

                e.Result = false;

                while (true)

                {

                    try

                    {

                        FileUtil.WriteLines(outFilePath, transform.Do(FileUtil.GetLines(inFilePath)));

                        e.Result = !transform.Cancelled;

                    }

                    catch (FileNotFoundException)

                    {

                        FileNotFoundShowError(inFilePath, showError);

                    }

                    catch (DirectoryNotFoundException)

                    {

                        DirectoryNotFoundShowError(outFilePath, showError);

                    }

                    catch (IOException)

                    {

                        if (showErrorWithRetry(string.Format(ErrorResources.IOException, outFilePath)))

                            continue;

                    }

                    catch (Exception ex)

                    {

                        showError(string.Format(ErrorResources.FileNotFoundException,

                            string.Concat(ex.GetType(), " : ", ex.Message)));

                    }

                    break;

                }

                lock (_lockObject)

                {

                    _transformsRunning.Remove(outFilePath);

                }

            };

            bg.RunWorkerCompleted += (s, e) =>

            {

                TransformDone(transformDone, result, (bool)e.Result);

            };

 

            StartBGAndCache(result, bg, transformDone);

        }

        return result;

    }

 

    private static void TransformDone(Action<bool, FileTransformInfo> transformDone,

        FileTransformInfo fileTransformInfo, bool transformResult)

    {

        if (transformDone != null)

            transformDone(transformResult, fileTransformInfo);

    }

 

    private static void StartBGAndCache(FileTransformInfo fileTransformInfo, BackgroundWorker bg,

        Action<bool, FileTransformInfo> transformDone)

    {

        lock (_lockObject)

        {

            if (fileTransformInfo.Transform.Cancelled)

            {

                TransformDone(transformDone, fileTransformInfo, false);

                return;

            }

            var inFilePath = fileTransformInfo.InFilePath;

            var outFilePath = fileTransformInfo.OutFilePath;

            RunWorkerCompletedEventHandler runWorkerCompletedTryAgain = null;

            Action<BackgroundWorker> tryAgain = (bgTryAgain) =>

            {

                StartBGAndCache(fileTransformInfo, bg, transformDone);

                bgTryAgain.RunWorkerCompleted -= runWorkerCompletedTryAgain;

            };

            if (_transformsRunning.ContainsKey(inFilePath))

            {

                var bgTryAgain = _transformsRunning[inFilePath];

                runWorkerCompletedTryAgain = (s, e) => tryAgain(bgTryAgain);

                bgTryAgain.RunWorkerCompleted += runWorkerCompletedTryAgain;

            }

            else if (_transformsRunning.ContainsKey(outFilePath))

            {

                var bgTryAgain = _transformsRunning[outFilePath];

                runWorkerCompletedTryAgain = (s, e) => tryAgain(bgTryAgain);

                bgTryAgain.RunWorkerCompleted += runWorkerCompletedTryAgain;

            }

            else

            {

                _transformsRunning.Add(outFilePath, bg);

                bg.RunWorkerAsync();

            }

        }

    }

 

    private static void DirectoryNotFoundShowError(string directoryPath, Action<string> showError)

    {

        showError(string.Format(ErrorResources.DirectoryNotFoundException, Path.GetDirectoryName(directoryPath)));

    }

 

    private static void FileNotFoundShowError(string filePath, Action<string> showError)

    {

        showError(string.Format(ErrorResources.FileNotFoundException, filePath));

    }

}

 
» Démarrer une discussion
 
Discussion démarée par killscores le 28/04/2009 à 17:06, 3 commentaire(s).